1var L = L || require("leaflet"); 2 3var _MAX_POINT_INTERVAL_MS = 15000; 4var _SECOND_IN_MILLIS = 1000; 5var _MINUTE_IN_MILLIS = 60 * _SECOND_IN_MILLIS; 6var _HOUR_IN_MILLIS = 60 * _MINUTE_IN_MILLIS; 7var _DAY_IN_MILLIS = 24 * _HOUR_IN_MILLIS; 8 9var _GPX_STYLE_NS = "http://www.topografix.com/GPX/gpx_style/0/2"; 10 11var _DEFAULT_MARKER_OPTS = { 12 startIconUrl: "pin-icon-start.png", 13 endIconUrl: "pin-icon-end.png", 14 shadowUrl: "pin-shadow.png", 15 wptIcons: [], 16 wptIconsType: [], 17 wptIconUrls: { 18 "": "pin-icon-wpt.png", 19 }, 20 wptIconTypeUrls: { 21 "": "pin-icon-wpt.png", 22 }, 23 pointMatchers: [], 24 iconSize: [33, 50], 25 shadowSize: [50, 50], 26 iconAnchor: [16, 45], 27 shadowAnchor: [16, 47], 28 clickable: false, 29}; 30var _DEFAULT_POLYLINE_OPTS = { 31 color: "blue", 32}; 33var _DEFAULT_GPX_OPTS = { 34 parseElements: ["track", "route", "waypoint"], 35 show_kilometer_point: false, 36 kilometer_point_options: { 37 kilometer_point_color: "blue", 38 kilometer_point_color_text: "white", 39 kilometer_point_intervall: 1, 40 kilometer_point_radius: 10, 41 }, 42 show_mile_point: true, 43 mile_point_options: { 44 mile_point_color: "blue", 45 mile_point_color_text: "white", 46 mile_intervall: 1, 47 mile_point_radius: 10, 48 }, 49 joinTrackSegments: true, 50}; 51 52L.GPX = L.FeatureGroup.extend({ 53 initialize: function (gpx, options) { 54 options.max_point_interval = 55 options.max_point_interval || _MAX_POINT_INTERVAL_MS; 56 options.marker_options = this._merge_objs( 57 _DEFAULT_MARKER_OPTS, 58 options.marker_options || {} 59 ); 60 options.polyline_options = options.polyline_options || {}; 61 options.gpx_options = this._merge_objs( 62 _DEFAULT_GPX_OPTS, 63 options.gpx_options || {} 64 ); 65 66 L.Util.setOptions(this, options); 67 68 // Base icon class for track pins. 69 L.GPXTrackIcon = L.Icon.extend({ options: options.marker_options }); 70 71 this._gpx = gpx; 72 this._layers = {}; 73 this._init_info(); 74 75 if (gpx) { 76 this._parse(gpx, options, this.options.async); 77 } 78 }, 79 80 get_duration_string: function (duration, hidems) { 81 var s = ""; 82 83 if (duration >= _DAY_IN_MILLIS) { 84 s += Math.floor(duration / _DAY_IN_MILLIS) + "d "; 85 duration = duration % _DAY_IN_MILLIS; 86 } 87 88 if (duration >= _HOUR_IN_MILLIS) { 89 s += Math.floor(duration / _HOUR_IN_MILLIS) + ":"; 90 duration = duration % _HOUR_IN_MILLIS; 91 } 92 93 var mins = Math.floor(duration / _MINUTE_IN_MILLIS); 94 duration = duration % _MINUTE_IN_MILLIS; 95 if (mins < 10) s += "0"; 96 s += mins + "'"; 97 98 var secs = Math.floor(duration / _SECOND_IN_MILLIS); 99 duration = duration % _SECOND_IN_MILLIS; 100 if (secs < 10) s += "0"; 101 s += secs; 102 103 if (!hidems && duration > 0) 104 s += "." + Math.round(Math.floor(duration) * 1000) / 1000; 105 else s += '"'; 106 107 return s; 108 }, 109 110 get_duration_string_iso: function (duration, hidems) { 111 var s = this.get_duration_string(duration, hidems); 112 return s.replace("'", ":").replace('"', ""); 113 }, 114 115 // Public methods 116 to_miles: function (v) { 117 return v / 1.60934; 118 }, 119 to_ft: function (v) { 120 return v * 3.28084; 121 }, 122 m_to_km: function (v) { 123 return v / 1000; 124 }, 125 m_to_mi: function (v) { 126 return v / 1609.34; 127 }, 128 129 get_name: function () { 130 return this._info.name; 131 }, 132 get_desc: function () { 133 return this._info.desc; 134 }, 135 get_author: function () { 136 return this._info.author; 137 }, 138 get_copyright: function () { 139 return this._info.copyright; 140 }, 141 get_distance: function () { 142 return this._info.length; 143 }, 144 get_distance_imp: function () { 145 return this.to_miles(this.m_to_km(this.get_distance())); 146 }, 147 148 get_start_time: function () { 149 return this._info.duration.start; 150 }, 151 get_end_time: function () { 152 return this._info.duration.end; 153 }, 154 get_moving_time: function () { 155 return this._info.duration.moving; 156 }, 157 get_total_time: function () { 158 return this._info.duration.total; 159 }, 160 161 get_moving_pace: function () { 162 return this.get_moving_time() / this.m_to_km(this.get_distance()); 163 }, 164 get_moving_pace_imp: function () { 165 return this.get_moving_time() / this.get_distance_imp(); 166 }, 167 168 get_moving_speed: function () { 169 return ( 170 this.m_to_km(this.get_distance()) / 171 (this.get_moving_time() / (3600 * 1000)) 172 ); 173 }, 174 get_moving_speed_imp: function () { 175 return ( 176 this.to_miles(this.m_to_km(this.get_distance())) / 177 (this.get_moving_time() / (3600 * 1000)) 178 ); 179 }, 180 181 get_total_speed: function () { 182 return ( 183 this.m_to_km(this.get_distance()) / 184 (this.get_total_time() / (3600 * 1000)) 185 ); 186 }, 187 get_total_speed_imp: function () { 188 return ( 189 this.to_miles(this.m_to_km(this.get_distance())) / 190 (this.get_total_time() / (3600 * 1000)) 191 ); 192 }, 193 194 get_elevation_gain: function () { 195 return this._info.elevation.gain; 196 }, 197 get_elevation_loss: function () { 198 return this._info.elevation.loss; 199 }, 200 get_elevation_gain_imp: function () { 201 return this.to_ft(this.get_elevation_gain()); 202 }, 203 get_elevation_loss_imp: function () { 204 return this.to_ft(this.get_elevation_loss()); 205 }, 206 get_elevation_data: function () { 207 var _this = this; 208 return this._info.elevation._points.map(function (p) { 209 return _this._prepare_data_point(p, _this.m_to_km, null, function (a, b) { 210 return a.toFixed(2) + " km, " + b.toFixed(0) + " m"; 211 }); 212 }); 213 }, 214 get_elevation_data_imp: function () { 215 var _this = this; 216 return this._info.elevation._points.map(function (p) { 217 return _this._prepare_data_point(p, _this.m_to_mi, _this.to_ft, function ( 218 a, 219 b 220 ) { 221 return a.toFixed(2) + " mi, " + b.toFixed(0) + " ft"; 222 }); 223 }); 224 }, 225 get_elevation_max: function () { 226 return this._info.elevation.max; 227 }, 228 get_elevation_min: function () { 229 return this._info.elevation.min; 230 }, 231 get_elevation_max_imp: function () { 232 return this.to_ft(this.get_elevation_max()); 233 }, 234 get_elevation_min_imp: function () { 235 return this.to_ft(this.get_elevation_min()); 236 }, 237 238 get_average_hr: function () { 239 return this._info.hr.avg; 240 }, 241 get_average_temp: function () { 242 return this._info.atemp.avg; 243 },
244 get_average_cadence: function () { 245 return this._info.cad.avg; 246 }, 247 get_heartrate_data: function () { 248 var _this = this; 249 return this._info.hr._points.map(function (p) { 250 return _this._prepare_data_point(p, _this.m_to_km, null, function (a, b) { 251 return a.toFixed(2) + " km, " + b.toFixed(0) + " bpm"; 252 }); 253 }); 254 }, 255 get_heartrate_data_imp: function () { 256 var _this = this; 257 return this._info.hr._points.map(function (p) { 258 return _this._prepare_data_point(p, _this.m_to_mi, null, function (a, b) { 259 return a.toFixed(2) + " mi, " + b.toFixed(0) + " bpm"; 260 }); 261 }); 262 }, 263 get_cadence_data: function () { 264 var _this = this; 265 return this._info.cad._points.map(function (p) { 266 return _this._prepare_data_point(p, _this.m_to_km, null, function (a, b) { 267 return a.toFixed(2) + " km, " + b.toFixed(0) + " rpm"; 268 }); 269 }); 270 }, 271 get_temp_data: function () { 272 var _this = this; 273 return this._info.atemp._points.map(function (p) { 274 return _this._prepare_data_point(p, _this.m_to_km, null, function (a, b) { 275 return a.toFixed(2) + " km, " + b.toFixed(0) + " degrees"; 276 }); 277 }); 278 }, 279 get_cadence_data_imp: function () { 280 var _this = this; 281 return this._info.cad._points.map(function (p) { 282 return _this._prepare_data_point(p, _this.m_to_mi, null, function (a, b) { 283 return a.toFixed(2) + " mi, " + b.toFixed(0) + " rpm"; 284 }); 285 }); 286 }, 287 get_temp_data_imp: function () { 288 var _this = this; 289 return this._info.atemp._points.map(function (p) { 290 return _this._prepare_data_point(p, _this.m_to_mi, null, function (a, b) { 291 return a.toFixed(2) + " mi, " + b.toFixed(0) + " degrees"; 292 }); 293 }); 294 }, 295 296 reload: function () { 297 this._init_info(); 298 this.clearLayers(); 299 this._parse(this._gpx, this.options, this.options.async); 300 }, 301 302 // Private methods 303 _merge_objs: function (a, b) { 304 var _ = {}; 305 for (var attr in a) { 306 _[attr] = a[attr]; 307 } 308 for (var attr in b) { 309 _[attr] = b[attr]; 310 } 311 return _; 312 }, 313 314 _prepare_data_point: function (p, trans1, trans2, trans_tooltip) { 315 var r = [ 316 (trans1 && trans1(p[0])) || p[0], 317 (trans2 && trans2(p[1])) || p[1], 318 ]; 319 r.push((trans_tooltip && trans_tooltip(r[0], r[1])) || r[0] + ": " + r[1]); 320 return r; 321 }, 322 323 _init_info: function () { 324 this._info = { 325 name: null, 326 length: 0.0, 327 elevation: { gain: 0.0, loss: 0.0, max: 0.0, min: Infinity, _points: [] }, 328 hr: { avg: 0, _total: 0, _points: [] }, 329 duration: { start: null, end: null, moving: 0, total: 0 }, 330 atemp: { avg: 0, _total: 0, _points: [] }, 331 cad: { avg: 0, _total: 0, _points: [] }, 332 }; 333 }, 334 335 _load_xml: function (url, cb, options, async) { 336 if (async == undefined) async = this.options.async; 337 if (options == undefined) options = this.options; 338 339 var req = new window.XMLHttpRequest(); 340 req.open("GET", url, async); 341 try { 342 req.overrideMimeType("text/xml"); // unsupported by IE 343 } catch (e) {} 344 req.onreadystatechange = function () { 345 if (req.readyState != 4) return; 346 if (req.status == 200) cb(req.responseXML, options); 347 }; 348 req.send(null); 349 }, 350 351 _parse: function (input, options, async) { 352 var _this = this; 353 var cb = function (gpx, options) { 354 var layers = _this._parse_gpx_data(gpx, options); 355 if (!layers) { 356 _this.fire("error", { 357 err: 358 "No parseable layers of type(s) " + 359 JSON.stringify(options.gpx_options.parseElements), 360 }); 361 return; 362 } 363 _this.addLayer(layers); 364 _this.fire("loaded", { layers: layers, element: gpx }); 365 }; 366 if (input.substr(0, 1) === "<") { 367 // direct XML has to start with a < 368 var parser = new DOMParser(); 369 if (async) { 370 setTimeout(function () { 371 cb(parser.parseFromString(input, "text/xml"), options); 372 }); 373 } else { 374 cb(parser.parseFromString(input, "text/xml"), options); 375 } 376 } else { 377 this._load_xml(input, cb, options, async); 378 } 379 }, 380 381 _parse_gpx_data: function (xml, options) { 382 var i, 383 t, 384 l, 385 el, 386 layers = []; 387 388 var name = xml.getElementsByTagName("name"); 389 if (name.length > 0) { 390 this._info.name = name[0].textContent; 391 } 392 var desc = xml.getElementsByTagName("desc"); 393 if (desc.length > 0) { 394 this._info.desc = desc[0].textContent; 395 } 396 var author = xml.getElementsByTagName("author"); 397 if (author.length > 0) { 398 this._info.author = author[0].textContent; 399 } 400 var copyright = xml.getElementsByTagName("copyright"); 401 if (copyright.length > 0) { 402 this._info.copyright = copyright[0].textContent; 403 } 404 405 var parseElements = options.gpx_options.parseElements; 406 if (parseElements.indexOf("route") > -1) { 407 // routes are <rtept> tags inside <rte> sections 408 var routes = xml.getElementsByTagName("rte"); 409 for (i = 0; i < routes.length; i++) { 410 layers = layers.concat( 411 this._parse_segment(routes[i], options, {}, "rtept") 412 ); 413 } 414 } 415 416 if (parseElements.indexOf("track") > -1) { 417 // tracks are <trkpt> tags in one or more <trkseg> sections in each <trk> 418 var tracks = xml.getElementsByTagName("trk"); 419 for (i = 0; i < tracks.length; i++) { 420 var track = tracks[i]; 421 var polyline_options = this._extract_styling(track); 422 423 if (options.gpx_options.joinTrackSegments) { 424 layers = layers.concat( 425 this._parse_segment(track, options, polyline_options, "trkpt") 426 ); 427 } else { 428 var segments = track.getElementsByTagName("trkseg"); 429 for (j = 0; j < segments.length; j++) { 430 layers = layers.concat( 431 this._parse_segment( 432 segments[j], 433 options, 434 polyline_options, 435 "trkpt" 436 ) 437 ); 438 } 439 } 440 } 441 } 442 443 this._info.hr.avg = Math.round( 444 this._info.hr._total / this._info.hr._points.length 445 ); 446 this._info.cad.avg = Math.round( 447 this._info.cad._total / this._info.cad._points.length 448 ); 449 this._info.atemp.avg = Math.round( 450 this._info.atemp._total / this._info.atemp._points.length 451 ); 452 453 // parse waypoints and add markers for each of them 454 if (parseElements.indexOf("waypoint") > -1) { 455 el = xml.getElementsByTagName("wpt"); 456 for (i = 0; i < el.length; i++) { 457 var ll = new L.LatLng( 458 el[i].getAttribute("lat"), 459 el[i].getAttribute("lon") 460 ); 461 462 var nameEl = el[i].getElementsByTagName("name"); 463 var name = ""; 464 if (nameEl.length > 0) { 465 name = nameEl[0].textContent; 466 } 467 468 var descEl = el[i].getElementsByTagName("desc"); 469 var desc = ""; 470 if (descEl.length > 0) { 471 desc = descEl[0].textContent; 472 } 473 474 var symEl = el[i].getElementsByTagName("sym"); 475 var symKey = ""; 476 if (symEl.length > 0) { 477 symKey = symEl[0].textContent; 478 } 479 480 var typeEl = el[i].getElementsByTagName("type"); 481 var typeKey = ""; 482 if (typeEl.length > 0) { 483 typeKey = typeEl[0].textContent; 484 } 485 486 /* 487 * Add waypoint marker based on the waypoint symbol key. 488 * 489 * First look for a configured icon for that symKey. If not found, look 490 * for a configured icon URL for that symKey and build an icon from it. 491 * Otherwise, fall back to the default icon if one was configured, or 492 * finally to the default icon URL. 493 */ 494 var wptIcons = options.marker_options.wptIcons; 495 var wptIconUrls = options.marker_options.wptIconUrls; 496 var wptIconsType = options.marker_options.wptIconsType; 497 var wptIconTypeUrls = options.marker_options.wptIconTypeUrls; 498 499 var symIcon; 500 if (wptIcons && wptIcons[symKey]) { 501 502 symIcon = wptIcons[symKey]; 503 } else if (wptIconsType && wptIconsType[typeKey]) { 504 505 symIcon = wptIconsType[typeKey]; 506 } else if (wptIconUrls && wptIconUrls[symKey]) { 507 508 symIcon = new L.GPXTrackIcon({ iconUrl: wptIconUrls[symKey] }); 509 } else if (wptIconTypeUrls && wptIconTypeUrls[typeKey]) { 510 511 symIcon = new L.GPXTrackIcon({ iconUrl: wptIconTypeUrls[typeKey] }); 512 } else if (wptIcons && wptIcons[""]) { 513 514 symIcon = wptIcons[""]; 515 } else if (wptIconUrls && wptIconUrls[""]) { 516 517 symIcon = new L.GPXTrackIcon({ iconUrl: wptIconUrls[""] }); 518 } else { 519 console.log(
520 'No icon or icon URL configured for symbol type "' + 521 symKey + 522 '", and no fallback configured; ignoring waypoint.' 523 ); 524 continue; 525 } 526 527 528 var marker = new L.Marker(ll, { 529 clickable: options.marker_options.clickable, 530 title: name, 531 icon: symIcon, 532 type: "waypoint", 533 }); 534 marker 535 .bindPopup( 536 "<b>" + name + "</b>" + (desc.length > 0 ? "<br>" + desc : "") 537 ) 538 .openPopup(); 539 this.fire("addpoint", { 540 point: marker, 541 point_type: "waypoint", 542 element: el[i], 543 }); 544 layers.push(marker); 545 } 546 } 547 548 if (layers.length > 1) { 549 return new L.FeatureGroup(layers); 550 } else if (layers.length == 1) { 551 return layers[0]; 552 } 553 }, 554 555 _parse_segment: function (line, options, polyline_options, tag) { 556 var el = line.getElementsByTagName(tag); 557 var kilometer_point_layers = []; 558 var mile_point_layers = []; 559 var _this = this; 560 561 if (!el.length) return []; 562 563 var coords = []; 564 var markers = []; 565 var layers = []; 566 var last = null; 567 568 for (var i = 0; i < el.length; i++) { 569 var _, 570 ll = new L.LatLng(el[i].getAttribute("lat"), el[i].getAttribute("lon")); 571 ll.meta = { time: null, ele: null, hr: null, cad: null, atemp: null }; 572 573 _ = el[i].getElementsByTagName("time"); 574 if (_.length > 0) { 575 ll.meta.time = new Date(Date.parse(_[0].textContent)); 576 } else { 577 ll.meta.time = new Date("1970-01-01T00:00:00"); 578 } 579 580 _ = el[i].getElementsByTagName("ele"); 581 if (_.length > 0) { 582 ll.meta.ele = parseFloat(_[0].textContent); 583 } 584 585 _ = el[i].getElementsByTagName("name"); 586 if (_.length > 0) { 587 var name = _[0].textContent; 588 var ptMatchers = options.marker_options.pointMatchers || []; 589 590 for (var j = 0; j < ptMatchers.length; j++) { 591 if (ptMatchers[j].regex.test(name)) { 592 markers.push({ 593 label: name, 594 coords: ll, 595 icon: ptMatchers[j].icon, 596 element: el[i], 597 }); 598 break; 599 } 600 } 601 } 602 603 _ = el[i].getElementsByTagNameNS("*", "hr"); 604 if (_.length > 0) { 605 ll.meta.hr = parseInt(_[0].textContent); 606 this._info.hr._points.push([this._info.length, ll.meta.hr]); 607 this._info.hr._total += ll.meta.hr; 608 } 609 610 _ = el[i].getElementsByTagNameNS("*", "cad"); 611 if (_.length > 0) { 612 ll.meta.cad = parseInt(_[0].textContent); 613 this._info.cad._points.push([this._info.length, ll.meta.cad]); 614 this._info.cad._total += ll.meta.cad; 615 } 616 617 _ = el[i].getElementsByTagNameNS("*", "atemp"); 618 if (_.length > 0) { 619 ll.meta.atemp = parseInt(_[0].textContent); 620 this._info.atemp._points.push([this._info.length, ll.meta.atemp]); 621 this._info.atemp._total += ll.meta.atemp; 622 } 623 624 if (ll.meta.ele > this._info.elevation.max) { 625 this._info.elevation.max = ll.meta.ele; 626 } 627 628 if (ll.meta.ele < this._info.elevation.min) { 629 this._info.elevation.min = ll.meta.ele; 630 } 631 632 this._info.elevation._points.push([this._info.length, ll.meta.ele]); 633 this._info.duration.end = ll.meta.time; 634 635 if (last != null) { 636 this._info.length += this._dist3d(last, ll); 637 638 /* 639 * Add points to the line. 640 */ 641 if ( 642 options.gpx_options.show_kilometer_point || 643 options.gpx_options.show_mile_point 644 ) { 645 if (this._parse_current_kilometer != null) { 646 // Kilometer Point 647 if (options.gpx_options.show_kilometer_point) { 648 if ( 649 parseInt(this._info.length / 1000) - 650 this._parse_current_kilometer > 651 options.gpx_options.kilometer_point_options 652 .kilometer_point_intervall - 653 1 654 ) { 655 this._parse_current_kilometer = parseInt( 656 this._info.length / 1000 657 ); 658 var marker = new L.circleMarker(ll, { 659 radius: 660 options.gpx_options.kilometer_point_options 661 .kilometer_point_radius, 662 stroke: false, 663 fillColor: 664 options.gpx_options.kilometer_point_options 665 .kilometer_point_color, 666 fillOpacity: 1, 667 }).bindTooltip(this._parse_current_kilometer.toString(), { 668 direction: "center", 669 permanent: true, 670 interactive: true, 671 className: "kilometer_tooltip", 672 }); 673 kilometer_point_layers.push(marker); 674 } 675 } 676 // Mile Point 677 if (options.gpx_options.show_mile_point) { 678 if ( 679 parseInt(this.to_miles(this._info.length) / 1000) - 680 this._parse_current_mile > 681 options.gpx_options.mile_point_options.mile_intervall 682 ) { 683 this._parse_current_mile = parseInt( 684 this.to_miles(this._info.length) / 1000 685 ); 686 var marker = new L.circleMarker(ll, { 687 radius: 688 options.gpx_options.mile_point_options.mile_point_radius, 689 stroke: false, 690 fillColor: 691 options.gpx_options.mile_point_options.mile_point_color, 692 fillOpacity: 1, 693 }).bindTooltip(this._parse_current_mile.toString(), { 694 direction: "center", 695 permanent: true, 696 interactive: true, 697 className: "mile_tooltip", 698 }); 699 mile_point_layers.push(marker); 700 } 701 } 702 } else { 703 this._parse_current_kilometer = parseInt(this._info.length / 1000); 704 this._parse_current_mile = parseInt(this._info.length / 1000); 705 706 // Append style element for the tooltip of the points 707 var element = document.createElement("style"); 708 document.head.appendChild(element); 709 var sheet = element.sheet; 710 var styles = ""; 711 styles += ".kilometer_tooltip, .mile_tooltip {"; 712 styles += "background: none!important;"; 713 styles += "border: none!important;"; 714 styles += "font-weight: 900!important;"; 715 styles += "font-size: larger!important;"; 716 styles += "box-shadow: none!important;"; 717 styles += "}"; 718 sheet.insertRule(styles, 0); 719 var styles_kilometer = ".kilometer_tooltip {"; 720 styles_kilometer += 721 "color: " + 722 options.gpx_options.kilometer_point_options 723 .kilometer_point_color_text + 724 ";"; 725 styles_kilometer += "}"; 726 sheet.insertRule(styles_kilometer, 0); 727 var styles_mile = ".mile_tooltip {"; 728 styles_mile += 729 "color: " + 730 options.gpx_options.mile_point_options.mile_point_color_text + 731 ";"; 732 styles_mile += "}"; 733 sheet.insertRule(styles_mile, 0); 734 } 735 } 736 737 var t = ll.meta.ele - last.meta.ele; 738 if (t > 0) { 739 this._info.elevation.gain += t; 740 } else { 741 this._info.elevation.loss += Math.abs(t); 742 } 743 744 t = Math.abs(ll.meta.time - last.meta.time); 745 this._info.duration.total += t; 746 if (t < options.max_point_interval) { 747 this._info.duration.moving += t; 748 } 749 } else if (this._info.duration.start == null) { 750 this._info.duration.start = ll.meta.time; 751 } 752 753 last = ll; 754 coords.push(ll); 755 } 756 757 if (kilometer_point_layers.length > 1) { 758 _this.addLayer(new L.FeatureGroup(kilometer_point_layers)); 759 } 760 761 if (mile_point_layers.length > 1) { 762 _this.addLayer(new L.FeatureGroup(mile_point_layers)); 763 } 764 765 // add track 766 var l = new L.Polyline( 767 coords, 768 this._extract_styling(line, polyline_options, options.polyline_options) 769 ); 770 this.fire("addline", { line: l, element: line }); 771 layers.push(l); 772 773 if ( 774 options.marker_options.startIcon || 775 options.marker_options.startIconUrl 776 ) { 777 // add start pin 778 var marker = new L.Marker(coords[0], { 779 clickable: options.marker_options.clickable, 780 icon: 781 options.marker_options.startIcon || 782 new L.GPXTrackIcon({ iconUrl: options.marker_options.startIconUrl }), 783 }); 784 this.fire("addpoint", { 785 point: marker, 786 point_type: "start", 787 element: el[0], 788 }); 789 layers.push(marker); 790 } 791 792 if (options.marker_options.endIcon || options.marker_options.endIconUrl) { 793 // add end pin 794 var marker = new L.Marker(coords[coords.length - 1], { 795 clickable: options.marker_options.clickable, 796 icon: 797 options.marker_options.endIcon || 798 new L.GPXTrackIcon({ iconUrl: options.marker_options.endIconUrl }), 799 }); 800 this.fire("addpoint", { 801 point: marker, 802 point_type: "end", 803 element: el[el.length - 1], 804 }); 805 layers.push(marker); 806 } 807 808 // add named markers 809 for (var i = 0; i < markers.length; i++) { 810 var marker = new L.Marker(markers[i].coords, {
811 clickable: options.marker_options.clickable, 812 title: markers[i].label, 813 icon: markers[i].icon, 814 }); 815 this.fire("addpoint", { 816 point: marker, 817 point_type: "label", 818 element: markers[i].element, 819 }); 820 layers.push(marker); 821 } 822 823 return layers; 824 }, 825 826 _extract_styling: function (el, base, overrides) { 827 var style = this._merge_objs(_DEFAULT_POLYLINE_OPTS, base); 828 var e = el.getElementsByTagNameNS(_GPX_STYLE_NS, "line"); 829 if (e.length > 0) { 830 var _ = e[0].getElementsByTagName("color"); 831 if (_.length > 0) style.color = "#" + _[0].textContent; 832 var _ = e[0].getElementsByTagName("opacity"); 833 if (_.length > 0) style.opacity = _[0].textContent; 834 var _ = e[0].getElementsByTagName("weight"); 835 if (_.length > 0) style.weight = _[0].textContent; 836 var _ = e[0].getElementsByTagName("linecap"); 837 if (_.length > 0) style.lineCap = _[0].textContent; 838 } 839 return this._merge_objs(style, overrides); 840 }, 841 842 _dist2d: function (a, b) { 843 var R = 6371000; 844 var dLat = this._deg2rad(b.lat - a.lat); 845 var dLon = this._deg2rad(b.lng - a.lng); 846 var r = 847 Math.sin(dLat / 2) * Math.sin(dLat / 2) + 848 Math.cos(this._deg2rad(a.lat)) * 849 Math.cos(this._deg2rad(b.lat)) * 850 Math.sin(dLon / 2) * 851 Math.sin(dLon / 2); 852 var c = 2 * Math.atan2(Math.sqrt(r), Math.sqrt(1 - r)); 853 var d = R * c; 854 return d; 855 }, 856 857 _dist3d: function (a, b) { 858 var planar = this._dist2d(a, b); 859 var height = Math.abs(b.meta.ele - a.meta.ele); 860 return Math.sqrt(Math.pow(planar, 2) + Math.pow(height, 2)); 861 }, 862 863 _deg2rad: function (deg) { 864 return (deg * Math.PI) / 180; 865 }, 866}); 867 868if (typeof module === "object" && typeof module.exports === "object") { 869 module.exports = L; 870} else if (typeof define === "function" && define.amd) { 871 define(L); 872}
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.