diff --git a/index.js b/index.js index 773425d..7da21ad 100644 --- a/index.js +++ b/index.js @@ -54,18 +54,19 @@ module.exports = function(point1, point2, units) { var dLon = toRad(coordinates2[0] - coordinates1[0]); var lat1 = toRad(coordinates1[1]); var lat2 = toRad(coordinates2[1]); - var a = Math.sin(dLat/2) * Math.sin(dLat/2) + - Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.sin(dLon / 2) * Math.sin(dLon / 2) * + Math.cos(lat1) * Math.cos(lat2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var R; - switch(units) { + switch (units) { case 'miles': - R = 3960; - break; case 'kilometers': case 'kilometres': - R = 6373; + case 'meters': + case undefined: + R = getEarthRadius(lat1, units); break; case 'degrees': R = 57.2957795; @@ -73,17 +74,75 @@ module.exports = function(point1, point2, units) { case 'radians': R = 1; break; - case undefined: - R = 6373; - break; default: throw new Error('unknown option given to "units"'); } + // Round result. var distance = R * c; - return distance; + + switch (units) { + case 'miles': + case 'kilometers': + case 'kilometres': + case undefined: + distance = Math.floor(distance * 1000) / 1000; + break; + + case 'meters': + distance = Math.floor(distance); + break; + + default: + break; + } + + return Number(distance); }; function toRad(degree) { return degree * Math.PI / 180; } + +function kmToMiles(kilometers) { + return kilometers * 0.621371192; +} + +function metersToMiles(meters) { + return meters * 0.000621371192; +} + +function getEarthRadius(latdeg, units) { + + var radiusMajorInMeters = 6378137.0; + var radiusMinorInMeters = 6356752.3142; + var radiusMajor = 0; + var radiusMinor = 0; + + switch (units) { + case 'miles': + radiusMajor = metersToMiles(radiusMajorInMeters); + radiusMinor = metersToMiles(radiusMinorInMeters); + break; + case 'kilometers': + case 'kilometres': + case undefined: + radiusMajor = radiusMajorInMeters / 1000; + radiusMinor = radiusMinorInMeters / 1000; + break; + case 'meters': + radiusMajor = radiusMajorInMeters; + radiusMinor = radiusMinorInMeters; + break; + default: + throw new Error('unknown option given to "units"'); + } + + // http://en.wikipedia.org/wiki/Earth_radius + var An = radiusMajor * radiusMajor * Math.cos(latdeg); + var Bn = radiusMinor * radiusMinor * Math.sin(latdeg); + var Ad = radiusMajor * Math.cos(latdeg); + var Bd = radiusMinor * Math.sin(latdeg); + + return Math.sqrt((An * An + Bn * Bn) / (Ad * Ad + Bd * Bd)); +} diff --git a/test.js b/test.js index f759fc5..c7a593c 100644 --- a/test.js +++ b/test.js @@ -1,25 +1,77 @@ var test = require('tape'); var distance = require('./'); -test('distance', function(t){ +test('distance', function(t) { var pt1 = { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.343, 39.984]} + "geometry": { + "type": "Point", + "coordinates": [-75.343, 39.984] + } }; var pt2 = { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.534, 39.123]} + "geometry": { + "type": "Point", + "coordinates": [-75.534, 39.123] + } }; - t.equal(distance(pt1, pt2, 'miles'), 60.37218405837491, 'miles'); - t.equal(distance(pt1, pt2, 'kilometers'), 97.15957803131901, 'kilometers'); - t.equal(distance(pt1, pt2, 'kilometres'), 97.15957803131901, 'kilometres'); + // Center point + var pt3 = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-73.5719678, 45.5260794] + } + }; + + // On the circle radius (center + 400 meters + 0 degres) + var pt4 = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-73.57196780000001, 45.52968474026626] + + } + }; + + // On the circle radius too (center + 400 meters + 270 degres) + var pt5 = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [-73.57711398872225, 45.52607928446446] + } + }; + + /* Test # 01 */ + t.equal(distance(pt1, pt2, 'miles'), 60.337, 'miles'); + /* Test # 02 */ + t.equal(distance(pt1, pt2, 'kilometers'), 97.103, 'kilometers'); + /* Test # 03 */ + t.equal(distance(pt1, pt2, 'kilometres'), 97.103, 'kilometres'); + /* Test # 04 */ t.equal(distance(pt1, pt2, 'radians'), 0.015245501024842149, 'radians'); + /* Test # 05 */ t.equal(distance(pt1, pt2, 'degrees'), 0.8735028650863799, 'degrees'); - t.equal(distance(pt1, pt2), 97.15957803131901, 'default=kilometers'); + /* Test # 06 */ + t.equal(distance(pt1, pt2), 97.103, 'default=kilometers'); + /* Test # 07 */ + t.equal(distance(pt3, pt4, 'meters'), 400, 'meters'); + /* Test # 08 */ + t.equal(distance(pt3, pt4, 'kilometers'), 0.400, 'kilometers'); + /* Test # 09 */ + t.equal(distance(pt3, pt5, 'meters'), 400, 'meters'); + /* Test # 10 */ + t.equal(distance(pt3, pt5, 'kilometers'), 0.400, 'kilometers'); + /* Test # 11 */ + t.equal(distance(pt3, pt4, 'meters'), distance(pt3, pt5, 'meters')); + /* Test # 12 */ + t.equal(distance(pt3, pt4, 'kilometers'), distance(pt3, pt5, 'kilometers')); t.throws(function() { - distance(pt1, pt2, 'blah'); + distance(pt1, pt2, 'blah'); }, 'unknown option given to units'); t.end();