Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Adds the destVincenty algorithm #1

Merged
merged 5 commits into from

2 participants

@l8nite

This change adds the destVincenty algorithm:
http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html

Updated the README.md to include new usage and examples of both synchronous and asynchronous operation.

I also slightly modified the existing function to:

  • make callback optional
  • check # of arguments on callback function, and if 3, unpack the result data into the arguments directly -- see the README for example
@l8nite

Also, thanks for packaging this up :+1:

@TankofVines TankofVines merged commit af5b9cc into from
@TankofVines
Owner

Awesome. Thanks for contributing.

@l8nite

Sure thing! Can you push a new package to npm? :)

@l8nite l8nite referenced this pull request in l8nite/spartan-missile-strike
Closed

switch to node-vincenty when it is updated #50

@TankofVines
Owner

I just published to npm. Let me know if you see any issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
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;
Something went wrong with that request. Please try again.