DEV Community

echoluoluo
echoluoluo

Posted on

How to Calculate Distance Between Two GPS Coordinates in JavaScript

When working with maps, GPS data, or location-based applications, one of the most common questions is:

How far apart are these two coordinates?

Suppose we have two locations:

Point A
Latitude:  40.7128
Longitude: -74.0060

Point B
Latitude:  34.0522
Longitude: -118.2437
Enter fullscreen mode Exit fullscreen mode

These are approximately New York City and Los Angeles.

We can't simply subtract the latitude and longitude values and treat them like normal Cartesian coordinates.

The Earth is curved.

For many web applications, a practical solution is the Haversine formula, which calculates the great-circle distance between two points on a sphere.

In this article, we'll build a reusable JavaScript implementation that supports:

  • Latitude and longitude validation
  • Degrees-to-radians conversion
  • The Haversine formula
  • Kilometers
  • Miles
  • Nautical miles
  • TypeScript
  • Unit tests
  • Real-world coordinate testing

Why Simple Euclidean Distance Doesn't Work

If we had two points on a flat coordinate plane:

(x1, y1)
(x2, y2)
Enter fullscreen mode Exit fullscreen mode

we could use:

distance = √((x2 - x1)² + (y2 - y1)²)
Enter fullscreen mode Exit fullscreen mode

Latitude and longitude don't work like that.

They represent positions on the Earth's surface.

A degree of longitude also does not represent the same physical distance everywhere.

Near the Equator, one degree of longitude covers a much larger distance than it does near the poles.

So this:

const distance = Math.sqrt(
  (lat2 - lat1) ** 2 +
  (lon2 - lon1) ** 2
);
Enter fullscreen mode Exit fullscreen mode

does not give us a meaningful distance in kilometers or miles.

We need to account for the Earth's curvature.


The Haversine Formula

The Haversine formula estimates the great-circle distance between two points using their latitude and longitude.

The main idea is:

Latitude / Longitude
        ↓
Convert degrees to radians
        ↓
Calculate angular distance
        ↓
Multiply by Earth's radius
        ↓
Physical distance
Enter fullscreen mode Exit fullscreen mode

We'll break the implementation into small pieces.


Convert Degrees to Radians

JavaScript's trigonometric functions such as:

Math.sin()
Math.cos()
Math.atan2()
Enter fullscreen mode Exit fullscreen mode

expect radians rather than degrees.

So we first need a helper:

function toRadians(degrees) {
  return degrees * Math.PI / 180;
}
Enter fullscreen mode Exit fullscreen mode

For example:

console.log(toRadians(180));
Enter fullscreen mode Exit fullscreen mode

returns approximately:

3.141592653589793
Enter fullscreen mode Exit fullscreen mode

which is π radians.


A Basic Distance Function

Let's start with a simple Haversine implementation:

function distanceBetweenCoordinates(
  lat1,
  lon1,
  lat2,
  lon2
) {
  const earthRadiusKm = 6371.0088;

  const dLat = toRadians(lat2 - lat1);
  const dLon = toRadians(lon2 - lon1);

  const latitude1 = toRadians(lat1);
  const latitude2 = toRadians(lat2);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(latitude1) *
      Math.cos(latitude2) *
      Math.sin(dLon / 2) ** 2;

  const c =
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a)
    );

  return earthRadiusKm * c;
}
Enter fullscreen mode Exit fullscreen mode

Usage:

const distance = distanceBetweenCoordinates(
  40.7128,
  -74.0060,
  34.0522,
  -118.2437
);

console.log(distance);
Enter fullscreen mode Exit fullscreen mode

The result is approximately:

3935 km
Enter fullscreen mode Exit fullscreen mode

depending on the Earth radius and rounding you use.


What Is Happening Inside the Formula?

Let's break the calculation down.

First, calculate the differences:

const dLat = toRadians(lat2 - lat1);
const dLon = toRadians(lon2 - lon1);
Enter fullscreen mode Exit fullscreen mode

Then convert both latitude values to radians:

const latitude1 = toRadians(lat1);
const latitude2 = toRadians(lat2);
Enter fullscreen mode Exit fullscreen mode

Next:

const a =
  Math.sin(dLat / 2) ** 2 +
  Math.cos(latitude1) *
    Math.cos(latitude2) *
    Math.sin(dLon / 2) ** 2;
