harborly
Port identity resolution, spatial search, and analytical sea routing.
harborly is a typed Python SDK and CLI. It resolves port identities, finds nearby ports, reviews ambiguous CSV matches, and calculates approximate sea-route distances in nautical miles.
[!IMPORTANT] Routes are analytical approximations on a maritime graph. They are not for navigation, voyage planning, or safety-critical use.
Features
- Resolves registry IDs, UN/LOCODEs, and exact aliases.
- Searches port names with exact, prefix, fuzzy, country, and proximity filters.
- Matches CSV rows with human-review decision files.
- Calculates sea routes and process-parallel distance matrices.
- Streams large distance matrices without memory growth.
- Serves port routing over a local FastAPI HTTP server.
- Visualizes ports and routes in a terminal UI or standalone HTML maps.
- Provides
AsyncSeaRouterfor async/await interface toroute()androute_sequence(). - Restricts passages like Suez, Panama, Kiel, Baban, and Northwest via
PassageRestriction. - Computes multi-leg routes over a list of ports using
route_sequence(). - Calculates vessel speed and ETA directly on routes (
speed_knots,duration_hours,duration_days). - Exports route data to KML format (
--kml,--format kml). - Exports route data to GeoParquet format (
--format geoparquet).
Installation
Install the core CLI with routing:
uv tool install 'harborly[routing]'
Install with all optional extras (routing, api, map, tui, fast, analysis):
uv tool install --force 'harborly[routing,api,map,tui,fast,analysis]'
For local development:
uv sync --dev --all-extras
uv run harborly info
Quick start
harborly search Mersin --country TR
harborly show TRMER
harborly near 39.87 26.16 --country TR --limit 5
harborly route TRMER GRPIR --geojson route.geojson --html-map route.html
Example output:
origin: Mersin (WPI:44860)
destination: Piraievs (WPI:42230)
distance_nmi: 594.46
great_circle_nmi: 528.19
detour_ratio: 1.125
quality_flag: ok
engine: searoute 1.6.0 (astar, networkx)
Python SDK
from harborly import PortRegistry, SeaRouter
registry = PortRegistry.bundled()
origin = registry.resolve("TRMER")
destination = registry.resolve("GRPIR")
router = SeaRouter(cache_path=".cache/harborly/routes.sqlite3")
route = router.route(origin, destination)
matrix = router.distance_matrix([origin, destination], max_workers=4)
print(f"Distance: {route.distance_nmi} nmi, status: {route.quality_flag}")
import asyncio
from harborly import AsyncSeaRouter, PassageRestriction, PortRegistry
async def async_example():
registry = PortRegistry.bundled()
mersin = registry.resolve("TRMER")
singapore = registry.resolve("SGSIN")
jebel_ali = registry.resolve("AEJEA")
# Restrictions are set on the router; route_sequence takes the port list
router = AsyncSeaRouter(restrictions=[PassageRestriction.SUEZ])
seq = await router.route_sequence([mersin, jebel_ali, singapore], speed_knots=15.0)
print(f"Sequence distance: {seq.total_distance_nmi:.0f} nmi")
print(f"Duration: {seq.duration_days:.1f} days ({seq.duration_hours:.0f} hours)")
for i, leg in enumerate(seq.legs, 1):
print(f" Leg {i}: {leg.origin.name} → {leg.destination.name}: {leg.distance_nmi:.0f} nmi")
asyncio.run(async_example())
CLI Reference
| Command | Operation |
|---|---|
info |
Inspect active registry statistics |
search |
Search ports by name, country, or code |
show |
Resolve a single port identity |
near |
Find ports near coordinates |
route |
Calculate a sea route between two ports |
matrix |
Calculate a parallel distance matrix |
cache |
Inspect, prune, or clear SQLite route cache |
match |
Match CSV rows and generate review files |
export |
Export port data as CSV, GeoJSON, KML, or GeoParquet |
tui |
Launch terminal map interface |
serve |
Run FastAPI HTTP service |
data prepare |
Download and build local reference data |
Exit codes
| Exit code | Meaning |
|---|---|
0 |
Success |
1 |
Data verification failed |
2 |
Validation, resolution, or routing error |
130 |
Interrupted (Ctrl-C) |
HTTP Service
Run the local API server:
harborly serve --host 127.0.0.1 --port 8000
GET /v1/livez— liveness probe.GET /v1/readyz— readiness probe.GET /v1/route?origin=TRMER&destination=GRPIR— calculate route.
Documentation Index
- Getting Started
- Reference — library, CLI, HTTP, schemas, routing, data, exports, and TUI.
- Configuration
- Architecture
- Development
- Testing
- Contributing — conduct, security reporting, and compatibility policy.
License
MIT License. See LICENSE for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file harborly-1.1.4.tar.gz.
File metadata
- Download URL: harborly-1.1.4.tar.gz
- Upload date:
- Size: 2.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b737bf6e7db5ae404ccf72dfe1285188727722d44c330be05551326a240f136
|
|
| MD5 |
ba704e2ac884b07aa2d4dfb2bc995403
|
|
| BLAKE2b-256 |
a4f29b59b4a07d030ee1639b376c77d78ffc7ccebdb14aae7622a3b75df0dc87
|
Provenance
The following attestation bundles were made for harborly-1.1.4.tar.gz:
Publisher:
release.yml on frogiraffe/harborly
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
harborly-1.1.4.tar.gz -
Subject digest:
2b737bf6e7db5ae404ccf72dfe1285188727722d44c330be05551326a240f136 - Sigstore transparency entry: 2396467277
- Sigstore integration time:
-
Permalink:
frogiraffe/harborly@44d1ace279c131a3880cc623d72a2f72fe38f564 -
Branch / Tag:
refs/tags/v1.1.4 - Owner: https://github.com/frogiraffe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@44d1ace279c131a3880cc623d72a2f72fe38f564 -
Trigger Event:
push
-
Statement type:
File details
Details for the file harborly-1.1.4-py3-none-any.whl.
File metadata
- Download URL: harborly-1.1.4-py3-none-any.whl
- Upload date:
- Size: 1.9 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e3badb9f98118bbf7b11eb4d8593631e21f46c1773e45bb14b8ca275e9c9f64
|
|
| MD5 |
58c9143ab8194e47b0d484f3be009d83
|
|
| BLAKE2b-256 |
9d60bc5befa0a5a060073c82c0028febcc3367836dc94e72b24478da76759739
|
Provenance
The following attestation bundles were made for harborly-1.1.4-py3-none-any.whl:
Publisher:
release.yml on frogiraffe/harborly
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
harborly-1.1.4-py3-none-any.whl -
Subject digest:
3e3badb9f98118bbf7b11eb4d8593631e21f46c1773e45bb14b8ca275e9c9f64 - Sigstore transparency entry: 2396467580
- Sigstore integration time:
-
Permalink:
frogiraffe/harborly@44d1ace279c131a3880cc623d72a2f72fe38f564 -
Branch / Tag:
refs/tags/v1.1.4 - Owner: https://github.com/frogiraffe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@44d1ace279c131a3880cc623d72a2f72fe38f564 -
Trigger Event:
push
-
Statement type: