npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arcnautical/maritime-routing

v1.0.0

Published

Production-grade maritime routing engine — A* ocean pathfinding, Dijkstra waypoint routing, 510+ port database, weather-aware ETA, EEZ analysis. Zero dependencies.

Readme

@arcnautical/maritime-routing

Production-grade maritime routing engine for JavaScript/TypeScript. Zero dependencies.

Compute realistic ocean routes between any two ports worldwide, with guaranteed land avoidance, weather-aware ETA, and EEZ transit analysis.

Why this exists

Every shipping tech startup reinvents maritime routing. The existing open-source options (searoute-js — unmaintained since 2020, uses year-2000 data) are inadequate for production use. Commercial APIs charge EUR 600-6,900/year.

This library is extracted from ArcNautical, a maritime risk intelligence platform. It has been tested against 1,790 real-world routes with 0% land crossing.

Features

| Feature | Description | |---------|-------------| | A* Ocean Pathfinding | Routes on a 0.05° ocean bitmap (7200x3600 cells) derived from OpenStreetMap. Routes are guaranteed to never cross land. | | Dijkstra Waypoint Routing | 48 strategic chokepoints (Suez, Panama, Malacca, Gibraltar, etc.) with distance-weighted graph routing. | | 510+ Port Database | UN/LOCODE standard. Search by name, country, or code. Includes coordinates, port type, and ocean region. | | Weather Speed Model | Beaufort-based speed reduction (Kwon 2008, Lu et al. 2015). Per vessel type and load condition. Direction-aware with Cbeta factors. | | EEZ Transit Analysis | Detect which Exclusive Economic Zones a route transits. Sanctions flagging included. | | Avoid Zones | Route around user-specified polygons (e.g., piracy areas, conflict zones). | | Via Waypoints | Force routes through specific coordinates. | | GeoJSON Output | All routes returned as GeoJSON LineString, ready for map rendering. |

Install

npm install @arcnautical/maritime-routing

Requires Node.js >= 18.

Quick Start

import { computeRoute } from '@arcnautical/maritime-routing';

// Compute a route from Singapore to Rotterdam
const route = computeRoute('SGSIN', 'NLRTM');

console.log(route.distance_nm);        // ~8,440 nm
console.log(route.duration_hours);      // ~603 hours at 14 knots
console.log(route.waypoints.length);    // waypoints transited
console.log(route.hazard_zones_crossed); // chokepoints on route

// GeoJSON for map rendering
const geojson = route.route_geojson;
// -> FeatureCollection with LineString geometry

API Reference

Route Computation

import {
  computeRoute,
  computeRouteInternal,
} from '@arcnautical/maritime-routing';

// Simple route (returns handler-compatible format)
const route = computeRoute('SGSIN', 'NLRTM');

// Route with options
const route = computeRoute('SGSIN', 'NLRTM', {
  avoid_zones: [[[30, 12], [50, 12], [50, 32], [30, 32], [30, 12]]], // avoid Red Sea
  via_waypoints: [{ lat: -34.4, lon: 18.5 }], // force via Cape of Good Hope
});

// Internal format (with segment breakdown)
const internal = computeRouteInternal('SGSIN', 'NLRTM');
console.log(internal.segments);       // per-leg distance + bearing
console.log(internal.totalDistanceNm);
console.log(internal.geojson);        // GeoJSON Feature

Ocean Pathfinding

import { findOceanPath } from '@arcnautical/maritime-routing';

// Find a path between two coordinates
// Returns [lon, lat][] (GeoJSON convention)
const path = findOceanPath(1.26, 103.84, 51.90, 4.50);
// -> ~500 coordinate pairs, guaranteed to never cross land

// Performance: 8-50ms for typical segments
// Automatically subdivides routes > 5,000nm

Port Database

import {
  searchPorts,
  getPortByLocode,
  getPortsByRegion,
  resolveAisDestination,
  findNearestPort,
  PORTS,
} from '@arcnautical/maritime-routing';

// Search by name, country, or LOCODE
const ports = searchPorts('Singapore');    // [{ locode: 'SGSIN', ... }]
const ports = searchPorts('Japan', 50);   // up to 50 results

// Exact lookup
const sg = getPortByLocode('SGSIN');      // { name: 'Singapore', lat: 1.26, ... }

// By region
const medPorts = getPortsByRegion('mediterranean'); // 30+ ports

// Parse messy AIS destination strings
const port = resolveAisDestination('NL RTM');   // -> Rotterdam
const port = resolveAisDestination('>>SGSIN<<'); // -> Singapore

// Find nearest port to coordinates
const port = findNearestPort(51.9, 4.5);  // -> Rotterdam

// All 510+ ports
console.log(PORTS.length);

Weather Speed Model

import {
  computeSeaStateFactor,
  computeRouteEta,
  windToBeaufort,
  waveToBeaufort,
} from '@arcnautical/maritime-routing';

