maritime-routing 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Junior Dantas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,332 @@
1
+ Metadata-Version: 2.4
2
+ Name: maritime-routing
3
+ Version: 0.1.0
4
+ Summary: A computational maritime navigation system that transforms global geographic data into a navigable graph, enabling route calculation between ports using GIS, ocean rasterization, and optimized pathfinding algorithms.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Junior Dantas
8
+ Author-email: juniordante01@gmail.com
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: geopandas (>=1.1.4)
18
+ Requires-Dist: kaleido (>=1.3.0)
19
+ Requires-Dist: numpy (>=2.5.1)
20
+ Requires-Dist: pandas (>=3.0.5)
21
+ Requires-Dist: plotly (>=6.9.0)
22
+ Requires-Dist: pyogrio (>=0.13.0)
23
+ Requires-Dist: pyproj (>=3.7.2)
24
+ Requires-Dist: rasterio (>=1.5.0)
25
+ Requires-Dist: scipy (>=1.18.0)
26
+ Requires-Dist: shapely (>=2.1.2)
27
+ Description-Content-Type: text/markdown
28
+
29
+ # maritime-routing
30
+
31
+ Global maritime routing in Python. Computes the navigable route between any
32
+ two ports in the world — or between two arbitrary points given by
33
+ latitude/longitude — going around continents and islands.
34
+
35
+ ## Motivation
36
+
37
+ Planning a sea crossing is, at its core, a shortest-path problem on a graph:
38
+ the ocean is the navigable space and the land is the set of obstacles. But
39
+ that space is a spherical surface covering the whole planet, with islands,
40
+ capes and straits that must be rounded — there are no predefined streets or
41
+ roads.
42
+
43
+ The goal of this package is to make that computation simple and programmable:
44
+
45
+ - based on **real data** (World Port Index for the ports and Natural Earth
46
+ 10m Land for the coastline), not on manual approximations;
47
+ - with a classic, transparent algorithm (**A\***) over a rasterized
48
+ ocean/land grid, using Haversine distance as the heuristic;
49
+ - returning ready-to-use **objects** (the route, the GeoJSON, the CSV and the
50
+ map figure), instead of just writing files.
51
+
52
+ It is an educational and extensible foundation for maritime routing — not a
53
+ real commercial routing system (which involves currents, winds, draft, EEZ,
54
+ canals and economic factors), but designed to grow in that direction.
55
+
56
+ ## What the package does
57
+
58
+ - Resolves ports by name (with country disambiguation) or accepts direct
59
+ coordinates for origin and destination.
60
+ - Builds a global navigable grid (1 = ocean, 0 = land) from the coastline,
61
+ with per-resolution caching.
62
+ - Runs A\* with 8 neighbors, real-distance step cost and a Haversine
63
+ heuristic (consistent, therefore optimal with weight 1.0).
64
+ - Returns the sequence of route coordinates, the total distance, and exports
65
+ GeoJSON/CSV and a map as in-memory **objects**.
66
+
67
+ ## Installation
68
+
69
+ With Poetry:
70
+
71
+ ```bash
72
+ cd maritime-routing
73
+ poetry install
74
+ poetry run maritime-routing-fetch # downloads the coastline and copies the ports.csv seed
75
+ ```
76
+
77
+ Or, without Poetry, with pip:
78
+
79
+ ```bash
80
+ python -m venv .venv && source .venv/bin/activate
81
+ pip install -e .
82
+ python -m maritime_routing.fetch
83
+ ```
84
+
85
+ > Dependencies and their minimum versions are declared in `pyproject.toml`
86
+ > — installation resolves everything automatically. Modern wheels already
87
+ > bundle GDAL/PROJ/GEOS, with no need for system libraries. Visualization
88
+ > uses plotly (with kaleido to export PNG), without cartopy.
89
+
90
+ ## Library usage (returns objects)
91
+
92
+ ```python
93
+ from maritime_routing import (
94
+ compute_route, compute_route_by_coords,
95
+ route_to_geojson, route_to_geojson_str, route_to_csv,
96
+ plot_route, save_geojson, save_csv, save_figure,
97
+ fetch_data,
98
+ )
99
+ ```
100
+
101
+ Before computing the first route, provision the data with a single call —
102
+ the coastline is downloaded and the `ports.csv` seed is copied into the
103
+ external `data/` folder:
104
+
105
+ ```python
106
+ fetch_data() # downloads coastline + copies the ports.csv seed
107
+ # fetch_data(include_ports=True) # also shows the WPI notice (optional)
108
+ ```
109
+
110
+ ### By port names
111
+
112
+ ```python
113
+ res = compute_route("Santos", "Shanghai",
114
+ grid_resolution=0.1, heuristic_weight=1.15)
115
+ res.distance_km # float: total distance in km
116
+ res.path # list[(lat, lon), ...]
117
+ res.cells # number of points
118
+ res.stats # {'iterations', 'expanded', 'reason'}
119
+
120
+ geojson = route_to_geojson(res) # dict (FeatureCollection)
121
+ csv_text = route_to_csv(res) # str
122
+ fig = plot_route(res) # plotly.graph_objects.Figure
123
+
124
+ # Write to disk (optional):
125
+ # save_geojson(geojson, "route.geojson")
126
+ # save_csv(csv_text, "route.csv")
127
+ # save_figure(fig, "route.png")
128
+ ```
129
+
130
+ ### By origin and destination latitude/longitude
131
+
132
+ ```python
133
+ res = compute_route_by_coords(
134
+ -23.9608, -46.3331, # origin (lat, lon) — Santos
135
+ 31.2304, 121.4737, # destination (lat, lon) — Shanghai
136
+ grid_resolution=0.1, heuristic_weight=1.15,
137
+ )
138
+ ```
139
+
140
+ Or directly through the `MaritimeRouter` class:
141
+
142
+ ```python
143
+ from maritime_routing import MaritimeRouter
144
+ router = MaritimeRouter(grid_resolution=0.1, heuristic_weight=1.15)
145
+ res = router.route("Santos", "Shanghai")
146
+ res2 = router.route_by_coords(-23.96, -46.33, 31.23, 121.47)
147
+ ```
148
+
149
+ ## CLI usage
150
+
151
+ ```bash
152
+ # By port names
153
+ maritime-routing --from "Santos" --to "Shanghai"
154
+
155
+ # Disambiguating the country
156
+ maritime-routing --from "New York" --from-country "United States" --to "Rotterdam"
157
+
158
+ # By coordinates (lat/lon of origin and destination)
159
+ maritime-routing --from-lat -23.96 --from-lon -46.33 \
160
+ --to-lat 31.23 --to-lon 121.47
161
+
162
+ # Coarser/faster, with a heuristic weight, writing to disk
163
+ maritime-routing --from "Santos" --to "Shanghai" \
164
+ --resolution 0.25 --heuristic-weight 1.15 --save
165
+ ```
166
+
167
+ Flags: `--resolution`, `--rebuild-grid`, `--max-iterations`,
168
+ `--heuristic-weight`, `--save` (writes GeoJSON+CSV to `data/routes/` and PNG
169
+ to `data/maps/`), `--no-map`, `--print-geojson`, `--print-csv`,
170
+ `--print-points`.
171
+
172
+ Also: `python -m maritime_routing ...` and `python -m maritime_routing.fetch`.
173
+
174
+ ## Structure
175
+
176
+ ```
177
+ maritime-routing/
178
+ ├── pyproject.toml # Poetry + console scripts
179
+ ├── README.md
180
+ ├── maritime_routing/ # the package
181
+ │ ├── __init__.py # public API
182
+ │ ├── __main__.py # python -m maritime_routing
183
+ │ ├── cli.py # CLI
184
+ │ ├── fetch.py # data download
185
+ │ ├── config.py
186
+ │ ├── distance.py # haversine / route_distance
187
+ │ ├── ports.py # PortDatabase (WPI)
188
+ │ ├── coastline.py # CoastlineMap (shapefile)
189
+ │ ├── raster.py # OceanGrid (navigable grid)
190
+ │ ├── astar.py # AStarRouter (A*)
191
+ │ ├── router.py # MaritimeRouter, RouteResult, compute_route[_by_coords]
192
+ │ ├── geojson.py # returns dict/str (+ save_*)
193
+ │ ├── visualize.py # returns Figure (+ save_figure)
194
+ │ └── data/ports.csv # embedded seed (~70 ports, read-only)
195
+ └── data/ # EXTERNAL to the package — user folder (generated)
196
+ ├── ne_10m_land.* # coastline (downloaded)
197
+ ├── ocean_grid_<res>.npy # grid caches (one per resolution)
198
+ ├── ports.csv # active (copied seed or user WPI)
199
+ ├── routes/ # route GeoJSON + CSV (--save)
200
+ └── maps/ # map PNG (--save)
201
+ ```
202
+
203
+ > The `data/` folder is created in the current working directory (or in
204
+ > `$MARITIME_ROUTING_DATA`). Nothing is written inside the installed
205
+ > package — only the `ports.csv` **seed** is embedded; the active file is
206
+ > external.
207
+
208
+ ## Data
209
+
210
+ - The active `ports.csv` (with Santos, Shanghai, etc.) lives in
211
+ `data/ports.csv` (external). `fetch_data()` copies the seed (~70 ports)
212
+ there on first run, **without overwriting** a CSV you may have placed
213
+ yourself. For the full database (WPI), replace the file with your own CSV.
214
+ - The coastline (`ne_10m_land.shp`) is downloaded into `data/` (a folder
215
+ external to the package; or `$MARITIME_ROUTING_DATA`) with a **single
216
+ call**: `fetch_data()` from the API, or `maritime-routing-fetch` from the
217
+ CLI.
218
+ - The grid is cached as `data/ocean_grid_<resolution>.npy` (one file per
219
+ resolution), so switching resolutions does not rebuild the grid.
220
+ - With `--save`, GeoJSON/CSV go to `data/routes/` and the PNG map to
221
+ `data/maps/`. Everything lives in the same user `data/` folder.
222
+
223
+ ## Configuration (`config.py`)
224
+
225
+ | Parameter | Default | Description |
226
+ |-------------------------|------------|---------------------------------------------------|
227
+ | `GRID_RESOLUTION` | `0.05` | Cell size in degrees |
228
+ | `MAX_ITERATIONS` | `20000000` | A\* node expansion limit |
229
+ | `NEIGHBOR_MODE` | `8` | 4 (rook) or 8 (with diagonals) |
230
+ | `HEURISTIC_WEIGHT` | `1.0` | Heuristic weight (1.0=optimal; >1=faster) |
231
+ | `SNAP_SEARCH_RADIUS_DEG`| `2.0` | Radius to "snap" ports/points to the coastline |
232
+
233
+ The `MARITIME_ROUTING_DATA` environment variable overrides the writable
234
+ data directory (coastline, caches, routes and maps). The default is the
235
+ `data/` folder in the current working directory.
236
+
237
+ ## Performance
238
+
239
+ - Global grid at `0.05°`: `3600 × 7200 ≈ 25.9M` cells (`uint8`, ~26 MB);
240
+ at `0.1°`: `1800 × 3600`; at `0.25°`: `720 × 1440`.
241
+ - Rasterization via `rasterio.features.rasterize` (vectorized) + per-resolution
242
+ caching.
243
+ - A\* with `heapq` (lazy deletion) and flat NumPy arrays for
244
+ `g_score`/`came_from`; longitudinal wrap-around (trans-Pacific routes).
245
+ - The Haversine heuristic is consistent (triangle inequality) → optimal A*
246
+ with weight 1.0.
247
+
248
+ ### Practical tips (global routes)
249
+
250
+ Intercontinental routes require crossing entire ocean basins; at `0.05°`
251
+ A\* may need millions of expansions:
252
+
253
+ ```bash
254
+ # Fast (seconds): coarser resolution
255
+ maritime-routing --from "Santos" --to "Shanghai" --resolution 0.25 --rebuild-grid
256
+
257
+ # Balanced
258
+ maritime-routing --from "Santos" --to "Shanghai" --resolution 0.1 --heuristic-weight 1.15
259
+
260
+ # High resolution (several minutes): weight > 1 focuses the search
261
+ maritime-routing --from "Santos" --to "Shanghai" --resolution 0.05 --heuristic-weight 1.15
262
+ ```
263
+
264
+ A `--heuristic-weight` > 1 greatly reduces expansions at the cost of a
265
+ slightly suboptimal route (< 1 %). If you hit "ITERATION LIMIT reached",
266
+ increase `--max-iterations` or the weight, or use a coarser resolution.
267
+
268
+ ## Limitations
269
+
270
+ - The route is the **shortest path on the grid** (optimizing Haversine
271
+ distance), not a real commercial route (currents, winds, draft, EEZ,
272
+ stopovers…).
273
+ - Suez/Panama canals are not modeled — A\* goes around Africa and South
274
+ America. (See the roadmap.)
275
+ - Points on land are automatically "snapped" to the nearest ocean cell.
276
+ - The lower the resolution, the faster the run and the coarser the route.
277
+
278
+ ## Roadmap (architecture ready for extensions)
279
+
280
+ - **Suez/Panama canals:** mark the canal cells as navigable (and/or with a
281
+ lower weight) in `raster.py`/`config.py`.
282
+ - **Depth restriction:** integrate bathymetry (GEBCO) and make cells
283
+ shallower than the draft non-navigable in `OceanGrid.create_grid()`
284
+ (multiply masks).
285
+ - **Real commercial routes / AIS:** per-cell weights (cost ≠ distance)
286
+ derived from traffic density in `astar._step_cost()`.
287
+ - **Economic weights:** expose a weight array `W[row,col]` and multiply the
288
+ step cost by it.
289
+
290
+ ## AI assistance
291
+
292
+ Parts of this project were developed with the assistance of an AI assistant (Claude).
293
+
294
+ The AI-assisted development process contributed to architectural improvements, code refinement,
295
+ documentation updates, usability enhancements, and implementation optimizations.
296
+
297
+ All generated suggestions and modifications were carefully reviewed, tested,
298
+ and verified to maintain the reliability and integrity of the project.
299
+
300
+
301
+ ## License
302
+
303
+ This project is licensed under the **MIT License** — see the
304
+ [LICENSE](LICENSE) file for details.
305
+
306
+ Copyright (c) 2026 Junior Dantas
307
+
308
+ ## Data Sources
309
+
310
+ This project uses the following open datasets:
311
+
312
+ - **World Port Index (WPI)**
313
+ National Geospatial-Intelligence Agency (NGA)
314
+ License: Public Domain
315
+ Source: https://msi.nga.mil/Publications/WPI
316
+
317
+ - **Natural Earth Data**
318
+ Global geographic datasets for coastline and land boundaries
319
+ License: Public Domain
320
+ Source: https://www.naturalearthdata.com/
321
+
322
+ - **Global Self-consistent Hierarchical High-resolution Geography Database (GSHHG)**
323
+ High-resolution shoreline and coastline data
324
+ License: GNU Lesser General Public License (LGPL)
325
+ Source: https://www.soest.hawaii.edu/pwessel/gshhg/
326
+
327
+ - **OpenStreetMap (OSM) data (when applicable)**
328
+ Geographic information used for map-related features
329
+ License: Open Database License (ODbL)
330
+ Source: https://www.openstreetmap.org/
331
+
332
+
@@ -0,0 +1,303 @@
1
+ # maritime-routing
2
+
3
+ Global maritime routing in Python. Computes the navigable route between any
4
+ two ports in the world — or between two arbitrary points given by
5
+ latitude/longitude — going around continents and islands.
6
+
7
+ ## Motivation
8
+
9
+ Planning a sea crossing is, at its core, a shortest-path problem on a graph:
10
+ the ocean is the navigable space and the land is the set of obstacles. But
11
+ that space is a spherical surface covering the whole planet, with islands,
12
+ capes and straits that must be rounded — there are no predefined streets or
13
+ roads.
14
+
15
+ The goal of this package is to make that computation simple and programmable:
16
+
17
+ - based on **real data** (World Port Index for the ports and Natural Earth
18
+ 10m Land for the coastline), not on manual approximations;
19
+ - with a classic, transparent algorithm (**A\***) over a rasterized
20
+ ocean/land grid, using Haversine distance as the heuristic;
21
+ - returning ready-to-use **objects** (the route, the GeoJSON, the CSV and the
22
+ map figure), instead of just writing files.
23
+
24
+ It is an educational and extensible foundation for maritime routing — not a
25
+ real commercial routing system (which involves currents, winds, draft, EEZ,
26
+ canals and economic factors), but designed to grow in that direction.
27
+
28
+ ## What the package does
29
+
30
+ - Resolves ports by name (with country disambiguation) or accepts direct
31
+ coordinates for origin and destination.
32
+ - Builds a global navigable grid (1 = ocean, 0 = land) from the coastline,
33
+ with per-resolution caching.
34
+ - Runs A\* with 8 neighbors, real-distance step cost and a Haversine
35
+ heuristic (consistent, therefore optimal with weight 1.0).
36
+ - Returns the sequence of route coordinates, the total distance, and exports
37
+ GeoJSON/CSV and a map as in-memory **objects**.
38
+
39
+ ## Installation
40
+
41
+ With Poetry:
42
+
43
+ ```bash
44
+ cd maritime-routing
45
+ poetry install
46
+ poetry run maritime-routing-fetch # downloads the coastline and copies the ports.csv seed
47
+ ```
48
+
49
+ Or, without Poetry, with pip:
50
+
51
+ ```bash
52
+ python -m venv .venv && source .venv/bin/activate
53
+ pip install -e .
54
+ python -m maritime_routing.fetch
55
+ ```
56
+
57
+ > Dependencies and their minimum versions are declared in `pyproject.toml`
58
+ > — installation resolves everything automatically. Modern wheels already
59
+ > bundle GDAL/PROJ/GEOS, with no need for system libraries. Visualization
60
+ > uses plotly (with kaleido to export PNG), without cartopy.
61
+
62
+ ## Library usage (returns objects)
63
+
64
+ ```python
65
+ from maritime_routing import (
66
+ compute_route, compute_route_by_coords,
67
+ route_to_geojson, route_to_geojson_str, route_to_csv,
68
+ plot_route, save_geojson, save_csv, save_figure,
69
+ fetch_data,
70
+ )
71
+ ```
72
+
73
+ Before computing the first route, provision the data with a single call —
74
+ the coastline is downloaded and the `ports.csv` seed is copied into the
75
+ external `data/` folder:
76
+
77
+ ```python
78
+ fetch_data() # downloads coastline + copies the ports.csv seed
79
+ # fetch_data(include_ports=True) # also shows the WPI notice (optional)
80
+ ```
81
+
82
+ ### By port names
83
+
84
+ ```python
85
+ res = compute_route("Santos", "Shanghai",
86
+ grid_resolution=0.1, heuristic_weight=1.15)
87
+ res.distance_km # float: total distance in km
88
+ res.path # list[(lat, lon), ...]
89
+ res.cells # number of points
90
+ res.stats # {'iterations', 'expanded', 'reason'}
91
+
92
+ geojson = route_to_geojson(res) # dict (FeatureCollection)
93
+ csv_text = route_to_csv(res) # str
94
+ fig = plot_route(res) # plotly.graph_objects.Figure
95
+
96
+ # Write to disk (optional):
97
+ # save_geojson(geojson, "route.geojson")
98
+ # save_csv(csv_text, "route.csv")
99
+ # save_figure(fig, "route.png")
100
+ ```
101
+
102
+ ### By origin and destination latitude/longitude
103
+
104
+ ```python
105
+ res = compute_route_by_coords(
106
+ -23.9608, -46.3331, # origin (lat, lon) — Santos
107
+ 31.2304, 121.4737, # destination (lat, lon) — Shanghai
108
+ grid_resolution=0.1, heuristic_weight=1.15,
109
+ )
110
+ ```
111
+
112
+ Or directly through the `MaritimeRouter` class:
113
+
114
+ ```python
115
+ from maritime_routing import MaritimeRouter
116
+ router = MaritimeRouter(grid_resolution=0.1, heuristic_weight=1.15)
117
+ res = router.route("Santos", "Shanghai")
118
+ res2 = router.route_by_coords(-23.96, -46.33, 31.23, 121.47)
119
+ ```
120
+
121
+ ## CLI usage
122
+
123
+ ```bash
124
+ # By port names
125
+ maritime-routing --from "Santos" --to "Shanghai"
126
+
127
+ # Disambiguating the country
128
+ maritime-routing --from "New York" --from-country "United States" --to "Rotterdam"
129
+
130
+ # By coordinates (lat/lon of origin and destination)
131
+ maritime-routing --from-lat -23.96 --from-lon -46.33 \
132
+ --to-lat 31.23 --to-lon 121.47
133
+
134
+ # Coarser/faster, with a heuristic weight, writing to disk
135
+ maritime-routing --from "Santos" --to "Shanghai" \
136
+ --resolution 0.25 --heuristic-weight 1.15 --save
137
+ ```
138
+
139
+ Flags: `--resolution`, `--rebuild-grid`, `--max-iterations`,
140
+ `--heuristic-weight`, `--save` (writes GeoJSON+CSV to `data/routes/` and PNG
141
+ to `data/maps/`), `--no-map`, `--print-geojson`, `--print-csv`,
142
+ `--print-points`.
143
+
144
+ Also: `python -m maritime_routing ...` and `python -m maritime_routing.fetch`.
145
+
146
+ ## Structure
147
+
148
+ ```
149
+ maritime-routing/
150
+ ├── pyproject.toml # Poetry + console scripts
151
+ ├── README.md
152
+ ├── maritime_routing/ # the package
153
+ │ ├── __init__.py # public API
154
+ │ ├── __main__.py # python -m maritime_routing
155
+ │ ├── cli.py # CLI
156
+ │ ├── fetch.py # data download
157
+ │ ├── config.py
158
+ │ ├── distance.py # haversine / route_distance
159
+ │ ├── ports.py # PortDatabase (WPI)
160
+ │ ├── coastline.py # CoastlineMap (shapefile)
161
+ │ ├── raster.py # OceanGrid (navigable grid)
162
+ │ ├── astar.py # AStarRouter (A*)
163
+ │ ├── router.py # MaritimeRouter, RouteResult, compute_route[_by_coords]
164
+ │ ├── geojson.py # returns dict/str (+ save_*)
165
+ │ ├── visualize.py # returns Figure (+ save_figure)
166
+ │ └── data/ports.csv # embedded seed (~70 ports, read-only)
167
+ └── data/ # EXTERNAL to the package — user folder (generated)
168
+ ├── ne_10m_land.* # coastline (downloaded)
169
+ ├── ocean_grid_<res>.npy # grid caches (one per resolution)
170
+ ├── ports.csv # active (copied seed or user WPI)
171
+ ├── routes/ # route GeoJSON + CSV (--save)
172
+ └── maps/ # map PNG (--save)
173
+ ```
174
+
175
+ > The `data/` folder is created in the current working directory (or in
176
+ > `$MARITIME_ROUTING_DATA`). Nothing is written inside the installed
177
+ > package — only the `ports.csv` **seed** is embedded; the active file is
178
+ > external.
179
+
180
+ ## Data
181
+
182
+ - The active `ports.csv` (with Santos, Shanghai, etc.) lives in
183
+ `data/ports.csv` (external). `fetch_data()` copies the seed (~70 ports)
184
+ there on first run, **without overwriting** a CSV you may have placed
185
+ yourself. For the full database (WPI), replace the file with your own CSV.
186
+ - The coastline (`ne_10m_land.shp`) is downloaded into `data/` (a folder
187
+ external to the package; or `$MARITIME_ROUTING_DATA`) with a **single
188
+ call**: `fetch_data()` from the API, or `maritime-routing-fetch` from the
189
+ CLI.
190
+ - The grid is cached as `data/ocean_grid_<resolution>.npy` (one file per
191
+ resolution), so switching resolutions does not rebuild the grid.
192
+ - With `--save`, GeoJSON/CSV go to `data/routes/` and the PNG map to
193
+ `data/maps/`. Everything lives in the same user `data/` folder.
194
+
195
+ ## Configuration (`config.py`)
196
+
197
+ | Parameter | Default | Description |
198
+ |-------------------------|------------|---------------------------------------------------|
199
+ | `GRID_RESOLUTION` | `0.05` | Cell size in degrees |
200
+ | `MAX_ITERATIONS` | `20000000` | A\* node expansion limit |
201
+ | `NEIGHBOR_MODE` | `8` | 4 (rook) or 8 (with diagonals) |
202
+ | `HEURISTIC_WEIGHT` | `1.0` | Heuristic weight (1.0=optimal; >1=faster) |
203
+ | `SNAP_SEARCH_RADIUS_DEG`| `2.0` | Radius to "snap" ports/points to the coastline |
204
+
205
+ The `MARITIME_ROUTING_DATA` environment variable overrides the writable
206
+ data directory (coastline, caches, routes and maps). The default is the
207
+ `data/` folder in the current working directory.
208
+
209
+ ## Performance
210
+
211
+ - Global grid at `0.05°`: `3600 × 7200 ≈ 25.9M` cells (`uint8`, ~26 MB);
212
+ at `0.1°`: `1800 × 3600`; at `0.25°`: `720 × 1440`.
213
+ - Rasterization via `rasterio.features.rasterize` (vectorized) + per-resolution
214
+ caching.
215
+ - A\* with `heapq` (lazy deletion) and flat NumPy arrays for
216
+ `g_score`/`came_from`; longitudinal wrap-around (trans-Pacific routes).
217
+ - The Haversine heuristic is consistent (triangle inequality) → optimal A*
218
+ with weight 1.0.
219
+
220
+ ### Practical tips (global routes)
221
+
222
+ Intercontinental routes require crossing entire ocean basins; at `0.05°`
223
+ A\* may need millions of expansions:
224
+
225
+ ```bash
226
+ # Fast (seconds): coarser resolution
227
+ maritime-routing --from "Santos" --to "Shanghai" --resolution 0.25 --rebuild-grid
228
+
229
+ # Balanced
230
+ maritime-routing --from "Santos" --to "Shanghai" --resolution 0.1 --heuristic-weight 1.15
231
+
232
+ # High resolution (several minutes): weight > 1 focuses the search
233
+ maritime-routing --from "Santos" --to "Shanghai" --resolution 0.05 --heuristic-weight 1.15
234
+ ```
235
+
236
+ A `--heuristic-weight` > 1 greatly reduces expansions at the cost of a
237
+ slightly suboptimal route (< 1 %). If you hit "ITERATION LIMIT reached",
238
+ increase `--max-iterations` or the weight, or use a coarser resolution.
239
+
240
+ ## Limitations
241
+
242
+ - The route is the **shortest path on the grid** (optimizing Haversine
243
+ distance), not a real commercial route (currents, winds, draft, EEZ,
244
+ stopovers…).
245
+ - Suez/Panama canals are not modeled — A\* goes around Africa and South
246
+ America. (See the roadmap.)
247
+ - Points on land are automatically "snapped" to the nearest ocean cell.
248
+ - The lower the resolution, the faster the run and the coarser the route.
249
+
250
+ ## Roadmap (architecture ready for extensions)
251
+
252
+ - **Suez/Panama canals:** mark the canal cells as navigable (and/or with a
253
+ lower weight) in `raster.py`/`config.py`.
254
+ - **Depth restriction:** integrate bathymetry (GEBCO) and make cells
255
+ shallower than the draft non-navigable in `OceanGrid.create_grid()`
256
+ (multiply masks).
257
+ - **Real commercial routes / AIS:** per-cell weights (cost ≠ distance)
258
+ derived from traffic density in `astar._step_cost()`.
259
+ - **Economic weights:** expose a weight array `W[row,col]` and multiply the
260
+ step cost by it.
261
+
262
+ ## AI assistance
263
+
264
+ Parts of this project were developed with the assistance of an AI assistant (Claude).
265
+
266
+ The AI-assisted development process contributed to architectural improvements, code refinement,
267
+ documentation updates, usability enhancements, and implementation optimizations.
268
+
269
+ All generated suggestions and modifications were carefully reviewed, tested,
270
+ and verified to maintain the reliability and integrity of the project.
271
+
272
+
273
+ ## License
274
+
275
+ This project is licensed under the **MIT License** — see the
276
+ [LICENSE](LICENSE) file for details.
277
+
278
+ Copyright (c) 2026 Junior Dantas
279
+
280
+ ## Data Sources
281
+
282
+ This project uses the following open datasets:
283
+
284
+ - **World Port Index (WPI)**
285
+ National Geospatial-Intelligence Agency (NGA)
286
+ License: Public Domain
287
+ Source: https://msi.nga.mil/Publications/WPI
288
+
289
+ - **Natural Earth Data**
290
+ Global geographic datasets for coastline and land boundaries
291
+ License: Public Domain
292
+ Source: https://www.naturalearthdata.com/
293
+
294
+ - **Global Self-consistent Hierarchical High-resolution Geography Database (GSHHG)**
295
+ High-resolution shoreline and coastline data
296
+ License: GNU Lesser General Public License (LGPL)
297
+ Source: https://www.soest.hawaii.edu/pwessel/gshhg/
298
+
299
+ - **OpenStreetMap (OSM) data (when applicable)**
300
+ Geographic information used for map-related features
301
+ License: Open Database License (ODbL)
302
+ Source: https://www.openstreetmap.org/
303
+