Enter fullscreen mode Exit fullscreen mode

This gives us an intermediate value representing the angular relationship between the two points.

Then:

const c =
  2 * Math.atan2(
    Math.sqrt(a),
    Math.sqrt(1 - a)
  );
Enter fullscreen mode Exit fullscreen mode

produces the central angle between the locations.

Finally:

earthRadiusKm * c
Enter fullscreen mode Exit fullscreen mode

converts that angular distance into kilometers.


Validate Latitude and Longitude

Before calculating distance, we should validate the inputs.

Latitude must be between:

-90 and 90
Enter fullscreen mode Exit fullscreen mode

Longitude must be between:

-180 and 180
Enter fullscreen mode Exit fullscreen mode

Let's create reusable validators:

function isValidLatitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -90 &&
    value <= 90
  );
}

function isValidLongitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -180 &&
    value <= 180
  );
}
Enter fullscreen mode Exit fullscreen mode

Then:

function validateCoordinate(
  latitude,
  longitude
) {
  if (!isValidLatitude(latitude)) {
    throw new RangeError(
      "Latitude must be between -90 and 90"
    );
  }

  if (!isValidLongitude(longitude)) {
    throw new RangeError(
      "Longitude must be between -180 and 180"
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Now our distance function can validate both points.


A More Reliable Implementation

Let's combine validation with the Haversine calculation:

function toRadians(degrees) {
  return degrees * Math.PI / 180;
}

function isValidLatitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -90 &&
    value <= 90
  );
}

function isValidLongitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -180 &&
    value <= 180
  );
}

function validateCoordinate(
  latitude,
  longitude
) {
  if (!isValidLatitude(latitude)) {
    throw new RangeError(
      "Latitude must be between -90 and 90"
    );
  }

  if (!isValidLongitude(longitude)) {
    throw new RangeError(
      "Longitude must be between -180 and 180"
    );
  }
}

function distanceBetweenCoordinates(
  lat1,
  lon1,
  lat2,
  lon2
) {
  validateCoordinate(lat1, lon1);
  validateCoordinate(lat2, lon2);

  const earthRadiusKm = 6371.0088;

  const latitude1 = toRadians(lat1);
  const latitude2 = toRadians(lat2);

  const dLat = toRadians(lat2 - lat1);
  const dLon = toRadians(lon2 - lon1);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(latitude1) *
      Math.cos(latitude2) *
      Math.sin(dLon / 2) ** 2;

  const c =
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a)
    );

  return earthRadiusKm * c;
}
Enter fullscreen mode Exit fullscreen mode

Now invalid coordinates are rejected immediately.

For example:

distanceBetweenCoordinates(
  120,
  0,
  40,
  20
);
Enter fullscreen mode Exit fullscreen mode

throws:

RangeError: Latitude must be between -90 and 90
Enter fullscreen mode Exit fullscreen mode

Support Kilometers, Miles, and Nautical Miles

A mapping tool may need more than kilometers.

Common units include:

Kilometers
Miles
Nautical miles
Enter fullscreen mode Exit fullscreen mode

We can define Earth-radius values for each unit:

const EARTH_RADIUS = {
  km: 6371.0088,
  miles: 3958.7613,
  nauticalMiles: 3440.0695
};
Enter fullscreen mode Exit fullscreen mode

Then update the function:

function distanceBetweenCoordinates(
  lat1,
  lon1,
  lat2,
  lon2,
  unit = "km"
) {
  validateCoordinate(lat1, lon1);
  validateCoordinate(lat2, lon2);

  const radius =
    EARTH_RADIUS[unit];

  if (!radius) {
    throw new Error(
      "Unsupported distance unit"
    );
  }

  const latitude1 = toRadians(lat1);
  const latitude2 = toRadians(lat2);

  const dLat = toRadians(lat2 - lat1);
  const dLon = toRadians(lon2 - lon1);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(latitude1) *
      Math.cos(latitude2) *
      Math.sin(dLon / 2) ** 2;

  const c =
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a)
    );

  return radius * c;
}
Enter fullscreen mode Exit fullscreen mode

Usage:

console.log(
  distanceBetweenCoordinates(
    40.7128,
    -74.0060,
    34.0522,
    -118.2437,
    "km"
  )
);
Enter fullscreen mode Exit fullscreen mode

Miles:

