1function _init_leaflet_linear_measurement () { 2 3 L.Control.LinearMeasurement = L.Control.extend({ 4 5 options: { 6 position: 'topleft', 7 unitSystem: 'imperial', // imperial | metric 8 color: '#4D90FE', 9 contrastingColor: '#fff', 10 show_last_node: false, 11 show_azimut: false 12 }, 13 14 clickSpeed: 200, 15 16 onAdd: function (map) { 17 var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'), 18 link = L.DomUtil.create('a', 'icon-ruler', container), 19 map_container = map.getContainer(), 20 me = this; 21 22 link.href = '#'; 23 link.title = 'Toggle measurement tool'; 24 25 L.DomEvent.on(link, 'click', L.DomEvent.stop).on(link, 'click', function () { 26 if (L.DomUtil.hasClass(link, 'icon-active')) { 27 me.resetRuler(!!me.mainLayer); 28 L.DomUtil.removeClass(link, 'icon-active'); 29 L.DomUtil.removeClass(map_container, 'ruler-map'); 30 31 } else { 32 me.initRuler(); 33 L.DomUtil.addClass(link, 'icon-active'); 34 L.DomUtil.addClass(map_container, 'ruler-map'); 35 } 36 }); 37 38 function contrastingColor(color) { 39 return (luma(color) >= 165) ? '000' : 'fff'; 40 } 41 42 function luma(color) { 43 var rgb = (typeof color === 'string') ? hexToRGBArray(color) : color; 44 return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]); // SMPTE C, Rec. 709 weightings 45 } 46 47 function hexToRGBArray(color) { 48 if (color.length === 3) 49 color = color.charAt(0) + color.charAt(0) + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2); 50 else if (color.length !== 6) 51 throw ('Invalid hex color: ' + color); 52 var rgb = []; 53 for (var i = 0; i <= 2; i++) 54 rgb[i] = parseInt(color.substr(i * 2, 2), 16); 55 return rgb; 56 } 57 58 if (this.options.color && this.options.color.indexOf('#') === -1) { 59 this.options.color = '#' + this.options.color; 60 } else if (!this.options.color) { 61 this.options.color = '#4D90FE'; 62 } 63 64 var originalColor = this.options.color.replace('#', ''); 65 66 this.options.contrastingColor = '#' + contrastingColor(originalColor); 67 68 return container; 69 }, 70 71 onRemove: function (map) { 72 this.resetRuler(!!this.mainLayer); 73 }, 74 75 initRuler: function () { 76 var me = this, 77 map = this._map; 78 79 this.mainLayer = L.featureGroup(); 80 this.mainLayer.addTo(this._map); 81 82 map.touchZoom.disable(); 83 map.doubleClickZoom.disable(); 84 map.boxZoom.disable(); 85 map.keyboard.disable(); 86 87 if (map.tap) { 88 map.tap.disable(); 89 } 90 91 this.dblClickEventFn = function (e) { 92 L.DomEvent.stop(e); 93 }; 94 95 this.clickEventFn = function (e) { 96 if (me.clickHandle) { 97 clearTimeout(me.clickHandle); 98 me.clickHandle = 0; 99 100 if (me.options.show_last_node) { 101 me.preClick(e); 102 me.getMouseClickHandler(e); 103 } 104 105 me.getDblClickHandler(e); 106 107 } else { 108 me.preClick(e); 109 110 me.clickHandle = setTimeout(function () { 111 me.getMouseClickHandler(e); 112 me.clickHandle = 0; 113 114 }, me.clickSpeed); 115 } 116 }; 117 118 this.moveEventFn = function (e) { 119 if (!me.clickHandle) { 120 me.getMouseMoveHandler(e); 121 } 122 }; 123 124 map.on('click', this.clickEventFn, this); 125 map.on('mousemove', this.moveEventFn, this); 126 127 this.resetRuler(); 128 }, 129 130 initLayer: function () { 131 this.layer = L.featureGroup(); 132 this.layer.addTo(this.mainLayer); 133 this.layer.on('selected', this.layerSelected); 134 this.layer.on('click', this.clickEventFn, this); 135 }, 136 137 resetRuler: function (resetLayer) { 138 var map = this._map; 139
140 if (resetLayer) { 141 map.off('click', this.clickEventFn, this); 142 map.off('mousemove', this.moveEventFn, this); 143 144 if (this.mainLayer) { 145 this._map.removeLayer(this.mainLayer); 146 } 147 148 this.mainLayer = null; 149 150 this._map.touchZoom.enable(); 151 this._map.boxZoom.enable(); 152 this._map.keyboard.enable(); 153 154 if (this._map.tap) { 155 this._map.tap.enable(); 156 } 157 } 158 159 this.layer = null; 160 this.prevLatlng = null; 161 this.poly = null; 162 this.multi = null; 163 this.latlngs = null; 164 this.latlngsList = []; 165 this.sum = 0; 166 this.distance = 0; 167 this.separation = 1; 168 this.last = 0; 169 this.fixedLast = 0; 170 this.totalIcon = null; 171 this.total = null; 172 this.lastCircle = null; 173 174 /* Leaflet return distances in meters */ 175 this.UNIT_CONV = 1000; 176 this.SUB_UNIT_CONV = 1000; 177 this.UNIT = 'km'; 178 this.SUB_UNIT = 'm'; 179 180 if (this.options.unitSystem === 'imperial') { 181 this.UNIT_CONV = 1609.344; 182 this.SUB_UNIT_CONV = 5280; 183 this.UNIT = 'mi'; 184 this.SUB_UNIT = 'ft'; 185 } 186 187 this.measure = { 188 scalar: 0, 189 unit: this.SUB_UNIT 190 }; 191 }, 192 193 cleanUpMarkers: function (fixed) { 194 var layer = this.layer; 195 196 if (layer) { 197 layer.eachLayer(function (l) { 198 if (l.options && l.options.type === 'tmp') { 199 if (fixed) { 200 l.options.type = 'fixed'; 201 } else { 202 layer.removeLayer(l); 203 } 204 } 205 }); 206 } 207 }, 208 209 cleanUpFixed: function () { 210 var layer = this.layer; 211 212 if (layer) { 213 layer.eachLayer(function (l) { 214 if (l.options && (l.options.type === 'fixed')) { 215 layer.removeLayer(l); 216 } 217 }); 218 } 219 }, 220 221 convertDots: function () { 222 var me = this, 223 layer = this.layer; 224 225 if (layer) { 226 layer.eachLayer(function (l) { 227 if (l.options && (l.options.type === 'dot')) { 228 229 var m = l.options.marker, 230 i = m ? m.options.icon.options : null, 231 il = i ? i.html : ''; 232 233 if (il && il.indexOf(me.measure.unit) === -1) { 234 var str = l.options.label, 235 s = str.split(' '), 236 e = parseFloat(s[0]), 237 u = s[1], 238 label = ''; 239 240 if (l.options.label.indexOf(me.measure.unit) !== -1) { 241 label = l.options.label; 242 243 } else if (u === me.UNIT) { 244 label = (e * me.SUB_UNIT_CONV).toFixed(2) + ' ' + me.SUB_UNIT; 245 246 } else if (u === me.SUB_UNIT) { 247 label = (e / me.SUB_UNIT_CONV).toFixed(2) + ' ' + me.UNIT; 248 } 249 250 var cicon = L.divIcon({ 251 className: 'total-popup-label', 252 html: label 253 }); 254 255 m.setIcon(cicon); 256 } 257 } 258 }); 259 } 260 }, 261 262 displayMarkers: function (latlngs, multi, sum) { 263 var x, y, label, ratio, p, 264 latlng = latlngs[latlngs.length - 1], 265 prevLatlng = latlngs[0], 266 original = prevLatlng.distanceTo(latlng) / this.UNIT_CONV, 267 dis = original; 268 269 var p2 = this._map.latLngToContainerPoint(latlng), 270 p1 = this._map.latLngToContainerPoint(prevLatlng), 271 unit = 1; 272 273 if (this.measure.unit === this.SUB_UNIT) { 274 unit = this.SUB_UNIT_CONV; 275 dis = dis * unit; 276 } 277 278 var t = (sum * unit) + dis, 279 qu = sum * unit; 280 281 for (var q = Math.floor(qu); q < t; q++) { 282 ratio = (t - q) / dis; 283 284 if (q % this.separation || q < qu) { 285 continue; 286 } 287 288 x = (p2.x - ratio * (p2.x - p1.x)); 289 y = (p2.y - ratio * (p2.y - p1.y)); 290 291 p = L.point(x, y); 292 293 /* render a circle spaced by separation */ 294 295 latlng = this._map.containerPointToLatLng(p); 296 297 label = (q + ' ' + this.measure.unit); 298 299 this.renderCircle(latlng, 0, this.layer, multi ? 'fixed' : 'tmp', label); 300 301 this.last = t; 302 } 303 304 return original; 305 }, 306 307 renderCircle: function (latLng, radius, layer, type, label) { 308 var color = this.options.color, 309 lineColor = this.options.color, 310 azimut = '', 311 nodeCls = ''; 312 313 type = type || 'circle'; 314 315 let linesHTML = []; 316 317 var options = { 318 color: lineColor, 319 fillOpacity: 1, 320 opacity: 1, 321 fill: true, 322 type: type 323 }; 324 325 var a = this.prevLatlng ? this._map.latLngToContainerPoint(this.prevLatlng) : null, 326 b = this._map.latLngToContainerPoint(latLng); 327 328 if (type === 'dot') { 329 nodeCls = 'node-label'; 330 331 if (a && this.options.show_azimut) { 332 azimut = ' <span class="azimut"> ' + this.lastAzimut + '°</span>'; 333 } 334 } 335 336 let p_latLng = this._map.containerPointToLatLng(b); 337 338 if (label) { 339 var cicon = L.divIcon({ 340 className: 'total-popup-label ' + nodeCls, 341 html: '<span style="color: ' + color + ';">' + label + azimut + '</span>' 342 }); 343 344 options.icon = cicon; 345 options.marker = L.marker(p_latLng, { icon: cicon, type: type }).addTo(layer); 346 options.label = label; 347 } 348 349 var circle = L.circleMarker(latLng, options); 350 351 circle.setRadius(3); 352 circle.addTo(layer); 353
354 return circle; 355 }, 356 357 getAzimut: function (a, b) { 358 var deg = 0; 359 360 if (a && b) { 361 deg = parseInt(Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI); 362 363 if (deg > 0) { 364 deg += 90; 365 } else if (deg < 0) { 366 deg = Math.abs(deg); 367 if (deg <= 90) { 368 deg = 90 - deg; 369 } else { 370 deg = 360 - (deg - 90); 371 } 372 } 373 } 374 375 this.lastAzimut = deg; 376 377 return deg; 378 }, 379 380 renderPolyline: function (latLngs, dashArray, layer) { 381 var poly = L.polyline(latLngs, { 382 color: this.options.color, 383 weight: 2, 384 opacity: 1, 385 dashArray: dashArray 386 }); 387 388 poly.addTo(layer); 389 390 return poly; 391 }, 392 393 renderMultiPolyline: function (latLngs, dashArray, layer) { 394 /* Leaflet version 1+ delegated the concept of multi-poly-line to the polyline */ 395 var multi; 396 397 if (L.version.startsWith('0')) { 398 multi = L.multiPolyline(latLngs, { 399 color: this.options.color, 400 weight: 2, 401 opacity: 1, 402 dashArray: dashArray 403 }); 404 } else { 405 multi = L.polyline(latLngs, { 406 color: this.options.color, 407 weight: 2, 408 opacity: 1, 409 dashArray: dashArray 410 }); 411 } 412 413 multi.addTo(layer); 414 415 return multi; 416 }, 417 418 formatDistance: function (distance, precision) { 419 var s = L.Util.formatNum((distance < 1 ? distance * parseFloat(this.SUB_UNIT_CONV) : distance), precision), 420 u = (distance < 1 ? this.SUB_UNIT : this.UNIT); 421 422 return { scalar: s, unit: u }; 423 }, 424 425 hasClass: function (target, classes) { 426 var fn = L.DomUtil.hasClass; 427 428 for (var i in classes) { 429 if (fn(target, classes[i])) { 430 return true; 431 } 432 } 433 434 return false; 435 }, 436 437 preClick: function (e) { 438 var me = this, 439 target = e.originalEvent.target; 440 441 if (this.hasClass(target, ['leaflet-popup', 'total-popup-content'])) { 442 return; 443 } 444 445 if (!me.layer) { 446 me.initLayer(); 447 } 448 449 me.cleanUpMarkers(true); 450 451 me.fixedLast = me.last; 452 me.prevLatlng = e.latlng; 453 me.sum = 0; 454 }, 455 456 getMouseClickHandler: function (e) { 457 var me = this; 458 me.fixedLast = me.last; 459 me.sum = 0; 460 461 if (me.poly) { 462 me.latlngsList.push(me.latlngs); 463 464 if (!me.multi) { 465 me.multi = me.renderMultiPolyline(me.latlngsList, '5 5', me.layer, 'dot'); 466 } else { 467 me.multi.setLatLngs(me.latlngsList); 468 } 469 } 470 471 var o, dis; 472 for (var l in me.latlngsList) { 473 o = me.latlngsList[l]; 474 me.sum += o[0].distanceTo(o[1]) / me.UNIT_CONV; 475 } 476 477 if (me.measure.unit === this.SUB_UNIT) { 478 dis = me.sum * me.SUB_UNIT_CONV; 479 } else { 480 dis = me.sum; 481 } 482 483 var s = dis.toFixed(2); 484 485 me.renderCircle(e.latlng, 0, me.layer, 'dot', parseInt(s) ? (s + ' ' + me.measure.unit) : ''); 486 me.prevLatlng = e.latlng; 487 }, 488 489 getMouseMoveHandler: function (e) { 490 var azimut = ''; 491 492 if (this.prevLatlng) { 493 var latLng = e.latlng; 494 495 this.latlngs = [this.prevLatlng, e.latlng]; 496 497 if (!this.poly) { 498 this.poly = this.renderPolyline(this.latlngs, '5 5', this.layer); 499 } else { 500 this.poly.setLatLngs(this.latlngs); 501 } 502 503 /* Distance in miles/meters */ 504 this.distance = parseFloat(this.prevLatlng.distanceTo(e.latlng)) / this.UNIT_CONV; 505 506 /* scalar and unit */ 507 this.measure = this.formatDistance(this.distance + this.sum, 2); 508 509 var a = this.prevLatlng ? this._map.latLngToContainerPoint(this.prevLatlng) : null, 510 b = this._map.latLngToContainerPoint(latLng); 511 512 if (a && this.options.show_azimut) { 513 var style = 'color: ' + this.options.contrastingColor + ';'; 514 azimut = ' <span class="azimut azimut-final" style="' + style + '"> ' + this.getAzimut(a, b) + '°</span>'; 515 } 516 517 /* tooltip with total distance */ 518 var label = this.measure.scalar + ' ' + this.measure.unit, 519 html = '<span class="total-popup-content" style="background-color:' + this.options.color + '; color: ' + this.options.contrastingColor + '">' + label + azimut + '</span>'; 520 521 if (!this.total) { 522 this.totalIcon = L.divIcon({ className: 'total-popup', html: html }); 523 524 this.total = L.marker(e.latlng, { 525 icon: this.totalIcon, 526 clickable: true 527 }).addTo(this.layer); 528 529 } else { 530 this.totalIcon = L.divIcon({ className: 'total-popup', html: html }); 531 this.total.setLatLng(e.latlng); 532 this.total.setIcon(this.totalIcon); 533 } 534 535 /* Rules for separation using only distance criteria */ 536 var ds = this.measure.scalar, 537 old_separation = this.separation, 538 digits = parseInt(ds).toString().length, 539 num = Math.pow(10, digits), 540 real = ds > (num / 2) ? (num / 10) : (num / 20), 541 dimension = 0; 542 543 this.separation = real; 544 545 /* If there is a change in the segment length we want to re-space 546 the dots on the multi line */ 547 if (old_separation !== this.separation && this.fixedLast) { 548 this.cleanUpMarkers(); 549 this.cleanUpFixed(); 550 551 var multi_latlngs = this.multi.getLatLngs();
552 553 for (var s in multi_latlngs) { 554 dimension += this.displayMarkers.apply(this, [multi_latlngs[s], true, dimension]); 555 } 556 557 this.displayMarkers.apply(this, [this.poly.getLatLngs(), false, this.sum]); 558 559 /* Review that the dots are in correct units */ 560 this.convertDots(); 561 562 } else { 563 this.cleanUpMarkers(); 564 this.displayMarkers.apply(this, [this.poly.getLatLngs(), false, this.sum]); 565 } 566 } 567 }, 568 569 getDblClickHandler: function (e) { 570 var azimut = '', 571 me = this; 572 573 if (!this.total) { 574 return; 575 } 576 577 this.layer.off('click'); 578 579 L.DomEvent.stop(e); 580 581 if (this.options.show_azimut) { 582 var style = 'color: ' + this.options.contrastingColor + ';'; 583 azimut = ' <span class="azimut azimut-final" style="' + style + '"> ' + this.lastAzimut + '°</span>'; 584 } 585 586 var workspace = this.layer, 587 label = this.measure.scalar + ' ' + this.measure.unit + ' ', 588 total_scalar = this.measure.unit === this.SUB_UNIT ? this.measure.scalar / this.UNIT_CONV : this.measure.scalar, 589 total_latlng = this.total.getLatLng(), 590 total_label = this.total, 591 html = [ 592 '<div class="total-popup-content" style="background-color:' + this.options.color + '; color: ' + this.options.contrastingColor + '">' + label + azimut, 593 ' <svg class="close" viewbox="0 0 45 35">', 594 ' <path style="stroke: ' + this.options.contrastingColor + '" class="close" d="M 10,10 L 30,30 M 30,10 L 10,30" />', 595 ' </svg>', 596 '</div>' 597 ].join(''); 598 599 this.totalIcon = L.divIcon({ className: 'total-popup', html: html }); 600 this.total.setIcon(this.totalIcon); 601 602 var data = { 603 total: this.measure, 604 total_label: total_label, 605 unit: this.UNIT_CONV, 606 sub_unit: this.SUB_UNIT_CONV 607 }; 608 609 var fireSelected = function (e) { 610 if (L.DomUtil.hasClass(e.originalEvent.target, 'close')) { 611 me.mainLayer.removeLayer(workspace); 612 } else { 613 workspace.fireEvent('selected', data); 614 } 615 }; 616 617 workspace.on('click', fireSelected); 618 workspace.fireEvent('selected', data); 619 620 this.resetRuler(false); 621 }, 622 623 purgeLayers: function (layers) { 624 for (var i in layers) { 625 if (layers[i]) { 626 this.layer.removeLayer(layers[i]); 627 } 628 } 629 }, 630 631 layerSelected: function (e) { } 632 }); 633 634}; 635 636export { _init_leaflet_linear_measurement }
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.