Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
plugins/document-navigator/docs/geospatial/geo/geo/README.md
Normal file
69
plugins/document-navigator/docs/geospatial/geo/geo/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# @suaveplan/geo - Abstract spherical and ellipsoidal geometry — pure math with no planet-specific constants.
|
||||
|
||||
## Summary
|
||||
|
||||
Abstract spherical and ellipsoidal geometry — pure math with no planet-specific constants. Every operation that needs a body's shape takes an explicit `Ellipsoid` parameter.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [angular](./src/angular/angular.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [angular](./src/angular/angular.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [observability](./src/observability/observability.md) — OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
- [observability](./src/observability/observability.md) — OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [schemas](./src/types/schemas.md) — The `schemas` module exports Zod schemas that validate every public geometric type defined in `@suaveplan/geo`. The sch…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
<!-- end:auto-docs --> - [angular](./src/angular/angular.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/types/schemas.md) — The `schemas` module exports Zod schemas that validate every public geometric type defined in `@suaveplan/geo`. The sch…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability.md) — OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
- [angular](./src/angular/angular.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/types/schemas.md) — The `schemas` module exports Zod schemas that validate every public geometric type defined in `@suaveplan/geo`. The sch…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability.md) — OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
- [angular](./src/angular/angular.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [schemas](./src/types/schemas.md) — The `schemas` module exports Zod schemas that validate every public geometric type defined in `@suaveplan/geo`. The sch…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability.md) — OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
- [angular](./src/angular/angular.md) — <!-- begin:auto-docs --> _(no submodules)_- [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_ - [bearing](./src/bearing/bearing.md) — <!-- begin:auto-docs --> _(no submodules)_- [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_ - [bounds](./src/bounds/bounds.md) — <!-- begin:auto-docs --> _(no submodules)_- [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_ - [distance](./src/distance/distance.md) — <!-- begin:auto-docs --> _(no submodules)_- [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_ - [ellipsoid](./src/ellipsoid/ellipsoid.md) — <!-- begin:auto-docs --> _(no submodules)_- [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_ - [polygon](./src/polygon/polygon.md) — <!-- begin:auto-docs --> _(no submodules)_- [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_ - [transforms](./src/transforms/transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [schemas](./src/types/schemas.md) — The `schemas` module exports Zod schemas that validate every public geometric type defined in `@suaveplan/geo`. The sch…
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_ - [units](./src/units/units.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability.md) — OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/geo
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid, greatCircleDistance, bearing, destination } from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const london = { latitude: 51.5074, longitude: -0.1278 };
|
||||
const paris = { latitude: 48.8566, longitude: 2.3522 };
|
||||
|
||||
console.log(greatCircleDistance(london, paris, wgs84, "kilometre")); // ~344
|
||||
console.log(bearing(london, paris)); // ~154°
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- [`src/ellipsoid/ellipsoid.md`](src/ellipsoid/ellipsoid.md) — `Ellipsoid`, `defineEllipsoid`
|
||||
- [`src/transforms/transforms.md`](src/transforms/transforms.md) — `sphericalToCartesian`, `cartesianToSpherical`, `toRadians`, `toDegrees`
|
||||
- [`src/distance/distance.md`](src/distance/distance.md) — `greatCircleDistance`, `vincentyDistance`
|
||||
- [`src/bearing/bearing.md`](src/bearing/bearing.md) — `bearing`, `destination`, `midpoint`
|
||||
- [`src/angular/angular.md`](src/angular/angular.md) — `angularSeparation`
|
||||
- [`src/bounds/bounds.md`](src/bounds/bounds.md) — `BoundingBox`, `createBoundingBox`, `containsPoint`, `expandBox`, `mergeBoxes`, `intersects`
|
||||
- [`src/polygon/polygon.md`](src/polygon/polygon.md) — `pointInPolygon`, `polygonArea`, `polygonPerimeter`, `simplifyPolygon`
|
||||
- [`src/units/units.md`](src/units/units.md) — `DistanceUnit`, `convertDistance`
|
||||
- [`src/types/types.md`](src/types/types.md) — Zod schemas for all public types
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `angular` module computes the angular separation between two points
|
||||
expressed as `SphericalCoord` values, returning the central angle subtended
|
||||
on a unit sphere. The result is a pure spherical angle in radians,
|
||||
independent of any planetary radius or ellipsoid, which makes it the right
|
||||
primitive for astronomy (angular distance between celestial objects),
|
||||
antenna and dish pointing, instrument field-of-view checks, and any
|
||||
calculation where you want to multiply by a chosen radius later rather than
|
||||
bake one in upfront. Building on this primitive keeps the higher-level
|
||||
distance kernels (`greatCircleDistance`, `vincentyDistance`) free to apply
|
||||
their own ellipsoid-aware radii.
|
||||
|
||||
## Features
|
||||
|
||||
- `angularSeparation(a, b)` — central angle in radians using the spherical
|
||||
law of cosines.
|
||||
- Inputs are degrees (matching the rest of `@suaveplan/geo`); the kernel
|
||||
converts to radians internally.
|
||||
- Result is clamped to `[0, π]` by guarding the `acos` argument against
|
||||
floating-point drift past `±1`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { angularSeparation } from "@suaveplan/geo";
|
||||
|
||||
const angle = angularSeparation(
|
||||
{ latitude: 0, longitude: 0 },
|
||||
{ latitude: 0, longitude: 90 }
|
||||
);
|
||||
console.log(angle); // ~1.5708 radians (90°)
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
Combine `angularSeparation` with `toDegrees` and a chosen radius to compute
|
||||
arc-length on bodies other than Earth — for example, on the Moon's mean
|
||||
radius — without instantiating an ellipsoid:
|
||||
|
||||
```ts
|
||||
import { angularSeparation, toDegrees } from "@suaveplan/geo";
|
||||
|
||||
const MOON_RADIUS_KM = 1737.4;
|
||||
|
||||
const sep = angularSeparation(
|
||||
{ latitude: 0, longitude: 23.47 }, // Apollo 11 site
|
||||
{ latitude: -3.01, longitude: -23.42 } // Apollo 12 site
|
||||
);
|
||||
|
||||
const arcKm = sep * MOON_RADIUS_KM;
|
||||
console.log(`${toDegrees(sep).toFixed(2)}° / ${arcKm.toFixed(1)} km`);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `angularSeparation(a: SphericalCoord, b: SphericalCoord): number`
|
||||
|
||||
Returns the spherical angle in radians between `a` and `b`. The argument to
|
||||
`Math.acos` is clamped to `[-1, 1]` so identical inputs deterministically
|
||||
return `0` and antipodal inputs return `π` even when intermediate
|
||||
floating-point arithmetic produces values like `1.0000000000000002`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The implementation uses the spherical law of cosines:
|
||||
`acos(sin φ₁ sin φ₂ + cos φ₁ cos φ₂ cos Δλ)`. For separations below ~1 km
|
||||
on Earth the haversine formula is more numerically stable, but for the
|
||||
astronomy and pointing use cases this primitive targets — separations of
|
||||
many degrees — the clamped cosine formulation is sufficient and faster.
|
||||
Consumers that need sub-metre Earth-surface precision should reach for
|
||||
`greatCircleDistance` (haversine) or `vincentyDistance` (ellipsoidal)
|
||||
instead.
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `bearing` module computes initial bearings, destination points, and midpoints on an ellipsoid. These are the fundamental navigation primitives for routing, waypoint generation, and boundary interpolation.
|
||||
|
||||
## Features
|
||||
|
||||
- `bearing(from, to)` — initial compass bearing in degrees (0–360)
|
||||
- `destination(start, bearingDeg, distanceM, ellipsoid)` — computes the point reached after travelling a given distance on a given bearing
|
||||
- `midpoint(a, b)` — geographic midpoint between two coordinates
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { bearing } from "@suaveplan/geo";
|
||||
|
||||
const brg = bearing(
|
||||
{ latitude: 51.5, longitude: -0.1 },
|
||||
{ latitude: 48.9, longitude: 2.3 }
|
||||
);
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid, destination, bearing } from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const start = { latitude: 51.5, longitude: -0.1 };
|
||||
const end = destination(start, 90, 100_000, wgs84);
|
||||
const back = bearing(end, start); // ~270
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `bearing(from, to): number`
|
||||
|
||||
Returns initial bearing in degrees clockwise from north.
|
||||
|
||||
### `destination(start, bearingDeg, distanceM, ellipsoid): SphericalCoord`
|
||||
|
||||
Returns the coordinate reached after travelling `distanceM` metres on `bearingDeg`.
|
||||
|
||||
### `midpoint(a, b): SphericalCoord`
|
||||
|
||||
Returns the geographic midpoint.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Destination uses the spherical law of cosines with the ellipsoid's semi-major axis as radius. For high-precision geodesic destinations, consumers should iterate `bearing` + `vincentyDistance`.
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `bounds` module provides axis-aligned bounding boxes for spherical coordinates. Boxes support containment tests, expansion, merging, and intersection — the primitives needed for spatial indexing, map viewport clipping, and collision detection.
|
||||
|
||||
## Features
|
||||
|
||||
- `createBoundingBox` — construct from an array of coordinates
|
||||
- `containsPoint` — point-in-box test
|
||||
- `expandBox` — grow a box to include a new point
|
||||
- `mergeBoxes` — combine two boxes
|
||||
- `intersects` — box-box overlap test
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { createBoundingBox, containsPoint } from "@suaveplan/geo";
|
||||
|
||||
const box = createBoundingBox([
|
||||
{ latitude: 0, longitude: 0 },
|
||||
{ latitude: 10, longitude: 10 },
|
||||
]);
|
||||
console.log(containsPoint(box, { latitude: 5, longitude: 5 })); // true
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { createBoundingBox, mergeBoxes, intersects } from "@suaveplan/geo";
|
||||
|
||||
const a = createBoundingBox([{ latitude: 0, longitude: 0 }, { latitude: 10, longitude: 10 }]);
|
||||
const b = createBoundingBox([{ latitude: 5, longitude: 5 }, { latitude: 15, longitude: 15 }]);
|
||||
console.log(intersects(a, b)); // true
|
||||
const merged = mergeBoxes(a, b);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `createBoundingBox(coords): BoundingBox | null`
|
||||
|
||||
Returns `null` for an empty array.
|
||||
|
||||
### `containsPoint(box, point): boolean`
|
||||
|
||||
Inclusive on all boundaries.
|
||||
|
||||
### `expandBox(box, point): BoundingBox`
|
||||
|
||||
Returns a new box; does not mutate the input.
|
||||
|
||||
### `mergeBoxes(a, b): BoundingBox`
|
||||
|
||||
Returns the smallest box containing both inputs.
|
||||
|
||||
### `intersects(a, b): boolean`
|
||||
|
||||
Returns `true` if the boxes share any area.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Bounding boxes use simple min/max on latitude and longitude. They do not handle the antimeridian (`±180°` longitude wrap). Consumers crossing the antimeridian should split geometries or use a custom coordinate system.
|
||||
@@ -0,0 +1,100 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `distance` module computes geodesic distances between two
|
||||
`SphericalCoord` values on a configurable `Ellipsoid`. Two algorithms are
|
||||
exported so callers can pick the speed/accuracy tradeoff that fits the use
|
||||
case: a fast haversine great-circle approximation that treats the
|
||||
ellipsoid's semi-major axis as a sphere radius, and the iterative Vincenty
|
||||
inverse formula which solves on the actual ellipsoid and is accurate to
|
||||
millimetres for any pair of points that aren't nearly antipodal. Both
|
||||
functions return their result in a caller-chosen `DistanceUnit`, delegating
|
||||
the conversion to `convertDistance` so the caller never has to remember
|
||||
the metre→mile or metre→nautical-mile factor.
|
||||
|
||||
## Features
|
||||
|
||||
- `greatCircleDistance(a, b, ellipsoid, unit?)` — O(1) haversine; ideal
|
||||
when the ellipsoid is approximately spherical or when ~0.5% accuracy is
|
||||
acceptable.
|
||||
- `vincentyDistance(a, b, ellipsoid, unit?)` — iterative inverse Vincenty
|
||||
with up to 100 iterations and a `1e-12` convergence tolerance; converges
|
||||
to millimetre precision on WGS-84.
|
||||
- Both functions accept any `DistanceUnit` (`"metre"`, `"kilometre"`,
|
||||
`"mile"`, `"nauticalMile"`) for the return value, defaulting to `"metre"`.
|
||||
- Both functions handle identical inputs deterministically: distance `0`,
|
||||
no NaN.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid, greatCircleDistance } from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const km = greatCircleDistance(
|
||||
{ latitude: 51.5, longitude: -0.1 }, // London
|
||||
{ latitude: 48.9, longitude: 2.3 }, // Paris
|
||||
wgs84,
|
||||
"kilometre"
|
||||
);
|
||||
console.log(km.toFixed(1)); // ~344.0
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
Compare the two algorithms to characterise the haversine error against
|
||||
Vincenty for a particular use case:
|
||||
|
||||
```ts
|
||||
import {
|
||||
defineEllipsoid,
|
||||
greatCircleDistance,
|
||||
vincentyDistance,
|
||||
} from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
|
||||
const a = { latitude: 35.6762, longitude: 139.6503 }; // Tokyo
|
||||
const b = { latitude: -33.8688, longitude: 151.2093 }; // Sydney
|
||||
|
||||
const haversineMetres = greatCircleDistance(a, b, wgs84, "metre");
|
||||
const vincentyMetres = vincentyDistance(a, b, wgs84, "metre");
|
||||
const errorPpm =
|
||||
((haversineMetres - vincentyMetres) / vincentyMetres) * 1_000_000;
|
||||
|
||||
console.log(`haversine: ${haversineMetres.toFixed(0)} m`);
|
||||
console.log(`vincenty: ${vincentyMetres.toFixed(0)} m`);
|
||||
console.log(`error: ${errorPpm.toFixed(1)} ppm`);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `greatCircleDistance(a, b, ellipsoid, unit?): number`
|
||||
|
||||
Haversine great-circle distance using `ellipsoid.semiMajorAxis` as the
|
||||
sphere radius. `unit` defaults to `"metre"`. Treats both points as
|
||||
spherical, so the answer is a great-circle arc, not an ellipsoidal
|
||||
geodesic.
|
||||
|
||||
### `vincentyDistance(a, b, ellipsoid, unit?): number`
|
||||
|
||||
Vincenty's inverse formula. Iterates up to 100 times with a `1e-12`
|
||||
convergence tolerance on `λ`. `unit` defaults to `"metre"`. Returns `0`
|
||||
immediately for coincident points (when `sinSigma === 0`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Vincenty does not converge for nearly antipodal points; the 100-iteration
|
||||
ceiling will simply return the last computed value rather than throw. In
|
||||
production code that may receive arbitrary input, validate the input
|
||||
geometry first or fall back to `greatCircleDistance` when convergence is
|
||||
suspect. For all realistic Earth-surface use cases — same-continent or
|
||||
even trans-oceanic — the algorithm converges in well under a dozen
|
||||
iterations. The kernels read only `semiMajorAxis`, `semiMinorAxis`, and
|
||||
`flattening` from the ellipsoid, so any ellipsoid produced by
|
||||
`defineEllipsoid` is compatible.
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `ellipsoid` module defines the `Ellipsoid` type and the `defineEllipsoid` factory used throughout the `@suaveplan/geo` family. An ellipsoid is the geometric shape that approximates planets and moons, characterized by a semi-major axis (equatorial radius) and a semi-minor axis (polar radius). This module derives flattening and eccentricity automatically so consumers never hand-roll the same arithmetic.
|
||||
|
||||
## Features
|
||||
|
||||
- Immutable `Ellipsoid` interface with derived properties
|
||||
- `defineEllipsoid(a, b)` factory that computes flattening and eccentricity
|
||||
- Pure functions — no side effects, no planet-specific constants
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid } from "@suaveplan/geo/ellipsoid/ellipsoid";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
console.log(wgs84.flattening); // ~0.00335
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid } from "@suaveplan/geo/ellipsoid/ellipsoid";
|
||||
|
||||
const mars = defineEllipsoid(3_396_200, 3_376_200);
|
||||
const sphere = defineEllipsoid(6_371_000, 6_371_000);
|
||||
console.log(sphere.flattening); // 0
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `interface Ellipsoid`
|
||||
|
||||
- `semiMajorAxis: number` — equatorial radius in metres
|
||||
- `semiMinorAxis: number` — polar radius in metres
|
||||
- `flattening: number` — derived `(a − b) / a`
|
||||
- `eccentricity: number` — derived from flattening
|
||||
|
||||
### `defineEllipsoid(semiMajorAxis, semiMinorAxis): Ellipsoid`
|
||||
|
||||
Creates an `Ellipsoid` from the given axes and derives flattening and eccentricity.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Eccentricity is computed as `sqrt(2f − f²)` where `f` is flattening. For a perfect sphere (`a === b`), both flattening and eccentricity are exactly zero.
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
OpenTelemetry tracer wrapper for `@suaveplan/geo`.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { withSpan } from "@suaveplan/geo";
|
||||
|
||||
const result = withSpan("compute", () => compute());
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `polygon` module provides point-in-polygon, area, perimeter, and simplification for spherical polygons. These are essential for geofencing, territory mapping, and route buffer calculations.
|
||||
|
||||
## Features
|
||||
|
||||
- `pointInPolygon` — ray-casting algorithm for spherical coordinates
|
||||
- `polygonArea` — spherical excess area on a given ellipsoid
|
||||
- `polygonPerimeter` — sum of geodesic edge lengths
|
||||
- `simplifyPolygon` — Douglas-Peucker with great-circle perpendicular distance
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { pointInPolygon } from "@suaveplan/geo";
|
||||
|
||||
const square = [
|
||||
{ latitude: 0, longitude: 0 },
|
||||
{ latitude: 0, longitude: 10 },
|
||||
{ latitude: 10, longitude: 10 },
|
||||
{ latitude: 10, longitude: 0 },
|
||||
];
|
||||
console.log(pointInPolygon({ latitude: 5, longitude: 5 }, square)); // true
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid, polygonArea, simplifyPolygon } from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const complex = [/* many points */];
|
||||
const simple = simplifyPolygon(complex, 100, wgs84);
|
||||
const area = polygonArea(simple, wgs84);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `pointInPolygon(point, polygon): boolean`
|
||||
|
||||
Ray-casting (even-odd rule). Does not handle polygons that cross the antimeridian.
|
||||
|
||||
### `polygonArea(polygon, ellipsoid): number`
|
||||
|
||||
Returns area in square metres. Returns `0` for fewer than 3 vertices.
|
||||
|
||||
### `polygonPerimeter(polygon, ellipsoid): number`
|
||||
|
||||
Returns perimeter in metres using `greatCircleDistance` for each edge.
|
||||
|
||||
### `simplifyPolygon(polygon, toleranceMetres, ellipsoid): SphericalCoord[]`
|
||||
|
||||
Douglas-Peucker simplification. Returns the original polygon if it has 2 or fewer points.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`polygonArea` uses the spherical excess formula with the ellipsoid's semi-major axis as radius. `simplifyPolygon` computes perpendicular distance using great-circle distances on the supplied ellipsoid rather than planar Euclidean distance, preserving accuracy for large polygons.
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `transforms` module converts between spherical coordinates (latitude, longitude, altitude) and 3-D Cartesian vectors on a given ellipsoid. It also provides radian/degree helpers used by every other module in `@suaveplan/geo`.
|
||||
|
||||
## Features
|
||||
|
||||
- `sphericalToCartesian` — converts lat/lon/alt to ECEF (Earth-Centered Earth-Fixed) Cartesian
|
||||
- `cartesianToSpherical` — inverse conversion using Bowring's iterative method
|
||||
- `toRadians` / `toDegrees` — conversion helpers
|
||||
- `SphericalCoord`, `Vec3`, `Mat3x3` types
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid, sphericalToCartesian } from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const vec = sphericalToCartesian({ latitude: 51.5, longitude: -0.1, altitude: 0 }, wgs84);
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import { defineEllipsoid, sphericalToCartesian, cartesianToSpherical } from "@suaveplan/geo";
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const coord = { latitude: 51.5, longitude: -0.1, altitude: 100 };
|
||||
const vec = sphericalToCartesian(coord, wgs84);
|
||||
const back = cartesianToSpherical(vec, wgs84);
|
||||
console.log(back.latitude); // ~51.5
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sphericalToCartesian(coord, ellipsoid): Vec3`
|
||||
|
||||
Converts a `SphericalCoord` to a Cartesian `Vec3` on the given `Ellipsoid`.
|
||||
|
||||
### `cartesianToSpherical(vec, ellipsoid): SphericalCoord`
|
||||
|
||||
Converts a `Vec3` back to a `SphericalCoord` on the given `Ellipsoid`.
|
||||
|
||||
### `toRadians(degrees): number`
|
||||
|
||||
Converts degrees to radians.
|
||||
|
||||
### `toDegrees(radians): number`
|
||||
|
||||
Converts radians to degrees.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Cartesian-to-spherical uses Bowring's iterative formula which converges rapidly for all latitudes including the poles. The prime vertical radius of curvature `N` is computed from the eccentricity squared.
|
||||
@@ -0,0 +1,102 @@
|
||||
## Purpose
|
||||
|
||||
The `schemas` module exports Zod schemas that validate every public geometric
|
||||
type defined in `@suaveplan/geo`. The schemas are used at API boundaries —
|
||||
HTTP handlers, message-bus envelopes, persistence reads — wherever runtime
|
||||
input must be coerced and rejected before it reaches the geometry kernels.
|
||||
Pairing the schemas with the typed interfaces in `types.ts` lets consumers
|
||||
infer the same TypeScript shape via `z.infer` without re-declaring a parallel
|
||||
type, eliminating drift between compile-time and runtime contracts.
|
||||
|
||||
## Features
|
||||
|
||||
- `EllipsoidSchema` — validates `semiMajorAxis`/`semiMinorAxis` are positive
|
||||
numbers and `flattening`/`eccentricity` are finite numbers.
|
||||
- `SphericalCoordSchema` — clamps `latitude` to `[-90, 90]`, `longitude` to
|
||||
`[-180, 180]`, and accepts an optional `altitude` number.
|
||||
- `Vec3Schema` — three finite-number Cartesian components.
|
||||
- `Mat3x3Schema` — nine finite-number row-major rotation-matrix entries.
|
||||
- `BoundingBoxSchema` — both latitude bounds in `[-90, 90]` and both
|
||||
longitude bounds in `[-180, 180]`.
|
||||
- `DistanceUnitSchema` — enum over `"metre" | "kilometre" | "mile" |
|
||||
"nauticalMile"`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { SphericalCoordSchema } from "@suaveplan/geo";
|
||||
|
||||
const coord = SphericalCoordSchema.parse({
|
||||
latitude: 51.5,
|
||||
longitude: -0.1,
|
||||
});
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
BoundingBoxSchema,
|
||||
DistanceUnitSchema,
|
||||
EllipsoidSchema,
|
||||
} from "@suaveplan/geo";
|
||||
import type { z } from "zod";
|
||||
|
||||
type ParsedEllipsoid = z.infer<typeof EllipsoidSchema>;
|
||||
|
||||
function loadConfig(raw: unknown): {
|
||||
ellipsoid: ParsedEllipsoid;
|
||||
box: z.infer<typeof BoundingBoxSchema>;
|
||||
unit: z.infer<typeof DistanceUnitSchema>;
|
||||
} {
|
||||
const obj = raw as Record<string, unknown>;
|
||||
return {
|
||||
ellipsoid: EllipsoidSchema.parse(obj.ellipsoid),
|
||||
box: BoundingBoxSchema.parse(obj.box),
|
||||
unit: DistanceUnitSchema.parse(obj.unit),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `EllipsoidSchema`
|
||||
|
||||
`z.object({ semiMajorAxis: z.number().positive(), semiMinorAxis:
|
||||
z.number().positive(), flattening: z.number(), eccentricity: z.number() })`.
|
||||
Throws `ZodError` on negative or non-finite axes.
|
||||
|
||||
### `SphericalCoordSchema`
|
||||
|
||||
`z.object({ latitude: z.number().min(-90).max(90), longitude:
|
||||
z.number().min(-180).max(180), altitude: z.number().optional() })`.
|
||||
|
||||
### `Vec3Schema`
|
||||
|
||||
`z.object({ x, y, z })` — three `z.number()` fields.
|
||||
|
||||
### `Mat3x3Schema`
|
||||
|
||||
`z.object({ m00..m22 })` — nine `z.number()` fields in row-major order.
|
||||
|
||||
### `BoundingBoxSchema`
|
||||
|
||||
`z.object({ minLatitude, maxLatitude, minLongitude, maxLongitude })` with
|
||||
each value clamped to spherical ranges. Note that the schema does NOT verify
|
||||
`minLatitude <= maxLatitude`; that ordering check is the caller's job.
|
||||
|
||||
### `DistanceUnitSchema`
|
||||
|
||||
`z.enum(["metre", "kilometre", "mile", "nauticalMile"])`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The schemas are deliberately permissive on cross-field invariants — for
|
||||
example, `BoundingBoxSchema` does not verify that `min` ≤ `max`, and
|
||||
`EllipsoidSchema` does not verify that `semiMinorAxis ≤ semiMajorAxis`.
|
||||
Cross-field invariants belong with the constructors (`createBoundingBox`,
|
||||
`defineEllipsoid`), which are tested for those invariants separately. The
|
||||
schemas exist to reject malformed wire data, not to substitute for the
|
||||
domain constructors.
|
||||
@@ -0,0 +1,114 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `types` module declares the shared TypeScript interfaces and enums that
|
||||
every other module in `@suaveplan/geo` consumes — `Ellipsoid`,
|
||||
`SphericalCoord`, `Vec3`, `Mat3x3`, `BoundingBox`, and the `DistanceUnit`
|
||||
string-literal union. Centralising the shapes here keeps the rest of the
|
||||
package free of duplicate declarations and ensures that any change to a
|
||||
core geometric structure has exactly one source of truth. The module is
|
||||
runtime-free: it emits zero JavaScript and is safe to import from any
|
||||
tier (universal, browser, or server).
|
||||
|
||||
## Features
|
||||
|
||||
- `Ellipsoid` — readonly `semiMajorAxis`/`semiMinorAxis`/`flattening`/
|
||||
`eccentricity`; produced by `defineEllipsoid`.
|
||||
- `SphericalCoord` — `latitude`, `longitude`, optional `altitude` (all
|
||||
degrees / metres).
|
||||
- `Vec3` — `x`, `y`, `z` Cartesian components.
|
||||
- `Mat3x3` — nine row-major rotation-matrix entries (`m00`..`m22`).
|
||||
- `BoundingBox` — `minLatitude`, `maxLatitude`, `minLongitude`,
|
||||
`maxLongitude`.
|
||||
- `DistanceUnit` — `"metre" | "kilometre" | "mile" | "nauticalMile"`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import type { Ellipsoid, SphericalCoord } from "@suaveplan/geo";
|
||||
|
||||
function describe(point: SphericalCoord, ellipsoid: Ellipsoid): string {
|
||||
return `(${point.latitude}, ${point.longitude}) on ${ellipsoid.semiMajorAxis}m sphere`;
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
Compose the types into a domain-specific record without re-declaring any
|
||||
field shapes:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
BoundingBox,
|
||||
DistanceUnit,
|
||||
SphericalCoord,
|
||||
} from "@suaveplan/geo";
|
||||
|
||||
interface Region {
|
||||
name: string;
|
||||
centre: SphericalCoord;
|
||||
bbox: BoundingBox;
|
||||
measurementUnit: DistanceUnit;
|
||||
}
|
||||
|
||||
const london: Region = {
|
||||
name: "Greater London",
|
||||
centre: { latitude: 51.5074, longitude: -0.1278 },
|
||||
bbox: {
|
||||
minLatitude: 51.28,
|
||||
maxLatitude: 51.69,
|
||||
minLongitude: -0.51,
|
||||
maxLongitude: 0.33,
|
||||
},
|
||||
measurementUnit: "kilometre",
|
||||
};
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `Ellipsoid`
|
||||
|
||||
`{ readonly semiMajorAxis: number; readonly semiMinorAxis: number;
|
||||
readonly flattening: number; readonly eccentricity: number }`. All values
|
||||
are in metres except the dimensionless `flattening` and `eccentricity`.
|
||||
|
||||
### `SphericalCoord`
|
||||
|
||||
`{ latitude: number; longitude: number; altitude?: number }`. `latitude`
|
||||
and `longitude` are degrees; `altitude` (when present) is metres above
|
||||
the ellipsoid surface.
|
||||
|
||||
### `Vec3`
|
||||
|
||||
`{ x: number; y: number; z: number }` — Cartesian components, typically in
|
||||
ECEF metres.
|
||||
|
||||
### `Mat3x3`
|
||||
|
||||
Nine numeric fields `m00`..`m22` in row-major order. The rotation
|
||||
operators in the `transforms` module produce and consume this shape.
|
||||
|
||||
### `BoundingBox`
|
||||
|
||||
`{ minLatitude: number; maxLatitude: number; minLongitude: number;
|
||||
maxLongitude: number }`. Caller is responsible for ensuring `min ≤ max`;
|
||||
`createBoundingBox` enforces this invariant.
|
||||
|
||||
### `DistanceUnit`
|
||||
|
||||
`"metre" | "kilometre" | "mile" | "nauticalMile"` — accepted everywhere a
|
||||
unit is required. Conversion factors live in `units.ts`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The interfaces are intentionally structural — there are no nominal brands
|
||||
— so values produced by other libraries that match the shape are accepted
|
||||
without conversion. Runtime validation lives in the sibling `schemas.ts`
|
||||
module via Zod, which mirrors these shapes one-for-one. When you change a
|
||||
type here, update the matching schema in the same commit; the package's
|
||||
test suite includes round-trip checks that catch drift between the two.
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `units` module converts scalar distance values between metres,
|
||||
kilometres, statute miles, and nautical miles. It is the single conversion
|
||||
hub used by every higher-level kernel in `@suaveplan/geo` — every distance
|
||||
function returns its result by piping the kernel-internal metre value
|
||||
through `convertDistance` to the caller's chosen unit, so consumers never
|
||||
have to remember a conversion factor or copy-paste one. Exposing the
|
||||
function publicly also lets callers normalise mixed-unit external inputs
|
||||
(e.g. an aviation feed that mixes nautical miles and metres) into a single
|
||||
working unit before invoking other geometry primitives.
|
||||
|
||||
## Features
|
||||
|
||||
- `convertDistance(value, from, to)` — bidirectional conversion across all
|
||||
supported units in O(1).
|
||||
- `DistanceUnit` string-literal union — `"metre" | "kilometre" | "mile" |
|
||||
"nauticalMile"`.
|
||||
- Identity short-circuit: when `from === to` the input is returned as-is
|
||||
without any floating-point round-trip.
|
||||
- Conversion factors are exact per international standards (no
|
||||
hand-rounded constants).
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```ts
|
||||
import { convertDistance } from "@suaveplan/geo";
|
||||
|
||||
const km = convertDistance(1000, "metre", "kilometre"); // 1
|
||||
const miles = convertDistance(10_000, "metre", "mile"); // 6.2137...
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
|
||||
Build a unit-aware helper that accepts any input unit and emits a
|
||||
canonicalised metre value for downstream geometry kernels:
|
||||
|
||||
```ts
|
||||
import {
|
||||
convertDistance,
|
||||
type DistanceUnit,
|
||||
greatCircleDistance,
|
||||
defineEllipsoid,
|
||||
} from "@suaveplan/geo";
|
||||
|
||||
interface Reading {
|
||||
value: number;
|
||||
unit: DistanceUnit;
|
||||
}
|
||||
|
||||
function toMetres(r: Reading): number {
|
||||
return convertDistance(r.value, r.unit, "metre");
|
||||
}
|
||||
|
||||
const wgs84 = defineEllipsoid(6_378_137, 6_356_752.314245);
|
||||
const allowance = toMetres({ value: 5, unit: "nauticalMile" }); // 9260
|
||||
const distance = greatCircleDistance(
|
||||
{ latitude: 51.5, longitude: -0.1 },
|
||||
{ latitude: 48.9, longitude: 2.3 },
|
||||
wgs84,
|
||||
"metre"
|
||||
);
|
||||
const withinRange = distance <= allowance;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `convertDistance(value: number, from: DistanceUnit, to: DistanceUnit): number`
|
||||
|
||||
Returns `value` expressed in `to`. When `from === to` the function returns
|
||||
`value` unchanged — no multiplication and no division, which keeps the
|
||||
identity case bit-exact. Otherwise it computes
|
||||
`value * factor[from] / factor[to]` where the factor table is
|
||||
`{ metre: 1, kilometre: 1000, mile: 1609.344, nauticalMile: 1852 }`.
|
||||
|
||||
### `type DistanceUnit`
|
||||
|
||||
`"metre" | "kilometre" | "mile" | "nauticalMile"`. Re-exported from the
|
||||
package root.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Conversion factors are exact under the modern international definitions:
|
||||
1 statute mile = 1609.344 m and 1 nautical mile = 1852 m. The function
|
||||
performs at most one multiply and one divide, so the output is within
|
||||
one ULP of the mathematical result and round-trip
|
||||
(`x → from → to → from`) is stable. No rounding is applied internally;
|
||||
callers that need a fixed display precision should round at presentation
|
||||
time.
|
||||
Reference in New Issue
Block a user