1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | (function (window, undefined) {
|
14 |
|
15 | var radius = 6378137;
|
16 | var sexagesimalPattern = /^([0-9]{1,3})°\s*([0-9]{1,3})'\s*(([0-9]{1,3}(\.([0-9]{1,2}))?)"\s*)?([NEOSW]?)$/;
|
17 |
|
18 | var geolib = {
|
19 |
|
20 | decimal: {},
|
21 |
|
22 | sexagesimal: {},
|
23 |
|
24 | distance: 0,
|
25 |
|
26 | |
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | getKeys: function(point) {
|
37 |
|
38 | var latitude = point.hasOwnProperty('lat') ? 'lat' : 'latitude';
|
39 |
|
40 | var longitude = (point.hasOwnProperty('lng') ? 'lng' : false) ||
|
41 | (point.hasOwnProperty('long') ? 'long' : false) ||
|
42 | 'longitude';
|
43 |
|
44 | var elevation = (point.hasOwnProperty('alt') ? 'alt' : false) ||
|
45 | (point.hasOwnProperty('altitude') ? 'altitude' : false) ||
|
46 | (point.hasOwnProperty('elev') ? 'elev' : false) ||
|
47 | 'elevation';
|
48 |
|
49 | return {
|
50 | latitude: latitude,
|
51 | longitude: longitude,
|
52 | elevation: elevation
|
53 | };
|
54 |
|
55 | },
|
56 |
|
57 | |
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | getDistance: function(start, end, accuracy) {
|
70 |
|
71 | var keys = geolib.getKeys(start);
|
72 | var latitude = keys.latitude;
|
73 | var longitude = keys.longitude;
|
74 | var elevation = keys.elevation;
|
75 |
|
76 | accuracy = Math.floor(accuracy) || 1;
|
77 |
|
78 | var coord1 = {}, coord2 = {};
|
79 | coord1[latitude] = geolib.useDecimal(start[latitude]);
|
80 | coord1[longitude] = geolib.useDecimal(start[longitude]);
|
81 |
|
82 | coord2[latitude] = geolib.useDecimal(end[latitude]);
|
83 | coord2[longitude] = geolib.useDecimal(end[longitude]);
|
84 |
|
85 | var a = 6378137, b = 6356752.314245, f = 1/298.257223563;
|
86 | var L = (coord2[longitude]-coord1[longitude]).toRad();
|
87 |
|
88 | var cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM, sinSigma;
|
89 |
|
90 | var U1 = Math.atan((1-f) * Math.tan(parseFloat(coord1[latitude]).toRad()));
|
91 | var U2 = Math.atan((1-f) * Math.tan(parseFloat(coord2[latitude]).toRad()));
|
92 | var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
|
93 | var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
|
94 |
|
95 | var lambda = L, lambdaP, iterLimit = 100;
|
96 | do {
|
97 | var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
|
98 | sinSigma = (
|
99 | Math.sqrt(
|
100 | (
|
101 | cosU2 * sinLambda
|
102 | ) * (
|
103 | cosU2 * sinLambda
|
104 | ) + (
|
105 | cosU1 * sinU2 - sinU1 * cosU2 * cosLambda
|
106 | ) * (
|
107 | cosU1 * sinU2 - sinU1 * cosU2 * cosLambda
|
108 | )
|
109 | )
|
110 | );
|
111 | if (sinSigma === 0) {
|
112 | return geolib.distance = 0;
|
113 | }
|
114 |
|
115 | cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
|
116 | sigma = Math.atan2(sinSigma, cosSigma);
|
117 | sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
|
118 | cosSqAlpha = 1 - sinAlpha * sinAlpha;
|
119 | cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
|
120 |
|
121 | if (isNaN(cos2SigmaM)) {
|
122 | cos2SigmaM = 0;
|
123 | }
|
124 | var C = (
|
125 | f / 16 * cosSqAlpha * (
|
126 | 4 + f * (
|
127 | 4 - 3 * cosSqAlpha
|
128 | )
|
129 | )
|
130 | );
|
131 | lambdaP = lambda;
|
132 | lambda = (
|
133 | L + (
|
134 | 1 - C
|
135 | ) * f * sinAlpha * (
|
136 | sigma + C * sinSigma * (
|
137 | cos2SigmaM + C * cosSigma * (
|
138 | -1 + 2 * cos2SigmaM * cos2SigmaM
|
139 | )
|
140 | )
|
141 | )
|
142 | );
|
143 |
|
144 | } while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0);
|
145 |
|
146 | if (iterLimit === 0) {
|
147 | return NaN;
|
148 | }
|
149 |
|
150 | var uSq = (
|
151 | cosSqAlpha * (
|
152 | a * a - b * b
|
153 | ) / (
|
154 | b*b
|
155 | )
|
156 | );
|
157 |
|
158 | var A = (
|
159 | 1 + uSq / 16384 * (
|
160 | 4096 + uSq * (
|
161 | -768 + uSq * (
|
162 | 320 - 175 * uSq
|
163 | )
|
164 | )
|
165 | )
|
166 | );
|
167 |
|
168 | var B = (
|
169 | uSq / 1024 * (
|
170 | 256 + uSq * (
|
171 | -128 + uSq * (
|
172 | 74-47 * uSq
|
173 | )
|
174 | )
|
175 | )
|
176 | );
|
177 |
|
178 | var deltaSigma = (
|
179 | B * sinSigma * (
|
180 | cos2SigmaM + B / 4 * (
|
181 | cosSigma * (
|
182 | -1 + 2 * cos2SigmaM * cos2SigmaM
|
183 | ) -B / 6 * cos2SigmaM * (
|
184 | -3 + 4 * sinSigma * sinSigma
|
185 | ) * (
|
186 | -3 + 4 * cos2SigmaM * cos2SigmaM
|
187 | )
|
188 | )
|
189 | )
|
190 | );
|
191 |
|
192 | var distance = b * A * (sigma - deltaSigma);
|
193 |
|
194 | distance = distance.toFixed(3);
|
195 |
|
196 | if (start.hasOwnProperty(elevation) && end.hasOwnProperty(elevation)) {
|
197 | var climb = Math.abs(start[elevation] - end[elevation]);
|
198 | distance = Math.sqrt(distance*distance + climb*climb);
|
199 | }
|
200 |
|
201 | return geolib.distance = Math.floor(Math.round(distance/accuracy)*accuracy);
|
202 |
|
203 | |
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 | },
|
212 |
|
213 |
|
214 | |
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | getDistanceSimple: function(start, end, accuracy) {
|
224 |
|
225 | var keys = geolib.getKeys(start);
|
226 | var latitude = keys.latitude;
|
227 | var longitude = keys.longitude;
|
228 |
|
229 | accuracy = Math.floor(accuracy) || 1;
|
230 |
|
231 | var coord1 = {}, coord2 = {};
|
232 | coord1[latitude] = parseFloat(geolib.useDecimal(start[latitude])).toRad();
|
233 | coord1[longitude] = parseFloat(geolib.useDecimal(start[longitude])).toRad();
|
234 |
|
235 | coord2[latitude] = parseFloat(geolib.useDecimal(end[latitude])).toRad();
|
236 | coord2[longitude] = parseFloat(geolib.useDecimal(end[longitude])).toRad();
|
237 |
|
238 | var distance =
|
239 | Math.round(
|
240 | Math.acos(
|
241 | Math.sin(
|
242 | coord2[latitude]
|
243 | ) *
|
244 | Math.sin(
|
245 | coord1[latitude]
|
246 | ) +
|
247 | Math.cos(
|
248 | coord2[latitude]
|
249 | ) *
|
250 | Math.cos(
|
251 | coord1[latitude]
|
252 | ) *
|
253 | Math.cos(
|
254 | coord1[longitude] - coord2[longitude]
|
255 | )
|
256 | ) * radius
|
257 | );
|
258 |
|
259 | return geolib.distance = Math.floor(Math.round(distance/accuracy)*accuracy);
|
260 |
|
261 | },
|
262 |
|
263 |
|
264 | |
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | getCenter: function(coords) {
|
271 |
|
272 | if (!coords.length) {
|
273 | return false;
|
274 | }
|
275 |
|
276 | var keys = geolib.getKeys(coords[0]);
|
277 | var latitude = keys.latitude;
|
278 | var longitude = keys.longitude;
|
279 |
|
280 | var max = function( array ){
|
281 | return Math.max.apply( Math, array );
|
282 | };
|
283 |
|
284 | var min = function( array ){
|
285 | return Math.min.apply( Math, array );
|
286 | };
|
287 |
|
288 | var lat, lng, splitCoords = {lat: [], lng: []};
|
289 |
|
290 | for(var coord in coords) {
|
291 | splitCoords.lat.push(geolib.useDecimal(coords[coord][latitude]));
|
292 | splitCoords.lng.push(geolib.useDecimal(coords[coord][longitude]));
|
293 | }
|
294 |
|
295 | var minLat = min(splitCoords.lat);
|
296 | var minLng = min(splitCoords.lng);
|
297 | var maxLat = max(splitCoords.lat);
|
298 | var maxLng = max(splitCoords.lng);
|
299 |
|
300 | lat = ((minLat + maxLat)/2).toFixed(6);
|
301 | lng = ((minLng + maxLng)/2).toFixed(6);
|
302 |
|
303 |
|
304 | var distance = geolib.convertUnit('km', geolib.getDistance({lat:minLat, lng:minLng}, {lat:maxLat, lng:maxLng}));
|
305 |
|
306 | return {"latitude": lat, "longitude": lng, "distance": distance};
|
307 |
|
308 | },
|
309 |
|
310 | |
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 | getBounds: function(coords) {
|
321 | if (!coords.length) {
|
322 | return false;
|
323 | }
|
324 |
|
325 | var keys = geolib.getKeys(coords[0]);
|
326 | var latitude = keys.latitude;
|
327 | var longitude = keys.longitude;
|
328 | var elevation = keys.elevation;
|
329 |
|
330 | var useElevation = coords[0].hasOwnProperty(elevation);
|
331 | var stats = {
|
332 | maxLat: 0,
|
333 | minLat: Infinity,
|
334 | maxLng: 0,
|
335 | minLng: Infinity
|
336 | };
|
337 |
|
338 | if (useElevation) {
|
339 | stats.maxElev = 0;
|
340 | stats.minElev = Infinity;
|
341 | }
|
342 |
|
343 | for (var i = 0, l = coords.length; i < l; ++i) {
|
344 | stats.maxLat = Math.max(coords[i][latitude], stats.maxLat);
|
345 | stats.minLat = Math.min(coords[i][latitude], stats.minLat);
|
346 | stats.maxLng = Math.max(coords[i][longitude], stats.maxLng);
|
347 | stats.minLng = Math.min(coords[i][longitude], stats.minLng);
|
348 | if (useElevation) {
|
349 | stats.maxElev = Math.max(coords[i][elevation], stats.maxElev);
|
350 | stats.minElev = Math.min(coords[i][elevation], stats.minElev);
|
351 | }
|
352 | }
|
353 | return stats;
|
354 | },
|
355 |
|
356 | |
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 | isPointInside: function(latlng, coords) {
|
365 |
|
366 | var keys = geolib.getKeys(latlng);
|
367 | var latitude = keys.latitude;
|
368 | var longitude = keys.longitude;
|
369 |
|
370 | for(var c = false, i = -1, l = coords.length, j = l - 1; ++i < l; j = i) {
|
371 |
|
372 | if(
|
373 | (
|
374 | (coords[i][longitude] <= latlng[longitude] && latlng[longitude] < coords[j][longitude]) ||
|
375 | (coords[j][longitude] <= latlng[longitude] && latlng[longitude] < coords[i][longitude])
|
376 | ) &&
|
377 | (
|
378 | latlng[latitude] < (coords[j][latitude] - coords[i][latitude]) *
|
379 | (latlng[longitude] - coords[i][longitude]) /
|
380 | (coords[j][longitude] - coords[i][longitude]) +
|
381 | coords[i][latitude]
|
382 | )
|
383 | ) {
|
384 | c = !c;
|
385 | }
|
386 |
|
387 | }
|
388 |
|
389 | return c;
|
390 |
|
391 | },
|
392 |
|
393 |
|
394 | |
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 | isPointInCircle: function(latlng, center, radius) {
|
403 |
|
404 | return geolib.getDistance(latlng, center) < radius;
|
405 |
|
406 | },
|
407 |
|
408 |
|
409 | |
410 |
|
411 |
|
412 |
|
413 |
|
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 |
|
421 |
|
422 | getRhumbLineBearing: function(originLL, destLL) {
|
423 |
|
424 | var keys = geolib.getKeys(originLL);
|
425 | var latitude = keys.latitude;
|
426 | var longitude = keys.longitude;
|
427 |
|
428 |
|
429 | var diffLon = geolib.useDecimal(destLL[longitude]).toRad() - geolib.useDecimal(originLL[longitude]).toRad();
|
430 |
|
431 |
|
432 | 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));
|
433 |
|
434 |
|
435 | if(Math.abs(diffLon) > Math.PI) {
|
436 | if(diffLon > 0) {
|
437 | diffLon = (2 * Math.PI - diffLon) * -1;
|
438 | }
|
439 | else {
|
440 | diffLon = 2 * Math.PI + diffLon;
|
441 | }
|
442 | }
|
443 |
|
444 |
|
445 | return (Math.atan2(diffLon, diffPhi).toDeg() + 360) % 360;
|
446 |
|
447 | },
|
448 |
|
449 |
|
450 | |
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 |
|
457 | getBearing: function(originLL, destLL) {
|
458 |
|
459 | var keys = geolib.getKeys(originLL);
|
460 | var latitude = keys.latitude;
|
461 | var longitude = keys.longitude;
|
462 |
|
463 | destLL[latitude] = geolib.useDecimal(destLL[latitude]);
|
464 | destLL[longitude] = geolib.useDecimal(destLL[longitude]);
|
465 | originLL[latitude] = geolib.useDecimal(originLL[latitude]);
|
466 | originLL[longitude] = geolib.useDecimal(originLL[longitude]);
|
467 |
|
468 | var bearing = (
|
469 | (
|
470 | Math.atan2(
|
471 | Math.sin(
|
472 | destLL[longitude].toRad() -
|
473 | originLL[longitude].toRad()
|
474 | ) *
|
475 | Math.cos(
|
476 | destLL[latitude].toRad()
|
477 | ),
|
478 | Math.cos(
|
479 | originLL[latitude].toRad()
|
480 | ) *
|
481 | Math.sin(
|
482 | destLL[latitude].toRad()
|
483 | ) -
|
484 | Math.sin(
|
485 | originLL[latitude].toRad()
|
486 | ) *
|
487 | Math.cos(
|
488 | destLL[latitude].toRad()
|
489 | ) *
|
490 | Math.cos(
|
491 | destLL[longitude].toRad() - originLL[longitude].toRad()
|
492 | )
|
493 | )
|
494 | ).toDeg() + 360
|
495 | ) % 360;
|
496 |
|
497 | return bearing;
|
498 |
|
499 | },
|
500 |
|
501 |
|
502 | |
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 | getCompassDirection: function(originLL, destLL, bearingMode) {
|
511 |
|
512 | var direction, bearing;
|
513 | if(bearingMode == 'circle') {
|
514 | bearing = geolib.getBearing(originLL, destLL);
|
515 | } else {
|
516 | bearing = geolib.getRhumbLineBearing(originLL, destLL);
|
517 | }
|
518 |
|
519 | switch(Math.round(bearing/22.5)) {
|
520 | case 1:
|
521 | direction = {exact: "NNE", rough: "N"};
|
522 | break;
|
523 | case 2:
|
524 | direction = {exact: "NE", rough: "N"};
|
525 | break;
|
526 | case 3:
|
527 | direction = {exact: "ENE", rough: "E"};
|
528 | break;
|
529 | case 4:
|
530 | direction = {exact: "E", rough: "E"};
|
531 | break;
|
532 | case 5:
|
533 | direction = {exact: "ESE", rough: "E"};
|
534 | break;
|
535 | case 6:
|
536 | direction = {exact: "SE", rough: "E"};
|
537 | break;
|
538 | case 7:
|
539 | direction = {exact: "SSE", rough: "S"};
|
540 | break;
|
541 | case 8:
|
542 | direction = {exact: "S", rough: "S"};
|
543 | break;
|
544 | case 9:
|
545 | direction = {exact: "SSW", rough: "S"};
|
546 | break;
|
547 | case 10:
|
548 | direction = {exact: "SW", rough: "S"};
|
549 | break;
|
550 | case 11:
|
551 | direction = {exact: "WSW", rough: "W"};
|
552 | break;
|
553 | case 12:
|
554 | direction = {exact: "W", rough: "W"};
|
555 | break;
|
556 | case 13:
|
557 | direction = {exact: "WNW", rough: "W"};
|
558 | break;
|
559 | case 14:
|
560 | direction = {exact: "NW", rough: "W"};
|
561 | break;
|
562 | case 15:
|
563 | direction = {exact: "NNW", rough: "N"};
|
564 | break;
|
565 | default:
|
566 | direction = {exact: "N", rough: "N"};
|
567 | }
|
568 |
|
569 | return direction;
|
570 |
|
571 | },
|
572 |
|
573 |
|
574 | |
575 |
|
576 |
|
577 |
|
578 |
|
579 |
|
580 |
|
581 | orderByDistance: function(latlng, coords) {
|
582 |
|
583 | var keys = geolib.getKeys(latlng);
|
584 | var latitude = keys.latitude;
|
585 | var longitude = keys.longitude;
|
586 |
|
587 | var coordsArray = [];
|
588 | for(var coord in coords) {
|
589 | var d = geolib.getDistance(latlng, coords[coord]);
|
590 | coordsArray.push({key: coord, latitude: coords[coord][latitude], longitude: coords[coord][longitude], distance: d});
|
591 | }
|
592 |
|
593 | return coordsArray.sort(function(a, b) { return a.distance - b.distance; });
|
594 |
|
595 | },
|
596 |
|
597 |
|
598 | |
599 |
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 | findNearest: function(latlng, coords, offset) {
|
606 |
|
607 | offset = offset || 0;
|
608 | var ordered = geolib.orderByDistance(latlng, coords);
|
609 | return ordered[offset];
|
610 |
|
611 | },
|
612 |
|
613 |
|
614 | |
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 | getPathLength: function(coords) {
|
621 |
|
622 | var dist = 0, last;
|
623 | for (var i = 0, l = coords.length; i < l; ++i) {
|
624 | if(last) {
|
625 | dist += geolib.getDistance(coords[i], last);
|
626 | }
|
627 | last = coords[i];
|
628 | }
|
629 |
|
630 | return dist;
|
631 |
|
632 | },
|
633 |
|
634 | |
635 |
|
636 |
|
637 |
|
638 |
|
639 |
|
640 |
|
641 |
|
642 | convertUnit: function(unit, distance, round) {
|
643 |
|
644 | if(distance === 0 || typeof distance == 'undefined') {
|
645 |
|
646 | if(geolib.distance === 0) {
|
647 |
|
648 | return 0;
|
649 | } else {
|
650 | distance = geolib.distance;
|
651 | }
|
652 |
|
653 | }
|
654 |
|
655 | unit = unit || 'm';
|
656 | round = (null == round ? 4 : round);
|
657 |
|
658 | switch(unit) {
|
659 |
|
660 | case 'm':
|
661 | return geolib.round(distance, round);
|
662 | case 'km':
|
663 | return geolib.round(distance / 1000, round);
|
664 | case 'cm':
|
665 | return geolib.round(distance * 100, round);
|
666 | case 'mm':
|
667 | return geolib.round(distance * 1000, round);
|
668 | case 'mi':
|
669 | return geolib.round(distance * (1 / 1609.344), round);
|
670 | case 'sm':
|
671 | return geolib.round(distance * (1 / 1852.216), round);
|
672 | case 'ft':
|
673 | return geolib.round(distance * (100 / 30.48), round);
|
674 | case 'in':
|
675 | return geolib.round(distance * 100 / 2.54, round);
|
676 | case 'yd':
|
677 | return geolib.round(distance * (1 / 0.9144), round);
|
678 | }
|
679 |
|
680 | return distance;
|
681 |
|
682 | },
|
683 |
|
684 |
|
685 | |
686 |
|
687 |
|
688 |
|
689 |
|
690 |
|
691 | useDecimal: function(value) {
|
692 |
|
693 | value = value.toString().replace(/\s*/, '');
|
694 |
|
695 |
|
696 |
|
697 | if (!isNaN(parseFloat(value)) && parseFloat(value).toString() == value) {
|
698 | return parseFloat(value);
|
699 |
|
700 | } else if(geolib.isSexagesimal(value) === true) {
|
701 | return parseFloat(geolib.sexagesimal2decimal(value));
|
702 | } else {
|
703 | throw 'Unknown format.';
|
704 | }
|
705 |
|
706 | },
|
707 |
|
708 |
|
709 | |
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 | decimal2sexagesimal: function(dec) {
|
716 |
|
717 | if (dec in geolib.sexagesimal) {
|
718 | return geolib.sexagesimal[dec];
|
719 | }
|
720 |
|
721 | var tmp = dec.toString().split('.');
|
722 |
|
723 | var deg = Math.abs(tmp[0]);
|
724 | var min = ('0.' + tmp[1])*60;
|
725 | var sec = min.toString().split('.');
|
726 |
|
727 | min = Math.floor(min);
|
728 | sec = (('0.' + sec[1]) * 60).toFixed(2);
|
729 |
|
730 | geolib.sexagesimal[dec] = (deg + '° ' + min + "' " + sec + '"');
|
731 |
|
732 | return geolib.sexagesimal[dec];
|
733 |
|
734 | },
|
735 |
|
736 |
|
737 | |
738 |
|
739 |
|
740 |
|
741 |
|
742 |
|
743 | sexagesimal2decimal: function(sexagesimal) {
|
744 |
|
745 | if (sexagesimal in geolib.decimal) {
|
746 | return geolib.decimal[sexagesimal];
|
747 | }
|
748 |
|
749 | var regEx = new RegExp(sexagesimalPattern);
|
750 | var data = regEx.exec(sexagesimal);
|
751 | var min = 0, sec = 0;
|
752 |
|
753 | if(data) {
|
754 | min = parseFloat(data[2]/60);
|
755 | sec = parseFloat(data[4]/3600) || 0;
|
756 | }
|
757 |
|
758 | var dec = ((parseFloat(data[1]) + min + sec)).toFixed(8);
|
759 |
|
760 | dec = (data[7] == 'S' || data[7] == 'W') ? dec * -1 : dec;
|
761 |
|
762 | geolib.decimal[sexagesimal] = dec;
|
763 |
|
764 | return dec;
|
765 |
|
766 | },
|
767 |
|
768 |
|
769 | |
770 |
|
771 |
|
772 |
|
773 |
|
774 |
|
775 | isSexagesimal: function(value) {
|
776 |
|
777 | return sexagesimalPattern.test(value);
|
778 |
|
779 | },
|
780 |
|
781 | round: function(value, n) {
|
782 | var decPlace = Math.pow(10, n);
|
783 | return Math.round(value * decPlace)/decPlace;
|
784 | }
|
785 |
|
786 | };
|
787 |
|
788 | if (typeof(Number.prototype.toRad) === "undefined") {
|
789 | Number.prototype.toRad = function() {
|
790 | return this * Math.PI / 180;
|
791 | };
|
792 | }
|
793 |
|
794 | if (typeof(Number.prototype.toDeg) === "undefined") {
|
795 | Number.prototype.toDeg = function() {
|
796 | return this * 180 / Math.PI;
|
797 | };
|
798 | }
|
799 |
|
800 |
|
801 | window.geolib = geolib;
|
802 | if (typeof module != 'undefined') {
|
803 | module.exports = geolib;
|
804 | }
|
805 |
|
806 | }(this));
|
807 | (function(global) {
|
808 |
|
809 | var geolib = global.geolib;
|
810 |
|
811 |
|
812 |
|
813 |
|
814 | |
815 |
|
816 |
|
817 |
|
818 |
|
819 | geolib.getElevation = function() {
|
820 | if (typeof window.navigator !== 'undefined') {
|
821 | geolib.getElevationClient.apply(this, arguments);
|
822 | } else {
|
823 | geolib.getElevationServer.apply(this, arguments);
|
824 | }
|
825 | };
|
826 |
|
827 | geolib.getElevationClient = function(coords, cb) {
|
828 |
|
829 | if (!window.google) {
|
830 | throw new Error("Google maps api not loaded");
|
831 | }
|
832 |
|
833 | if (coords.length === 0) {
|
834 | return cb(null, null);
|
835 | }
|
836 |
|
837 | if (coords.length === 1) {
|
838 | return cb(new Error("getElevation requires at least 2 points."));
|
839 | }
|
840 |
|
841 | var path = [];
|
842 | var keys = geolib.getKeys(coords[0]);
|
843 | var latitude = keys.latitude;
|
844 | var longitude = keys.longitude;
|
845 |
|
846 | for(var i = 0; i < coords.length; i++) {
|
847 | path.push(new google.maps.LatLng(
|
848 | geolib.useDecimal(coords[i][latitude]),
|
849 | geolib.useDecimal(coords[i][longitude])
|
850 | ));
|
851 | }
|
852 |
|
853 | var positionalRequest = {
|
854 | 'path': path,
|
855 | 'samples': path.length
|
856 | };
|
857 | var elevationService = new google.maps.ElevationService();
|
858 | elevationService.getElevationAlongPath(positionalRequest,function (results, status) {
|
859 | geolib.elevationHandler(results, status, coords, keys, cb);
|
860 | });
|
861 |
|
862 | };
|
863 |
|
864 | geolib.getElevationServer = function(coords, cb) {
|
865 |
|
866 | if (coords.length === 0) {
|
867 | return cb(null, null);
|
868 | }
|
869 |
|
870 | if (coords.length === 1) {
|
871 | return cb(new Error("getElevation requires at least 2 points."));
|
872 | }
|
873 |
|
874 | var gm = require('googlemaps');
|
875 | var path = [];
|
876 | var keys = geolib.getKeys(coords[0]);
|
877 |
|
878 | var latitude = keys.latitude;
|
879 | var longitude = keys.longitude;
|
880 |
|
881 | for(var i = 0; i < coords.length; i++) {
|
882 | path.push(geolib.useDecimal(coords[i][latitude]) + ',' +
|
883 | geolib.useDecimal(coords[i][longitude]));
|
884 | }
|
885 |
|
886 | gm.elevationFromPath(path.join('|'), path.length, function(err, results) {
|
887 | geolib.elevationHandler(results.results, results.status, coords, keys, cb);
|
888 | });
|
889 |
|
890 | },
|
891 |
|
892 | geolib.elevationHandler = function(results, status, coords, keys, cb){
|
893 | var latsLngsElevs = [];
|
894 | var latitude = keys.latitude;
|
895 | var longitude = keys.longitude;
|
896 | if (status == "OK" ) {
|
897 | for (var i = 0; i < results.length; i++) {
|
898 | latsLngsElevs.push({
|
899 | "lat":coords[i][latitude],
|
900 | "lng":coords[i][longitude],
|
901 | "elev":results[i].elevation
|
902 | });
|
903 | }
|
904 | cb(null, latsLngsElevs);
|
905 | } else {
|
906 | cb(new Error("Could not get elevation using Google's API"), elevationResult.status);
|
907 | }
|
908 | };
|
909 |
|
910 | |
911 |
|
912 |
|
913 |
|
914 |
|
915 | geolib.getGrade = function(coords){
|
916 | var keys = geolib.getKeys(coords[0]);
|
917 | var elevation = keys.elevation;
|
918 | var rise = Math.abs(coords[coords.length-1][elevation] - coords[0][elevation]);
|
919 | var run = geolib.getPathLength(coords);
|
920 | return Math.floor((rise/run)*100);
|
921 | };
|
922 |
|
923 | |
924 |
|
925 |
|
926 |
|
927 |
|
928 | geolib.getTotalElevationGainAndLoss = function(coords){
|
929 | var keys = geolib.getKeys(coords[0]);
|
930 | var elevation = keys.elevation;
|
931 | var gain = 0;
|
932 | var loss = 0;
|
933 | for(var i = 0; i < coords.length - 1; i++){
|
934 | var deltaElev = coords[i][elevation] - coords[i + 1][elevation];
|
935 | if (deltaElev > 0) {
|
936 | loss += deltaElev;
|
937 | } else {
|
938 | gain += Math.abs(deltaElev);
|
939 | }
|
940 | }
|
941 | return {
|
942 | "gain": gain,
|
943 | "loss": loss
|
944 | };
|
945 | };
|
946 |
|
947 | })(this); |
\ | No newline at end of file |