// Compute speed reduction from weather
const result = computeSeaStateFactor({
  baseSpeedKnots: 14,
  waveHeightM: 3.0,
  windSpeedKt: 25,
  swellHeightM: 1.5,
  vesselType: 'bulk',     // container | bulk | tanker | lng | general
  loadCondition: 'laden', // laden | ballast
  // Optional direction-aware inputs:
  waveDirectionDeg: 270,
  vesselHeadingDeg: 90,   // heading into waves = worst case
});

console.log(result.effectiveSpeedKnots); // reduced speed
console.log(result.beaufortNumber);      // 6
console.log(result.speedReductionPct);   // % reduction
console.log(result.seaStateFactor);      // 0-1 retention

// Compute ETA for a full route
const eta = computeRouteEta(
  segments,          // [{ distanceNm, bearing }]
  segmentWeather,    // per-segment weather data
  14,                // base speed (knots)
  'container',       // vessel type
  'laden',           // load condition
);
console.log(eta.totalTransitHours);
console.log(eta.worstSegmentIndex);

EEZ Transit Analysis

import { analyzeRouteEezTransit } from '@arcnautical/maritime-routing';

const result = analyzeRouteEezTransit(segments, totalDistanceNm);

for (const eez of result.transitEezs) {
  console.log(`${eez.countryName}: ${(eez.routeFraction * 100).toFixed(1)}%`);
  if (eez.isSanctioned) console.log('  ⚠ SANCTIONED');
}

console.log(result.sanctionedEezFraction); // 0-1
console.log(result.summary);              // human-readable

Geodesic Utilities

import {
  haversineDistance,
  haversineDistanceNm,
  bearing,
  interpolateGreatCircle,
  pointInPolygon,
} from '@arcnautical/maritime-routing';

// Distance in nautical miles
const nm = haversineDistanceNm(1.26, 103.84, 51.90, 4.50); // ~5,700

// Initial bearing (degrees)
const brg = bearing(1.26, 103.84, 51.90, 4.50); // ~315°

// Great circle arc interpolation
const points = interpolateGreatCircle(0, 0, 45, 90, 100);
// -> 101 [lat, lon] pairs along the arc

// Point-in-polygon test
const inside = pointInPolygon(5, 5, [[0,0], [10,0], [10,10], [0,10]]);

Data Sources

| Data | Source | License | |------|--------|---------| | Ocean grid | OpenStreetMap water polygons | ODbL | | Port database | UN/LOCODE + public maritime data | Public domain | | Speed model | Kwon 2008, Lu et al. 2015 | Academic literature | | EEZ boundaries | UN CLCS / Flanders Marine Institute | Public domain | | Waypoint graph | Public maritime knowledge | N/A |

Performance

| Operation | Time | |-----------|------| | Port lookup (by LOCODE) | < 0.1ms | | Port search (by name) | < 1ms | | Ocean pathfinding (single segment) | 8-50ms | | Full route computation (Singapore → Rotterdam) | ~250ms | | Speed model computation | < 0.1ms | | Grid loading (first call only) | ~900ms |

Package size: ~200KB (compiled JS) + 106KB (ocean grid) + 90KB (EEZ data).

Comparison with searoute-js

| Feature | @arcnautical/maritime-routing | searoute-js | |---------|-------------------------------|-------------| | Last updated | 2026 | 2020 | | TypeScript | Native | No | | Dependencies | Zero | Multiple | | Land avoidance | Guaranteed (bitmap) | No (graph only) | | Port database | 510+ ports | None | | Weather model | Beaufort + Cbeta | None | | EEZ analysis | Yes | None | | Avoid zones | Yes | No | | Via waypoints | Yes | No | | Narrow passages | Suez, Panama, Bosphorus, etc. | Limited | | Resolution | 0.05° (~5.5km) | Variable | | Data source | OSM (2024) | ORNL (2000) |

Advanced Usage

Custom Ocean Grid

import { setGridPath, loadGridFromBuffer } from '@arcnautical/maritime-routing';

// Use a custom grid file
setGridPath('/path/to/my-ocean-grid.bin.gz');

// Or load from a buffer (e.g., fetched from CDN)
const buffer = await fetch('https://cdn.example.com/ocean-grid.bin.gz')
  .then(r => r.arrayBuffer());
loadGridFromBuffer(new Uint8Array(buffer));

Generate Your Own Ocean Grid

The ocean grid is generated from OSM water polygon shapefiles:

# Download water polygons from osmdata.openstreetmap.de
# Then run the generator:
npx tsx scripts/generate-ocean-grid.ts /path/to/water_polygons.shp

Ocean Regions

Ports are classified into 16 ocean regions used for routing:

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

Vessel Types

The speed model supports 5 vessel types with distinct sea-keeping characteristics:

| Type | Reference Speed | Typical DWT | |------|----------------|-------------| | container | 20 kt | 50,000-200,000 | | bulk | 14 kt | 30,000-400,000 | | tanker | 14 kt | 50,000-320,000 | | lng | 19 kt | 70,000-180,000 | | general | 14 kt | 5,000-40,000 |

License

MIT - see LICENSE.

About

Built by ArcNautical — maritime risk intelligence for fleet managers, underwriters, and compliance teams.

ArcNautical 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.

If you need maritime risk intelligence beyond routing, get in touch.