console.log(
  distanceBetweenCoordinates(
    40.7128,
    -74.0060,
    34.0522,
    -118.2437,
    "miles"
  )
);
Enter fullscreen mode Exit fullscreen mode

Nautical miles:

console.log(
  distanceBetweenCoordinates(
    40.7128,
    -74.0060,
    34.0522,
    -118.2437,
    "nauticalMiles"
  )
);
Enter fullscreen mode Exit fullscreen mode

Return Structured Data

For a real application, returning just one number may be too limited.

Instead, we can calculate the distance once and return multiple units.

function getDistance(
  lat1,
  lon1,
  lat2,
  lon2
) {
  const kilometers =
    distanceBetweenCoordinates(
      lat1,
      lon1,
      lat2,
      lon2,
      "km"
    );

  return {
    kilometers,
    miles: kilometers * 0.621371,
    nauticalMiles:
      kilometers * 0.539957
  };
}
Enter fullscreen mode Exit fullscreen mode

Usage:

console.log(
  getDistance(
    40.7128,
    -74.0060,
    34.0522,
    -118.2437
  )
);
Enter fullscreen mode Exit fullscreen mode

Example output:

{
  kilometers: 3935.75,
  miles: 2445.56,
  nauticalMiles: 2125.13
}
Enter fullscreen mode Exit fullscreen mode

You can then decide how many decimal places to show in the UI.


Keep Calculation Precision Separate from Display Precision

Avoid rounding too early.

For example, don't do this inside the calculation:

return Number(
  distance.toFixed(2)
);
Enter fullscreen mode Exit fullscreen mode

if the value will later be reused in other calculations.

Instead, return the full value:

return distance;
Enter fullscreen mode Exit fullscreen mode

and format it only when displaying it:

const distance =
  distanceBetweenCoordinates(
    40.7128,
    -74.0060,
    34.0522,
    -118.2437
  );

console.log(
  `${distance.toFixed(2)} km`
);
Enter fullscreen mode Exit fullscreen mode

This keeps your calculation layer more accurate and reusable.


Handle Identical Coordinates

What happens if both points are exactly the same?

distanceBetweenCoordinates(
  40.7128,
  -74.006,
  40.7128,
  -74.006
);
Enter fullscreen mode Exit fullscreen mode

The Haversine formula naturally returns:

0
Enter fullscreen mode Exit fullscreen mode

So you usually don't need a special condition.

However, if your application wants to optimize this case:

if (
  lat1 === lat2 &&
  lon1 === lon2
) {
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

This can avoid unnecessary trigonometric calculations.

For most applications, either approach is fine.


Example: London to Paris

Let's try a shorter real-world distance.

London:

51.5074, -0.1278
Enter fullscreen mode Exit fullscreen mode

Paris:

48.8566, 2.3522
Enter fullscreen mode Exit fullscreen mode

JavaScript:

const londonToParis =
  distanceBetweenCoordinates(
    51.5074,
    -0.1278,
    48.8566,
    2.3522
  );

console.log(
  `${londonToParis.toFixed(2)} km`
);
Enter fullscreen mode Exit fullscreen mode

The result is roughly:

344 km
Enter fullscreen mode Exit fullscreen mode

This is the straight-line great-circle distance, not driving distance.

That's an important distinction.


Great-Circle Distance Is Not Driving Distance

The Haversine formula answers:

What is the shortest distance over the Earth's surface between two coordinates?

It does not consider:

  • Roads
  • Bridges
  • Traffic
  • Mountains
  • Walking routes
  • Rail networks
  • Borders
  • One-way streets

So:

GPS coordinate distance
Enter fullscreen mode Exit fullscreen mode

and:

driving distance
Enter fullscreen mode Exit fullscreen mode

are two different problems.

For driving distance, you need a routing engine or routing API.

The Haversine formula is better suited to:

  • Straight-line map distance
  • Nearby-location filtering
  • GPS proximity checks
  • Geographic analysis
  • Approximate travel radius
  • Location ranking

What About Earth's Shape?

The Haversine formula assumes the Earth is a sphere.

In reality, the Earth is closer to an oblate ellipsoid.

That means Haversine distance is an approximation.

For most:

  • Web applications
  • Map tools
  • Location searches
  • GPS utilities
  • Nearby-place calculations

the approximation is usually sufficient.

Applications requiring survey-grade or high-precision geodesic calculations should use an ellipsoidal model such as WGS84 with a proper geodesic algorithm.

So choose the calculation method based on the accuracy requirements of your application.


A TypeScript Version

We can make the unit options explicit with TypeScript.

type DistanceUnit =
  | "km"
  | "miles"
  | "nauticalMiles";
Enter fullscreen mode Exit fullscreen mode

Define the radius map:

const EARTH_RADIUS: Record<
  DistanceUnit,
  number
> = {
  km: 6371.0088,
  miles: 3958.7613,
  nauticalMiles: 3440.0695
};
Enter fullscreen mode Exit fullscreen mode

Then:

function distanceBetweenCoordinates(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number,
  unit: DistanceUnit = "km"
): number {
  validateCoordinate(lat1, lon1);
  validateCoordinate(lat2, lon2);

  const radius =
    EARTH_RADIUS[unit];

  const latitude1 =
    toRadians(lat1);

  const latitude2 =
    toRadians(lat2);

  const dLat =
    toRadians(lat2 - lat1);

  const dLon =
    toRadians(lon2 - lon1);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(latitude1) *
      Math.cos(latitude2) *
      Math.sin(dLon / 2) ** 2;

  const c =
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a)
    );

  return radius * c;
}
Enter fullscreen mode Exit fullscreen mode

