Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #9 from ifit/master

Added a number of new features
  • Loading branch information...
commit d3fa444135e2a7a3c06cdcda0648dcb24f205bb6 2 parents e6deabd + f3a93b5
@manuelbieh manuelbieh authored
View
329 geolib.js
@@ -23,6 +23,32 @@
distance: 0,
+
+ /**
+ * Get the key names for a geo point.
+ *
+ * @param object Point position {latitude: 123, longitude: 123, elevation: 123}
+ * @return object { longitude: 'lng|long|longitude',
+ * latitude: 'lat|latitude',
+ * elevation: 'alt|altitude|elev|elevation' }
+ */
+
+ getKeys: function(point) {
+ var latitude = point.hasOwnProperty('lat') ? 'lat' : 'latitude';
+ var longitude = (point.hasOwnProperty('lng') ? 'lng' : false) ||
+ (point.hasOwnProperty('long') ? 'long' : false) ||
+ 'longitude';
+ var elevation = (point.hasOwnProperty('alt') ? 'alt' : false) ||
+ (point.hasOwnProperty('altitude') ? 'altitude' : false) ||
+ (point.hasOwnProperty('elev') ? 'elev' : false) ||
+ 'elevation';
+ return {
+ latitude: latitude,
+ longitude: longitude,
+ elevation: elevation
+ };
+ },
+
/**
* Calculates geodetic distance between two points specified by latitude/longitude using
* Vincenty inverse formula for ellipsoids
@@ -37,20 +63,25 @@
getDistance: function(start, end, accuracy) {
- accuracy = parseInt(accuracy, 10) || 1;
+ var keys = geolib.getKeys(start);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+ var elevation = keys.elevation;
+
+ accuracy = Math.floor(accuracy) || 1;
var coord1 = {}, coord2 = {};
- coord1.latitude = geolib.useDecimal(start.latitude);
- coord1.longitude = geolib.useDecimal(start.longitude);
+ coord1[latitude] = geolib.useDecimal(start[latitude]);
+ coord1[longitude] = geolib.useDecimal(start[longitude]);
- coord2.latitude = geolib.useDecimal(end.latitude);
- coord2.longitude = geolib.useDecimal(end.longitude);
+ coord2[latitude] = geolib.useDecimal(end[latitude]);
+ coord2[longitude] = geolib.useDecimal(end[longitude]);
var a = 6378137, b = 6356752.314245, f = 1/298.257223563; // WGS-84 ellipsoid params
- var L = (coord2.longitude-coord1.longitude).toRad();
+ var L = (coord2[longitude]-coord1[longitude]).toRad();
- var U1 = Math.atan((1-f) * Math.tan(parseFloat(coord1.latitude).toRad()));
- var U2 = Math.atan((1-f) * Math.tan(parseFloat(coord2.latitude).toRad()));
+ var U1 = Math.atan((1-f) * Math.tan(parseFloat(coord1[latitude]).toRad()));
+ var U2 = Math.atan((1-f) * Math.tan(parseFloat(coord2[latitude]).toRad()));
var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
@@ -152,7 +183,11 @@
var distance = b * A * (sigma - deltaSigma);
distance = distance.toFixed(3); // round to 1mm precision
- return geolib.distance = parseInt(Math.round(distance/accuracy)*accuracy, 10)
+ if (start.hasOwnProperty(elevation) && end.hasOwnProperty(elevation)) {
+ var climb = Math.abs(start[elevation] - end[elevation]);
+ distance = Math.sqrt(distance*distance + climb*climb);
+ }
+ return geolib.distance = Math.floor(Math.round(distance/accuracy)*accuracy)
/*
// note: to return initial/final bearings in addition to distance, use something like:
@@ -176,37 +211,41 @@
*/
getDistanceSimple: function(start, end, accuracy) {
- accuracy = parseInt(accuracy, 10) || 1;
+ var keys = geolib.getKeys(start);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
+ accuracy = Math.floor(accuracy) || 1;
var coord1 = {}, coord2 = {};
- coord1.latitude = parseFloat(geolib.useDecimal(start.latitude)).toRad();
- coord1.longitude = parseFloat(geolib.useDecimal(start.longitude)).toRad();
+ coord1[latitude] = parseFloat(geolib.useDecimal(start[latitude])).toRad();
+ coord1[longitude] = parseFloat(geolib.useDecimal(start[longitude])).toRad();
- coord2.latitude = parseFloat(geolib.useDecimal(end.latitude)).toRad();
- coord2.longitude = parseFloat(geolib.useDecimal(end.longitude)).toRad();
+ coord2[latitude] = parseFloat(geolib.useDecimal(end[latitude])).toRad();
+ coord2[longitude] = parseFloat(geolib.useDecimal(end[longitude])).toRad();
var distance =
Math.round(
Math.acos(
Math.sin(
- coord2.latitude
+ coord2[latitude]
) *
Math.sin(
- coord1.latitude
+ coord1[latitude]
) +
Math.cos(
- coord2.latitude
+ coord2[latitude]
) *
Math.cos(
- coord1.latitude
+ coord1[latitude]
) *
Math.cos(
- coord1.longitude - coord2.longitude
+ coord1[longitude] - coord2[longitude]
)
) * radius
);
- return geolib.distance = parseInt(Math.round(distance/accuracy)*accuracy, 10);
+ return geolib.distance = Math.floor(Math.round(distance/accuracy)*accuracy);
},
@@ -219,6 +258,14 @@
*/
getCenter: function(coords) {
+ if (!coords.length) {
+ return false;
+ }
+
+ var keys = geolib.getKeys(coords[0]);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
var max = function( array ){
return Math.max.apply( Math, array );
};
@@ -230,8 +277,8 @@
var lat, lng, splitCoords = {lat: [], lng: []};
for(var coord in coords) {
- splitCoords.lat.push(geolib.useDecimal(coords[coord].latitude));
- splitCoords.lng.push(geolib.useDecimal(coords[coord].longitude));
+ splitCoords.lat.push(geolib.useDecimal(coords[coord][latitude]));
+ splitCoords.lng.push(geolib.useDecimal(coords[coord][longitude]));
}
var minLat = min(splitCoords.lat);
@@ -243,12 +290,57 @@
lng = ((minLng + maxLng)/2).toFixed(6);
// distance from the deepest left to the highest right point (diagonal distance)
- var distance = geolib.convertUnit('km', geolib.getDistance({latitude: minLat, longitude: minLng}, {latitude: maxLat, longitude: maxLng}));
+ var distance = geolib.convertUnit('km', geolib.getDistance({lat:minLat, lng:minLng}, {lat:maxLat, lng:maxLng}));
return {"latitude": lat, "longitude": lng, "distance": distance};
},
+ /**
+ * Gets the max and min, latitude, longitude, and elevation (if provided).
+ * @param array array with coords e.g. [{latitude: 51.5143, longitude: 7.4138}, {latitude: 123, longitude: 123}, ...]
+ * @return object {maxLat: maxLat,
+ * minLat: minLat
+ * maxLng: maxLng,
+ * minLng: minLng,
+ * maxElev: maxElev,
+ * minElev: minElev}
+ */
+ getBounds: function(coords) {
+ if (!coords.length) {
+ return false;
+ }
+
+ var keys = geolib.getKeys(coords[0]);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+ var elevation = keys.elevation;
+
+ var useElevation = coords[0].hasOwnProperty(elevation);
+ var stats = {
+ maxLat: 0,
+ minLat: Infinity,
+ maxLng: 0,
+ minLng: Infinity,
+ };
+
+ if (useElevation) {
+ stats.maxElev = 0;
+ stats.minElev = Infinity;
+ }
+
+ for (var i = 0, l = coords.length; i < l; ++i) {
+ stats.maxLat = Math.max(coords[i][latitude], stats.maxLat);
+ stats.minLat = Math.min(coords[i][latitude], stats.minLat);
+ stats.maxLng = Math.max(coords[i][longitude], stats.maxLng);
+ stats.minLng = Math.min(coords[i][longitude], stats.minLng);
+ if (useElevation) {
+ stats.maxElev = Math.max(coords[i][elevation], stats.maxElev);
+ stats.minElev = Math.min(coords[i][elevation], stats.minElev);
+ }
+ }
+ return stats;
+ },
/**
* Checks whether a point is inside of a polygon or not.
@@ -260,15 +352,19 @@
*/
isPointInside: function(latlng, coords) {
+ var keys = geolib.getKeys(latlng);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
for(var c = false, i = -1, l = coords.length, j = l - 1; ++i < l; j = i) {
(
- (coords[i].longitude <= latlng.longitude && latlng.longitude < coords[j].longitude) ||
- (coords[j].longitude <= latlng.longitude && latlng.longitude < coords[i].longitude)
+ (coords[i][longitude] <= latlng[longitude] && latlng[longitude] < coords[j][longitude]) ||
+ (coords[j][longitude] <= latlng[longitude] && latlng[longitude] < coords[i][longitude])
)
- && (latlng.latitude < (coords[j].latitude - coords[i].latitude)
- * (latlng.longitude - coords[i].longitude)
- / (coords[j].longitude - coords[i].longitude) + coords[i].latitude)
+ && (latlng[latitude] < (coords[j][latitude] - coords[i][latitude])
+ * (latlng[longitude] - coords[i][longitude])
+ / (coords[j][longitude] - coords[i][longitude]) + coords[i][latitude])
&& (c = !c);
}
@@ -308,11 +404,15 @@
*/
getRhumbLineBearing: function(originLL, destLL) {
+ var keys = geolib.getKeys(originLL);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
// difference of longitude coords
- var diffLon = geolib.useDecimal(destLL.longitude).toRad() - geolib.useDecimal(originLL.longitude).toRad();
+ var diffLon = geolib.useDecimal(destLL[longitude]).toRad() - geolib.useDecimal(originLL[longitude]).toRad();
// difference latitude coords phi
- var diffPhi = Math.log(Math.tan(geolib.useDecimal(destLL.latitude).toRad() / 2 + Math.PI / 4) / Math.tan(geolib.useDecimal(originLL.latitude).toRad() / 2 + Math.PI / 4));
+ var diffPhi = Math.log(Math.tan(geolib.useDecimal(destLL[latitude]).toRad() / 2 + Math.PI / 4) / Math.tan(geolib.useDecimal(originLL[latitude]).toRad() / 2 + Math.PI / 4));
// recalculate diffLon if it is greater than pi
if(Math.abs(diffLon) > Math.PI) {
@@ -339,35 +439,39 @@
*/
getBearing: function(originLL, destLL) {
- destLL.latitude = geolib.useDecimal(destLL.latitude);
- destLL.longitude = geolib.useDecimal(destLL.longitude);
- originLL.latitude = geolib.useDecimal(originLL.latitude);
- originLL.longitude = geolib.useDecimal(originLL.longitude);
+ var keys = geolib.getKeys(originLL);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
+ destLL[latitude] = geolib.useDecimal(destLL[latitude]);
+ destLL[longitude] = geolib.useDecimal(destLL[longitude]);
+ originLL[latitude] = geolib.useDecimal(originLL[latitude]);
+ originLL[longitude] = geolib.useDecimal(originLL[longitude]);
var bearing = (
(
Math.atan2(
Math.sin(
- destLL.longitude.toRad() -
- originLL.longitude.toRad()
+ destLL[longitude].toRad() -
+ originLL[longitude].toRad()
) *
Math.cos(
- destLL.latitude.toRad()
+ destLL[latitude].toRad()
),
Math.cos(
- originLL.latitude.toRad()
+ originLL[latitude].toRad()
) *
Math.sin(
- destLL.latitude.toRad()
+ destLL[latitude].toRad()
) -
Math.sin(
- originLL.latitude.toRad()
+ originLL[latitude].toRad()
) *
Math.cos(
- destLL.latitude.toRad()
+ destLL[latitude].toRad()
) *
Math.cos(
- destLL.longitude.toRad() - originLL.longitude.toRad()
+ destLL[longitude].toRad() - originLL[longitude].toRad()
)
)
).toDeg() + 360
@@ -459,10 +563,14 @@
*/
orderByDistance: function(latlng, coords) {
+ var keys = geolib.getKeys(latlng);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
var coordsArray = [];
for(var coord in coords) {
var d = geolib.getDistance(latlng, coords[coord]);
- coordsArray.push({key: coord, latitude: coords[coord].latitude, longitude: coords[coord].longitude, distance: d});
+ coordsArray.push({key: coord, latitude: coords[coord][latitude], longitude: coords[coord][longitude], distance: d});
}
return coordsArray.sort(function(a, b) { return a.distance - b.distance; });
@@ -494,16 +602,137 @@
*/
getPathLength: function(coords) {
- var l = 0, last;
- for(var coord in coords) {
+ var dist = 0, last;
+ for (var i = 0, l = coords.length; i < l; ++i) {
if(last) {
- l += geolib.getDistance(coords[coord], last);
+ dist += geolib.getDistance(coords[i], last);
}
- last = coords[coord];
+ last = coords[i];
}
- return l;
+ return dist;
+
+ },
+ /**
+ * @param Array Collection of coords [{latitude: 51.510, longitude: 7.1321}, {latitude: 49.1238, longitude: "8° 30' W"}, ...]
+ *
+ * @return Array [{lat:#lat, lng:#lng, elev:#elev},....]}
+ */
+ getElevation: function() {
+ if (typeof window.navigator !== 'undefined') {
+ geolib.getElevationClient.apply(this, arguments);
+ } else {
+ geolib.getElevationServer.apply(this, arguments);
+ }
+ },
+
+ getElevationClient: function(coords, cb) {
+ if (!window.google) {
+ throw new Error("Google maps api not loaded");
+ }
+ if (coords.length == 0) {
+ return cb(null, null);
+ }
+ if (coords.length == 1) {
+ return cb(new Error("getElevation requires at least 2 points."));
+ }
+ var path = [];
+ var keys = geolib.getKeys(coords[0]);
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+
+ for(var i = 0; i < coords.length; i++) {
+ path.push(new google.maps.LatLng(
+ geolib.useDecimal(coords[i][latitude]),
+ geolib.useDecimal(coords[i][longitude])
+ ));
+ }
+ var positionalRequest = {
+ 'path': path,
+ 'samples': path.length
+ };
+ var elevationService = new google.maps.ElevationService();
+ elevationService.getElevationAlongPath(positionalRequest,function (results, status){
+ geolib.elevationHandler(results, status, coords, keys, cb);
+ });
+ },
+
+ getElevationServer: function(coords, cb){
+ if (coords.length == 0) {
+ return cb(null, null);
+ }
+ if (coords.length == 1) {
+ return cb(new Error("getElevation requires at least 2 points."));
+ }
+ var gm = require('googlemaps');
+ var path = [];
+ var keys = geolib.getKeys(coords[0]);
+ coords[0]
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+ for(var i = 0; i < coords.length; i++) {
+ path.push(geolib.useDecimal(coords[i][latitude]) + ',' +
+ geolib.useDecimal(coords[i][longitude]));
+ }
+ gm.elevationFromPath(path.join('|'), path.length, function(err, results) {
+ geolib.elevationHandler(results.results, results.status, coords, keys, cb)
+ });
+ },
+
+ elevationHandler: function(results, status, coords, keys, cb){
+ var latsLngsElevs = [];
+ var latitude = keys.latitude;
+ var longitude = keys.longitude;
+ if (status == "OK" ) {
+ for (var i = 0; i < results.length; i++) {
+ latsLngsElevs.push({
+ "lat":coords[i][latitude],
+ "lng":coords[i][longitude],
+ "elev":results[i].elevation
+ });
+ }
+ cb(null, latsLngsElevs);
+ } else {
+ cb(new Error("Could not get elevation using Google's API"), elevationResult.status);
+ }
+ },
+
+ /**
+ * @param Array [{lat:#lat, lng:#lng, elev:#elev},....]}
+ *
+ * @return Number % grade
+ */
+ getGrade: function(coords){
+ var keys = geolib.getKeys(coords[0]);
+ var elevation = keys.elevation;
+ var rise = Math.abs(coords[coords.length-1][elevation] - coords[0][elevation]);
+ var run = geolib.getPathLength(coords);
+ return Math.floor((rise/run)*100);
+ },
+
+ /**
+ * @param Array [{lat:#lat, lng:#lng, elev:#elev},....]}
+ *
+ * @return Object {gain:#gain, loss:#loss}
+ */
+ getTotalElevationGainAndLoss: function(coords){
+ var keys = geolib.getKeys(coords[0]);
+ var elevation = keys.elevation;
+ var gain = 0;
+ var loss = 0;
+ for(var i = 0; i < coords.length - 1; i++){
+ var deltaElev = coords[i][elevation] - coords[i + 1][elevation];
+ if (deltaElev > 0) {
+ loss += deltaElev;
+ } else {
+ gain += Math.abs(deltaElev);
+ }
+ }
+ return {
+ "gain": gain,
+ "loss": loss
+ };
},
/**
@@ -607,7 +836,7 @@
var min = ('0.' + tmp[1])*60;
var sec = min.toString().split('.');
- min = parseInt(min, 10);
+ min = Math.floor(min);
sec = (('0.' + sec[1]) * 60).toFixed(2);
geolib.sexagesimal[dec] = (deg + '° ' + min + "' " + sec + '"');
@@ -688,4 +917,4 @@
module.exports = geolib;
}
-})(this);
+})(this);
View
5 geolib.min.js
@@ -4,9 +4,8 @@
* WGS 84 (World Geodetic System 1984)
*
* @author Manuel Bieh
- * @url http://www.manuel-bieh.de/
+ * @url http://www.manuelbieh.com/
* @version 1.1.7
* @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},getRhumbLineBearing:function(a,b){var c=e.useDecimal(b.longitude).toRad()-e.useDecimal(a.longitude).toRad();var d=Math.log(Math.tan(e.useDecimal(b.latitude).toRad()/2+Math.PI/4)/Math.tan(e.useDecimal(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){b.latitude=e.useDecimal(b.latitude);b.longitude=e.useDecimal(b.longitude);a.latitude=e.useDecimal(a.latitude);a.longitude=e.useDecimal(a.longitude);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 parseFloat(a)}else if(e.isSexagesimal(a)==true){return parseFloat(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=Math.abs(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}}if(typeof a.navigator!="undefined"){a.geolib=e}else{module.exports=e}})(this)
+ */(function(a,b){var c=6378137,d=/^([0-9]{1,3})°\s*([0-9]{1,3})'\s*(([0-9]{1,3}(\.([0-9]{1,2}))?)"\s*)?([NEOSW]?)$/,e={decimal:{},sexagesimal:{},distance:0,getKeys:function(a){var b=a.hasOwnProperty("lat")?"lat":"latitude",c=(a.hasOwnProperty("lng")?"lng":!1)||(a.hasOwnProperty("long")?"long":!1)||"longitude",d=(a.hasOwnProperty("alt")?"alt":!1)||(a.hasOwnProperty("altitude")?"altitude":!1)||(a.hasOwnProperty("elev")?"elev":!1)||"elevation";return{latitude:b,longitude:c,elevation:d}},getDistance:function(a,b,c){var d=e.getKeys(a),f=d.latitude,g=d.longitude,h=d.elevation;c=Math.floor(c)||1;var i={},j={};i[f]=e.useDecimal(a[f]),i[g]=e.useDecimal(a[g]),j[f]=e.useDecimal(b[f]),j[g]=e.useDecimal(b[g]);var k=6378137,l=6356752.314245,m=1/298.257223563,n=(j[g]-i[g]).toRad(),o=Math.atan((1-m)*Math.tan(parseFloat(i[f]).toRad())),p=Math.atan((1-m)*Math.tan(parseFloat(j[f]).toRad())),q=Math.sin(o),r=Math.cos(o),s=Math.sin(p),t=Math.cos(p),u=n,v,w=100;do{var x=Math.sin(u),y=Math.cos(u),z=Math.sqrt(t*x*t*x+(r*s-q*t*y)*(r*s-q*t*y));if(z==0)return e.distance=0;var A=q*s+r*t*y,B=Math.atan2(z,A),C=r*t*x/z,D=1-C*C,E=A-2*q*s/D;isNaN(E)&&(E=0);var F=m/16*D*(4+m*(4-3*D));v=u,u=n+(1-F)*m*C*(B+F*z*(E+F*A*(-1+2*E*E)))}while(Math.abs(u-v)>1e-12&&--w>0);if(w==0)return NaN;var G=D*(k*k-l*l)/(l*l),H=1+G/16384*(4096+G*(-768+G*(320-175*G))),I=G/1024*(256+G*(-128+G*(74-47*G))),J=I*z*(E+I/4*(A*(-1+2*E*E)-I/6*E*(-3+4*z*z)*(-3+4*E*E))),K=l*H*(B-J);K=K.toFixed(3);if(a.hasOwnProperty(h)&&b.hasOwnProperty(h)){var L=Math.abs(a[h]-b[h]);K=Math.sqrt(K*K+L*L)}return e.distance=Math.floor(Math.round(K/c)*c)},getDistanceSimple:function(a,b,d){var f=e.getKeys(a),g=f.latitude,h=f.longitude;d=Math.floor(d)||1;var i={},j={};i[g]=parseFloat(e.useDecimal(a[g])).toRad(),i[h]=parseFloat(e.useDecimal(a[h])).toRad(),j[g]=parseFloat(e.useDecimal(b[g])).toRad(),j[h]=parseFloat(e.useDecimal(b[h])).toRad();var k=Math.round(Math.acos(Math.sin(j[g])*Math.sin(i[g])+Math.cos(j[g])*Math.cos(i[g])*Math.cos(i[h]-j[h]))*c);return e.distance=Math.floor(Math.round(k/d)*d)},getCenter:function(a){if(!a.length)return!1;var b=e.getKeys(a[0]),c=b.latitude,d=b.longitude,f=function(a){return Math.max.apply(Math,a)},g=function(a){return Math.min.apply(Math,a)},h,i,j={lat:[],lng:[]};for(var k in a)j.lat.push(e.useDecimal(a[k][c])),j.lng.push(e.useDecimal(a[k][d]));var l=g(j.lat),m=g(j.lng),n=f(j.lat),o=f(j.lng);h=((l+n)/2).toFixed(6),i=((m+o)/2).toFixed(6);var p=e.convertUnit("km",e.getDistance({lat:l,lng:m},{lat:n,lng:o}));return{latitude:h,longitude:i,distance:p}},getBounds:function(a){if(!a.length)return!1;var b=e.getKeys(a[0]),c=b.latitude,d=b.longitude,f=b.elevation,g=a[0].hasOwnProperty(f),h={maxLat:0,minLat:Infinity,maxLng:0,minLng:Infinity};g&&(h.maxElev=0,h.minElev=Infinity);for(var i=0,j=a.length;i<j;++i)h.maxLat=Math.max(a[i][c],h.maxLat),h.minLat=Math.min(a[i][c],h.minLat),h.maxLng=Math.max(a[i][d],h.maxLng),h.minLng=Math.min(a[i][d],h.minLng),g&&(h.maxElev=Math.max(a[i][f],h.maxElev),h.minElev=Math.min(a[i][f],h.minElev));return h},isPointInside:function(a,b){var c=e.getKeys(a),d=c.latitude,f=c.longitude;for(var g=!1,h=-1,i=b.length,j=i-1;++h<i;j=h)(b[h][f]<=a[f]&&a[f]<b[j][f]||b[j][f]<=a[f]&&a[f]<b[h][f])&&a[d]<(b[j][d]-b[h][d])*(a[f]-b[h][f])/(b[j][f]-b[h][f])+b[h][d]&&(g=!g);return g},isPointInCircle:function(a,b,c){return e.getDistance(a,b)<c},getRhumbLineBearing:function(a,b){var c=e.getKeys(a),d=c.latitude,f=c.longitude,g=e.useDecimal(b[f]).toRad()-e.useDecimal(a[f]).toRad(),h=Math.log(Math.tan(e.useDecimal(b[d]).toRad()/2+Math.PI/4)/Math.tan(e.useDecimal(a[d]).toRad()/2+Math.PI/4));return Math.abs(g)>Math.PI&&(g>0?g=(2*Math.PI-g)*-1:g=2*Math.PI+g),(Math.atan2(g,h).toDeg()+360)%360},getBearing:function(a,b){var c=e.getKeys(a),d=c.latitude,f=c.longitude;b[d]=e.useDecimal(b[d]),b[f]=e.useDecimal(b[f]),a[d]=e.useDecimal(a[d]),a[f]=e.useDecimal(a[f]);var g=(Math.atan2(Math.sin(b[f].toRad()-a[f].toRad())*Math.cos(b[d].toRad()),Math.cos(a[d].toRad())*Math.sin(b[d].toRad())-Math.sin(a[d].toRad())*Math.cos(b[d].toRad())*Math.cos(b[f].toRad()-a[f].toRad())).toDeg()+360)%360;return g},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=e.getKeys(a),d=c.latitude,f=c.longitude,g=[];for(var h in b){var i=e.getDistance(a,b[h]);g.push({key:h,latitude:b[h][d],longitude:b[h][f],distance:i})}return g.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=0,f=a.length;d<f;++d)c&&(b+=e.getDistance(a[d],c)),c=a[d];return b},getElevation:function(){typeof a.navigator!="undefined"?e.getElevationClient.apply(this,arguments):e.getElevationServer.apply(this,arguments)},getElevationClient:function(b,c){if(!a.google)throw new Error("Google maps api not loaded");if(b.length==0)return c(null,null);if(b.length==1)return c(new Error("getElevation requires at least 2 points."));var d=[],f=e.getKeys(b[0]),g=f.latitude,h=f.longitude;for(var i=0;i<b.length;i++)d.push(new google.maps.LatLng(e.useDecimal(b[i][g]),e.useDecimal(b[i][h])));var j={path:d,samples:d.length},k=new google.maps.ElevationService;k.getElevationAlongPath(j,function(a,d){e.elevationHandler(a,d,b,f,c)})},getElevationServer:function(a,b){if(a.length==0)return b(null,null);if(a.length==1)return b(new Error("getElevation requires at least 2 points."));var c=require("googlemaps"),d=[],f=e.getKeys(a[0]);a[0];var g=f.latitude,h=f.longitude;for(var i=0;i<a.length;i++)d.push(e.useDecimal(a[i][g])+","+e.useDecimal(a[i][h]));c.elevationFromPath(d.join("|"),d.length,function(c,d){e.elevationHandler(d.results,d.status,a,f,b)})},elevationHandler:function(a,b,c,d,e){var f=[],g=d.latitude,h=d.longitude;if(b=="OK"){for(var i=0;i<a.length;i++)f.push({lat:c[i][g],lng:c[i][h],elev:a[i].elevation});e(null,f)}else e(new Error("Could not get elevation using Google's API"),elevationResult.status)},getGrade:function(a){var b=e.getKeys(a[0]),c=b.elevation,d=Math.abs(a[a.length-1][c]-a[0][c]),f=e.getPathLength(a);return Math.floor(d/f*100)},getTotalElevationGainAndLoss:function(a){var b=e.getKeys(a[0]),c=b.elevation,d=0,f=0;for(var g=0;g<a.length-1;g++){var h=a[g][c]-a[g+1][c];h>0?f+=h:d+=Math.abs(h)}return{gain:d,loss:f}},convertUnit:function(a,b,c){if(b==0||typeof b=="undefined"){if(e.distance==0)return 0;b=e.distance}a=a||"m",c=c||4;switch(a){case"m":return e.round(b,c);case"km":return e.round(b/1e3,c);case"cm":return e.round(b*100,c);case"mm":return e.round(b*1e3,c);case"mi":return e.round(b*(1/1609.344),c);case"sm":return e.round(b*(1/1852.216),c);case"ft":return e.round(b*(100/30.48),c);case"in":return e.round(b*100/2.54,c);case"yd":return e.round(b*(1/.9144),c)}return b},useDecimal:function(a){a=a.toString().replace(/\s*/,"");if(!isNaN(parseFloat(a))&&parseFloat(a).toString()==a)return parseFloat(a);if(e.isSexagesimal(a)==1)return parseFloat(e.sexagesimal2decimal(a));throw"Unknown format."},decimal2sexagesimal:function(a){if(a in e.sexagesimal)return e.sexagesimal[a];var b=a.toString().split("."),c=Math.abs(b[0]),d=("0."+b[1])*60,f=d.toString().split(".");return d=Math.floor(d),f=(("0."+f[1])*60).toFixed(2),e.sexagesimal[a]=c+"° "+d+"' "+f+'"',e.sexagesimal[a]},sexagesimal2decimal:function(a){if(a in e.decimal)return e.decimal[a];var b=new RegExp(d),c=b.exec(a);if(c)var f=parseFloat(c[2]/60),g=parseFloat(c[4]/3600)||0;var h=(parseFloat(c[1])+f+g).toFixed(8);return h=c[7]=="S"||c[7]=="W"?h*-1:h,e.decimal[a]=h,h},isSexagesimal:function(a){return d.test(a)},round:function(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c}};typeof Number.prototype.toRad=="undefined"&&(Number.prototype.toRad=function(){return this*Math.PI/180}),typeof Number.prototype.toDeg=="undefined"&&(Number.prototype.toDeg=function(){return this*180/Math.PI}),typeof a.navigator!="undefined"?a.geolib=e:module.exports=e})(this);
View
44 package.json
@@ -1,20 +1,26 @@
{
- "name": "geolib",
- "homepage": "http://github.com/manuelbieh/Geolib",
- "author": {
- "name": "Manuel Bieh",
- "url": "http://www.manuelbieh.com/"
- },
- "bin": {
- "geolib" : "./geolib.js"
- },
- "repository": {
- "type" : "git",
- "url" : "http://github.com/manuelbieh/geolib.git"
- },
- "files": ["geolib.js"],
- "description": "Growing library to perform geo specific tasks",
- "keywords": ["geolocation", "geo", "distance"],
- "version": "1.1.7",
- "main": "./geolib"
-}
+ "name": "geolib",
+ "homepage": "http://github.com/manuelbieh/Geolib",
+ "author": {
+ "name": "Manuel Bieh",
+ "url": "http://www.manuelbieh.com/"
+ },
+ "bin": {
+ "geolib" : "./geolib.js"
+ },
+ "repository": {
+ "type" : "git",
+ "url" : "http://github.com/manuelbieh/geolib.git"
+ },
+ "dependencies": {
+ "googlemaps": ">= 0.1.6"
+ },
+ "devDependencies": {
+ "mocha": "*"
+ },
+ "files": ["geolib.js"],
+ "description": "Growing library to perform geo specific tasks",
+ "keywords": ["geolocation", "geo", "distance"],
+ "version": "1.1.7",
+ "main": "./geolib"
+}
View
104 tests/geolib.test.html
@@ -5,6 +5,8 @@
<meta charset="utf-8">
<script src="http://code.jquery.com/jquery-latest.js"></script>
+ <!-- script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=AIzaSyDxikL7zFrgea3gBya8OECSsOgvKuuFLb8&sensor=true"></script-->
+ <script type="text/javascript" src='http://maps.googleapis.com/maps/api/js?sensor=true'></script>
<script type="text/javascript" src="http://code.jquery.com/qunit/git/qunit.js"></script>
<script type="text/javascript" src="../geolib.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css" type="text/css" media="screen" />
@@ -47,6 +49,13 @@
{"latitude": 51.513317451, "longitude": 7.45574331}
];
+ var polygon2 = [
+ {"latitude": 51.513357512, "longitude": 7.45574331, "elevation":523.92},
+ {"latitude": 51.515400598, "longitude": 7.45518541, "elevation":524.54},
+ {"latitude": 51.516241842, "longitude": 7.456494328, "elevation":523.12},
+ {"latitude": 51.516722545, "longitude": 7.459863183, "elevation":522.77},
+ {"latitude": 51.517443592, "longitude": 7.463232037, "elevation":521.12}
+ ];
test("Testing distance calculation: getDistance()", function() {
@@ -76,6 +85,99 @@
});
+ test("Testing bounding box: getBounds()", function() {
+ expect(12);
+
+ var box = geolib.getBounds(polygon);
+
+ equal(box.maxLat, 51.5177507, "maxLat should be 51.5177507");
+ equal(box.minLat, 51.509831973, "minLat should be 51.509831973");
+ equal(box.maxLng, 7.472909447, "maxLng should be 7.472909447");
+ equal(box.minLng, 7.45518541, "minLng should be 7.45518541");
+ equal(typeof box.minElev, "undefined", "minElev should be undefind");
+ equal(typeof box.maxElev, "undefined", "maxElev should be undefind");
+
+ box = geolib.getBounds(polygon2);
+
+ equal(box.maxLat, 51.517443592, "maxLat should be 51.517443592");
+ equal(box.minLat, 51.513357512, "minLat should be 51.513357512");
+ equal(box.maxLng, 7.463232037, "maxLng should be 7.463232037");
+ equal(box.minLng, 7.45518541, "minLng should be 7.45518541");
+ equal(box.maxElev, 524.54, "maxElev should be 524.54");
+ equal(box.minElev, 521.12, "minElev should be 521.12");
+ });
+
+ asyncTest("Testing elevation: getElevation()", function() {
+ expect(4);
+
+ var latsLngsElevs;
+ var coords1 = [{"lat":33.76346,"lng":-84.43430000000001},
+ {"lat":33.76418,"lng":-84.42999999999995}];
+ var coords2 = [{"lat":41.73549,"lng":-111.85842000000002},
+ {"lat":41.73600999999999,"lng":-111.85572000000002}];
+
+ var doneCount = 0;
+ var done = function (){
+ ++doneCount;
+ if (doneCount === 2) {
+ start();
+ }
+ };
+
+ geolib.getElevation(coords1,function(err, results){
+ if (err) {
+ throw err;
+ }
+ latsLngsElevs = results;
+ equal(latsLngsElevs[0].elev, 297.8508605957031, "1st elev should be 297.8508605957031");
+ equal(latsLngsElevs[1].elev, 281.1884155273438, "2nd elev should be 281.1884155273438");
+ done();
+ });
+
+ geolib.getElevation(coords2,function(err, results){
+ if (err) {
+ throw err;
+ }
+ latsLngsElevs = results;
+ equal(latsLngsElevs[0].elev, 1358.223999023438, "1st elev should be 1358.223999023438");
+ equal(latsLngsElevs[1].elev, 1360.70654296875, "2nd elev should be 1360.70654296875");
+ done();
+ });
+
+ });
+
+ test("Testing grade: getGrade()", function() {
+ expect(2);
+ var coords1 = [{"lat":41.72977,"lng":-111.77621999999997,"elev":1702.72412109375},
+ {"lat":41.73198,"lng":-111.77636999999999,"elev":1849.7333984375}];
+ var coords2 = [{"lat":40.75402,"lng":-111.75475,"elev":2209.137451171875},
+ {"lat":40.76481,"lng":-111.76778999999999,"elev":1660.49609375}];
+
+ var grade = geolib.getGrade(coords1);
+ equal(grade, 51, "grade should be 51");
+
+ grade = geolib.getGrade(coords2);
+ equal(grade, 31, "grade should be 31");
+ });
+
+ test("Testing elevation gain and loss: getTotalElevationGainAndLoss()", function() {
+ expect(4);
+ var coords1 = [{"lat":41.72975,"lng":-111.77580999999998,"elev":1707.123046875},
+ {"lat":41.73298475750587,"lng":-111.77603699785413,"elev":1922.056396484375},
+ {"lat":41.73517,"lng":-111.77881000000002,"elev":1893.9931640625}];
+ var coords2 = [{"lat":40.79162,"lng":-111.76560999999998,"elev":2211.202880859375},
+ {"lat":40.79938945887229,"lng":-111.76680525603354,"elev":1995.89990234375},
+ {"lat":40.80354,"lng":-111.77384999999998,"elev":1978.573120117188}];
+
+ var gainAndLoss = geolib.getTotalElevationGainAndLoss(coords1);
+ equal(gainAndLoss.gain, 214.933349609375, "gain should be 214.933349609375");
+ equal(gainAndLoss.loss, 28.063232421875, "loss should be 28.063232421875");
+
+ gainAndLoss = geolib.getTotalElevationGainAndLoss(coords2);
+ equal(gainAndLoss.gain, 0, "gain should be 0");
+ equal(gainAndLoss.loss, 232.62976074218705, "loss should be 232.62976074218705");
+ });
+
test("Testing conversion: sexagesimal2decimal()", function() {
expect(7);
@@ -188,4 +290,4 @@ <h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body>
-</html>
+</html>
View
27 tests/getElevationServer.js
@@ -0,0 +1,27 @@
+var util = require('util')
+ , http = require('http')
+ , geolib = require('../geolib');
+
+describe('Geolib', function() {
+ describe('getElevationServer', function() {
+ it('should getElevation for three points', function(done) {
+ var coords = [
+ {"lat":40.79162,"lng":-111.76560999999998},
+ {"lat":40.79938945887229,"lng":-111.76680525603354},
+ {"lat":40.80354,"lng":-111.77384999999998}
+ ];
+ geolib.getElevation(coords, function (err, results){
+ if (err){
+ throw new Error("Could not get elevation");
+ } else {
+ if (Math.floor(results[0].elev) !== 2211 ||
+ Math.floor(results[1].elev) !== 2011 ||
+ Math.floor(results[2].elev) !== 1978) {
+ throw new Error("wrong elevation results: " + JSON.stringify(results));
+ }
+ }
+ done();
+ });
+ });
+ });
+});
Please sign in to comment.
Something went wrong with that request. Please try again.