Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

NEW: Added getCompassDirection() to find out the compass direction fr…

…om one coordinate to another.
  • Loading branch information...
commit f16d5487c9ff1ca75c82e189c618201db831f26f 1 parent 7f2bd43
Manuel Bieh authored
Showing with 167 additions and 11 deletions.
  1. +165 −9 geolib.js
  2. +2 −2 geolib.min.js
174 geolib.js
View
@@ -4,8 +4,8 @@
* WGS 84 (World Geodetic System 1984)
*
* @author Manuel Bieh
- * @url http://www.manuel-bieh.de/
- * @version 1.1.4
+ * @url http://www.manuelbieh.com/
+ * @version 1.1.5
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPL
*
*/
@@ -17,11 +17,9 @@
var geolib = {
- decimal: {
- },
+ decimal: {},
- sexagesimal: {
- },
+ sexagesimal: {},
distance: 0,
@@ -29,7 +27,7 @@
* Calculates geodetic distance between two points specified by latitude/longitude using
* Vincenty inverse formula for ellipsoids
* Vincenty Inverse Solution of Geodesics on the Ellipsoid (c) Chris Veness 2002-2010
- * CC BY 3.0
+ * (Licensed under CC BY 3.0)
*
* @param object Start position {latitude: 123, longitude: 123}
* @param object End position {latitude: 123, longitude: 123}
@@ -283,8 +281,8 @@
/**
* Checks whether a point is inside of a circle or not.
*
- * @param object coordinate to check e.g. {latitude: 51.5023, longitude: 7.3815}
- * @param object coordinate of the circle's center e.g. {latitude: 51.4812, longitude: 7.4025}
+ * @param object coordinate to check (e.g. {latitude: 51.5023, longitude: 7.3815})
+ * @param object coordinate of the circle's center (e.g. {latitude: 51.4812, longitude: 7.4025})
* @param integer maximum radius in meters
* @return bool true if the coordinate is inside the given radius
*/
@@ -296,6 +294,158 @@
/**
+ * Gets rhumb line bearing of two points. Find out about the difference between rhumb line and
+ * great circle bearing on Wikipedia. It's quite complicated. Rhumb line should be fine in most cases:
+ *
+ * http://en.wikipedia.org/wiki/Rhumb_line#General_and_mathematical_description
+ *
+ * Function heavily based on Doug Vanderweide's great PHP version (licensed under GPL 3.0)
+ * http://www.dougv.com/2009/07/13/calculating-the-bearing-and-compass-rose-direction-between-two-latitude-longitude-coordinates-in-php/
+ *
+ * @param object origin coordinate (e.g. {latitude: 51.5023, longitude: 7.3815})
+ * @param object destination coordinate
+ * @return integer calculated bearing
+ */
+ getRhumbLineBearing: function(originLL, destLL) {
+
+ // difference of longitude coords
+ var diffLon = destLL.longitude.toRad() - originLL.longitude.toRad();
+
+ // difference latitude coords phi
+ var diffPhi = Math.log(Math.tan(destLL.latitude.toRad() / 2 + Math.PI / 4) / Math.tan(originLL.latitude.toRad() / 2 + Math.PI / 4));
+
+ // recalculate diffLon if it is greater than pi
+ if(Math.abs(diffLon) > Math.PI) {
+ if(diffLon > 0) {
+ diffLon = (2 * Math.PI - diffLon) * -1;
+ }
+ else {
+ diffLon = 2 * Math.PI + diffLon;
+ }
+ }
+
+ //return the angle, normalized
+ return (Math.atan2(diffLon, diffPhi).toDeg() + 360) % 360;
+
+ },
+
+
+ /**
+ * Gets great circle bearing of two points. See description of getRhumbLineBearing for more information
+ *
+ * @param object origin coordinate (e.g. {latitude: 51.5023, longitude: 7.3815})
+ * @param object destination coordinate
+ * @return integer calculated bearing
+ */
+ getBearing: function(originLL, destLL) {
+
+ var bearing = (
+ (
+ Math.atan2(
+ Math.sin(
+ destLL.longitude.toRad() -
+ originLL.longitude.toRad()
+ ) *
+ Math.cos(
+ destLL.latitude.toRad()
+ ),
+ Math.cos(
+ originLL.latitude.toRad()
+ ) *
+ Math.sin(
+ destLL.latitude.toRad()
+ ) -
+ Math.sin(
+ originLL.latitude.toRad()
+ ) *
+ Math.cos(
+ destLL.latitude.toRad()
+ ) *
+ Math.cos(
+ destLL.longitude.toRad() - originLL.longitude.toRad()
+ )
+ )
+ ).toDeg() + 360
+ ) % 360;
+
+ return bearing;
+
+ },
+
+
+ /**
+ * Gets the compass direction from an origin coordinate to a destination coordinate.
+ *
+ * @param object origin coordinate (e.g. {latitude: 51.5023, longitude: 7.3815})
+ * @param object destination coordinate
+ * @param string Bearing mode. Can be either circle or rhumbline
+ * @return object Returns an object with a rough (NESW) and an exact direction (NNE, NE, ENE, E, ESE, etc).
+ */
+ getCompassDirection: function(originLL, destLL, bearingMode) {
+
+ var direction;
+ if(bearingMode == 'circle') { // use great circle bearing
+ var bearing = geolib.getBearing(originLL, destLL);
+ } else { // default is rhumb line bearing
+ var bearing = geolib.getRhumbLineBearing(originLL, destLL);
+ }
+
+ switch(Math.round(bearing/22.5)) {
+ case 1:
+ direction = {exact: "NNE", rough: "N"};
+ break;
+ case 2:
+ direction = {exact: "NE", rough: "N"}
+ break;
+ case 3:
+ direction = {exact: "ENE", rough: "E"}
+ break;
+ case 4:
+ direction = {exact: "E", rough: "E"}
+ break;
+ case 5:
+ direction = {exact: "ESE", rough: "E"}
+ break;
+ case 6:
+ direction = {exact: "SE", rough: "E"}
+ break;
+ case 7:
+ direction = {exact: "SSE", rough: "S"}
+ break;
+ case 8:
+ direction = {exact: "S", rough: "S"}
+ break;
+ case 9:
+ direction = {exact: "SSW", rough: "S"}
+ break;
+ case 10:
+ direction = {exact: "SW", rough: "S"}
+ break;
+ case 11:
+ direction = {exact: "WSW", rough: "W"}
+ break;
+ case 12:
+ direction = {exact: "W", rough: "W"}
+ break;
+ case 13:
+ direction = {exact: "WNW", rough: "W"}
+ break;
+ case 14:
+ direction = {exact: "NW", rough: "W"}
+ break;
+ case 15:
+ direction = {exact: "NNW", rough: "N"}
+ break;
+ default:
+ direction = {exact: "N", rough: "N"}
+ }
+
+ return direction;
+
+ },
+
+
+ /**
* Sorts an array of coords by distance from a reference coordinate
*
* @param object reference coordinate e.g. {latitude: 51.5023, longitude: 7.3815}
@@ -518,6 +668,12 @@
}
}
+ if (typeof(Number.prototype.toDeg) === "undefined") {
+ Number.prototype.toDeg = function() {
+ return this * 180 / Math.PI;
+ }
+ }
+
window.geolib = geolib;
})(this);
4 geolib.min.js
View
@@ -5,8 +5,8 @@
*
* @author Manuel Bieh
* @url http://www.manuel-bieh.de/
- * @version 1.1.4
+ * @version 1.1.5
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPL
*
*/
-;(function(a,b){var c=6378137;var d=/^([0-9]{1,3})°\s*([0-9]{1,3})'\s*(([0-9]{1,3}(\.([0-9]{1,2}))?)"\s*)?([NEOSW]?)$/;var e={decimal:{},sexagesimal:{},distance:0,getDistance:function(a,b,c){c=parseInt(c,10)||1;var d={},f={};d.latitude=e.useDecimal(a.latitude);d.longitude=e.useDecimal(a.longitude);f.latitude=e.useDecimal(b.latitude);f.longitude=e.useDecimal(b.longitude);var g=6378137,h=6356752.314245,i=1/298.257223563;var j=(f.longitude-d.longitude).toRad();var k=Math.atan((1-i)*Math.tan(parseFloat(d.latitude).toRad()));var l=Math.atan((1-i)*Math.tan(parseFloat(f.latitude).toRad()));var m=Math.sin(k),n=Math.cos(k);var o=Math.sin(l),p=Math.cos(l);var q=j,r,s=100;do{var t=Math.sin(q),u=Math.cos(q);var v=Math.sqrt(p*t*p*t+(n*o-m*p*u)*(n*o-m*p*u));if(v==0){return e.distance=0}var w=m*o+n*p*u;var x=Math.atan2(v,w);var y=n*p*t/v;var z=1-y*y;var A=w-2*m*o/z;if(isNaN(A)){A=0}var B=i/16*z*(4+i*(4-3*z));r=q;q=j+(1-B)*i*y*(x+B*v*(A+B*w*(-1+2*A*A)))}while(Math.abs(q-r)>1e-12&&--s>0);if(s==0){return NaN}var C=z*(g*g-h*h)/(h*h);var D=1+C/16384*(4096+C*(-768+C*(320-175*C)));var E=C/1024*(256+C*(-128+C*(74-47*C)));var F=E*v*(A+E/4*(w*(-1+2*A*A)-E/6*A*(-3+4*v*v)*(-3+4*A*A)));var G=h*D*(x-F);G=G.toFixed(3);return e.distance=parseInt(Math.round(G/c)*c,10)},getDistanceSimple:function(a,b,d){d=parseInt(d,10)||1;var f={},g={};f.latitude=parseFloat(e.useDecimal(a.latitude)).toRad();f.longitude=parseFloat(e.useDecimal(a.longitude)).toRad();g.latitude=parseFloat(e.useDecimal(b.latitude)).toRad();g.longitude=parseFloat(e.useDecimal(b.longitude)).toRad();var h=Math.round(Math.acos(Math.sin(g.latitude)*Math.sin(f.latitude)+Math.cos(g.latitude)*Math.cos(f.latitude)*Math.cos(f.longitude-g.longitude))*c);return e.distance=parseInt(Math.round(h/d)*d,10)},getCenter:function(a){var b=function(a){return Math.max.apply(Math,a)};var c=function(a){return Math.min.apply(Math,a)};var d,f,g={lat:[],lng:[]};for(var h in a){g.lat.push(e.useDecimal(a[h].latitude));g.lng.push(e.useDecimal(a[h].longitude))}var i=c(g.lat);var j=c(g.lng);var k=b(g.lat);var l=b(g.lng);d=((i+k)/2).toFixed(6);f=((j+l)/2).toFixed(6);var m=e.convertUnit("km",e.getDistance(i,j,k,l));return{latitude:d,longitude:f,distance:m}},isPointInside:function(a,b){for(var c=false,d=-1,e=b.length,f=e-1;++d<e;f=d){(b[d].longitude<=a.longitude&&a.longitude<b[f].longitude||b[f].longitude<=a.longitude&&a.longitude<b[d].longitude)&&a.latitude<(b[f].latitude-b[d].latitude)*(a.longitude-b[d].longitude)/(b[f].longitude-b[d].longitude)+b[d].latitude&&(c=!c)}return c},isPointInCircle:function(a,b,c){return e.getDistance(a,b)<c},orderByDistance:function(a,b){var c=[];for(var d in b){var f=e.getDistance(a,b[d]);c.push({key:d,latitude:b[d].latitude,longitude:b[d].longitude,distance:f})}return c.sort(function(a,b){return a.distance-b.distance})},findNearest:function(a,b,c){c=c||0;var d=e.orderByDistance(a,b);return d[c]},getPathLength:function(a){var b=0,c;for(var d in a){if(c){b+=e.getDistance(a[d],c)}c=a[d]}return b},convertUnit:function(a,b,c){if(b==0||typeof b=="undefined"){if(e.distance==0){return 0}else{b=e.distance}}a=a||"m";c=c||4;switch(a){case"m":return e.round(b,c);break;case"km":return e.round(b/1e3,c);break;case"cm":return e.round(b*100,c);break;case"mm":return e.round(b*1e3,c);break;case"mi":return e.round(b*(1/1609.344),c);break;case"sm":return e.round(b*(1/1852.216),c);break;case"ft":return e.round(b*(100/30.48),c);break;case"in":return e.round(b*100/2.54,c);break;case"yd":return e.round(b*(1/.9144),c);break}return b},useDecimal:function(a){a=a.toString().replace(/\s*/,"");if(!isNaN(parseFloat(a))&&parseFloat(a).toString()==a){return a}else if(e.isSexagesimal(a)==true){return e.sexagesimal2decimal(a)}else{throw"Unknown format."}},decimal2sexagesimal:function(a){if(a in e.sexagesimal){return e.sexagesimal[a]}var b=a.toString().split(".");var c=b[0];var d=("0."+b[1])*60;var f=d.toString().split(".");d=parseInt(d,10);f=(("0."+f[1])*60).toFixed(2);e.sexagesimal[a]=c+"° "+d+"' "+f+'"';return e.sexagesimal[a]},sexagesimal2decimal:function(a){if(a in e.decimal){return e.decimal[a]}var b=new RegExp(d);var c=b.exec(a);if(c){var f=parseFloat(c[2]/60);var g=parseFloat(c[4]/3600)||0}var h=(parseFloat(c[1])+f+g).toFixed(8);h=c[7]=="S"||c[7]=="W"?h*-1:h;e.decimal[a]=h;return h},isSexagesimal:function(a){return d.test(a)},round:function(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c}};if(typeof Number.prototype.toRad==="undefined"){Number.prototype.toRad=function(){return this*Math.PI/180}}a.geolib=e})(this)
+(function(a,b){var c=6378137;var d=/^([0-9]{1,3})°\s*([0-9]{1,3})'\s*(([0-9]{1,3}(\.([0-9]{1,2}))?)"\s*)?([NEOSW]?)$/;var e={decimal:{},sexagesimal:{},distance:0,getDistance:function(a,b,c){c=parseInt(c,10)||1;var d={},f={};d.latitude=e.useDecimal(a.latitude);d.longitude=e.useDecimal(a.longitude);f.latitude=e.useDecimal(b.latitude);f.longitude=e.useDecimal(b.longitude);var g=6378137,h=6356752.314245,i=1/298.257223563;var j=(f.longitude-d.longitude).toRad();var k=Math.atan((1-i)*Math.tan(parseFloat(d.latitude).toRad()));var l=Math.atan((1-i)*Math.tan(parseFloat(f.latitude).toRad()));var m=Math.sin(k),n=Math.cos(k);var o=Math.sin(l),p=Math.cos(l);var q=j,r,s=100;do{var t=Math.sin(q),u=Math.cos(q);var v=Math.sqrt(p*t*p*t+(n*o-m*p*u)*(n*o-m*p*u));if(v==0){return e.distance=0}var w=m*o+n*p*u;var x=Math.atan2(v,w);var y=n*p*t/v;var z=1-y*y;var A=w-2*m*o/z;if(isNaN(A)){A=0}var B=i/16*z*(4+i*(4-3*z));r=q;q=j+(1-B)*i*y*(x+B*v*(A+B*w*(-1+2*A*A)))}while(Math.abs(q-r)>1e-12&&--s>0);if(s==0){return NaN}var C=z*(g*g-h*h)/(h*h);var D=1+C/16384*(4096+C*(-768+C*(320-175*C)));var E=C/1024*(256+C*(-128+C*(74-47*C)));var F=E*v*(A+E/4*(w*(-1+2*A*A)-E/6*A*(-3+4*v*v)*(-3+4*A*A)));var G=h*D*(x-F);G=G.toFixed(3);return e.distance=parseInt(Math.round(G/c)*c,10)},getDistanceSimple:function(a,b,d){d=parseInt(d,10)||1;var f={},g={};f.latitude=parseFloat(e.useDecimal(a.latitude)).toRad();f.longitude=parseFloat(e.useDecimal(a.longitude)).toRad();g.latitude=parseFloat(e.useDecimal(b.latitude)).toRad();g.longitude=parseFloat(e.useDecimal(b.longitude)).toRad();var h=Math.round(Math.acos(Math.sin(g.latitude)*Math.sin(f.latitude)+Math.cos(g.latitude)*Math.cos(f.latitude)*Math.cos(f.longitude-g.longitude))*c);return e.distance=parseInt(Math.round(h/d)*d,10)},getCenter:function(a){var b=function(a){return Math.max.apply(Math,a)};var c=function(a){return Math.min.apply(Math,a)};var d,f,g={lat:[],lng:[]};for(var h in a){g.lat.push(e.useDecimal(a[h].latitude));g.lng.push(e.useDecimal(a[h].longitude))}var i=c(g.lat);var j=c(g.lng);var k=b(g.lat);var l=b(g.lng);d=((i+k)/2).toFixed(6);f=((j+l)/2).toFixed(6);var m=e.convertUnit("km",e.getDistance(i,j,k,l));return{latitude:d,longitude:f,distance:m}},isPointInside:function(a,b){for(var c=false,d=-1,e=b.length,f=e-1;++d<e;f=d){(b[d].longitude<=a.longitude&&a.longitude<b[f].longitude||b[f].longitude<=a.longitude&&a.longitude<b[d].longitude)&&a.latitude<(b[f].latitude-b[d].latitude)*(a.longitude-b[d].longitude)/(b[f].longitude-b[d].longitude)+b[d].latitude&&(c=!c)}return c},isPointInCircle:function(a,b,c){return e.getDistance(a,b)<c},getRhumbLineBearing:function(a,b){var c=b.longitude.toRad()-a.longitude.toRad();var d=Math.log(Math.tan(b.latitude.toRad()/2+Math.PI/4)/Math.tan(a.latitude.toRad()/2+Math.PI/4));if(Math.abs(c)>Math.PI){if(c>0){c=(2*Math.PI-c)*-1}else{c=2*Math.PI+c}}return(Math.atan2(c,d).toDeg()+360)%360},getBearing:function(a,b){var c=(Math.atan2(Math.sin(b.longitude.toRad()-a.longitude.toRad())*Math.cos(b.latitude.toRad()),Math.cos(a.latitude.toRad())*Math.sin(b.latitude.toRad())-Math.sin(a.latitude.toRad())*Math.cos(b.latitude.toRad())*Math.cos(b.longitude.toRad()-a.longitude.toRad())).toDeg()+360)%360;return c},getCompassDirection:function(a,b,c){var d;if(c=="circle"){var f=e.getBearing(a,b)}else{var f=e.getRhumbLineBearing(a,b)}switch(Math.round(f/22.5)){case 1:d={exact:"NNE",rough:"N"};break;case 2:d={exact:"NE",rough:"N"};break;case 3:d={exact:"ENE",rough:"E"};break;case 4:d={exact:"E",rough:"E"};break;case 5:d={exact:"ESE",rough:"E"};break;case 6:d={exact:"SE",rough:"E"};break;case 7:d={exact:"SSE",rough:"S"};break;case 8:d={exact:"S",rough:"S"};break;case 9:d={exact:"SSW",rough:"S"};break;case 10:d={exact:"SW",rough:"S"};break;case 11:d={exact:"WSW",rough:"W"};break;case 12:d={exact:"W",rough:"W"};break;case 13:d={exact:"WNW",rough:"W"};break;case 14:d={exact:"NW",rough:"W"};break;case 15:d={exact:"NNW",rough:"N"};break;default:d={exact:"N",rough:"N"}}return d},orderByDistance:function(a,b){var c=[];for(var d in b){var f=e.getDistance(a,b[d]);c.push({key:d,latitude:b[d].latitude,longitude:b[d].longitude,distance:f})}return c.sort(function(a,b){return a.distance-b.distance})},findNearest:function(a,b,c){c=c||0;var d=e.orderByDistance(a,b);return d[c]},getPathLength:function(a){var b=0,c;for(var d in a){if(c){b+=e.getDistance(a[d],c)}c=a[d]}return b},convertUnit:function(a,b,c){if(b==0||typeof b=="undefined"){if(e.distance==0){return 0}else{b=e.distance}}a=a||"m";c=c||4;switch(a){case"m":return e.round(b,c);break;case"km":return e.round(b/1e3,c);break;case"cm":return e.round(b*100,c);break;case"mm":return e.round(b*1e3,c);break;case"mi":return e.round(b*(1/1609.344),c);break;case"sm":return e.round(b*(1/1852.216),c);break;case"ft":return e.round(b*(100/30.48),c);break;case"in":return e.round(b*100/2.54,c);break;case"yd":return e.round(b*(1/.9144),c);break}return b},useDecimal:function(a){a=a.toString().replace(/\s*/,"");if(!isNaN(parseFloat(a))&&parseFloat(a).toString()==a){return a}else if(e.isSexagesimal(a)==true){return e.sexagesimal2decimal(a)}else{throw"Unknown format."}},decimal2sexagesimal:function(a){if(a in e.sexagesimal){return e.sexagesimal[a]}var b=a.toString().split(".");var c=b[0];var d=("0."+b[1])*60;var f=d.toString().split(".");d=parseInt(d,10);f=(("0."+f[1])*60).toFixed(2);e.sexagesimal[a]=c+"° "+d+"' "+f+'"';return e.sexagesimal[a]},sexagesimal2decimal:function(a){if(a in e.decimal){return e.decimal[a]}var b=new RegExp(d);var c=b.exec(a);if(c){var f=parseFloat(c[2]/60);var g=parseFloat(c[4]/3600)||0}var h=(parseFloat(c[1])+f+g).toFixed(8);h=c[7]=="S"||c[7]=="W"?h*-1:h;e.decimal[a]=h;return h},isSexagesimal:function(a){return d.test(a)},round:function(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c}};if(typeof Number.prototype.toRad==="undefined"){Number.prototype.toRad=function(){return this*Math.PI/180}}if(typeof Number.prototype.toDeg==="undefined"){Number.prototype.toDeg=function(){return this*180/Math.PI}}a.geolib=e})(this)
Please sign in to comment.
Something went wrong with that request. Please try again.