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.
Metadata
Release files for harborly 1.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| harborly-1.1.4.tar.gz | 2.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| harborly-1.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.9 MB
Release files / harborly-1.1.4.tar.gz
| Download URL | harborly-1.1.4.tar.gz |
|---|---|
| Size | 2.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2b737bf6e7db5ae404ccf72dfe1285188727722d44c330be05551326a240f136
|
|
BLAKE2b-256 checksum How to use checksums |
a4f29b59b4a07d030ee1639b376c77d78ffc7ccebdb14aae7622a3b75df0dc87
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency logRelease files / harborly-1.1.4-py3-none-any.whl
| Download URL | harborly-1.1.4-py3-none-any.whl |
|---|---|
| Size | 1.9 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e3badb9f98118bbf7b11eb4d8593631e21f46c1773e45bb14b8ca275e9c9f64
|
|
BLAKE2b-256 checksum How to use checksums |
9d60bc5befa0a5a060073c82c0028febcc3367836dc94e72b24478da76759739
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency log