npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verifyhash/geo-distance

v0.1.0

Published

Zero-dependency great-circle and WGS-84 ellipsoidal geodesy: haversine/Vincenty distance, bearings, destination point, midpoint, and unit conversions.

Readme

geo-distance

TODO (owner): pick the final npm name/scope before publishing. The npm package name is not finalized by this project. Convention used here: package.json keeps the working slug geo-distance as a placeholder — replace it (and the npm install line below) with the real published name/scope before running npm publish.

A tiny, zero-dependency Node.js library for great-circle and ellipsoidal geodesy from latitude/longitude points. Compute distances, bearings, destination points, and midpoints, plus a handful of exact unit conversions.

No network, no I/O, no daemon, no external packages — just pure functions and node:assert for the tests. Drop src/geo.js into any project.

What it is

Given two points on Earth expressed as { lat, lon } in decimal degrees, geo-distance answers the everyday spherical-trig questions:

  • How far apart are they? (spherical haversine, or ellipsoidal Vincenty)
  • What compass bearing do I leave on, and what bearing do I arrive on?
  • If I walk 343.5 km from London on bearing 148°, where do I end up?
  • What's the half-way point of the route?
  • How many miles / nautical miles is that?

Who it's for

Mapping, logistics, aviation/marine, and travel developers who need correct geodesy without pulling in a heavy GIS dependency — e.g. estimating delivery radii, sorting "nearest store" results, drawing a route's midpoint label, or converting a leg length from kilometres to nautical miles.

Two distance models — and when to use which

| Function | Model | Accuracy | Notes | |---|---|---|---| | haversineDistance | Sphere (mean radius 6 371 008.77 m) | ~0.3–0.5% vs the real ellipsoid | Fast, always converges, great for ranking/filtering | | vincentyDistance | WGS-84 ellipsoid, iterative | sub-millimetre | Slower; does not converge for near-antipodal points |

Honest limits. The haversine model treats Earth as a perfect sphere, so it can be off by a few tenths of a percent (up to ~0.5% on long north–south legs). Vincenty's inverse formula is accurate to a fraction of a millimetre but is iterative and fails to converge when the two points are almost exactly on opposite sides of the planet — that case throws (see below) so you can fall back to haversineDistance. Bearings, destinationPoint, and midpoint use the spherical model, which is what you want for map labels and short/medium legs; they are not ellipsoidal.

Install / use

There is nothing to install. Require the module directly:

const geo = require('./src/geo.js');

const london = { lat: 51.5074, lon: -0.1278 };
const paris  = { lat: 48.8566, lon: 2.3522 };

geo.haversineDistance(london, paris);   // => 343498... metres (~343.5 km)
geo.vincentyDistance(london, paris);    // => 343923... metres (~343.9 km)
geo.initialBearing(london, paris);      // => ~148.1 (degrees, clockwise from N)

Worked example: fall back to haversine on antipodal failure

function robustDistance(a, b) {
  try {
    return geo.vincentyDistance(a, b);          // sub-mm on the ellipsoid
  } catch (e) {
    if (e.code === 'VINCENTY_NO_CONVERGE') {
      return geo.haversineDistance(a, b);       // spherical fallback
    }
    throw e;                                     // real error (bad input)
  }
}

Worked example: walk a bearing and convert units

const start = { lat: 40.7128, lon: -74.0060 };  // New York
const dest  = geo.destinationPoint(start, 90, 100_000); // 100 km due east
// dest => { lat: ~40.71, lon: ~-72.82 }

const km = geo.haversineDistance(start, dest) / 1000;   // ~100
geo.units.kmToNauticalMiles(km);                        // ~54.0 nm

API

Points are { lat, lon } in decimal degrees, lat ∈ [-90, 90], lon ∈ [-180, 180]. Every function validates its inputs and throws a TypeError (wrong type / missing field) or RangeError (out of range). Distances are metres; bearings are degrees in [0, 360) measured clockwise from true north.

  • haversineDistance(a, b)number (metres). Spherical great-circle distance. Coincident points return 0.

  • vincentyDistance(a, b, [opts])number (metres). WGS-84 ellipsoidal distance via Vincenty's inverse formula. opts.maxIterations (default 1000) and opts.tolerance (default 1e-12) tune convergence. Throws an Error with .code === 'VINCENTY_NO_CONVERGE' for near-antipodal inputs.

  • initialBearing(a, b)number (degrees). Forward azimuth leaving a.

  • finalBearing(a, b)number (degrees). Direction of travel on arrival at b.

  • destinationPoint(a, bearingDeg, distanceM){ lat, lon }. Where you arrive travelling distanceM metres from a on bearingDeg (spherical).

  • midpoint(a, b){ lat, lon }. Great-circle half-way point.

  • validatePoint(p, [label])p. Throws on invalid input; returns the point otherwise. Useful for validating your own data.

  • units — exact conversions (international mile = 1609.344 m, nautical mile = 1852 m): kmToMiles, milesToKm, kmToNauticalMiles, nauticalMilesToKm, metresToKm, metresToMiles, metresToNauticalMiles.

  • constantsWGS84_A, WGS84_B, WGS84_F, EARTH_RADIUS_M, METRES_PER_KM, METRES_PER_MILE, METRES_PER_NAUTICAL_MILE.

Running the tests

One command, no dependencies or test runner required:

node test/geo.test.js

The suite uses node:assert with known-city fixtures and stated tolerances: London→Paris ≈ 343.5 km (haversine) / 343.9 km (Vincenty), NYC→LA ≈ 3936 km, London→Sydney ≈ 16 990 km, plus equator/pole edge cases, the antipodal non-convergence path, bearing round-trips through destinationPoint, midpoint equidistance, and unit-conversion round-trips. It prints an ok line per check and a final assertion count, exiting non-zero on the first failure.

Status

This is an incubator project and an npm-graduation candidate — it is not published to any registry. Publishing is a separate, human-approved step.

License

MIT.

Install

Placeholder name: the geo-distance below is the working slug, not a finalized npm name — see the owner TODO near the top of this README.

npm install geo-distance
const geo = require('geo-distance');

const london = { lat: 51.5074, lon: -0.1278 };
const paris  = { lat: 48.8566, lon: 2.3522 };
geo.haversineDistance(london, paris); // ~343498 metres (~343.5 km)
geo.initialBearing(london, paris);    // ~148.1 degrees (clockwise from N)