{"_id":"@arcnautical/maritime-routing","_rev":"2-bee3594855d05f89017eaf6551648cff","name":"@arcnautical/maritime-routing","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@arcnautical/maritime-routing","version":"1.0.0","keywords":["maritime","routing","ocean","pathfinding","shipping","navigation","sea-route","ports","geospatial","astar","dijkstra","ETA","beaufort","EEZ","vessel","marine","logistics","GeoJSON"],"author":{"url":"https://arcnautical.com","name":"ArcNautical","email":"hello@arcnautical.com"},"license":"MIT","_id":"@arcnautical/maritime-routing@1.0.0","maintainers":[{"name":"saltytaro","email":"notoverkil@gmail.com"}],"homepage":"https://github.com/SaltyTaro/maritime-routing","bugs":{"url":"https://github.com/SaltyTaro/maritime-routing/issues"},"dist":{"shasum":"15a2eeeed93bac628599657d3781313411f21fac","tarball":"https://registry.npmjs.org/@arcnautical/maritime-routing/-/maritime-routing-1.0.0.tgz","fileCount":33,"integrity":"sha512-fncDe262WP5OijW1yzkkqCg8NTCwKhTNtbtqRSfXGkDo9LllfOi6pW7W8XnVUFqhAFtaev3EopmK5jiUoOWuxA==","signatures":[{"sig":"MEUCIQCnPcdz6QyaY3WV7eed+Hp84yl+OqPxBmXWY4sEgJLkIAIgaX50IvJe11rx95MClUttgJ/tc/kG4QmwGkkTIpoOPiE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":589601},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./geo":{"types":"./dist/geo/index.d.ts","import":"./dist/geo/index.js"},"./ports":{"types":"./dist/ports/index.d.ts","import":"./dist/ports/index.js"},"./routing":{"types":"./dist/routing/index.d.ts","import":"./dist/routing/index.js"},"./weather":{"types":"./dist/weather/index.d.ts","import":"./dist/weather/index.js"},"./geography":{"types":"./dist/geography/index.d.ts","import":"./dist/geography/index.js"},"./pathfinding":{"types":"./dist/pathfinding/index.d.ts","import":"./dist/pathfinding/index.js"}},"gitHead":"40d4f40079f39ec900c7cddbfeaffff891c4b0e2","scripts":{"test":"node --test --import tsx dist/tests/*.test.js","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"saltytaro","email":"notoverkil@gmail.com"},"repository":{"url":"git+https://github.com/SaltyTaro/maritime-routing.git","type":"git"},"_npmVersion":"11.6.2","description":"Production-grade maritime routing engine — A* ocean pathfinding, Dijkstra waypoint routing, 510+ port database, weather-aware ETA, EEZ analysis. Zero dependencies.","directories":{},"_nodeVersion":"24.13.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/maritime-routing_1.0.0_1774210258226_0.37219510979598014","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"_id":"@arcnautical/maritime-routing@1.0.1","bugs":{"url":"https://github.com/arcnautical/maritime-routing/issues"},"dist":{"shasum":"729e81f1e888d6078dacd6485f1adbe163a16590","tarball":"https://registry.npmjs.org/@arcnautical/maritime-routing/-/maritime-routing-1.0.1.tgz","fileCount":33,"integrity":"sha512-6v+IrFiKaooGG20PreD/3Z2Frcz4/JCnN1X0JSPFDM2whPZ0gUJMstu+qvPQBTvkgWjuULIlx84ud7Hc/P7gDQ==","signatures":[{"sig":"MEUCIQC3r6Br57mGTP/SuBpAVY7r5Xlp1dmiZ87PX9oDGHZvtwIgThsDvGejvF8R95lZFtUZzRpFxPepXNopYluEQ3HRBJA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFGggDrqUleBnmO821uiuG0xazdj4HcDZ1+EBkYYwwuHAiEA0rJ9ixzY7gmHMomeY37j9od2T4ujbCA5t+RihIHOueE="}],"unpackedSize":590297},"main":"./dist/index.js","name":"@arcnautical/maritime-routing","type":"module","types":"./dist/index.d.ts","author":{"url":"https://arcnautical.com","name":"ArcNautical","email":"hello@arcnautical.com"},"engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./geo":{"types":"./dist/geo/index.d.ts","import":"./dist/geo/index.js"},"./ports":{"types":"./dist/ports/index.d.ts","import":"./dist/ports/index.js"},"./routing":{"types":"./dist/routing/index.d.ts","import":"./dist/routing/index.js"},"./weather":{"types":"./dist/weather/index.d.ts","import":"./dist/weather/index.js"},"./geography":{"types":"./dist/geography/index.d.ts","import":"./dist/geography/index.js"},"./pathfinding":{"types":"./dist/pathfinding/index.d.ts","import":"./dist/pathfinding/index.js"}},"gitHead":"68fbf7f7bacdfc9b34594d26f5a4f8e17d2bf3f4","license":"MIT","scripts":{"test":"node --test --import tsx tests/maritime-routing.test.mts","build":"tsc","prepublishOnly":"npm run build"},"version":"1.0.1","_npmUser":{"name":"saltytaro","email":"notoverkil@gmail.com"},"homepage":"https://github.com/arcnautical/maritime-routing","keywords":["maritime","routing","ocean","pathfinding","shipping","navigation","sea-route","ports","geospatial","astar","dijkstra","ETA","beaufort","EEZ","vessel","marine","logistics","GeoJSON"],"repository":{"url":"git+https://github.com/arcnautical/maritime-routing.git","type":"git"},"_npmVersion":"11.6.2","description":"Production-grade maritime routing engine — A* ocean pathfinding, Dijkstra waypoint routing, 510+ port database, weather-aware ETA, EEZ analysis. Zero dependencies.","directories":{},"maintainers":[{"name":"saltytaro","email":"notoverkil@gmail.com"}],"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/maritime-routing_1.0.1_1789743014760_0.8100642865143395"}}},"time":{"created":"2026-03-22T20:10:58.145Z","modified":"2026-09-18T14:50:15.038Z","1.0.0":"2026-03-22T20:10:58.413Z","1.0.1":"2026-09-18T14:50:14.857Z"},"bugs":{"url":"https://github.com/arcnautical/maritime-routing/issues"},"author":{"url":"https://arcnautical.com","name":"ArcNautical","email":"hello@arcnautical.com"},"license":"MIT","homepage":"https://github.com/arcnautical/maritime-routing","keywords":["maritime","routing","ocean","pathfinding","shipping","navigation","sea-route","ports","geospatial","astar","dijkstra","ETA","beaufort","EEZ","vessel","marine","logistics","GeoJSON"],"repository":{"url":"git+https://github.com/arcnautical/maritime-routing.git","type":"git"},"description":"Production-grade maritime routing engine — A* ocean pathfinding, Dijkstra waypoint routing, 510+ port database, weather-aware ETA, EEZ analysis. Zero dependencies.","maintainers":[{"name":"saltytaro","email":"notoverkil@gmail.com"}],"readme":"# @arcnautical/maritime-routing\n\n[![npm version](https://img.shields.io/npm/v/@arcnautical/maritime-routing.svg?style=flat-square)](https://www.npmjs.com/package/@arcnautical/maritime-routing)\n[![npm downloads](https://img.shields.io/npm/dm/@arcnautical/maritime-routing.svg?style=flat-square)](https://www.npmjs.com/package/@arcnautical/maritime-routing)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE)\n[![Tests: 42 passing](https://img.shields.io/badge/tests-42%20passing-success?style=flat-square)](./tests/maritime-routing.test.mts)\n[![Zero dependencies](https://img.shields.io/badge/dependencies-0-success?style=flat-square)](./package.json)\n[![GitHub stars](https://img.shields.io/github/stars/arcnautical/maritime-routing?style=flat-square)](https://github.com/arcnautical/maritime-routing)\n\nProduction-grade maritime routing engine for JavaScript/TypeScript. **Zero dependencies.**\n\nCompute realistic ocean routes between any two ports worldwide, with guaranteed land avoidance, weather-aware ETA, and EEZ transit analysis.\n\n## Why this exists\n\nEvery shipping tech startup reinvents maritime routing. The existing open-source options ([searoute-js](https://www.npmjs.com/package/searoute-js) — unmaintained since 2020, uses year-2000 data) are inadequate for production use. Commercial APIs charge EUR 600-6,900/year.\n\nThis library is extracted from [ArcNautical](https://arcnautical.com), a maritime risk intelligence platform. It has been tested against 1,790 real-world routes with 0% land crossing.\n\n> **This engine powers a live voyage-risk product.**\n> `@arcnautical/maritime-routing` is the open-source core of [ArcNautical](https://arcnautical.com)'s\n> lane-risk intelligence. The live app takes any origin -> destination lane and scores it for\n> sanctions exposure, piracy/conflict, chokepoint dependency and weather, then emails you a\n> dated lane-risk brief for that specific corridor.\n>\n> Try it on one of your own lanes -- the full computed result is shown with no signup:\n> **https://arcnautical.com**\n\n## Features\n\n| Feature | Description |\n|---------|-------------|\n| **A\\* Ocean Pathfinding** | Routes on a 0.05° ocean bitmap (7200x3600 cells) derived from OpenStreetMap. Routes are guaranteed to never cross land. |\n| **Dijkstra Waypoint Routing** | 48 strategic chokepoints (Suez, Panama, Malacca, Gibraltar, etc.) with distance-weighted graph routing. |\n| **510+ Port Database** | UN/LOCODE standard. Search by name, country, or code. Includes coordinates, port type, and ocean region. |\n| **Weather Speed Model** | Beaufort-based speed reduction (Kwon 2008, Lu et al. 2015). Per vessel type and load condition. Direction-aware with Cbeta factors. |\n| **EEZ Transit Analysis** | Detect which Exclusive Economic Zones a route transits. Sanctions flagging included. |\n| **Avoid Zones** | Route around user-specified polygons (e.g., piracy areas, conflict zones). |\n| **Via Waypoints** | Force routes through specific coordinates. |\n| **GeoJSON Output** | All routes returned as GeoJSON LineString, ready for map rendering. |\n\n## Install\n\n```bash\nnpm install @arcnautical/maritime-routing\n```\n\nRequires Node.js >= 18.\n\n## Quick Start\n\n```ts\nimport { computeRoute } from '@arcnautical/maritime-routing';\n\n// Compute a route from Singapore to Rotterdam\nconst route = computeRoute('SGSIN', 'NLRTM');\n\nconsole.log(route.distance_nm);        // ~8,440 nm\nconsole.log(route.duration_hours);      // ~603 hours at 14 knots\nconsole.log(route.waypoints.length);    // waypoints transited\nconsole.log(route.hazard_zones_crossed); // chokepoints on route\n\n// GeoJSON for map rendering\nconst geojson = route.route_geojson;\n// -> FeatureCollection with LineString geometry\n```\n\n## API Reference\n\n### Route Computation\n\n```ts\nimport {\n  computeRoute,\n  computeRouteInternal,\n} from '@arcnautical/maritime-routing';\n\n// Simple route (returns handler-compatible format)\nconst route = computeRoute('SGSIN', 'NLRTM');\n\n// Route with options\nconst route = computeRoute('SGSIN', 'NLRTM', {\n  avoid_zones: [[[30, 12], [50, 12], [50, 32], [30, 32], [30, 12]]], // avoid Red Sea\n  via_waypoints: [{ lat: -34.4, lon: 18.5 }], // force via Cape of Good Hope\n});\n\n// Internal format (with segment breakdown)\nconst internal = computeRouteInternal('SGSIN', 'NLRTM');\nconsole.log(internal.segments);       // per-leg distance + bearing\nconsole.log(internal.totalDistanceNm);\nconsole.log(internal.geojson);        // GeoJSON Feature\n```\n\n### Ocean Pathfinding\n\n```ts\nimport { findOceanPath } from '@arcnautical/maritime-routing';\n\n// Find a path between two coordinates\n// Returns [lon, lat][] (GeoJSON convention)\nconst path = findOceanPath(1.26, 103.84, 51.90, 4.50);\n// -> ~500 coordinate pairs, guaranteed to never cross land\n\n// Performance: 8-50ms for typical segments\n// Automatically subdivides routes > 5,000nm\n```\n\n### Port Database\n\n```ts\nimport {\n  searchPorts,\n  getPortByLocode,\n  getPortsByRegion,\n  resolveAisDestination,\n  findNearestPort,\n  PORTS,\n} from '@arcnautical/maritime-routing';\n\n// Search by name, country, or LOCODE\nconst ports = searchPorts('Singapore');    // [{ locode: 'SGSIN', ... }]\nconst ports = searchPorts('Japan', 50);   // up to 50 results\n\n// Exact lookup\nconst sg = getPortByLocode('SGSIN');      // { name: 'Singapore', lat: 1.26, ... }\n\n// By region\nconst medPorts = getPortsByRegion('mediterranean'); // 30+ ports\n\n// Parse messy AIS destination strings\nconst port = resolveAisDestination('NL RTM');   // -> Rotterdam\nconst port = resolveAisDestination('>>SGSIN<<'); // -> Singapore\n\n// Find nearest port to coordinates\nconst port = findNearestPort(51.9, 4.5);  // -> Rotterdam\n\n// All 510+ ports\nconsole.log(PORTS.length);\n```\n\n### Weather Speed Model\n\n```ts\nimport {\n  computeSeaStateFactor,\n  computeRouteEta,\n  windToBeaufort,\n  waveToBeaufort,\n} from '@arcnautical/maritime-routing';\n\n// Compute speed reduction from weather\nconst result = computeSeaStateFactor({\n  baseSpeedKnots: 14,\n  waveHeightM: 3.0,\n  windSpeedKt: 25,\n  swellHeightM: 1.5,\n  vesselType: 'bulk',     // container | bulk | tanker | lng | general\n  loadCondition: 'laden', // laden | ballast\n  // Optional direction-aware inputs:\n  waveDirectionDeg: 270,\n  vesselHeadingDeg: 90,   // heading into waves = worst case\n});\n\nconsole.log(result.effectiveSpeedKnots); // reduced speed\nconsole.log(result.beaufortNumber);      // 6\nconsole.log(result.speedReductionPct);   // % reduction\nconsole.log(result.seaStateFactor);      // 0-1 retention\n\n// Compute ETA for a full route\nconst eta = computeRouteEta(\n  segments,          // [{ distanceNm, bearing }]\n  segmentWeather,    // per-segment weather data\n  14,                // base speed (knots)\n  'container',       // vessel type\n  'laden',           // load condition\n);\nconsole.log(eta.totalTransitHours);\nconsole.log(eta.worstSegmentIndex);\n```\n\n### EEZ Transit Analysis\n\n```ts\nimport { analyzeRouteEezTransit } from '@arcnautical/maritime-routing';\n\nconst result = analyzeRouteEezTransit(segments, totalDistanceNm);\n\nfor (const eez of result.transitEezs) {\n  console.log(`${eez.countryName}: ${(eez.routeFraction * 100).toFixed(1)}%`);\n  if (eez.isSanctioned) console.log('  ⚠ SANCTIONED');\n}\n\nconsole.log(result.sanctionedEezFraction); // 0-1\nconsole.log(result.summary);              // human-readable\n```\n\n### Geodesic Utilities\n\n```ts\nimport {\n  haversineDistance,\n  haversineDistanceNm,\n  bearing,\n  interpolateGreatCircle,\n  pointInPolygon,\n} from '@arcnautical/maritime-routing';\n\n// Distance in nautical miles\nconst nm = haversineDistanceNm(1.26, 103.84, 51.90, 4.50); // ~5,700\n\n// Initial bearing (degrees)\nconst brg = bearing(1.26, 103.84, 51.90, 4.50); // ~315°\n\n// Great circle arc interpolation\nconst points = interpolateGreatCircle(0, 0, 45, 90, 100);\n// -> 101 [lat, lon] pairs along the arc\n\n// Point-in-polygon test\nconst inside = pointInPolygon(5, 5, [[0,0], [10,0], [10,10], [0,10]]);\n```\n\n## Data Sources\n\n| Data | Source | License |\n|------|--------|---------|\n| Ocean grid | OpenStreetMap water polygons | ODbL |\n| Port database | UN/LOCODE + public maritime data | Public domain |\n| Speed model | Kwon 2008, Lu et al. 2015 | Academic literature |\n| EEZ boundaries | UN CLCS / Flanders Marine Institute | Public domain |\n| Waypoint graph | Public maritime knowledge | N/A |\n\n## Performance\n\n| Operation | Time |\n|-----------|------|\n| Port lookup (by LOCODE) | < 0.1ms |\n| Port search (by name) | < 1ms |\n| Ocean pathfinding (single segment) | 8-50ms |\n| Full route computation (Singapore → Rotterdam) | ~250ms |\n| Speed model computation | < 0.1ms |\n| Grid loading (first call only) | ~900ms |\n\nPackage size: ~200KB (compiled JS) + 106KB (ocean grid) + 90KB (EEZ data).\n\n## Comparison with searoute-js\n\n| Feature | @arcnautical/maritime-routing | searoute-js |\n|---------|-------------------------------|-------------|\n| Last updated | 2026 | 2020 |\n| TypeScript | Native | No |\n| Dependencies | **Zero** | Multiple |\n| Land avoidance | **Guaranteed** (bitmap) | No (graph only) |\n| Port database | 510+ ports | None |\n| Weather model | Beaufort + Cbeta | None |\n| EEZ analysis | Yes | None |\n| Avoid zones | Yes | No |\n| Via waypoints | Yes | No |\n| Narrow passages | Suez, Panama, Bosphorus, etc. | Limited |\n| Resolution | 0.05° (~5.5km) | Variable |\n| Data source | OSM (2024) | ORNL (2000) |\n\n## Advanced Usage\n\n### Custom Ocean Grid\n\n```ts\nimport { setGridPath, loadGridFromBuffer } from '@arcnautical/maritime-routing';\n\n// Use a custom grid file\nsetGridPath('/path/to/my-ocean-grid.bin.gz');\n\n// Or load from a buffer (e.g., fetched from CDN)\nconst buffer = await fetch('https://cdn.example.com/ocean-grid.bin.gz')\n  .then(r => r.arrayBuffer());\nloadGridFromBuffer(new Uint8Array(buffer));\n```\n\n### Generate Your Own Ocean Grid\n\nThe ocean grid is generated from OSM water polygon shapefiles:\n\n```bash\n# Download water polygons from osmdata.openstreetmap.de\n# Then run the generator:\nnpx tsx scripts/generate-ocean-grid.ts /path/to/water_polygons.shp\n```\n\n### Ocean Regions\n\nPorts are classified into 16 ocean regions used for routing:\n\n`pacific`, `south_china_sea`, `southeast_asia`, `indian_ocean`, `persian_gulf`, `red_sea`, `mediterranean`, `atlantic_north`, `atlantic_south`, `caribbean`, `north_sea`, `baltic`, `black_sea`, `arctic`, `oceania`, `east_pacific`\n\n### Vessel Types\n\nThe speed model supports 5 vessel types with distinct sea-keeping characteristics:\n\n| Type | Reference Speed | Typical DWT |\n|------|----------------|-------------|\n| `container` | 20 kt | 50,000-200,000 |\n| `bulk` | 14 kt | 30,000-400,000 |\n| `tanker` | 14 kt | 50,000-320,000 |\n| `lng` | 19 kt | 70,000-180,000 |\n| `general` | 14 kt | 5,000-40,000 |\n\n## License\n\nMIT - see [LICENSE](LICENSE).\n\n## About\n\nBuilt by [ArcNautical](https://arcnautical.com) — maritime risk intelligence for fleet managers, underwriters, and compliance teams.\n\nArcNautical provides voyage risk scoring, sanctions screening, piracy/conflict threat intelligence, and automated compliance reports. The routing engine you're using here powers our commercial platform.\n\nIf you need maritime risk intelligence beyond routing, [get in touch](https://arcnautical.com).\n","readmeFilename":"README.md"}