Skip to main content

Maritime Routing

figure example

Global maritime routing in Python. Computes the navigable route between any two ports in the world — or between two arbitrary points given by latitude/longitude — going around continents and islands.

Motivation

Planning a sea crossing is, at its core, a shortest-path problem on a graph: the ocean is the navigable space and the land is the set of obstacles. But that space is a spherical surface covering the whole planet, with islands, capes and straits that must be rounded — there are no predefined streets or roads.

The goal of this package is to make that computation simple and programmable:

  • based on real data (World Port Index for the ports and Natural Earth 10m Land for the coastline), not on manual approximations;
  • with a classic, transparent algorithm (A*) over a rasterized ocean/land grid, using Haversine distance as the heuristic;
  • returning ready-to-use objects (the route, the GeoJSON, the CSV and the map figure), instead of just writing files.

It is an educational and extensible foundation for maritime routing — not a real commercial routing system (which involves currents, winds, draft, EEZ, canals and economic factors), but designed to grow in that direction.

What the package does

  • Resolves ports by name (with country disambiguation) or accepts direct coordinates for origin and destination.
  • Builds a global navigable grid (1 = ocean, 0 = land) from the coastline, with per-resolution caching.
  • Runs A* with 8 neighbors, real-distance step cost and a Haversine heuristic (consistent, therefore optimal with weight 1.0).
  • Returns the sequence of route coordinates, the total distance, and exports GeoJSON/CSV and a map as in-memory objects.

Installation

With Poetry:

cd maritime-routing
poetry install
poetry run maritime-routing-fetch      # downloads the coastline and copies the ports.csv seed

Or, without Poetry, with pip:

python -m venv .venv && source .venv/bin/activate
pip install -e .
python -m maritime_routing.fetch

Dependencies and their minimum versions are declared in pyproject.toml — installation resolves everything automatically. Modern wheels already bundle GDAL/PROJ/GEOS, with no need for system libraries. Visualization uses plotly (with kaleido to export PNG), without cartopy.

Library usage (returns objects)

from maritime_routing import (
    compute_route, compute_route_by_coords,
    route_to_geojson, route_to_geojson_str, route_to_csv,
    plot_route, save_geojson, save_csv, save_figure,
    fetch_data,
)

Before computing the first route, provision the data with a single call — the coastline is downloaded and the ports.csv seed is copied into the external data/ folder:

fetch_data()                 # downloads coastline + copies the ports.csv seed
# fetch_data(include_ports=True)  # also shows the WPI notice (optional)

By port names

res = compute_route("Santos", "Shanghai",
                    grid_resolution=0.1, heuristic_weight=1.15)
res.distance_km          # float: total distance in km
res.path                 # list[(lat, lon), ...]
res.cells                # number of points
res.stats                # {'iterations', 'expanded', 'reason'}

geojson = route_to_geojson(res)     # dict (FeatureCollection)
csv_text  = route_to_csv(res)       # str
fig       = plot_route(res)         # plotly.graph_objects.Figure

# Write to disk (optional):
# save_geojson(geojson, "route.geojson")
# save_csv(csv_text, "route.csv")
# save_figure(fig, "route.png")

By origin and destination latitude/longitude

res = compute_route_by_coords(
    -23.9608, -46.3331,    # origin (lat, lon) — Santos
     31.2304, 121.4737,    # destination (lat, lon) — Shanghai
    grid_resolution=0.1, heuristic_weight=1.15,
)

Or directly through the MaritimeRouter class:

from maritime_routing import MaritimeRouter
router = MaritimeRouter(grid_resolution=0.1, heuristic_weight=1.15)
res = router.route("Santos", "Shanghai")
res2 = router.route_by_coords(-23.96, -46.33, 31.23, 121.47)

CLI usage

# By port names
maritime-routing --from "Santos" --to "Shanghai"

# Disambiguating the country
maritime-routing --from "New York" --from-country "United States" --to "Rotterdam"

# By coordinates (lat/lon of origin and destination)
maritime-routing --from-lat -23.96 --from-lon -46.33 \
                 --to-lat   31.23  --to-lon   121.47

# Coarser/faster, with a heuristic weight, writing to disk
maritime-routing --from "Santos" --to "Shanghai" \
    --resolution 0.25 --heuristic-weight 1.15 --save

Flags: --resolution, --rebuild-grid, --max-iterations, --heuristic-weight, --save (writes GeoJSON+CSV to data/routes/ and PNG to data/maps/), --no-map, --print-geojson, --print-csv, --print-points.

Also: python -m maritime_routing ... and python -m maritime_routing.fetch.

Structure

maritime-routing/
├── pyproject.toml                  # Poetry + console scripts
├── README.md
├── maritime_routing/               # the package
│   ├── __init__.py                  # public API
│   ├── __main__.py                  # python -m maritime_routing
│   ├── cli.py                       # CLI
│   ├── fetch.py                     # data download
│   ├── config.py
│   ├── distance.py                  # haversine / route_distance
│   ├── ports.py                     # PortDatabase (WPI)
│   ├── coastline.py                 # CoastlineMap (shapefile)
│   ├── raster.py                    # OceanGrid (navigable grid)
│   ├── astar.py                     # AStarRouter (A*)
│   ├── router.py                    # MaritimeRouter, RouteResult, compute_route[_by_coords]
│   ├── geojson.py                   # returns dict/str (+ save_*)
│   ├── visualize.py                 # returns Figure (+ save_figure)
│   └── data/ports.csv               # embedded seed (~70 ports, read-only)
└── data/                            # EXTERNAL to the package — user folder (generated)
    ├── ne_10m_land.*                #   coastline (downloaded)
    ├── ocean_grid_<res>.npy         #   grid caches (one per resolution)
    ├── ports.csv                    #   active (copied seed or user WPI)
    ├── routes/                      #   route GeoJSON + CSV (--save)
    └── maps/                        #   map PNG (--save)

