Skip to content

feat: accept UN/LOCODE port codes as origin/destination - #20

Merged
mayurrawte merged 1 commit into
mainfrom
feat/issue-7-port-codes
Jul 3, 2026
Merged

feat: accept UN/LOCODE port codes as origin/destination#20
mayurrawte merged 1 commit into
mainfrom
feat/issue-7-port-codes

Conversation

@mayurrawte

@mayurrawte mayurrawte commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Logistics and supply-chain users think in port codes, not coordinates. This PR lets a UN/LOCODE string be used anywhere an origin/destination is accepted:

import 'searoute-ts/ports'; // enables UN/LOCODE strings on the core API
import { seaRoute } from 'searoute-ts';

seaRoute('CNSHA', 'NLRTM');     // Shanghai → Rotterdam
seaRoute('CNSHA', [4.4, 51.9]); // mixing a code and coordinates is fine

Design

  • Dataset behind a subpath export. The ~1,600-port dataset ships as searoute-ts/ports so the core stays lean and consumers only pay for it if they use it. Importing the subpath (for any export, or purely for its side effect) registers a UN/LOCODE→coordinates resolver into the core, which is what enables seaRoute('CNSHA', …). package.json sideEffects is narrowed to an array so bundlers keep that registration.
  • Resolve then reuse the pipeline. A string input is resolved to [lon, lat] and then follows the existing snap → Dijkstra path — no new routing logic. Works for seaRoute, seaRouteMulti, and seaRouteAlternatives.
  • Clear errors. Unknown/malformed codes throw the new UnknownPortError; if a code is passed without the dataset registered, the error explains to import searoute-ts/ports.

New API

  • searoute-ts/ports: lookupPort(code) → { code, name, country, coordinates }, resolvePort(code) → [lon, lat], PORTS, PORT_COUNT, types Port / PortRecord.
  • Core: UnknownPortError, registerPortResolver, type PortResolver; PointInput now also accepts string.

Dataset & provenance

  • Source: marchah/sea-ports (MIT), itself derived from UN/LOCODE. Regenerate with scripts/build-ports.cjs (documented URL + steps in the script header).
  • 1,610 codes (primary UN/LOCODEs + aliases), indexed case-insensitively. Entries without valid coordinates are dropped. Coordinates are port-city granularity — fine here since inputs are snapped to the network anyway.
  • I deliberately used this seaport-specific dataset rather than the raw UN/LOCODE CSV: the CSV has empty coordinates for many major seaports (Shanghai, Jebel Ali, Felixstowe, Valencia, Port Said…), so it can't power the headline example.

Notes

Validation

All steps CI runs, from a clean tree:

  • npm ci
  • npm run lint
  • npm run format:check
  • npm run build
  • npm test ✅ (54 tests; +1 core test that a code throws UnknownPortError without the dataset, +9 in a new src/ports/index.spec.ts covering lookup/resolve, case-insensitivity, aliases, unknown-code errors, and seaRoute('CNSHA','NLRTM') landing in the Shanghai→Rotterdam distance band)

Also verified end-to-end against the built dist in both CJS (require) and ESM (import): seaRoute('CNSHA','NLRTM') ≈ 19,753 km, mixed code+coord identical, PORT_COUNT 1610, unknown code → UnknownPortError.

Closes #7

@mayurrawte
mayurrawte force-pushed the feat/issue-7-port-codes branch 2 times, most recently from 01ce5b2 to 905b499 Compare July 3, 2026 09:10
seaRoute/seaRouteMulti/seaRouteAlternatives accept a UN/LOCODE string
anywhere a point is accepted (mixable with coordinates). The ~1600-port
dataset ships behind the searoute-ts/ports subpath. Adds loadPorts(url) to
fetch and register the dataset at runtime instead of bundling it, and emits
a raw dist/ports.json so jsDelivr/unpkg serve it versioned.

Closes #7
@mayurrawte
mayurrawte force-pushed the feat/issue-7-port-codes branch from 905b499 to 656cd64 Compare July 3, 2026 09:11
@mayurrawte
mayurrawte merged commit 4099667 into main Jul 3, 2026
4 checks passed
@mayurrawte
mayurrawte deleted the feat/issue-7-port-codes branch July 3, 2026 09:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accept port codes (UN/LOCODE) as input — seaRoute('CNSHA', 'NLRTM')

1 participant