Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View 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).

View File

@@ -0,0 +1,44 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { distanceKm, distanceMetresRaw, distanceNm } from "./distance.js";
const SINGAPORE = { latitude: 1.2644, longitude: 103.8198 };
const ROTTERDAM = { latitude: 51.9, longitude: 4.49 };
describe("distance helpers", () => {
it("distanceNm zero between identical coords", { timeout: 5000 }, () => {
expect(distanceNm(SINGAPORE, SINGAPORE)).toBe(0);
});
it("distanceKm zero between identical coords", { timeout: 5000 }, () => {
expect(distanceKm(SINGAPORE, SINGAPORE)).toBe(0);
});
it("distanceMetresRaw produces unrounded metres", { timeout: 5000 }, () => {
const raw = distanceMetresRaw(SINGAPORE, ROTTERDAM);
expect(raw).toBeGreaterThan(10_000_000);
expect(raw).toBeLessThan(12_000_000);
});
it("Singapore to Rotterdam great-circle is ~5800 nm", {
timeout: 5000,
}, () => {
const nm = distanceNm(SINGAPORE, ROTTERDAM);
expect(nm).toBeGreaterThan(5600);
expect(nm).toBeLessThan(6000);
});
it("Singapore to Rotterdam great-circle is ~10800 km", {
timeout: 5000,
}, () => {
const km = distanceKm(SINGAPORE, ROTTERDAM);
expect(km).toBeGreaterThan(10500);
expect(km).toBeLessThan(11200);
});
it("nm * 1.852 ≈ km within 1% tolerance", { timeout: 5000 }, () => {
const nm = distanceNm(SINGAPORE, ROTTERDAM);
const km = distanceKm(SINGAPORE, ROTTERDAM);
const expected = nm * 1.852;
expect(Math.abs(km - expected) / km).toBeLessThan(0.01);
});
});

View File

@@ -0,0 +1,31 @@
import type { SphericalCoord } from "@suaveplan/geo";
import { greatCircleDistance } from "@suaveplan/geo";
import { EARTH_SPHERE } from "../constants/index.js";
/**
* Great-circle distance between two coordinates in nautical miles. Uses
* the IUGG mean-radius sphere bound in `EARTH_SPHERE`. The result is
* rounded to two decimal places so equality comparisons remain stable
* across platforms.
*/
export function distanceNm(a: SphericalCoord, b: SphericalCoord): number {
const nm = greatCircleDistance(a, b, EARTH_SPHERE, "nauticalMile");
return Math.round(nm * 100) / 100;
}
/**
* Great-circle distance between two coordinates in kilometres. Mirrors
* `distanceNm` rounding behaviour for the kilometre unit.
*/
export function distanceKm(a: SphericalCoord, b: SphericalCoord): number {
const km = greatCircleDistance(a, b, EARTH_SPHERE, "kilometre");
return Math.round(km * 100) / 100;
}
/** Unrounded raw great-circle distance in metres (for internal use). */
export function distanceMetresRaw(
a: SphericalCoord,
b: SphericalCoord
): number {
return greatCircleDistance(a, b, EARTH_SPHERE, "metre");
}

View File

@@ -0,0 +1 @@
export { distanceKm, distanceMetresRaw, distanceNm } from "./distance.js";