Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #1 from l8nite/master

Adds the destVincenty algorithm
  • Loading branch information...
commit af5b9cc14c8242c39c4493bdf2b76614673cd830 2 parents 27a1fc5 + 989ca70
@TankofVines authored
Showing with 122 additions and 13 deletions.
  1. +34 −6 README.md
  2. +88 −7 vincenty.js
View
40 README.md
@@ -1,22 +1,50 @@
node-vincenty
=============
-Repackaged some vanilla js that calculates metric distance between two latitude and longitude coordinates based on Chris Vereen's script here:
+Repackaged some vanilla js that calculates metric distance between two latitude and longitude coordinates based on Chris Veness' script here:
http://www.movable-type.co.uk/scripts/latlong-vincenty.html
+http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
-The module exports one function distVincenty(lat1, long1, lat2, long2, callback) which accepts four parameters representing the latitude and longitude of the input coordinates.
+The module exports two functions:
+ distVincenty(lat1, long1, lat2, long2, callback) which accepts four parameters representing the latitude and longitude of the input coordinates.
+ destVincenty(lat1, lon1, brng, dist) which calculates the destination point given the starting latitude and longitude with a given bearing and distance
Install:
npm install node-vincenty
-Example:
+Usage:
var vincenty = require('node-vincenty');
- vincenty.distVincenty(30.5, -100.6, 31.7, -101.8, function(distance){
- console.log(distance);
+Examples:
+
+ // these are synchronous
+ console.log( vincenty.distVincenty(30.5, -100.6, 31.7, -101.8) );
+ console.log( vincenty.destVincenty(30.5, -100.6, 175518.816, -40.4035) );
+
+ // these are asynchronous
+ vincenty.distVincenty(30.5, -100.6, 31.7, -101.8, function (distance) {
+ console.log(distance);
+ });
+
+ vincenty.distVincenty(30.5, -100.6, 31.7, -101.8, function (distance, initialBearing, finalBearing) {
+ console.log(distance, initialBearing, finalBearing);
+ });
+
+ vincenty.destVincenty(30.5, -100.6, 175518.816, -40.4035, function (lat, lon, finalBearing) {
+ console.log(lat, lon, finalBearing);
});
-$ 17551.816
+Outputs:
+
+ { distance: '175518.816',
+ initialBearing: -40.40353176919702,
+ finalBearing: -41.02342255854039 }
+ { lat: 30.50034497548687,
+ lon: -100.59986425485471,
+ finalBearing: -161.1839311037904 }
+ 175518.816
+ 175518.816 -40.40353176919702 -41.02342255854039
+ 30.50034497548687 -100.59986425485471 -161.1839311037904
View
95 vincenty.js
@@ -1,3 +1,11 @@
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Vincenty Direct Solution of Geodesics on the Ellipsoid (c) Chris Veness 2005-2012 */
+/* */
+/* from: Vincenty direct formula - T Vincenty, "Direct and Inverse Solutions of Geodesics on the */
+/* Ellipsoid with application of nested equations", Survey Review, vol XXII no 176, 1975 */
+/* http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
function toRad(Value) {
/** Converts numeric degrees to radians */
return Value * Math.PI / 180;
@@ -18,11 +26,11 @@ function distVincenty(lat1, lon1, lat2, lon2, callback) {
var U2 = Math.atan(( 1 - f ) * Math.tan( toRad(lat2) ));
var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
-
+
var lambda = L, lambdaP, iterLimit = 100;
do {
var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
- var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+ var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
(cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
if (sinSigma==0) return 0; // co-incident points
var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
@@ -45,14 +53,87 @@ function distVincenty(lat1, lon1, lat2, lon2, callback) {
var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
var s = b*A*(sigma-deltaSigma);
-
+
s = s.toFixed(3); // round to 1mm precision
- callback(s);
-
+
// note: to return initial/final bearings in addition to distance, use something like:
var fwdAz = Math.atan2(cosU2*sinLambda, cosU1*sinU2-sinU1*cosU2*cosLambda);
var revAz = Math.atan2(cosU1*sinLambda, -sinU1*cosU2+cosU1*sinU2*cosLambda);
- return { distance: s, initialBearing: toDeg(fwdAz), finalBearing: toDeg(revAz) };
+ var result = { distance: s, initialBearing: toDeg(fwdAz), finalBearing: toDeg(revAz) };
+
+ if (callback !== undefined && callback instanceof Function) {
+ if (callback.length === 3) {
+ callback(result.distance, result.initialBearing, result.finalBearing);
+ }
+ else {
+ callback(result.distance);
+ }
+ }
+
+ return result;
+}
+
+
+/**
+ * Calculates destination point given start point lat/long, bearing & distance,
+ * using Vincenty inverse formula for ellipsoids
+ *
+ * @param {Number} lat1, lon1: first point in decimal degrees
+ * @param {Number} brng: initial bearing in decimal degrees
+ * @param {Number} dist: distance along bearing in metres
+ * @returns (LatLon} destination point
+ */
+function destVincenty(lat1, lon1, brng, dist, callback) {
+ var a = 6378137, b = 6356752.3142, f = 1/298.257223563; // WGS-84 ellipsiod
+ var s = dist;
+ var alpha1 = toRad(brng);
+ var sinAlpha1 = Math.sin(alpha1);
+ var cosAlpha1 = Math.cos(alpha1);
+
+ var tanU1 = (1-f) * Math.tan(toRad(lat1));
+ var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
+ var sigma1 = Math.atan2(tanU1, cosAlpha1);
+ var sinAlpha = cosU1 * sinAlpha1;
+ var cosSqAlpha = 1 - sinAlpha*sinAlpha;
+ var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+ var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+ var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+
+ var sigma = s / (b*A), sigmaP = 2*Math.PI;
+ while (Math.abs(sigma-sigmaP) > 1e-12) {
+ var cos2SigmaM = Math.cos(2*sigma1 + sigma);
+ var sinSigma = Math.sin(sigma);
+ var cosSigma = Math.cos(sigma);
+ var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+ B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+ sigmaP = sigma;
+ sigma = s / (b*A) + deltaSigma;
+ }
+
+ var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
+ var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
+ (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
+ var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
+ var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+ var L = lambda - (1-C) * f * sinAlpha *
+ (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+ var lon2 = (toRad(lon1)+L+3*Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180
+
+ var revAz = Math.atan2(sinAlpha, -tmp); // final bearing, if required
+
+ var result = { lat: toDeg(lat2), lon: toDeg(lon2), finalBearing: toDeg(revAz) };
+
+ if (callback !== undefined && callback instanceof Function) {
+ if (callback.length === 3) {
+ callback(result.lat, result.lon, result.finalBearing);
+ }
+ else {
+ callback(result);
+ }
+ }
+
+ return result;
}
-exports.distVincenty = distVincenty;
+exports.distVincenty = distVincenty;
+exports.destVincenty = destVincenty;
Please sign in to comment.
Something went wrong with that request. Please try again.