Now this is valid:

distanceBetweenCoordinates(
  40.7128,
  -74.006,
  34.0522,
  -118.2437,
  "miles"
);
Enter fullscreen mode Exit fullscreen mode

while this will be caught by TypeScript:

distanceBetweenCoordinates(
  40.7128,
  -74.006,
  34.0522,
  -118.2437,
  "meters"
);
Enter fullscreen mode Exit fullscreen mode

unless you explicitly support that unit.


Testing With Vitest

Geographic calculation functions are good candidates for unit tests.

Example:

import {
  describe,
  expect,
  it
} from "vitest";

describe(
  "distanceBetweenCoordinates",
  () => {
    it(
      "returns zero for identical coordinates",
      () => {
        const distance =
          distanceBetweenCoordinates(
            40.7128,
            -74.006,
            40.7128,
            -74.006
          );

        expect(distance).toBe(0);
      }
    );

    it(
      "calculates London to Paris",
      () => {
        const distance =
          distanceBetweenCoordinates(
            51.5074,
            -0.1278,
            48.8566,
            2.3522
          );

        expect(distance)
          .toBeGreaterThan(340);

        expect(distance)
          .toBeLessThan(350);
      }
    );

    it(
      "rejects invalid latitude",
      () => {
        expect(() =>
          distanceBetweenCoordinates(
            100,
            0,
            40,
            20
          )
        ).toThrow(RangeError);
      }
    );
  }
);
Enter fullscreen mode Exit fullscreen mode

For geographic calculations, testing a reasonable range is often safer than checking a value to many decimal places.

Different Earth-radius assumptions can produce slightly different results.


Build a Simple Distance Calculator UI

A basic HTML interface might contain:

<input
  id="lat1"
  type="number"
  step="any"
  placeholder="Latitude 1"
/>

<input
  id="lon1"
  type="number"
  step="any"
  placeholder="Longitude 1"
/>

<input
  id="lat2"
  type="number"
  step="any"
  placeholder="Latitude 2"
/>

<input
  id="lon2"
  type="number"
  step="any"
  placeholder="Longitude 2"
/>

<button id="calculate">
  Calculate Distance
</button>

<p id="result"></p>
Enter fullscreen mode Exit fullscreen mode

Then:

document
  .querySelector("#calculate")
  .addEventListener(
    "click",
    () => {
      const lat1 = Number(
        document
          .querySelector("#lat1")
          .value
      );

      const lon1 = Number(
        document
          .querySelector("#lon1")
          .value
      );

      const lat2 = Number(
        document
          .querySelector("#lat2")
          .value
      );

      const lon2 = Number(
        document
          .querySelector("#lon2")
          .value
      );

      try {
        const distance =
          distanceBetweenCoordinates(
            lat1,
            lon1,
            lat2,
            lon2
          );

        document
          .querySelector("#result")
          .textContent =
            `${distance.toFixed(2)} km`;
      } catch (error) {
        document
          .querySelector("#result")
          .textContent =
            error.message;
      }
    }
  );
Enter fullscreen mode Exit fullscreen mode