The data/ folder is created in the current working directory (or in $MARITIME_ROUTING_DATA). Nothing is written inside the installed package — only the ports.csv seed is embedded; the active file is external.

Data

  • The active ports.csv (with Santos, Shanghai, etc.) lives in data/ports.csv (external). fetch_data() copies the seed (~70 ports) there on first run, without overwriting a CSV you may have placed yourself. For the full database (WPI), replace the file with your own CSV.
  • The coastline (ne_10m_land.shp) is downloaded into data/ (a folder external to the package; or $MARITIME_ROUTING_DATA) with a single call: fetch_data() from the API, or maritime-routing-fetch from the CLI.
  • The grid is cached as data/ocean_grid_<resolution>.npy (one file per resolution), so switching resolutions does not rebuild the grid.
  • With --save, GeoJSON/CSV go to data/routes/ and the PNG map to data/maps/. Everything lives in the same user data/ folder.

Configuration (config.py)

Parameter Default Description
GRID_RESOLUTION 0.05 Cell size in degrees
MAX_ITERATIONS 20000000 A* node expansion limit
NEIGHBOR_MODE 8 4 (rook) or 8 (with diagonals)
HEURISTIC_WEIGHT 1.0 Heuristic weight (1.0=optimal; >1=faster)
SNAP_SEARCH_RADIUS_DEG 2.0 Radius to "snap" ports/points to the coastline

The MARITIME_ROUTING_DATA environment variable overrides the writable data directory (coastline, caches, routes and maps). The default is the data/ folder in the current working directory.

Performance

  • Global grid at 0.05°: 3600 × 7200 ≈ 25.9M cells (uint8, ~26 MB); at 0.1°: 1800 × 3600; at 0.25°: 720 × 1440.
  • Rasterization via rasterio.features.rasterize (vectorized) + per-resolution caching.
  • A* with heapq (lazy deletion) and flat NumPy arrays for g_score/came_from; longitudinal wrap-around (trans-Pacific routes).
  • The Haversine heuristic is consistent (triangle inequality) → optimal A* with weight 1.0.

Practical tips (global routes)

Intercontinental routes require crossing entire ocean basins; at 0.05° A* may need millions of expansions:

# Fast (seconds): coarser resolution
maritime-routing --from "Santos" --to "Shanghai" --resolution 0.25 --rebuild-grid

# Balanced
maritime-routing --from "Santos" --to "Shanghai" --resolution 0.1 --heuristic-weight 1.15

# High resolution (several minutes): weight > 1 focuses the search
maritime-routing --from "Santos" --to "Shanghai" --resolution 0.05 --heuristic-weight 1.15

A --heuristic-weight > 1 greatly reduces expansions at the cost of a slightly suboptimal route (< 1 %). If you hit "ITERATION LIMIT reached", increase --max-iterations or the weight, or use a coarser resolution.

Limitations

  • The route is the shortest path on the grid (optimizing Haversine distance), not a real commercial route (currents, winds, draft, EEZ, stopovers…).
  • Suez/Panama canals are not modeled — A* goes around Africa and South America. (See the roadmap.)
  • Points on land are automatically "snapped" to the nearest ocean cell.
  • The lower the resolution, the faster the run and the coarser the route.

Roadmap (architecture ready for extensions)

  • Suez/Panama canals: mark the canal cells as navigable (and/or with a lower weight) in raster.py/config.py.
  • Depth restriction: integrate bathymetry (GEBCO) and make cells shallower than the draft non-navigable in OceanGrid.create_grid() (multiply masks).
  • Real commercial routes / AIS: per-cell weights (cost ≠ distance) derived from traffic density in astar._step_cost().
  • Economic weights: expose a weight array W[row,col] and multiply the step cost by it.

AI assistance

Parts of this project were developed with the assistance of an AI assistant (Claude).

The AI-assisted development process contributed to architectural improvements, code refinement, documentation updates, usability enhancements, and implementation optimizations.

All generated suggestions and modifications were carefully reviewed, tested, and verified to maintain the reliability and integrity of the project.

License

This project is licensed under the MIT License — see the LICENSE file for details.

Copyright (c) 2026 Junior Dantas

Data Sources

This project uses the following open datasets:

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

maritime_routing-0.1.1.tar.gz (31.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

maritime_routing-0.1.1-py3-none-any.whl (35.0 kB view details)

Uploaded Python 3

File details

Details for the file maritime_routing-0.1.1.tar.gz.

File metadata

  • Download URL: maritime_routing-0.1.1.tar.gz
  • Upload date:
  • Size: 31.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for maritime_routing-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4d867b8f3fb137d0e8739e9d6c95d50ad862c1aa879400e7cb97592f5260f890
MD5 355abc86f607b53ba495e59007cf62be
BLAKE2b-256 0f269fffca802f9703e9d38bfad69a082a5c2a6ea5b911e9f1a68a1e7c26db08

See more details on using hashes here.

File details

Details for the file maritime_routing-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for maritime_routing-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c495a4ff5a44b0b9cad9355d2c7acc86272428bc34bb3dbc540161785fcaeb93
MD5 a2bb274b713c51a0c18dd43b34b31256
BLAKE2b-256 cf0056b844c07dbab9034733e05cf75ced7fd5137abab5806f0d9eddc9412e8a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page