first commit
This commit is contained in:
104
docs/geospatial/logistics/ports/README.md
Normal file
104
docs/geospatial/logistics/ports/README.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# @suaveplan/ports - Global port database with shipping-lane routing, ETA estimation, and UN/LOCODE port lookups for sea ports, airports, and rail terminals.
|
||||
|
||||
Global port database with shipping-lane routing, ETA estimation, and
|
||||
UN/LOCODE port lookups for sea ports, airports, and rail terminals.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [airports](./src/data/airports/airports.md) — Curated dataset of major international air-cargo airports, exported as the readonly `airports` array. Each record confo…
|
||||
- [airports](./src/data/airports/airports.md) — Curated dataset of major international air-cargo airports, exported as the readonly `airports` array. Each record confo…
|
||||
- [all-ports](./src/data/all-ports/all-ports.md) — Aggregates every curated dataset — sea ports, airports, and rail terminals — into a single readonly `allPorts` array an…
|
||||
- [all-ports](./src/data/all-ports/all-ports.md) — Aggregates every curated dataset — sea ports, airports, and rail terminals — into a single readonly `allPorts` array an…
|
||||
- [rail-terminals](./src/data/rail-terminals/rail-terminals.md) — Curated dataset of major rail freight terminals — primarily the inland container hubs on the New Silk Road and Trans-Eu…
|
||||
- [rail-terminals](./src/data/rail-terminals/rail-terminals.md) — Curated dataset of major rail freight terminals — primarily the inland container hubs on the New Silk Road and Trans-Eu…
|
||||
- [sea-ports](./src/data/sea-ports/sea-ports.md) — Aggregates every regional sea-port dataset (`seaPortsAsia`, `seaPortsEurope`, `seaPortsAmericas`, `seaPortsAfricaMena`,…
|
||||
- [sea-ports](./src/data/sea-ports/sea-ports.md) — Aggregates every regional sea-port dataset (`seaPortsAsia`, `seaPortsEurope`, `seaPortsAmericas`, `seaPortsAfricaMena`,…
|
||||
- [sea-ports-africa-mena](./src/data/sea-ports-africa-mena/sea-ports-africa-mena.md) — Curated dataset of major sea ports across Africa and the Middle East / North Africa region, exported as the readonly `s…
|
||||
- [sea-ports-africa-mena](./src/data/sea-ports-africa-mena/sea-ports-africa-mena.md) — Curated dataset of major sea ports across Africa and the Middle East / North Africa region, exported as the readonly `s…
|
||||
- [sea-ports-americas](./src/data/sea-ports-americas/sea-ports-americas.md) — Curated dataset of major sea ports across the Americas, exported as the readonly `seaPortsAmericas` array. Every record…
|
||||
- [sea-ports-americas](./src/data/sea-ports-americas/sea-ports-americas.md) — Curated dataset of major sea ports across the Americas, exported as the readonly `seaPortsAmericas` array. Every record…
|
||||
- [sea-ports-asia](./src/data/sea-ports-asia/sea-ports-asia.md) — Curated dataset of major sea ports across Asia, exported as the readonly `seaPortsAsia` array. Every record conforms to…
|
||||
- [sea-ports-asia](./src/data/sea-ports-asia/sea-ports-asia.md) — Curated dataset of major sea ports across Asia, exported as the readonly `seaPortsAsia` array. Every record conforms to…
|
||||
- [sea-ports-europe](./src/data/sea-ports-europe/sea-ports-europe.md) — Curated dataset of major sea ports across Europe, exported as the readonly `seaPortsEurope` array. Every record conform…
|
||||
- [sea-ports-europe](./src/data/sea-ports-europe/sea-ports-europe.md) — Curated dataset of major sea ports across Europe, exported as the readonly `seaPortsEurope` array. Every record conform…
|
||||
- [sea-ports-oceania](./src/data/sea-ports-oceania/sea-ports-oceania.md) — Curated dataset of major sea ports across Oceania, exported as the readonly `seaPortsOceania` array. Every record confo…
|
||||
- [sea-ports-oceania](./src/data/sea-ports-oceania/sea-ports-oceania.md) — Curated dataset of major sea ports across Oceania, exported as the readonly `seaPortsOceania` array. Every record confo…
|
||||
- [shipping-lanes](./src/data/shipping-lanes/shipping-lanes.md) — Curated registry of major global shipping lanes — Asia–Europe via Suez, Asia–Europe via Cape, Transpacific, Transatlant…
|
||||
- [shipping-lanes](./src/data/shipping-lanes/shipping-lanes.md) — Curated registry of major global shipping lanes — Asia–Europe via Suez, Asia–Europe via Cape, Transpacific, Transatlant…
|
||||
- [distance](./src/distance/distance.md) — Wraps `@suaveplan/geo`'s `greatCircleDistance` against a sphere whose radius matches the IUGG mean Earth radius (6 371…
|
||||
- [distance](./src/distance/distance.md) — Wraps `@suaveplan/geo`'s `greatCircleDistance` against a sphere whose radius matches the IUGG mean Earth radius (6 371…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Declares the eight stable error codes emitted by `@suaveplan/ports` and the matching typed `PortsError` subclasses. Eve…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Declares the eight stable error codes emitted by `@suaveplan/ports` and the matching typed `PortsError` subclasses. Eve…
|
||||
- [eta](./src/eta/eta.md) — Estimates the time of arrival for a vessel travelling along a `ShippingLane`. Resolves an effective cruise speed from (…
|
||||
- [eta](./src/eta/eta.md) — Estimates the time of arrival for a vessel travelling along a `ShippingLane`. Resolves an effective cruise speed from (…
|
||||
- [find-port](./src/find-port/find-port.md) — Provides the lookup surface for `@suaveplan/ports`. Every public function consumes the curated `allPorts` registry by d…
|
||||
- [find-port](./src/find-port/find-port.md) — Provides the lookup surface for `@suaveplan/ports`. Every public function consumes the curated `allPorts` registry by d…
|
||||
- [lane-distance](./src/lane-distance/lane-distance.md) — Computes great-circle distance metrics for a `ShippingLane`'s waypoint chain. `estimateLaneDistance` sums every segment…
|
||||
- [lane-distance](./src/lane-distance/lane-distance.md) — Computes great-circle distance metrics for a `ShippingLane`'s waypoint chain. `estimateLaneDistance` sums every segment…
|
||||
- [lane-lookup](./src/lane-lookup/lane-lookup.md) — Public lookup surface for the curated `shippingLanes` registry. Mirrors the port-lookup module in style: an O(1) `Map`…
|
||||
- [lane-lookup](./src/lane-lookup/lane-lookup.md) — Public lookup surface for the curated `shippingLanes` registry. Mirrors the port-lookup module in style: an O(1) `Map`…
|
||||
- [lane-progress](./src/lane-progress/lane-progress.md) — Projects a vessel position onto a shipping lane and reports how much of the lane has been traversed, how much remains,…
|
||||
- [lane-progress](./src/lane-progress/lane-progress.md) — Projects a vessel position onto a shipping lane and reports how much of the lane has been traversed, how much remains,…
|
||||
- [nearest-port](./src/nearest-port/nearest-port.md) — Public proximity-search API. `findNearestPort` returns the single closest port to a coordinate; `findPortsInRadius` ret…
|
||||
- [nearest-port](./src/nearest-port/nearest-port.md) — Public proximity-search API. `findNearestPort` returns the single closest port to a coordinate; `findPortsInRadius` ret…
|
||||
- [otel](./src/otel/otel.md) — Wires `@suaveplan/ports`' OpenTelemetry instrumentation: three counters that track lookup volume per kind, lane lookup…
|
||||
- [otel](./src/otel/otel.md) — Wires `@suaveplan/ports`' OpenTelemetry instrumentation: three counters that track lookup volume per kind, lane lookup…
|
||||
- [schemas](./src/schemas/schemas.md) — Defines Zod schemas for every public type emitted by `@suaveplan/ports`. API plugins, message-queue handlers, and any o…
|
||||
- [schemas](./src/schemas/schemas.md) — Defines Zod schemas for every public type emitted by `@suaveplan/ports`. API plugins, message-queue handlers, and any o…
|
||||
- [spatial-index](./src/spatial-index/spatial-index.md) — `PortSpatialIndex` buckets a port set by 5-degree latitude/longitude cells at construction time so `findNearest` and `f…
|
||||
- [spatial-index](./src/spatial-index/spatial-index.md) — `PortSpatialIndex` buckets a port set by 5-degree latitude/longitude cells at construction time so `findNearest` and `f…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/ports
|
||||
```
|
||||
|
||||
## What's in the box
|
||||
|
||||
- Curated `Port` registry: ~100 sea ports across five regions, ~30
|
||||
international cargo airports, ~30 rail freight terminals.
|
||||
- Lookups: by UN/LOCODE (`findPort` / `findPortStrict`), by ISO country
|
||||
(`findPortsByCountry`), by `PortType` (`findPortsByType`), by name
|
||||
substring (`searchPorts`).
|
||||
- Proximity: `findNearestPort` and `findPortsInRadius` (5°-bucketed
|
||||
`PortSpatialIndex` under the hood).
|
||||
- Curated `shippingLanes` waypoint graph for the major sea, rail, and
|
||||
air freight routes.
|
||||
- Lane distance (`estimateLaneDistance`, `cumulativeDistances`), lane
|
||||
projection (`findPositionOnLane`), and ETA estimation (`estimateETA`).
|
||||
- 8-code typed error registry under the `PORTS.*` namespace via
|
||||
`@suaveplan/error`.
|
||||
- Zod schemas for every public type under `./schemas/schemas`.
|
||||
- OpenTelemetry instrumentation: three counters and three histograms
|
||||
via `./otel/otel`.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```ts
|
||||
import {
|
||||
findPort,
|
||||
findNearestPort,
|
||||
getShippingLaneStrict,
|
||||
estimateETA,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
const shanghai = findPort("CNSHA");
|
||||
const nearestSea = findNearestPort({ latitude: 1.26, longitude: 103.82 });
|
||||
const suez = getShippingLaneStrict("asia-europe-suez");
|
||||
const eta = estimateETA(
|
||||
{ latitude: 12.5, longitude: 43.3 },
|
||||
suez,
|
||||
14,
|
||||
{ departureTime: new Date() }
|
||||
);
|
||||
console.log(eta.etaDate, eta.confidence);
|
||||
```
|
||||
|
||||
## Capability spec
|
||||
|
||||
See the canonical capability
|
||||
specification (requirements + scenarios + `**Verified by:**` links).
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# airports
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major international air-cargo airports, exported as
|
||||
the readonly `airports` array. Each record conforms to the `Port`
|
||||
interface (with `type: "air"`) and carries the IATA code, runway
|
||||
length, and freight-throughput figures used by ETA estimation on air
|
||||
shipping lanes and proximity searches in `findNearestPort`.
|
||||
|
||||
## Features
|
||||
|
||||
- Coverage of the world's busiest cargo hubs across Asia, Europe, North
|
||||
America, the Middle East, and Latin America.
|
||||
- Each record carries `iataCode`, `runwayLengthM`, and
|
||||
`annualThroughputTonnes` so consumers can filter by capacity.
|
||||
- Facility tags reflect cargo-handling capability (`cargo`,
|
||||
`intermodal`, `bonded_warehouse`, `free_trade_zone`, `passenger`,
|
||||
`reefer`).
|
||||
- IANA timezones populated for every record.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { airports } from "@suaveplan/ports/data/airports";
|
||||
|
||||
const memphis = airports.find((p) => p.iataCode === "MEM");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { airports, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 25.2532, longitude: 55.3657 },
|
||||
airports
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `airports: readonly Port[]` — curated international air-cargo airport set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses the IATA-prefixed UN/LOCODE convention (e.g. `USMEM`
|
||||
for Memphis). Throughput figures use underscores as digit separators
|
||||
for readability. The dataset is curated rather than exhaustive — it
|
||||
targets the cargo network's primary hubs and the regional gateways
|
||||
most commonly used in international logistics workflows, not every
|
||||
licensed airfield.
|
||||
@@ -0,0 +1,60 @@
|
||||
# all-ports
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Aggregates every curated dataset — sea ports, airports, and rail
|
||||
terminals — into a single readonly `allPorts` array and re-exports each
|
||||
sub-list. This is the default candidate set consumed by `findPort`,
|
||||
`findNearestPort`, and `findPortsInRadius` when callers do not supply a
|
||||
custom port set.
|
||||
|
||||
## Features
|
||||
|
||||
- Single import point for the full curated port universe.
|
||||
- Each sub-list is re-exported with its original name so consumers can
|
||||
scope lookups by transport mode.
|
||||
- Order is `seaPorts → airports → railTerminals`, matching the
|
||||
main-branch convention; consumers rely on `locode` as the identity.
|
||||
- All entries conform to the `Port` interface and validate against
|
||||
`PortSchema`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { allPorts } from "@suaveplan/ports";
|
||||
|
||||
console.log("total curated ports:", allPorts.length);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
allPorts,
|
||||
findPortsByType,
|
||||
findNearestPort,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
const air = findPortsByType("air");
|
||||
const nearestSeaToCargoHub = findNearestPort(
|
||||
{ latitude: 35.61, longitude: 139.73 },
|
||||
allPorts
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `allPorts: readonly Port[]` — combined sea + air + rail set.
|
||||
- `seaPorts`, `airports`, `railTerminals` — sub-lists re-exported.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Aggregation is computed once at module load via spread. Every lookup
|
||||
path keys on `locode` so callers can treat the aggregate as a flat
|
||||
namespace without worrying about cross-modal collisions (UN/LOCODE
|
||||
allocations are unique per terminal). Sub-list re-exports preserve
|
||||
referential identity so identity comparisons keep working.
|
||||
@@ -0,0 +1,57 @@
|
||||
# rail-terminals
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major rail freight terminals — primarily the
|
||||
inland container hubs on the New Silk Road and Trans-European rail
|
||||
freight corridors — exported as the readonly `railTerminals` array.
|
||||
Each record conforms to the `Port` interface (with `type: "rail"`) and
|
||||
carries a UIC railway code for cross-system identification.
|
||||
|
||||
## Features
|
||||
|
||||
- Coverage spans China's interior rail hubs, the Trans-Siberian and
|
||||
Trans-Mongolian routes, the European TEN-T corridors, and select
|
||||
North American intermodal centres.
|
||||
- Each record carries `uicCode` (where assigned) and `iataCode`
|
||||
(intermodal interchange terminals).
|
||||
- Facility tags reflect rail-specific capabilities (`intermodal`,
|
||||
`bonded_warehouse`, `container`, `bulk`, `roro`).
|
||||
- IANA timezones populated for every record so consumers can derive
|
||||
local times for cross-border schedules.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { railTerminals } from "@suaveplan/ports/data/rail-terminals";
|
||||
|
||||
console.log("rail terminal count:", railTerminals.length);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { railTerminals, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 50.1, longitude: 8.7 }, // Frankfurt
|
||||
railTerminals
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `railTerminals: readonly Port[]` — curated rail-freight terminal set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses a UN/LOCODE primary key. `uicCode` is populated only
|
||||
where the terminal has an assigned UIC code — pure intermodal yards
|
||||
without a UIC registration leave the field undefined. The dataset is
|
||||
curated rather than exhaustive: it captures the rail-freight hubs most
|
||||
likely to appear in cross-modal ETA calculations against this package's
|
||||
shipping-lane registry.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sea-ports-africa-mena
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major sea ports across Africa and the Middle East / North Africa region, exported as the
|
||||
readonly `seaPortsAfricaMena` array. Every record conforms to the `Port`
|
||||
interface and the runtime `PortSchema`. The list focuses on
|
||||
container-handling ports ranked by TEU throughput plus regional
|
||||
dry-bulk, tanker, and roll-on/roll-off hubs to give realistic coverage
|
||||
for shipping-lane calculations and proximity searches.
|
||||
|
||||
## Features
|
||||
|
||||
- ~14 entries covering the major maritime nations of Africa and the Middle East / North Africa region.
|
||||
- Container throughput hints in TEU (latest published figures at time
|
||||
of curation).
|
||||
- Maximum-vessel-size hints in TEU where available.
|
||||
- Facility tags (container, bulk, tanker, reefer, roro, passenger,
|
||||
free_trade_zone, bonded_warehouse) reflect real operational
|
||||
capabilities.
|
||||
- IANA timezones populated for every record so consumers can derive
|
||||
local times without lookup tables of their own.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { seaPortsAfricaMena } from "@suaveplan/ports/data/sea-ports";
|
||||
|
||||
console.log(seaPortsAfricaMena.length, "Africa and the Middle East / North Africa region sea ports");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { seaPortsAfricaMena, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 51.9, longitude: 4.49 },
|
||||
seaPortsAfricaMena
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `seaPortsAfricaMena: readonly Port[]` — curated Africa and the Middle East / North Africa region sea-port set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses a UN/LOCODE primary key. Throughput figures use
|
||||
underscores as digit separators for readability. The dataset is
|
||||
intentionally append-only — re-ordering entries would not break
|
||||
consumers but would generate a misleading diff against the main-branch
|
||||
source. This is a curated reference set, not a complete UN/LOCODE
|
||||
mirror; it targets the busiest container ports and the regional hubs
|
||||
most likely to appear in shipping-lane calculations against this
|
||||
Africa and the Middle East / North Africa region region.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sea-ports-americas
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major sea ports across the Americas, exported as the
|
||||
readonly `seaPortsAmericas` array. Every record conforms to the `Port`
|
||||
interface and the runtime `PortSchema`. The list focuses on
|
||||
container-handling ports ranked by TEU throughput plus regional
|
||||
dry-bulk, tanker, and roll-on/roll-off hubs to give realistic coverage
|
||||
for shipping-lane calculations and proximity searches.
|
||||
|
||||
## Features
|
||||
|
||||
- ~20 entries covering the major maritime nations of the Americas.
|
||||
- Container throughput hints in TEU (latest published figures at time
|
||||
of curation).
|
||||
- Maximum-vessel-size hints in TEU where available.
|
||||
- Facility tags (container, bulk, tanker, reefer, roro, passenger,
|
||||
free_trade_zone, bonded_warehouse) reflect real operational
|
||||
capabilities.
|
||||
- IANA timezones populated for every record so consumers can derive
|
||||
local times without lookup tables of their own.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { seaPortsAmericas } from "@suaveplan/ports/data/sea-ports";
|
||||
|
||||
console.log(seaPortsAmericas.length, "the Americas sea ports");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { seaPortsAmericas, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 51.9, longitude: 4.49 },
|
||||
seaPortsAmericas
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `seaPortsAmericas: readonly Port[]` — curated the Americas sea-port set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses a UN/LOCODE primary key. Throughput figures use
|
||||
underscores as digit separators for readability. The dataset is
|
||||
intentionally append-only — re-ordering entries would not break
|
||||
consumers but would generate a misleading diff against the main-branch
|
||||
source. This is a curated reference set, not a complete UN/LOCODE
|
||||
mirror; it targets the busiest container ports and the regional hubs
|
||||
most likely to appear in shipping-lane calculations against this
|
||||
the Americas region.
|
||||
@@ -0,0 +1,59 @@
|
||||
# sea-ports-asia
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major sea ports across Asia, exported as the readonly
|
||||
`seaPortsAsia` array. Every record conforms to the `Port` interface and
|
||||
the runtime `PortSchema`. The list focuses on container-handling ports
|
||||
ranked by TEU throughput plus regional dry-bulk and tanker hubs to give
|
||||
realistic coverage for shipping-lane calculations.
|
||||
|
||||
## Features
|
||||
|
||||
- ~30 entries covering China, Japan, South Korea, Hong Kong, Taiwan,
|
||||
Singapore, Malaysia, Vietnam, Thailand, Indonesia, the Philippines,
|
||||
India, Sri Lanka, Bangladesh, and Pakistan.
|
||||
- Container throughput hints in TEU (latest published figures at time
|
||||
of curation).
|
||||
- Maximum-vessel-size hints in TEU.
|
||||
- Facility tags (container, bulk, tanker, reefer, free_trade_zone,
|
||||
passenger, roro) reflect real operational capabilities.
|
||||
- IANA timezones populated for every record so consumers can derive
|
||||
local times without lookup tables of their own.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { seaPortsAsia } from "@suaveplan/ports/data/sea-ports";
|
||||
|
||||
console.log(seaPortsAsia.length, "Asia sea ports");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { seaPortsAsia, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 22.32, longitude: 114.17 },
|
||||
seaPortsAsia
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `seaPortsAsia: readonly Port[]` — curated Asia sea-port set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses a UN/LOCODE primary key. Some throughput figures use
|
||||
underscores as digit separators for readability. The dataset is
|
||||
intentionally append-only: re-ordering entries would not break
|
||||
consumers but would generate a misleading diff against the main-branch
|
||||
source. Curated rather than complete — this is not a substitute for a
|
||||
live UN/LOCODE database, it's a fixed reference set for ETA and
|
||||
proximity calculations against the world's busiest container ports.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sea-ports-europe
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major sea ports across Europe, exported as the
|
||||
readonly `seaPortsEurope` array. Every record conforms to the `Port`
|
||||
interface and the runtime `PortSchema`. The list focuses on
|
||||
container-handling ports ranked by TEU throughput plus regional
|
||||
dry-bulk, tanker, and roll-on/roll-off hubs to give realistic coverage
|
||||
for shipping-lane calculations and proximity searches.
|
||||
|
||||
## Features
|
||||
|
||||
- ~24 entries covering the major maritime nations of Europe.
|
||||
- Container throughput hints in TEU (latest published figures at time
|
||||
of curation).
|
||||
- Maximum-vessel-size hints in TEU where available.
|
||||
- Facility tags (container, bulk, tanker, reefer, roro, passenger,
|
||||
free_trade_zone, bonded_warehouse) reflect real operational
|
||||
capabilities.
|
||||
- IANA timezones populated for every record so consumers can derive
|
||||
local times without lookup tables of their own.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { seaPortsEurope } from "@suaveplan/ports/data/sea-ports";
|
||||
|
||||
console.log(seaPortsEurope.length, "Europe sea ports");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { seaPortsEurope, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 51.9, longitude: 4.49 },
|
||||
seaPortsEurope
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `seaPortsEurope: readonly Port[]` — curated Europe sea-port set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses a UN/LOCODE primary key. Throughput figures use
|
||||
underscores as digit separators for readability. The dataset is
|
||||
intentionally append-only — re-ordering entries would not break
|
||||
consumers but would generate a misleading diff against the main-branch
|
||||
source. This is a curated reference set, not a complete UN/LOCODE
|
||||
mirror; it targets the busiest container ports and the regional hubs
|
||||
most likely to appear in shipping-lane calculations against this
|
||||
Europe region.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sea-ports-oceania
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated dataset of major sea ports across Oceania, exported as the
|
||||
readonly `seaPortsOceania` array. Every record conforms to the `Port`
|
||||
interface and the runtime `PortSchema`. The list focuses on
|
||||
container-handling ports ranked by TEU throughput plus regional
|
||||
dry-bulk, tanker, and roll-on/roll-off hubs to give realistic coverage
|
||||
for shipping-lane calculations and proximity searches.
|
||||
|
||||
## Features
|
||||
|
||||
- ~10 entries covering the major maritime nations of Oceania.
|
||||
- Container throughput hints in TEU (latest published figures at time
|
||||
of curation).
|
||||
- Maximum-vessel-size hints in TEU where available.
|
||||
- Facility tags (container, bulk, tanker, reefer, roro, passenger,
|
||||
free_trade_zone, bonded_warehouse) reflect real operational
|
||||
capabilities.
|
||||
- IANA timezones populated for every record so consumers can derive
|
||||
local times without lookup tables of their own.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { seaPortsOceania } from "@suaveplan/ports/data/sea-ports";
|
||||
|
||||
console.log(seaPortsOceania.length, "Oceania sea ports");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { seaPortsOceania, findNearestPort } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort(
|
||||
{ latitude: 51.9, longitude: 4.49 },
|
||||
seaPortsOceania
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `seaPortsOceania: readonly Port[]` — curated Oceania sea-port set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every entry uses a UN/LOCODE primary key. Throughput figures use
|
||||
underscores as digit separators for readability. The dataset is
|
||||
intentionally append-only — re-ordering entries would not break
|
||||
consumers but would generate a misleading diff against the main-branch
|
||||
source. This is a curated reference set, not a complete UN/LOCODE
|
||||
mirror; it targets the busiest container ports and the regional hubs
|
||||
most likely to appear in shipping-lane calculations against this
|
||||
Oceania region.
|
||||
@@ -0,0 +1,65 @@
|
||||
# sea-ports
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Aggregates every regional sea-port dataset (`seaPortsAsia`,
|
||||
`seaPortsEurope`, `seaPortsAmericas`, `seaPortsAfricaMena`,
|
||||
`seaPortsOceania`) into a single readonly `seaPorts` array and re-exports
|
||||
each regional sub-list. This is the curated set consumed by
|
||||
`@suaveplan/ports`' `findPort`, `findNearestPort`, and
|
||||
`findPortsInRadius` lookups when callers do not pass a custom port set.
|
||||
|
||||
## Features
|
||||
|
||||
- Single import point for the global sea-port set.
|
||||
- Every regional sub-list is re-exported with the original name so
|
||||
callers can scope a search to a region.
|
||||
- Order is `Asia → Europe → Americas → Africa/MENA → Oceania`, matching
|
||||
the main-branch convention; consumers rely on `locode` as the
|
||||
identity rather than array index.
|
||||
- All entries conform to the `Port` interface and validate against
|
||||
`PortSchema`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { seaPorts } from "@suaveplan/ports/data/sea-ports";
|
||||
|
||||
console.log("global sea-port count:", seaPorts.length);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
seaPortsAsia,
|
||||
seaPortsEurope,
|
||||
findPortsInRadius,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
const pair = [...seaPortsAsia, ...seaPortsEurope];
|
||||
const candidates = findPortsInRadius(
|
||||
{ latitude: 36, longitude: 30 },
|
||||
1500,
|
||||
pair
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `seaPorts: readonly Port[]` — global aggregate.
|
||||
- `seaPortsAsia`, `seaPortsEurope`, `seaPortsAmericas`,
|
||||
`seaPortsAfricaMena`, `seaPortsOceania` — regional sub-lists
|
||||
re-exported.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Aggregation is computed once at module load via spread. The cost is
|
||||
linear in the total port count and pays off immediately because every
|
||||
lookup-by-locode path hits the merged set. Regional sub-lists remain
|
||||
referentially distinct from their aggregate appearance, so identity
|
||||
checks (`port === seaPortsAsia[0]`) keep working after aggregation.
|
||||
@@ -0,0 +1,57 @@
|
||||
# shipping-lanes
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Curated registry of major global shipping lanes — Asia–Europe via Suez,
|
||||
Asia–Europe via Cape, Transpacific, Transatlantic, Europe–MENA, plus
|
||||
rail and air freight lanes — exported as the readonly `shippingLanes`
|
||||
array. Each lane carries an ordered waypoint chain that ETA, lane
|
||||
distance, and lane-progress calculations project vessel positions
|
||||
against.
|
||||
|
||||
## Features
|
||||
|
||||
- Sea lanes: Asia–Europe via Suez, Asia–Europe via Cape, Transpacific
|
||||
Asia–US West Coast, Transatlantic Europe–US East Coast.
|
||||
- Rail lane: China–Europe via Trans-Siberian / Trans-Mongolian.
|
||||
- Air lane: Asia–Europe.
|
||||
- Each lane carries `typicalTransitDays` and `typicalSpeedKnots`
|
||||
(sea/rail) or `typicalSpeedKmh` (air/rail metric).
|
||||
- Waypoint chains capture strait passages (Malacca, Bab el-Mandeb,
|
||||
Gibraltar, Suez Canal) so lane-progress reporting reflects real
|
||||
navigational milestones.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { shippingLanes } from "@suaveplan/ports/data/shipping-lanes";
|
||||
|
||||
console.log("curated lane count:", shippingLanes.length);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { shippingLanes, estimateLaneDistance } from "@suaveplan/ports";
|
||||
|
||||
for (const lane of shippingLanes) {
|
||||
console.log(lane.id, estimateLaneDistance(lane), "nm");
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `shippingLanes: readonly ShippingLane[]` — curated lane set.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Waypoint chains are coarse — they capture the strategic geography of
|
||||
the route, not turn-by-turn navigation. Distance and ETA computations
|
||||
are therefore representative of typical transit, not exact in-water
|
||||
distance with weather routing. Each lane id is stable across versions
|
||||
so external systems can reference lanes by string id without worrying
|
||||
about array reorder.
|
||||
69
docs/geospatial/logistics/ports/src/distance/distance.md
Normal file
69
docs/geospatial/logistics/ports/src/distance/distance.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# distance
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Wraps `@suaveplan/geo`'s `greatCircleDistance` against a sphere whose
|
||||
radius matches the IUGG mean Earth radius (6 371 km), bound once as the
|
||||
exported `EARTH_SPHERE` ellipsoid. The package's lane-distance,
|
||||
lane-progress, ETA, and nearest-port modules all consume these wrappers
|
||||
so the entire package agrees on a single radius and the rounding policy
|
||||
is centralised.
|
||||
|
||||
## Features
|
||||
|
||||
- `distanceNm(a, b)` — great-circle distance in nautical miles, rounded
|
||||
to two decimals.
|
||||
- `distanceKm(a, b)` — same in kilometres, rounded to two decimals.
|
||||
- `distanceMetresRaw(a, b)` — unrounded metres, useful for accumulating
|
||||
segment sums without compounding rounding error.
|
||||
- Symmetric: `distanceNm(a, b) === distanceNm(b, a)` to floating
|
||||
precision.
|
||||
- Non-negative for any valid coordinate pair.
|
||||
- Coordinate inputs use `@suaveplan/geo`'s `SphericalCoord` shape so the
|
||||
wrappers are interoperable with the entire geospatial stack.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { distanceNm } from "@suaveplan/ports";
|
||||
|
||||
const nm = distanceNm(
|
||||
{ latitude: 1.26, longitude: 103.82 },
|
||||
{ latitude: 51.9, longitude: 4.49 }
|
||||
);
|
||||
console.log(nm); // ~5800 nautical miles
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { distanceMetresRaw } from "@suaveplan/ports";
|
||||
|
||||
function totalMetres(coords: { latitude: number; longitude: number }[]): number {
|
||||
let total = 0;
|
||||
for (let i = 0; i < coords.length - 1; i++) {
|
||||
total += distanceMetresRaw(coords[i], coords[i + 1]);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `distanceNm(a, b)` → `number`.
|
||||
- `distanceKm(a, b)` → `number`.
|
||||
- `distanceMetresRaw(a, b)` → `number` (unrounded).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
All three wrappers route through the single `EARTH_SPHERE` constant so
|
||||
the cost of ellipsoid construction is paid once at module load. Rounded
|
||||
helpers multiply by 100, round, then divide — this matches the
|
||||
main-branch rounding convention so cross-version diffs remain
|
||||
byte-for-byte stable on the rounded values. `distanceMetresRaw` is
|
||||
deliberately exposed for callers that need full precision (segment
|
||||
accumulation in lane-distance, deviation-ratio computation in ETA).
|
||||
@@ -0,0 +1,96 @@
|
||||
# error-codes
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Declares the eight stable error codes emitted by `@suaveplan/ports` and
|
||||
the matching typed `PortsError` subclasses. Every public function in the
|
||||
package throws one of these subclasses when a precondition fails, so
|
||||
downstream consumers — loggers, problem+json responders, retry middleware
|
||||
— can switch on `.code` without re-implementing the classifier. Codes
|
||||
flow through `defineCodes("PORTS", [...])` from `@suaveplan/error` so the
|
||||
full namespace is `PORTS.NO_PORTS`, `PORTS.INVALID_RADIUS`, and so on.
|
||||
|
||||
## Features
|
||||
|
||||
- Eight codes: `NO_PORTS`, `INVALID_RADIUS`,
|
||||
`INSUFFICIENT_WAYPOINTS`, `INVALID_SPEED`, `MISSING_SPEED`,
|
||||
`PORT_NOT_FOUND`, `LANE_NOT_FOUND`, `INVALID_LANE`.
|
||||
- Eight typed subclasses, each extending `PortsError` which itself
|
||||
extends `SuaveplanError<PortsErrorCode>`.
|
||||
- Context carriers — `PortsPortNotFoundError` exposes `locode`,
|
||||
`PortsLaneNotFoundError` exposes `laneId`, `PortsInvalidLaneError`
|
||||
exposes `issues` (Zod issue array).
|
||||
- i18n-ready: every constructor accepts a `message` override defaulting
|
||||
to an i18n key (`ports.errors.<camelCase>`).
|
||||
- Cause-chain pass-through (`cause`, `traceId`, `context`) honours the
|
||||
full `SuaveplanErrorOptions` shape.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
PortsNoPortsError,
|
||||
PortsInvalidRadiusError,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
function safeFind(): void {
|
||||
try {
|
||||
// ...
|
||||
} catch (err) {
|
||||
if (err instanceof PortsInvalidRadiusError) {
|
||||
// 400 Bad Request
|
||||
} else if (err instanceof PortsNoPortsError) {
|
||||
// 503 Service Unavailable
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
PORTS_CODES,
|
||||
PortsPortNotFoundError,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
const err = new PortsPortNotFoundError({
|
||||
locode: "XXNON",
|
||||
traceId: "01HXYZ",
|
||||
context: { source: "rest" },
|
||||
});
|
||||
|
||||
switch (err.code) {
|
||||
case PORTS_CODES.PORT_NOT_FOUND:
|
||||
// Render i18n-aware 404 message with locode in context.
|
||||
break;
|
||||
default:
|
||||
throw err;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `PORTS_CODES` — frozen registry: `{ NO_PORTS, INVALID_RADIUS,
|
||||
INSUFFICIENT_WAYPOINTS, INVALID_SPEED, MISSING_SPEED, PORT_NOT_FOUND,
|
||||
LANE_NOT_FOUND, INVALID_LANE }`.
|
||||
- `PortsErrorCode` — union type of every code.
|
||||
- `PortsError` — abstract base.
|
||||
- `PortsNoPortsError`, `PortsInvalidRadiusError`,
|
||||
`PortsInsufficientWaypointsError`, `PortsInvalidSpeedError`,
|
||||
`PortsMissingSpeedError`, `PortsPortNotFoundError`,
|
||||
`PortsLaneNotFoundError`, `PortsInvalidLaneError`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The registry is registered eagerly at module load via `defineCodes` so
|
||||
collisions across packages fail fast. Each subclass forwards the
|
||||
`pickOpts` filtered options bundle to `SuaveplanError`'s protected
|
||||
constructor so context, cause, and traceId remain optional but
|
||||
correctly typed. Default message keys follow `ports.errors.<camelCase>`
|
||||
so an i18n resolver can translate them at the boundary; consumers that
|
||||
prefer literal messages pass `{ message: "..." }`.
|
||||
70
docs/geospatial/logistics/ports/src/eta/eta.md
Normal file
70
docs/geospatial/logistics/ports/src/eta/eta.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# eta
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Estimates the time of arrival for a vessel travelling along a
|
||||
`ShippingLane`. Resolves an effective cruise speed from (in order) an
|
||||
explicit override, the lane's `typicalSpeedKnots`, or the km/h
|
||||
equivalent, then divides the remaining-distance figure produced by
|
||||
`findPositionOnLane` to compute `etaHours` and projects to an
|
||||
`etaDate`. The result includes a `confidence` band derived from
|
||||
deviation between travelled+remaining and the lane's total length so
|
||||
operators can flag vessels that have drifted off the planned route.
|
||||
|
||||
## Features
|
||||
|
||||
- Three-tier speed resolution: explicit override → lane knots → lane
|
||||
km/h (converted at 1.852 km/nm).
|
||||
- Injectable wall-clock — `opts.clock?: Clock` defaults to `Date.now`.
|
||||
- Explicit `opts.departureTime?` overrides the clock.
|
||||
- Confidence band: `high` (off-lane ≤3% of total length), `medium`
|
||||
(≤10%), `low` (more than 10%).
|
||||
- Throws `PortsInvalidSpeedError` on non-positive speed, and
|
||||
`PortsMissingSpeedError` when no speed source resolves.
|
||||
- Emits `ports.eta_total{confidence}` counter and
|
||||
`ports.eta_duration_ms` histogram per call.
|
||||
- `etaHours` rounded to two decimals; `etaDate` uses unrounded hours
|
||||
for sub-minute precision.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { estimateETA, getShippingLaneStrict } from "@suaveplan/ports";
|
||||
|
||||
const suez = getShippingLaneStrict("asia-europe-suez");
|
||||
const result = estimateETA({ latitude: 12.5, longitude: 43.3 }, suez);
|
||||
console.log(result.etaDate, result.confidence);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { estimateETA } from "@suaveplan/ports";
|
||||
import { createClock } from "@suaveplan/testing";
|
||||
|
||||
const clock = createClock(new Date("2026-05-25T00:00:00Z"));
|
||||
const eta = estimateETA(coord, lane, 18, {
|
||||
clock: () => clock.now(),
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `estimateETA(currentCoord, lane, avgSpeedKnots?, opts?)` → `ETAEstimate`.
|
||||
- `EstimateETAOptions`:
|
||||
- `departureTime?: Date` — explicit override.
|
||||
- `clock?: Clock` — wall-clock source (`() => number`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Speed resolution is centralised in a private helper so the order of
|
||||
fallbacks stays explicit. The `etaDate` is computed from the unrounded
|
||||
hour figure — rounding to two decimals before `* 3_600_000` would
|
||||
quantise arrival to 36-second buckets. The deviation ratio uses the
|
||||
lane's full great-circle length as the denominator so the band stays
|
||||
calibrated independent of vessel position. `performance.now()` is
|
||||
preferred over `Date.now()` for the duration histogram when available.
|
||||
78
docs/geospatial/logistics/ports/src/find-port/find-port.md
Normal file
78
docs/geospatial/logistics/ports/src/find-port/find-port.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# find-port
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides the lookup surface for `@suaveplan/ports`. Every public
|
||||
function consumes the curated `allPorts` registry by default but accepts
|
||||
an explicit `ports` argument so consumers can scope a search to a
|
||||
subset (a tenant-specific allowlist, an environment-restricted set, or
|
||||
test fixtures). UN/LOCODE lookups go through a lazily-built `Map`
|
||||
index keyed on the uppercased locode so the call cost is O(1) once the
|
||||
index is initialised.
|
||||
|
||||
## Features
|
||||
|
||||
- `findPort(locode)` — O(1) lookup by UN/LOCODE, case-insensitive,
|
||||
returns `undefined` on miss.
|
||||
- `findPortStrict(locode)` — throws `PortsPortNotFoundError`
|
||||
(`PORTS.PORT_NOT_FOUND`) on miss, carrying the locode in error
|
||||
context.
|
||||
- `findPortsByCountry(country, ports?)` — ISO 3166-1 alpha-2 filter
|
||||
(input uppercased).
|
||||
- `findPortsByType(type, ports?)` — `PortType` filter.
|
||||
- `searchPorts(query, ports?)` — case-insensitive substring match on
|
||||
`name`.
|
||||
- Every call emits the OTel counter `ports.lookup_total{kind}`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { findPort, findPortsByCountry } from "@suaveplan/ports";
|
||||
|
||||
const shanghai = findPort("cnsha");
|
||||
const dutchPorts = findPortsByCountry("nl");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
findPortStrict,
|
||||
findPortsByType,
|
||||
searchPorts,
|
||||
PortsPortNotFoundError,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
try {
|
||||
const port = findPortStrict("ZZZZZ");
|
||||
} catch (err) {
|
||||
if (err instanceof PortsPortNotFoundError) {
|
||||
// 404
|
||||
}
|
||||
}
|
||||
|
||||
const sea = findPortsByType("sea");
|
||||
const named = searchPorts("rotterdam", sea); // scoped search
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `findPort(locode: string): Port | undefined`.
|
||||
- `findPortStrict(locode: string): Port` — throws on miss.
|
||||
- `findPortsByCountry(country: string, ports?: readonly Port[]): Port[]`.
|
||||
- `findPortsByType(type: PortType, ports?: readonly Port[]): Port[]`.
|
||||
- `searchPorts(query: string, ports?: readonly Port[]): Port[]`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The locode index is built lazily on first lookup and cached at module
|
||||
scope for the lifetime of the process. Re-importing the module never
|
||||
duplicates the index because ES modules are singletons within a realm.
|
||||
Filtering helpers return fresh arrays so callers can mutate without
|
||||
affecting the curated registry. The substring match is allocator-light
|
||||
— it lowercases the query once and the port name once per record, no
|
||||
regex compilation in the hot path.
|
||||
@@ -0,0 +1,65 @@
|
||||
# lane-distance
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Computes great-circle distance metrics for a `ShippingLane`'s waypoint
|
||||
chain. `estimateLaneDistance` sums every segment for the lane total;
|
||||
`cumulativeDistances` returns the per-waypoint cumulative array used by
|
||||
`findPositionOnLane` and `estimateETA`. Both functions reject lanes
|
||||
with fewer than two waypoints because any distance metric over a
|
||||
single-point chain is undefined.
|
||||
|
||||
## Features
|
||||
|
||||
- `estimateLaneDistance(lane)` — total nautical miles, rounded to two
|
||||
decimals.
|
||||
- `cumulativeDistances(lane)` — `number[]` of length `waypoints.length`
|
||||
where entry 0 is 0 and entry i is the cumulative nm distance from
|
||||
`waypoints[0]` to `waypoints[i]`.
|
||||
- Throws `PortsInsufficientWaypointsError`
|
||||
(`PORTS.INSUFFICIENT_WAYPOINTS`) for lanes shorter than two
|
||||
waypoints.
|
||||
- Emits the `ports.lane_distance_duration_ms` histogram.
|
||||
- Uses the package's shared `distanceNm` helper so the rounding policy
|
||||
is centralised.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { estimateLaneDistance, getShippingLaneStrict } from "@suaveplan/ports";
|
||||
|
||||
const suez = getShippingLaneStrict("asia-europe-suez");
|
||||
const totalNm = estimateLaneDistance(suez);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { cumulativeDistances, getShippingLaneStrict } from "@suaveplan/ports";
|
||||
|
||||
const lane = getShippingLaneStrict("asia-europe-suez");
|
||||
const cum = cumulativeDistances(lane);
|
||||
// cum[i] - cum[i-1] gives segment length between waypoints i-1 and i.
|
||||
for (let i = 1; i < cum.length; i++) {
|
||||
console.log("segment", i, cum[i] - cum[i - 1], "nm");
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `estimateLaneDistance(lane: ShippingLane): number`.
|
||||
- `cumulativeDistances(lane: ShippingLane): number[]`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Each segment runs through `distanceNm` which itself routes through
|
||||
`@suaveplan/geo`'s `greatCircleDistance` against the
|
||||
`EARTH_SPHERE` constant. The cumulative function recomputes segment
|
||||
distances rather than reusing the running sum from
|
||||
`estimateLaneDistance` — this keeps the two functions independent so
|
||||
callers can use either in isolation without paying for both. The
|
||||
trivial perf cost is acceptable at curated-dataset scale.
|
||||
@@ -0,0 +1,67 @@
|
||||
# lane-lookup
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Public lookup surface for the curated `shippingLanes` registry. Mirrors
|
||||
the port-lookup module in style: an O(1) `Map` index by lane id, mode
|
||||
filtering, and a "get all" accessor for downstream consumers that want
|
||||
to render lists or build their own indices. The strict variant of
|
||||
`getShippingLane` throws a typed `PortsLaneNotFoundError` so consumers
|
||||
can branch on `.code` rather than nullable returns.
|
||||
|
||||
## Features
|
||||
|
||||
- `getShippingLane(id)` — lazy `Map<string, ShippingLane>` lookup.
|
||||
- `getShippingLaneStrict(id)` — throws on miss with `laneId` in error
|
||||
context.
|
||||
- `getShippingLanesByMode(mode, lanes?)` — `LaneMode` filter.
|
||||
- `getAllShippingLanes()` — returns the curated registry directly
|
||||
(readonly).
|
||||
- Every call emits `ports.lane_lookup_total{mode}` (mode is `all` for
|
||||
`getAllShippingLanes` and miss-paths).
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { getShippingLane, getShippingLanesByMode } from "@suaveplan/ports";
|
||||
|
||||
const suez = getShippingLane("asia-europe-suez");
|
||||
const seaLanes = getShippingLanesByMode("sea");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
getShippingLaneStrict,
|
||||
PortsLaneNotFoundError,
|
||||
} from "@suaveplan/ports";
|
||||
|
||||
try {
|
||||
const lane = getShippingLaneStrict("missing-lane");
|
||||
} catch (err) {
|
||||
if (err instanceof PortsLaneNotFoundError) {
|
||||
// err.context.laneId === "missing-lane"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `getShippingLane(id: string): ShippingLane | undefined`.
|
||||
- `getShippingLaneStrict(id: string): ShippingLane`.
|
||||
- `getShippingLanesByMode(mode: LaneMode, lanes?: readonly ShippingLane[]):
|
||||
ShippingLane[]`.
|
||||
- `getAllShippingLanes(): readonly ShippingLane[]`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The lane id index is built lazily on first call and cached at module
|
||||
scope for the lifetime of the process. `getAllShippingLanes` returns
|
||||
the curated registry directly rather than a copy — callers must not
|
||||
mutate it. Filtering helpers return fresh arrays so callers retain
|
||||
mutation safety on derived sets.
|
||||
@@ -0,0 +1,71 @@
|
||||
# lane-progress
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Projects a vessel position onto a shipping lane and reports how much of
|
||||
the lane has been traversed, how much remains, and which waypoint is
|
||||
nearest. Used by `estimateETA` to derive the remaining-distance figure
|
||||
that feeds the ETA computation, and exposed publicly so downstream UIs
|
||||
can render lane-progress bars without re-implementing the geometry.
|
||||
|
||||
## Features
|
||||
|
||||
- Finds the nearest waypoint by great-circle distance.
|
||||
- Estimates fractional progress within the nearest waypoint's adjacent
|
||||
segment (prev or next, whichever is closer).
|
||||
- Returns `{ progress, remainingDistanceNm, distanceTravelledNm,
|
||||
nearestWaypointIndex }` with `progress` clamped to `[0, 1]` and
|
||||
rounded to four decimals; distances clamped to `>= 0` and rounded to
|
||||
two decimals.
|
||||
- Throws `PortsInsufficientWaypointsError` for lanes with fewer than
|
||||
two waypoints.
|
||||
- Handles degenerate zero-length lanes (every waypoint identical) by
|
||||
returning zero progress.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { findPositionOnLane, getShippingLaneStrict } from "@suaveplan/ports";
|
||||
|
||||
const lane = getShippingLaneStrict("asia-europe-suez");
|
||||
const progress = findPositionOnLane(
|
||||
{ latitude: 12.5, longitude: 43.3 },
|
||||
lane
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { findPositionOnLane, type ShippingLane } from "@suaveplan/ports";
|
||||
|
||||
function eta(
|
||||
coord: { latitude: number; longitude: number },
|
||||
lane: ShippingLane,
|
||||
knots: number,
|
||||
departure: Date
|
||||
): Date {
|
||||
const { remainingDistanceNm } = findPositionOnLane(coord, lane);
|
||||
const hours = remainingDistanceNm / knots;
|
||||
return new Date(departure.getTime() + hours * 3_600_000);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `findPositionOnLane(coord, lane): LaneProgress`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The "nearest waypoint" pass is a linear scan. The adjacent-segment
|
||||
projection uses a 1D ratio (distance-to-nearest / segment-length)
|
||||
clamped to `[0, 1]`; this is an approximation that works well when the
|
||||
coord is reasonably close to the lane and degrades gracefully when the
|
||||
coord is far off-lane (the ETA `confidence` band downgrades to
|
||||
`medium`/`low`). The function never throws on out-of-lane coords —
|
||||
callers receive a clamped progress fraction and a non-negative
|
||||
remaining distance regardless of how far off-lane the vessel is.
|
||||
@@ -0,0 +1,65 @@
|
||||
# nearest-port
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Public proximity-search API. `findNearestPort` returns the single
|
||||
closest port to a coordinate; `findPortsInRadius` returns every port
|
||||
within a great-circle radius, sorted ascending by distance. When called
|
||||
against the default `allPorts` registry the work delegates to a
|
||||
pre-built `PortSpatialIndex`; explicit overrides fall through to a
|
||||
linear scan so the caller retains full control over the candidate set.
|
||||
|
||||
## Features
|
||||
|
||||
- O(neighbours) on the default registry via `PortSpatialIndex`.
|
||||
- Linear scan fallback for custom port sets.
|
||||
- `findNearestPort` throws `PortsNoPortsError` (`PORTS.NO_PORTS`) on an
|
||||
empty input.
|
||||
- `findPortsInRadius` throws `PortsInvalidRadiusError`
|
||||
(`PORTS.INVALID_RADIUS`) on `radiusKm <= 0`.
|
||||
- Distance returned in kilometres, rounded to two decimals.
|
||||
- Emits `ports.lookup_total{kind}` per call and the
|
||||
`ports.find_nearest_duration_ms` histogram for `findNearestPort`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { findNearestPort, findPortsInRadius } from "@suaveplan/ports";
|
||||
|
||||
const nearest = findNearestPort({ latitude: 1.26, longitude: 103.82 });
|
||||
const within500 = findPortsInRadius(
|
||||
{ latitude: 1.26, longitude: 103.82 },
|
||||
500
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { findNearestPort, findPortsByType } from "@suaveplan/ports";
|
||||
|
||||
const onlySea = findPortsByType("sea");
|
||||
const nearestSea = findNearestPort(
|
||||
{ latitude: 51.9, longitude: 4.49 },
|
||||
onlySea
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `findNearestPort(coord, ports?)` — `NearestPortResult`.
|
||||
- `findPortsInRadius(coord, radiusKm, ports?)` — `NearestPortResult[]`,
|
||||
sorted ascending by `distanceKm`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The spatial index is built lazily on first call against the default
|
||||
registry. The duration histogram uses `performance.now()` when
|
||||
available, falling back to `Date.now()` so the package stays usable in
|
||||
runtimes without a high-resolution timer. Custom port sets bypass the
|
||||
index because building a fresh index per call would dominate the
|
||||
linear-scan cost we are trying to avoid.
|
||||
72
docs/geospatial/logistics/ports/src/otel/otel.md
Normal file
72
docs/geospatial/logistics/ports/src/otel/otel.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# otel
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Wires `@suaveplan/ports`' OpenTelemetry instrumentation: three counters
|
||||
that track lookup volume per kind, lane lookup volume per mode, and ETA
|
||||
estimations per confidence band; plus three histograms recording the
|
||||
duration of `findNearestPort`, `estimateLaneDistance`, and `estimateETA`.
|
||||
Every public function in the package emits exactly one counter
|
||||
increment and (where relevant) one histogram sample on each call so
|
||||
operators can build dashboards without re-instrumenting consumers.
|
||||
|
||||
## Features
|
||||
|
||||
- Three counters: `ports.lookup_total{kind}`,
|
||||
`ports.lane_lookup_total{mode}`, `ports.eta_total{confidence}`.
|
||||
- Three histograms: `ports.find_nearest_duration_ms`,
|
||||
`ports.lane_distance_duration_ms`, `ports.eta_duration_ms`, all in
|
||||
millisecond units.
|
||||
- `refreshPortsMeters()` rebinds every instrument against the current
|
||||
global `MeterProvider` — tests call this after wiring an in-memory
|
||||
exporter so subsequent record calls land on the test provider rather
|
||||
than the module-load meter.
|
||||
- Recorder helpers (`recordPortLookup`, `recordLaneLookup`,
|
||||
`recordEta`, `record*Duration`) are 1:1 with the named instruments
|
||||
and accept the literal-typed label values directly.
|
||||
- Meter is named `@suaveplan/ports` at version `0.1.0`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { recordPortLookup } from "@suaveplan/ports";
|
||||
|
||||
recordPortLookup("findPort");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import { refreshPortsMeters } from "@suaveplan/ports";
|
||||
|
||||
const provider = new MeterProvider();
|
||||
metrics.setGlobalMeterProvider(provider);
|
||||
refreshPortsMeters();
|
||||
// Now every subsequent ports operation lands on `provider`.
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `recordPortLookup(kind)` — kind ∈ `findPort | byCountry | byType |
|
||||
search | findNearest | inRadius`.
|
||||
- `recordLaneLookup(mode)` — mode ∈ `sea | rail | air | all`.
|
||||
- `recordEta(confidence)` — confidence ∈ `high | medium | low`.
|
||||
- `recordFindNearestDuration(ms)`,
|
||||
`recordLaneDistanceDuration(ms)`, `recordEtaDuration(ms)`.
|
||||
- `refreshPortsMeters()` — re-resolve every instrument.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Instruments are module-scope `let` bindings so `refreshPortsMeters` can
|
||||
swap them in place; otherwise the OTel API would route emissions to the
|
||||
original meter even after `setGlobalMeterProvider`. The histograms use
|
||||
the `ms` unit hint so exporters render them as durations. The package
|
||||
deliberately does not start its own spans — span management is the
|
||||
caller's responsibility, and instruments stay focused on aggregate
|
||||
behaviour rather than per-call tracing.
|
||||
74
docs/geospatial/logistics/ports/src/schemas/schemas.md
Normal file
74
docs/geospatial/logistics/ports/src/schemas/schemas.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# schemas
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines Zod schemas for every public type emitted by `@suaveplan/ports`.
|
||||
API plugins, message-queue handlers, and any other consumer that
|
||||
receives a `Port` or `ShippingLane` over the wire passes the payload
|
||||
through `PortSchema.parse(...)` / `ShippingLaneSchema.parse(...)` to
|
||||
guarantee shape before the curated functions touch the data. The
|
||||
schemas mirror the TypeScript interfaces in `types/port.types.ts`
|
||||
exactly so a runtime success implies a sound compile-time `Port` or
|
||||
`ShippingLane`.
|
||||
|
||||
## Features
|
||||
|
||||
- Every TS interface (`Port`, `ShippingLane`, `LaneProgress`,
|
||||
`ETAEstimate`, `NearestPortResult`, `SphericalCoord`) has a matching
|
||||
Zod schema.
|
||||
- Locode is regex-validated as five uppercase A–Z characters.
|
||||
- Country is regex-validated as ISO 3166-1 alpha-2 (two uppercase).
|
||||
- IATA code (when present) is regex-validated as three uppercase A–Z.
|
||||
- Latitude clamped to `[-90, 90]`; longitude to `[-180, 180]`.
|
||||
- `ShippingLaneSchema` enforces at least two waypoints — matching the
|
||||
precondition of every lane-distance / lane-progress / ETA function.
|
||||
- Throughput and capacity fields are non-negative integers.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { PortSchema } from "@suaveplan/ports";
|
||||
|
||||
const port = PortSchema.parse(payload); // throws on shape mismatch
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { ShippingLaneSchema, PortsInvalidLaneError } from "@suaveplan/ports";
|
||||
|
||||
const result = ShippingLaneSchema.safeParse(payload);
|
||||
if (!result.success) {
|
||||
throw new PortsInvalidLaneError({
|
||||
issues: result.error.issues,
|
||||
});
|
||||
}
|
||||
const lane = result.data;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `SphericalCoordSchema` — `{ latitude, longitude, altitude? }`.
|
||||
- `PortSchema` — full `Port` shape.
|
||||
- `PortTypeSchema` — `z.enum(["sea","air","rail","multimodal"])`.
|
||||
- `PortFacilitySchema` — 10-value enum.
|
||||
- `ShippingLaneSchema` — `id, name, mode, waypoints≥2, typical*?`.
|
||||
- `LaneModeSchema` — `z.enum(["sea","rail","air"])`.
|
||||
- `LaneProgressSchema` — clamped-progress object.
|
||||
- `ETAEstimateSchema` — `{ remainingDistanceNm, etaHours, etaDate,
|
||||
confidence }`.
|
||||
- `NearestPortResultSchema` — `{ port, distanceKm }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Schemas use Zod's default-import form (`import z from "zod"`) as
|
||||
mandated by project rules. Optional fields are declared with
|
||||
`.optional()` so undefined and absence are interchangeable on the wire.
|
||||
The schemas do not enforce cross-field invariants (e.g. that
|
||||
`distanceTravelledNm + remainingDistanceNm <= totalLaneDistance`) —
|
||||
those invariants are guaranteed by the producing functions, not the
|
||||
inbound parse step.
|
||||
@@ -0,0 +1,76 @@
|
||||
# spatial-index
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`PortSpatialIndex` buckets a port set by 5-degree latitude/longitude
|
||||
cells at construction time so `findNearest` and `findInRadius` can run
|
||||
against a small candidate set instead of the full registry. The 3×3
|
||||
cell neighbourhood is ~600 km wide, so a 500-km radius query (the
|
||||
common case) visits at most nine cells. For the curated ~120-port
|
||||
dataset the absolute saving is small, but the API stays O(neighbours)
|
||||
as the dataset grows.
|
||||
|
||||
## Features
|
||||
|
||||
- 5° lat/lng cell bucketing — bucket key is
|
||||
`floor(lat / 5) + ":" + floor(lng / 5)`.
|
||||
- O(neighbours) `findNearest` that falls back to a full scan when the
|
||||
3×3 neighbourhood is empty (Pacific gyre, polar regions).
|
||||
- `findInRadius(coord, radiusKm)` returns ports within `radiusKm`
|
||||
sorted ascending by distance, switching to a full scan when the
|
||||
requested radius exceeds the half-width of the 3×3 neighbourhood
|
||||
(~830 km) to guarantee correctness.
|
||||
- Immutable after construction — no add/remove API, so callers rebuild
|
||||
to reflect updates and the index never falls out of sync with its
|
||||
source set.
|
||||
- `size` exposes the total indexed port count.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { PortSpatialIndex, allPorts } from "@suaveplan/ports";
|
||||
|
||||
const index = new PortSpatialIndex(allPorts);
|
||||
const nearest = index.findNearest({ latitude: 1.26, longitude: 103.82 });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { PortSpatialIndex } from "@suaveplan/ports";
|
||||
|
||||
// Tenant-scoped index built once per request.
|
||||
function buildIndex(allowed: Port[]): PortSpatialIndex {
|
||||
return new PortSpatialIndex(allowed);
|
||||
}
|
||||
|
||||
const tenant = buildIndex(allowedPortsForTenant);
|
||||
const nearby = tenant.findInRadius(
|
||||
{ latitude: 35.4, longitude: 139.6 },
|
||||
250
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new PortSpatialIndex(ports)` — pre-bucket the input.
|
||||
- `index.size` — total indexed port count.
|
||||
- `index.neighbourhood(coord)` — every port in the 3×3 cell around
|
||||
`coord`.
|
||||
- `index.findNearest(coord)` — nearest `NearestPortResult` or
|
||||
`undefined` when the index is empty.
|
||||
- `index.findInRadius(coord, radiusKm)` — sorted `NearestPortResult[]`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The 5° cell width was chosen because individual seas (the Med, the
|
||||
Caribbean, the Sea of Japan) span 2–4 cells, which keeps the
|
||||
neighbourhood candidate set tight without artificially excluding ports
|
||||
on cell boundaries. The radius branch threshold (`> 1.5 × 5° × 111`)
|
||||
ensures we never miss a port outside the neighbourhood: a circle of
|
||||
radius greater than half the neighbourhood width must be tested
|
||||
against the full set.
|
||||
80
docs/geospatial/logistics/ports/src/types/port.types.md
Normal file
80
docs/geospatial/logistics/ports/src/types/port.types.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# port.types
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines every type emitted by `@suaveplan/ports`: the curated `Port`
|
||||
record, the `ShippingLane` waypoint graph, the result types for lane
|
||||
projection (`LaneProgress`) and ETA estimation (`ETAEstimate`), and the
|
||||
discriminating unions for port type and facility tags. Every public
|
||||
function in the package consumes or returns one of these types so the
|
||||
file is the single source of truth for the package's data contract.
|
||||
|
||||
## Features
|
||||
|
||||
- `Port` carries a UN/LOCODE primary key plus geographic coordinate,
|
||||
country, type, timezone, and facility tags.
|
||||
- Optional capacity hints (`maxVesselSizeTEU`, `maxVesselSizeDWT`,
|
||||
`runwayLengthM`, `annualThroughputTEU`, `annualThroughputTonnes`).
|
||||
- `PortType` discriminates between `sea | air | rail | multimodal`.
|
||||
- `ShippingLane.waypoints` is an ordered `SphericalCoord` array.
|
||||
- `LaneMode` is `sea | rail | air`.
|
||||
- `ETAEstimate.confidence` is `high | medium | low` derived from
|
||||
deviation between travelled+remaining and the lane's total distance.
|
||||
- Re-exports `SphericalCoord` from `@suaveplan/geo` so consumers only
|
||||
import from a single canonical location.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import type { Port } from "@suaveplan/ports";
|
||||
|
||||
const port: Port = {
|
||||
locode: "SGSIN",
|
||||
name: "Singapore",
|
||||
country: "SG",
|
||||
coordinate: { latitude: 1.26, longitude: 103.82 },
|
||||
type: "sea",
|
||||
timezone: "Asia/Singapore",
|
||||
facilities: ["container", "bulk"],
|
||||
};
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import type { ETAEstimate, ShippingLane } from "@suaveplan/ports";
|
||||
|
||||
function logEta(eta: ETAEstimate, lane: ShippingLane): void {
|
||||
console.info({
|
||||
lane: lane.id,
|
||||
etaHours: eta.etaHours,
|
||||
confidence: eta.confidence,
|
||||
remainingNm: eta.remainingDistanceNm,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `Port` — curated port record with optional capacity hints.
|
||||
- `PortType` — `"sea" | "air" | "rail" | "multimodal"`.
|
||||
- `PortFacility` — 10-value union: container, bulk, roro, tanker,
|
||||
passenger, cargo, reefer, intermodal, bonded_warehouse,
|
||||
free_trade_zone.
|
||||
- `ShippingLane` — id, name, mode, waypoints, optional typical transit
|
||||
days and speeds (knots or km/h).
|
||||
- `LaneMode` — `"sea" | "rail" | "air"`.
|
||||
- `LaneProgress` — `{ progress, remainingDistanceNm,
|
||||
distanceTravelledNm, nearestWaypointIndex }`.
|
||||
- `ETAEstimate` — `{ remainingDistanceNm, etaHours, etaDate, confidence }`.
|
||||
- `NearestPortResult` — `{ port, distanceKm }`.
|
||||
- `SphericalCoord` — re-exported from `@suaveplan/geo`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Every interface is `readonly` end-to-end so curated records remain
|
||||
immutable across consumers. Optional fields are non-nullable
|
||||
unioned with `undefined` via TypeScript's exact-optional semantics —
|
||||
schemas in `schemas/schemas.ts` enforce the same shape at runtime for
|
||||
incoming payloads. The file deliberately contains no runtime code so
|
||||
type-only imports do not pay any bundle cost.
|
||||
Reference in New Issue
Block a user