This is enough to build a simple browser-based GPS distance calculator.


Using Map Clicks Instead of Manual Coordinate Entry

For map applications, users may prefer selecting points visually.

The flow becomes:

Click Point A
      ↓
Store coordinates

Click Point B
      ↓
Store coordinates

Calculate Haversine distance
      ↓
Display result
Enter fullscreen mode Exit fullscreen mode

For example:

const points = [];

map.on("click", event => {
  points.push({
    latitude: event.lngLat.lat,
    longitude: event.lngLat.lng
  });

  if (points.length === 2) {
    const [a, b] = points;

    const distance =
      distanceBetweenCoordinates(
        a.latitude,
        a.longitude,
        b.latitude,
        b.longitude
      );

    console.log(
      `${distance.toFixed(2)} km`
    );

    points.length = 0;
  }
});
Enter fullscreen mode Exit fullscreen mode

This interaction works especially well for map-based measurement tools.


Test With Real Coordinates

When working on distance calculations, it's useful to test the function with real places rather than only artificial coordinate values.

I've been building CoordMap's Measure Distance tool, which lets you work with real geographic points and measure distances between locations.

You can use it to pick two locations and compare the result with your JavaScript implementation.

For example:

London
51.5074, -0.1278

Paris
48.8566, 2.3522
Enter fullscreen mode Exit fullscreen mode

or:

New York
40.7128, -74.0060

Los Angeles
34.0522, -118.2437
Enter fullscreen mode Exit fullscreen mode

Testing different regions is useful because it helps catch common mistakes such as:

latitude / longitude swapped

degrees passed directly into Math.sin()

incorrect Earth radius

invalid coordinate ranges
Enter fullscreen mode Exit fullscreen mode

Complete Copy-Paste Version

Here's the full implementation:

const EARTH_RADIUS = {
  km: 6371.0088,
  miles: 3958.7613,
  nauticalMiles: 3440.0695
};

function toRadians(degrees) {
  return degrees * Math.PI / 180;
}

function isValidLatitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -90 &&
    value <= 90
  );
}

function isValidLongitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -180 &&
    value <= 180
  );
}

function validateCoordinate(
  latitude,
  longitude
) {
  if (!isValidLatitude(latitude)) {
    throw new RangeError(
      "Latitude must be between -90 and 90"
    );
  }

  if (!isValidLongitude(longitude)) {
    throw new RangeError(
      "Longitude must be between -180 and 180"
    );
  }
}

function distanceBetweenCoordinates(
  lat1,
  lon1,
  lat2,
  lon2,
  unit = "km"
) {
  validateCoordinate(lat1, lon1);
  validateCoordinate(lat2, lon2);

  const radius =
    EARTH_RADIUS[unit];

  if (!radius) {
    throw new Error(
      "Unsupported distance unit"
    );
  }

  if (
    lat1 === lat2 &&
    lon1 === lon2
  ) {
    return 0;
  }

  const latitude1 =
    toRadians(lat1);

  const latitude2 =
    toRadians(lat2);

  const dLat =
    toRadians(lat2 - lat1);

  const dLon =
    toRadians(lon2 - lon1);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(latitude1) *
      Math.cos(latitude2) *
      Math.sin(dLon / 2) ** 2;

  const c =
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a)
    );

  return radius * c;
}

const distance =
  distanceBetweenCoordinates(
    40.7128,
    -74.0060,
    34.0522,
    -118.2437
  );

console.log(
  `${distance.toFixed(2)} km`
);
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Calculating the distance between two GPS coordinates is a small problem with a few important details.

The core process is:

Two GPS coordinates
        ↓
Validate latitude / longitude
        ↓
Convert degrees to radians
        ↓
Apply Haversine formula
        ↓
Calculate angular distance
        ↓
Multiply by Earth radius
        ↓
Kilometers / miles / nautical miles
Enter fullscreen mode Exit fullscreen mode

The main things to remember are:

  • Latitude must stay between -90 and 90.
  • Longitude must stay between -180 and 180.
  • JavaScript trigonometric functions use radians.
  • The Haversine formula calculates great-circle distance.
  • Great-circle distance is not driving distance.
  • Avoid rounding until the presentation layer.
  • Use a more precise geodesic model when your application requires survey-grade accuracy.

For most web mapping, GPS, and proximity applications, the Haversine formula remains a useful and straightforward solution.

Top comments (0)