https://www.naturpark-aukrug.com/typo3conf/ext/npa/Resources/Public/JavaScript/Src/gpx.js?1692185445
1/** 2 * Copyright (C) 2011-2012 Pavel Shramov 3 * Copyright (C) 2013-2017 Maxime Petazzoni <[email protected]> 4 * All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * - Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/* 30 * Thanks to Pavel Shramov who provided the initial implementation and Leaflet 31 * integration. Original code was at https://github.com/shramov/leaflet-plugins. 32 * 33 * It was then cleaned-up and modified to record and make available more 34 * information about the GPX track while it is being parsed so that the result 35 * can be used to display additional information about the track that is 36 * rendered on the Leaflet map. 37 */ 38 39var L = L || require('leaflet'); 40 41var _MAX_POINT_INTERVAL_MS = 15000; 42var _SECOND_IN_MILLIS = 1000; 43var _MINUTE_IN_MILLIS = 60 * _SECOND_IN_MILLIS; 44var _HOUR_IN_MILLIS = 60 * _MINUTE_IN_MILLIS; 45var _DAY_IN_MILLIS = 24 * _HOUR_IN_MILLIS; 46 47var _GPX_STYLE_NS = 'http://www.topografix.com/GPX/gpx_style/0/2'; 48 49var _DEFAULT_MARKER_OPTS = { 50 startIconUrl: 'pin-icon-start.png', 51 endIconUrl: 'pin-icon-end.png', 52 shadowUrl: 'pin-shadow.png', 53 wptIcons: [], 54 wptIconsType: [], 55 wptIconUrls : { 56 '': 'pin-icon-wpt.png', 57 }, 58 wptIconTypeUrls : { 59 '': 'pin-icon-wpt.png', 60 }, 61 pointMatchers: [], 62 iconSize: [33, 50], 63 shadowSize: [50, 50], 64 iconAnchor: [16, 45], 65 shadowAnchor: [16, 47], 66 clickable: false 67}; 68var _DEFAULT_POLYLINE_OPTS = { 69 color: '#FF1493' 70}; 71var _DEFAULT_GPX_OPTS = { 72 parseElements: ['track', 'route', 'waypoint'], 73 joinTrackSegments: true 74}; 75 76L.GPX = L.FeatureGroup.extend({ 77 initialize: function(gpx, options) { 78 options.max_point_interval = options.max_point_interval || _MAX_POINT_INTERVAL_MS; 79 options.marker_options = this._merge_objs( 80 _DEFAULT_MARKER_OPTS, 81 options.marker_options || {}); 82 options.polyline_options = options.polyline_options || {}; 83 options.gpx_options = this._merge_objs( 84 _DEFAULT_GPX_OPTS, 85 options.gpx_options || {}); 86 87 L.Util.setOptions(this, options); 88 89 // Base icon class for track pins. 90 L.GPXTrackIcon = L.Icon.extend({ options: options.marker_options }); 91 92 this._gpx = gpx; 93 this._layers = {}; 94 this._init_info(); 95 96 if (gpx) { 97 this._parse(gpx, options, this.options.async); 98 } 99 }, 100 101 get_duration_string: function(duration, hidems) { 102 var s = ''; 103 104 if (duration >= _DAY_IN_MILLIS) { 105 s += Math.floor(duration / _DAY_IN_MILLIS) + 'd '; 106 duration = duration % _DAY_IN_MILLIS; 107 } 108 109 if (duration >= _HOUR_IN_MILLIS) { 110 s += Math.floor(duration / _HOUR_IN_MILLIS) + ':'; 111 duration = duration % _HOUR_IN_MILLIS; 112 } 113 114 var mins = Math.floor(duration / _MINUTE_IN_MILLIS); 115 duration = duration % _MINUTE_IN_MILLIS; 116 if (mins < 10) s += '0'; 117 s += mins + '\''; 118 119 var secs = Math.floor(duration / _SECOND_IN_MILLIS); 120 duration = duration % _SECOND_IN_MILLIS; 121 if (secs < 10) s += '0'; 122 s += secs; 123 124 if (!hidems && duration > 0) s += '.' + Math.round(Math.floor(duration)*1000)/1000; 125 else s += '"'; 126 127 return s; 128 }, 129 130 get_duration_string_iso: function(duration, hidems) { 131 var s = this.get_duration_string(duration, hidems); 132 return s.replace("'",':').replace('"',''); 133 }, 134 135 // Public methods 136 to_miles: function(v) { return v / 1.60934; }, 137 to_ft: function(v) { return v * 3.28084; }, 138 m_to_km: function(v) { return v / 1000; }, 139 m_to_mi: function(v) { return v / 1609.34; }, 140 141 get_name: function() { return this._info.name; }, 142 get_desc: function() { return this._info.desc; }, 143 get_author: function() { return this._info.author; }, 144 get_copyright: function() { return this._info.copyright; }, 145 get_distance: function() { return this._info.length; }, 146 get_distance_imp: function() { return this.to_miles(this.m_to_km(this.get_distance())); }, 147 148 get_start_time: function() { return this._info.duration.start; }, 149 get_end_time: function() { return this._info.duration.end; }, 150 get_moving_time: function() { return this._info.duration.moving; }, 151 get_total_time: function() { return this._info.duration.total; }, 152 153 get_moving_pace: function() { return this.get_moving_time() / this.m_to_km(this.get_distance()); }, 154 get_moving_pace_imp: function() { return this.get_moving_time() / this.get_distance_imp(); }, 155 156 get_moving_speed: function() { return this.m_to_km(this.get_distance()) / (this.get_moving_time() / (3600 * 1000)) ; }, 157 get_moving_speed_imp:function() { return this.to_miles(this.m_to_km(this.get_distance())) / (this.get_moving_time() / (3600 * 1000)) ; }, 158 159 get_total_speed: function() { return this.m_to_km(this.get_distance()) / (this.get_total_time() / (3600 * 1000)); }, 160 get_total_speed_imp: function() { return this.to_miles(this.m_to_km(this.get_distance())) / (this.get_total_time() / (3600 * 1000)); }, 161 162 get_elevation_gain: function() { return this._info.elevation.gain; }, 163 get_elevation_loss: function() { return this._info.elevation.loss; }, 164 get_elevation_gain_imp: function() { return this.to_ft(this.get_elevation_gain()); }, 165 get_elevation_loss_imp: function() { return this.to_ft(this.get_elevation_loss()); }, 166 get_elevation_data: function() { 167 var _this = this; 168 return this._info.elevation._points.map( 169 function(p) { return _this._prepare_data_point(p, _this.m_to_km, null, 170 function(a, b) { return a.toFixed(2) + ' km, ' + b.toFixed(0) + ' m'; }); 171 }); 172 }, 173 get_elevation_data_imp: function() { 174 var _this = this; 175 return this._info.elevation._points.map( 176 function(p) { return _this._prepare_data_point(p, _this.m_to_mi, _this.to_ft, 177 function(a, b) { return a.toFixed(2) + ' mi, ' + b.toFixed(0) + ' ft'; }); 178 }); 179 }, 180 get_elevation_max: function() { return this._info.elevation.max; }, 181 get_elevation_min: function() { return this._info.elevation.min; }, 182 get_elevation_max_imp: function() { return this.to_ft(this.get_elevation_max()); }, 183 get_elevation_min_imp: function() { return this.to_ft(this.get_elevation_min()); }, 184 185 get_average_hr: function() { return this._info.hr.avg; }, 186 get_average_temp: function() { return this._info.atemp.avg; },
187 get_average_cadence: function() { return this._info.cad.avg; }, 188 get_heartrate_data: function() { 189 var _this = this; 190 return this._info.hr._points.map( 191 function(p) { return _this._prepare_data_point(p, _this.m_to_km, null, 192 function(a, b) { return a.toFixed(2) + ' km, ' + b.toFixed(0) + ' bpm'; }); 193 }); 194 }, 195 get_heartrate_data_imp: function() { 196 var _this = this; 197 return this._info.hr._points.map( 198 function(p) { return _this._prepare_data_point(p, _this.m_to_mi, null, 199 function(a, b) { return a.toFixed(2) + ' mi, ' + b.toFixed(0) + ' bpm'; }); 200 }); 201 }, 202 get_cadence_data: function() { 203 var _this = this; 204 return this._info.cad._points.map( 205 function(p) { return _this._prepare_data_point(p, _this.m_to_km, null, 206 function(a, b) { return a.toFixed(2) + ' km, ' + b.toFixed(0) + ' rpm'; }); 207 }); 208 }, 209 get_temp_data: function() { 210 var _this = this; 211 return this._info.atemp._points.map( 212 function(p) { return _this._prepare_data_point(p, _this.m_to_km, null, 213 function(a, b) { return a.toFixed(2) + ' km, ' + b.toFixed(0) + ' degrees'; }); 214 }); 215 }, 216 get_cadence_data_imp: function() { 217 var _this = this; 218 return this._info.cad._points.map( 219 function(p) { return _this._prepare_data_point(p, _this.m_to_mi, null, 220 function(a, b) { return a.toFixed(2) + ' mi, ' + b.toFixed(0) + ' rpm'; }); 221 }); 222 }, 223 get_temp_data_imp: function() { 224 var _this = this; 225 return this._info.atemp._points.map( 226 function(p) { return _this._prepare_data_point(p, _this.m_to_mi, null, 227 function(a, b) { return a.toFixed(2) + ' mi, ' + b.toFixed(0) + ' degrees'; }); 228 }); 229 }, 230 231 reload: function() { 232 this._init_info(); 233 this.clearLayers(); 234 this._parse(this._gpx, this.options, this.options.async); 235 }, 236 237 // Private methods 238 _merge_objs: function(a, b) { 239 var _ = {}; 240 for (var attr in a) { _[attr] = a[attr]; } 241 for (var attr in b) { _[attr] = b[attr]; } 242 return _; 243 }, 244 245 _prepare_data_point: function(p, trans1, trans2, trans_tooltip) { 246 var r = [trans1 && trans1(p[0]) || p[0], trans2 && trans2(p[1]) || p[1]]; 247 r.push(trans_tooltip && trans_tooltip(r[0], r[1]) || (r[0] + ': ' + r[1])); 248 return r; 249 }, 250 251 _init_info: function() { 252 this._info = { 253 name: null, 254 length: 0.0, 255 elevation: {gain: 0.0, loss: 0.0, max: 0.0, min: Infinity, _points: []}, 256 hr: {avg: 0, _total: 0, _points: []}, 257 duration: {start: null, end: null, moving: 0, total: 0}, 258 atemp: {avg: 0, _total: 0, _points: []}, 259 cad: {avg: 0, _total: 0, _points: []} 260 }; 261 }, 262 263 _load_xml: function(url, cb, options, async) { 264 if (async == undefined) async = this.options.async; 265 if (options == undefined) options = this.options; 266 267 var req = new window.XMLHttpRequest(); 268 req.open('GET', url, async); 269 try { 270 req.overrideMimeType('text/xml'); // unsupported by IE 271 } catch(e) {} 272 req.onreadystatechange = function() { 273 if (req.readyState != 4) return; 274 if(req.status == 200) cb(req.responseXML, options); 275 }; 276 req.send(null); 277 }, 278 279 _parse: function(input, options, async) { 280 var _this = this; 281 var cb = function(gpx, options) { 282 var layers = _this._parse_gpx_data(gpx, options); 283 if (!layers) { 284 _this.fire('error', { err: 'No parseable layers of type(s) ' + JSON.stringify(options.gpx_options.parseElements) }); 285 return; 286 } 287 _this.addLayer(layers); 288 _this.fire('loaded', { layers: layers, element: gpx }); 289 } 290 if (input.substr(0,1)==='<') { // direct XML has to start with a < 291 var parser = new DOMParser(); 292 if (async) { 293 setTimeout(function() { 294 cb(parser.parseFromString(input, "text/xml"), options); 295 }); 296 } else { 297 cb(parser.parseFromString(input, "text/xml"), options); 298 } 299 } else { 300 this._load_xml(input, cb, options, async); 301 } 302 }, 303 304 _parse_gpx_data: function(xml, options) { 305 var i, t, l, el, layers = []; 306 307 var name = xml.getElementsByTagName('name'); 308 if (name.length > 0) { 309 this._info.name = name[0].textContent; 310 } 311 var desc = xml.getElementsByTagName('desc'); 312 if (desc.length > 0) { 313 this._info.desc = desc[0].textContent; 314 } 315 var author = xml.getElementsByTagName('author'); 316 if (author.length > 0) { 317 this._info.author = author[0].textContent; 318 } 319 var copyright = xml.getElementsByTagName('copyright'); 320 if (copyright.length > 0) { 321 this._info.copyright = copyright[0].textContent; 322 } 323 324 var parseElements = options.gpx_options.parseElements; 325 if (parseElements.indexOf('route') > -1) { 326 // routes are <rtept> tags inside <rte> sections 327 var routes = xml.getElementsByTagName('rte'); 328 for (i = 0; i < routes.length; i++) { 329 layers = layers.concat(this._parse_segment(routes[i], options, {}, 'rtept')); 330 } 331 } 332 333 if (parseElements.indexOf('track') > -1) { 334 // tracks are <trkpt> tags in one or more <trkseg> sections in each <trk> 335 var tracks = xml.getElementsByTagName('trk'); 336 for (i = 0; i < tracks.length; i++) { 337 var track = tracks[i]; 338 var polyline_options = this._extract_styling(track); 339 340 if (options.gpx_options.joinTrackSegments) { 341 layers = layers.concat(this._parse_segment(track, options, polyline_options, 'trkpt')); 342 } else { 343 var segments = track.getElementsByTagName('trkseg'); 344 for (j = 0; j < segments.length; j++) { 345 layers = layers.concat(this._parse_segment(segments[j], options, polyline_options, 'trkpt')); 346 } 347 } 348 } 349 } 350 351 this._info.hr.avg = Math.round(this._info.hr._total / this._info.hr._points.length); 352 this._info.cad.avg = Math.round(this._info.cad._total / this._info.cad._points.length); 353 this._info.atemp.avg = Math.round(this._info.atemp._total / this._info.atemp._points.length); 354 355 // parse waypoints and add markers for each of them 356 if (parseElements.indexOf('waypoint') > -1) { 357 el = xml.getElementsByTagName('wpt'); 358 for (i = 0; i < el.length; i++) { 359 var ll = new L.LatLng( 360 el[i].getAttribute('lat'), 361 el[i].getAttribute('lon')); 362 363 var nameEl = el[i].getElementsByTagName('name'); 364 var name = nameEl.length > 0 ? nameEl[0].textContent : null; 365 366 var descEl = el[i].getElementsByTagName('desc'); 367 var desc = descEl.length > 0 ? descEl[0].textContent : null; 368 369 var symEl = el[i].getElementsByTagName('sym'); 370 var symKey = symEl.length > 0 ? symEl[0].textContent : null; 371 372 var typeEl = el[i].getElementsByTagName('type'); 373 var typeKey = typeEl.length > 0 ? typeEl[0].textContent : null; 374 375 /* 376 * Add waypoint marker based on the waypoint symbol key. 377 * 378 * First look for a configured icon for that symKey. If not found, look 379 * for a configured icon URL for that symKey and build an icon from it. 380 * If none of those match, look through the point matchers for a match 381 * on the waypoint's name. 382 * 383 * Otherwise, fall back to the default icon if one was configured, or 384 * finally to the default icon URL, if one was configured. 385 */ 386 var wptIcons = options.marker_options.wptIcons; 387 var wptIconUrls = options.marker_options.wptIconUrls; 388 var wptIconsType = options.marker_options.wptIconsType; 389 var wptIconTypeUrls = options.marker_options.wptIconTypeUrls; 390 var ptMatchers = options.marker_options.pointMatchers || []; 391 var symIcon; 392 if (wptIcons && symKey && wptIcons[symKey]) { 393 symIcon = wptIcons[symKey]; 394 } else if (wptIconsType && typeKey && wptIconsType[typeKey]) { 395 symIcon = wptIconsType[typeKey]; 396 } else if (wptIconUrls && symKey && wptIconUrls[symKey]) { 397 symIcon = new L.GPXTrackIcon({iconUrl: wptIconUrls[symKey]}); 398 } else if (wptIconTypeUrls && typeKey && wptIconTypeUrls[typeKey]) { 399 symIcon = new L.GPXTrackIcon({iconUrl: wptIconTypeUrls[typeKey]}); 400 } else if (ptMatchers.length > 0) { 401 for (var j = 0; j < ptMatchers.length; j++) { 402 if (ptMatchers[j].regex.test(name)) { 403 symIcon = ptMatchers[j].icon; 404 break; 405 } 406 } 407 } else if (wptIcons && wptIcons['']) { 408 symIcon = wptIcons['']; 409 } else if (wptIconUrls && wptIconUrls['']) { 410 symIcon = new L.GPXTrackIcon({iconUrl: wptIconUrls['']}); 411 } 412 413 if (!symIcon) { 414 console.log(
415 'No waypoint icon could be matched for symKey=%s,typeKey=%s,name=%s on waypoint %o', 416 symKey, typeKey, name, el[i]); 417 continue; 418 } 419 420 var marker = new L.Marker(ll, { 421 clickable: options.marker_options.clickable, 422 title: name, 423 icon: symIcon, 424 type: 'waypoint' 425 }); 426 marker.bindPopup("<b>" + name + "</b>" + (desc.length > 0 ? '<br>' + desc : '')).openPopup(); 427 this.fire('addpoint', { point: marker, point_type: 'waypoint', element: el[i] }); 428 layers.push(marker); 429 } 430 } 431 432 if (layers.length > 1) { 433 return new L.FeatureGroup(layers); 434 } else if (layers.length == 1) { 435 return layers[0]; 436 } 437 }, 438 439 _parse_segment: function(line, options, polyline_options, tag) { 440 var el = line.getElementsByTagName(tag); 441 if (!el.length) return []; 442 443 var coords = []; 444 var markers = []; 445 var layers = []; 446 var last = null; 447 448 for (var i = 0; i < el.length; i++) { 449 var _, ll = new L.LatLng( 450 el[i].getAttribute('lat'), 451 el[i].getAttribute('lon')); 452 ll.meta = { time: null, ele: null, hr: null, cad: null, atemp: null }; 453 454 _ = el[i].getElementsByTagName('time'); 455 if (_.length > 0) { 456 ll.meta.time = new Date(Date.parse(_[0].textContent)); 457 } else { 458 ll.meta.time = new Date('1970-01-01T00:00:00'); 459 } 460 461 _ = el[i].getElementsByTagName('ele'); 462 if (_.length > 0) { 463 ll.meta.ele = parseFloat(_[0].textContent); 464 } 465 466 _ = el[i].getElementsByTagName('name'); 467 if (_.length > 0) { 468 var name = _[0].textContent; 469 var ptMatchers = options.marker_options.pointMatchers || []; 470 471 for (var j = 0; j < ptMatchers.length; j++) { 472 if (ptMatchers[j].regex.test(name)) { 473 markers.push({ label: name, coords: ll, icon: ptMatchers[j].icon, element: el[i] }); 474 break; 475 } 476 } 477 } 478 479 _ = el[i].getElementsByTagNameNS('*', 'hr'); 480 if (_.length > 0) { 481 ll.meta.hr = parseInt(_[0].textContent); 482 this._info.hr._points.push([this._info.length, ll.meta.hr]); 483 this._info.hr._total += ll.meta.hr; 484 } 485 486 _ = el[i].getElementsByTagNameNS('*', 'cad'); 487 if (_.length > 0) { 488 ll.meta.cad = parseInt(_[0].textContent); 489 this._info.cad._points.push([this._info.length, ll.meta.cad]); 490 this._info.cad._total += ll.meta.cad; 491 } 492 493 _ = el[i].getElementsByTagNameNS('*', 'atemp'); 494 if (_.length > 0) { 495 ll.meta.atemp = parseInt(_[0].textContent); 496 this._info.atemp._points.push([this._info.length, ll.meta.atemp]); 497 this._info.atemp._total += ll.meta.atemp; 498 } 499 500 if (ll.meta.ele > this._info.elevation.max) { 501 this._info.elevation.max = ll.meta.ele; 502 } 503 504 if (ll.meta.ele < this._info.elevation.min) { 505 this._info.elevation.min = ll.meta.ele; 506 } 507 508 this._info.elevation._points.push([this._info.length, ll.meta.ele]); 509 this._info.duration.end = ll.meta.time; 510 511 if (last != null) { 512 this._info.length += this._dist3d(last, ll); 513 514 var t = ll.meta.ele - last.meta.ele; 515 if (t > 0) { 516 this._info.elevation.gain += t; 517 } else { 518 this._info.elevation.loss += Math.abs(t); 519 } 520 521 t = Math.abs(ll.meta.time - last.meta.time); 522 this._info.duration.total += t; 523 if (t < options.max_point_interval) { 524 this._info.duration.moving += t; 525 } 526 } else if (this._info.duration.start == null) { 527 this._info.duration.start = ll.meta.time; 528 } 529 530 last = ll; 531 coords.push(ll); 532 } 533 534 // add track 535 var l = new L.Polyline(coords, this._extract_styling(line, polyline_options, options.polyline_options)); 536 this.fire('addline', { line: l, element: line }); 537 layers.push(l); 538 539 if (options.marker_options.startIcon || options.marker_options.startIconUrl) { 540 // add start pin 541 var marker = new L.Marker(coords[0], { 542 clickable: options.marker_options.clickable, 543 icon: options.marker_options.startIcon || new L.GPXTrackIcon({iconUrl: options.marker_options.startIconUrl}) 544 }); 545 this.fire('addpoint', { point: marker, point_type: 'start', element: el[0] }); 546 layers.push(marker); 547 } 548 549 if (options.marker_options.endIcon || options.marker_options.endIconUrl) { 550 // add end pin 551 var marker = new L.Marker(coords[coords.length-1], { 552 clickable: options.marker_options.clickable, 553 icon: options.marker_options.endIcon || new L.GPXTrackIcon({iconUrl: options.marker_options.endIconUrl}) 554 }); 555 this.fire('addpoint', { point: marker, point_type: 'end', element: el[el.length-1] }); 556 layers.push(marker); 557 } 558 559 // add named markers 560 for (var i = 0; i < markers.length; i++) { 561 var marker = new L.Marker(markers[i].coords, { 562 clickable: options.marker_options.clickable, 563 title: markers[i].label, 564 icon: markers[i].icon 565 }); 566 this.fire('addpoint', { point: marker, point_type: 'label', element: markers[i].element }); 567 layers.push(marker); 568 } 569 570 return layers; 571 }, 572 573 _extract_styling: function(el, base, overrides) { 574 var style = this._merge_objs(_DEFAULT_POLYLINE_OPTS, base); 575 var e = el.getElementsByTagNameNS(_GPX_STYLE_NS, 'line'); 576 if (e.length > 0) { 577 var _ = e[0].getElementsByTagName('color'); 578 if (_.length > 0) style.color = '#' + _[0].textContent; 579 var _ = e[0].getElementsByTagName('opacity'); 580 if (_.length > 0) style.opacity = _[0].textContent; 581 var _ = e[0].getElementsByTagName('weight'); 582 if (_.length > 0) style.weight = _[0].textContent; 583 var _ = e[0].getElementsByTagName('linecap'); 584 if (_.length > 0) style.lineCap = _[0].textContent; 585 } 586 return this._merge_objs(style, overrides) 587 }, 588 589 _dist2d: function(a, b) { 590 var R = 6371000; 591 var dLat = this._deg2rad(b.lat - a.lat); 592 var dLon = this._deg2rad(b.lng - a.lng); 593 var r = Math.sin(dLat/2) * 594 Math.sin(dLat/2) + 595 Math.cos(this._deg2rad(a.lat)) * 596 Math.cos(this._deg2rad(b.lat)) * 597 Math.sin(dLon/2) * 598 Math.sin(dLon/2); 599 var c = 2 * Math.atan2(Math.sqrt(r), Math.sqrt(1-r)); 600 var d = R * c; 601 return d; 602 }, 603 604 _dist3d: function(a, b) { 605 var planar = this._dist2d(a, b); 606 var height = Math.abs(b.meta.ele - a.meta.ele); 607 return Math.sqrt(Math.pow(planar, 2) + Math.pow(height, 2)); 608 }, 609 610 _deg2rad: function(deg) { 611 return deg * Math.PI / 180; 612 } 613}); 614 615if (typeof module === 'object' && typeof module.exports === 'object') { 616 module.exports = L; 617} else if (typeof define === 'function' && define.amd) { 618 define(L); 619}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.