Lightweight, dependency-free WGS-84 navigation utilities for JavaScript. Handles coordinate conversion (WGS-84 → ECEF → ENU), azimuth and distance calculations, heading smoothing, and Plus Code zone encoding.
Originally developed for a location-based AR project. Core coordinate math ported and refactored from dat-ng/ar-location-based-android.
npm install spatial-utilsOr drop index.js directly into your project — no dependencies, pure ESM.
import {
wgs84ToEcef,
ecefToEnu,
getAzimuth,
getHorizontalDistance,
headingDelta,
emaSmooth,
encodePlus6,
} from 'spatial-utils';
// Convert two GPS positions to ECEF
const userEcef = wgs84ToEcef(37.7749, -122.4194, 0); // San Francisco
const poiEcef = wgs84ToEcef(37.7751, -122.4180, 0); // nearby POI
// Project into local East-North-Up frame centred on the user
const enu = ecefToEnu(37.7749, -122.4194, userEcef, poiEcef);
// Bearing and ground distance to the POI
const bearing = getAzimuth(enu); // e.g. 78.3°
const distance = getHorizontalDistance(enu); // e.g. 118.4 m
// How much to turn from current heading to face the POI
const delta = headingDelta(compassHeading, bearing);
// negative → turn left, positive → turn right
// Smooth raw compass readings
const smoothed = emaSmooth(compassHeading);
// Plus Code zone key for tile-based caching
const tile = encodePlus6(37.7749, -122.4194); // e.g. "9Q9J5R.json"Converts WGS-84 geodetic coordinates to Earth-Centred Earth-Fixed (ECEF).
Throws RangeError on invalid coordinates.
| Param | Type | Description |
|---|---|---|
lat |
number |
Geodetic latitude, degrees [−90, 90] |
lon |
number |
Longitude, degrees [−180, 180] |
alt |
number |
Ellipsoidal altitude, metres |
Projects the vector from observer to POI into the local ENU frame.
userLat/userLon are required to construct the rotation matrix even if
ecefUser was already computed from the same position.
Bearing from observer to POI, degrees [0, 360).
3-D Euclidean distance in metres. Includes the vertical component — useful for drone or altitude-aware use cases.
Ground distance in metres, ignoring elevation difference. Prefer this for navigation and heading-error calculations.
Signed shortest-path difference from bearing a to bearing b, degrees [−180, +180].
Negative = turn left, positive = turn right.
Offsets a WGS-84 coordinate by a northing and easting in metres. Uses WGS-84 ellipsoid radii of curvature (not the flat-earth 111 320 m/° approximation).
Factory for a stateful exponential moving average compass smoother. Handles the 0°/360° wrap-around correctly.
const smooth = makeEma(0.2); // independent instance
smooth(350); // → 350
smooth(5); // → 352 (wraps correctly)Default singleton instance at α = 0.1. Fine for single-sensor use;
use makeEma() when you need multiple independent streams.
Encodes a coordinate to a 6-character Open Location Code zone key, appended
with .json, for use as a tile cache filename.
Throws RangeError on invalid coordinates.
encodePlus6(37.7749, -122.4194); // → "9Q9J5R.json"See CHANGELOG.md.
MIT © 2026 Timothy Nishimura. Core coordinate math ported from dat-ng/ar-location-based-android © 2019 Dat Nguyen, also MIT licensed.