PageSourceSearch

https://brianhoskins.uk/_content/LeafletForBlazor/plugins/Leaflet.LinearMeasurement.27ix8o99a7.js

js brianhoskins.uk collected 2026-10-04 03:53:35 UTC 21,596 bytes, 636 lines download raw bytes

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 + '&deg;</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 + '"> &nbsp; ' + this.getAzimut(a, b) + '&deg;</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 + '"> &nbsp; ' + this.lastAzimut + '&deg;</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.