Skip to content

Instantly share code, notes, and snippets.

@jtribble
Created March 7, 2016 19:14
Show Gist options
  • Save jtribble/ae95f491e2da9d2c0d44 to your computer and use it in GitHub Desktop.
Save jtribble/ae95f491e2da9d2c0d44 to your computer and use it in GitHub Desktop.
Compute the distance between two points using the Haversine formula. See: https://en.wikipedia.org/wiki/Haversine_formula
//
// Compute the distance between two points using the Haversine formula
//
// See: https://en.wikipedia.org/wiki/Haversine_formula
//
// Example: geoCoordDistance(
// {lat: '48° 12′ 30″ N', lng: '16° 22′ 23″ E'},
// {lat: '23° 33′ 0″ S', lng: '46° 38′ 0″ W'}
// ) => 10130
//
;(function(global) {
'use strict';
/**
* Polyfill for Number.prototype.toRadians
*/
if (typeof Number.prototype.toRadians === 'undefined') {
Number.prototype.toRadians = function() {
return this * Math.PI / 180;
}
}
/**
* @var {object} - Regex codes for °, ′, and ″
*/
const symbols = {
degrees: unescape('%B0'), // °
minutes: unescape('%u2032'), // ′
seconds: unescape('%u2033') // ″
};
/**
* @var {number} - Earth's radius, in kilometers
*/
const earthRadius = 6371;
/**
* Convert lat/lng from DMS to decimal degrees format
*
* @param {number} degrees - The degree information
* @param {number} minutes - The minute information
* @param {number} seconds - The second information
* @param {string} cardinalDir - N, S, E, W
* @return {number} - Decimal format
*/
const dmsToDecDeg = (degrees, minutes, seconds, cardinalDir) => {
const decDeg = degrees + (minutes / 60) + (seconds / 3600);
return (~['N', 'E'].indexOf(cardinalDir)) ? decDeg : -decDeg;
};
/**
* Convert geo coordinate string (e.g. 48° 12′ 30″ N) to a geo coord shaped object
*
* @param {string} str - The lat/lng string
* @return {number} - The value in radians
*/
const coordStrToObj = str => {
const degPieces = str.split(symbols.degrees).map(s => s.trim());
const minPieces = degPieces[1].split(symbols.minutes).map(s => s.trim());
const secPieces = minPieces[1].split(symbols.seconds).map(s => s.trim());
const degrees = Number(degPieces[0]);
const minutes = Number(minPieces[0]);
const seconds = Number(secPieces[0]);
const cardinalDir = secPieces[1];
const decimalDegrees = dmsToDecDeg(degrees, minutes, seconds, cardinalDir);
const decimalRadians = decimalDegrees.toRadians();
return {
decimalDegrees,
decimalRadians,
degrees,
minutes,
seconds,
cardinalDir
};
};
/**
* Given lat and lng string (e.g. 48° 12′ 30″ N), return distance between them in kilometers
*
* @param {object} one - The first point
* @param {string} one.lat - The first point's latitude string
* @param {string} one.lng - The first point's longitude string
* @param {object} two - The second point
* @param {string} two.lat - The second point's latitude string
* @param {string} two.lng - The second point's longitude string
* @return {number} - The distance in kilometers
*/
const geoCoordDistance = (one, two) => {
const ptOne = {
lat: coordStrToObj(one.lat),
lng: coordStrToObj(one.lng)
};
const ptTwo = {
lat: coordStrToObj(two.lat),
lng: coordStrToObj(two.lng)
};
const latDelta = (ptTwo.lat.decimalDegrees - ptOne.lat.decimalDegrees).toRadians();
const lngDelta = (ptTwo.lng.decimalDegrees - ptOne.lng.decimalDegrees).toRadians();
const a = (
Math.pow(Math.sin(latDelta / 2), 2) +
Math.cos(ptOne.lat.decimalRadians) * Math.cos(ptTwo.lat.decimalRadians) *
Math.pow(Math.sin(lngDelta / 2), 2)
);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = earthRadius * c;
// return the distance
return d;
};
// export function to global object
global.geoCoordDistance = geoCoordDistance;
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment