PageSourceSearch

https://naparbideak.com/Leaflet/leaflet.polylineDecorator.js

js naparbideak.com collected 2026-09-27 12:41:30 UTC 15,983 bytes, 478 lines download raw bytes

vendor: 15,983 bytes, lines 1-478
1(function (global, factory) {
2	typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('leaflet')) :
3	typeof define === 'function' && define.amd ? define(['leaflet'], factory) :
4	(factory(global.L));
5}(this, (function (L$1) { 'use strict';
6
7L$1 = L$1 && L$1.hasOwnProperty('default') ? L$1['default'] : L$1;
8
9// functional re-impl of L.Point.distanceTo,
10// with no dependency on Leaflet for easier testing
11function pointDistance(ptA, ptB) {
12    var x = ptB.x - ptA.x;
13    var y = ptB.y - ptA.y;
14    return Math.sqrt(x * x + y * y);
15}
16
17var computeSegmentHeading = function computeSegmentHeading(a, b) {
18    return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI + 90 + 360) % 360;
19};
20
21var asRatioToPathLength = function asRatioToPathLength(_ref, totalPathLength) {
22    var value = _ref.value,
23        isInPixels = _ref.isInPixels;
24    return isInPixels ? value / totalPathLength : value;
25};
26
27function parseRelativeOrAbsoluteValue(value) {
28    if (typeof value === 'string' && value.indexOf('%') !== -1) {
29        return {
30            value: parseFloat(value) / 100,
31            isInPixels: false
32        };
33    }
34    var parsedValue = value ? parseFloat(value) : 0;
35    return {
36        value: parsedValue,
37        isInPixels: parsedValue > 0
38    };
39}
40
41var pointsEqual = function pointsEqual(a, b) {
42    return a.x === b.x && a.y === b.y;
43};
44
45function pointsToSegments(pts) {
46    return pts.reduce(function (segments, b, idx, points) {
47        // this test skips same adjacent points
48        if (idx > 0 && !pointsEqual(b, points[idx - 1])) {
49            var a = points[idx - 1];
50            var distA = segments.length > 0 ? segments[segments.length - 1].distB : 0;
51            var distAB = pointDistance(a, b);
52            segments.push({
53                a: a,
54                b: b,
55                distA: distA,
56                distB: distA + distAB,
57                heading: computeSegmentHeading(a, b)
58            });
59        }
60        return segments;
61    }, []);
62}
63
64function projectPatternOnPointPath(pts, pattern) {
65    // 1. split the path into segment infos
66    var segments = pointsToSegments(pts);
67    var nbSegments = segments.length;
68    if (nbSegments === 0) {
69        return [];
70    }
71
72    var totalPathLength = segments[nbSegments - 1].distB;
73
74    var offset = asRatioToPathLength(pattern.offset, totalPathLength);
75    var endOffset = asRatioToPathLength(pattern.endOffset, totalPathLength);
76    var repeat = asRatioToPathLength(pattern.repeat, totalPathLength);
77
78    var repeatIntervalPixels = totalPathLength * repeat;
79    var startOffsetPixels = offset > 0 ? totalPathLength * offset : 0;
80    var endOffsetPixels = endOffset > 0 ? totalPathLength * endOffset : 0;
81
82    // 2. generate the positions of the pattern as offsets from the path start
83    var positionOffsets = [];
84    var positionOffset = startOffsetPixels;
85    do {
86        positionOffsets.push(positionOffset);
87        positionOffset += repeatIntervalPixels;
88    } while (repeatIntervalPixels > 0 && positionOffset < totalPathLength - endOffsetPixels);
89
90    // 3. projects offsets to segments
91    var segmentIndex = 0;
92    var segment = segments[0];
93    return positionOffsets.map(function (positionOffset) {
94        // find the segment matching the offset,
95        // starting from the previous one as offsets are ordered
96        while (positionOffset > segment.distB && segmentIndex < nbSegments - 1) {
97            segmentIndex++;
98            segment = segments[segmentIndex];
99        }
100
101        var segmentRatio = (positionOffset - segment.distA) / (segment.distB - segment.distA);
102        return {
103            pt: interpolateBetweenPoints(segment.a, segment.b, segmentRatio),
104            heading: segment.heading
105        };
106    });
107}
108
109/**
110* Finds the point which lies on the segment defined by points A and B,
111* at the given ratio of the distance from A to B, by linear interpolation.
112*/
113function interpolateBetweenPoints(ptA, ptB, ratio) {
114    if (ptB.x !== ptA.x) {
115        return {
116            x: ptA.x + ratio * (ptB.x - ptA.x),
117            y: ptA.y + ratio * (ptB.y - ptA.y)
118        };
119    }
120    // special case where points lie on the same vertical axis
121    return {
122        x: ptA.x,
123        y: ptA.y + (ptB.y - ptA.y) * ratio
124    };
125}
126
127(function() {
128    // save these original methods before they are overwritten
129    var proto_initIcon = L.Marker.prototype._initIcon;
130    var proto_setPos = L.Marker.prototype._setPos;
131
132    var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
133
134    L.Marker.addInitHook(function () {
135        var iconOptions = this.options.icon && this.options.icon.options;
136        var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
137        if (iconAnchor) {
138            iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
139        }
140        this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;
141        this.options.rotationAngle = this.options.rotationAngle || 0;
142
143        // Ensure marker keeps rotated during dragging
144        this.on('drag', function(e) { e.target._applyRotation(); });
145    });
146
147    L.Marker.include({
148        _initIcon: function() {
149            proto_initIcon.call(this);
150        },
151
152        _setPos: function (pos) {
153            proto_setPos.call(this, pos);
154            this._applyRotation();
155        },
156
157        _applyRotation: function () {
158            if(this.options.rotationAngle) {
159                this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;
160
161                if(oldIE) {
162                    // for IE 9, use the 2D rotation
163                    this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
164                } else {
165                    // for modern browsers, prefer the 3D accelerated version
166                    this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';
167                }
168            }
169        },
170
171        setRotationAngle: function(angle) {
172            this.options.rotationAngle = angle;
173            this.update();
174            return this;
175        },
176
177        setRotationOrigin: function(origin) {
178            this.options.rotationOrigin = origin;
179            this.update();
180            return this;
181        }
182    });
183})();
184
185L$1.Symbol = L$1.Symbol || {};
186
187/**
188* A simple dash symbol, drawn as a Polyline.
189* Can also be used for dots, if 'pixelSize' option is given the 0 value.
190*/
191L$1.Symbol.Dash = L$1.Class.extend({
192    options: {
193        pixelSize: 10,
194        pathOptions: {}
195    },
196
197    initialize: function initialize(options) {
198        L$1.Util.setOptions(this, options);
199        this.options.pathOptions.clickable = false;
200    },
201
202    buildSymbol: function buildSymbol(dirPoint, latLngs, map, index, total) {
203        var opts = this.options;
204        var d2r = Math.PI / 180;
205
206        // for a dot, nothing more to compute
207        if (opts.pixelSize <= 1) {
208            return L$1.polyline([dirPoint.latLng, dirPoint.latLng], opts.pathOptions);
209        }
210
211        var midPoint = map.project(dirPoint.latLng);
212        var angle = -(dirPoint.heading - 90) * d2r;
213        var a = L$1.point(midPoint.x + opts.pixelSize * Math.cos(angle + Math.PI) / 2, midPoint.y + opts.pixelSize * Math.sin(angle) / 2);
214        // compute second point by central symmetry to avoid unecessary cos/sin
215        var b = midPoint.add(midPoint.subtract(a));
216        return L$1.polyline([map.unproject(a), map.unproject(b)], opts.pathOptions);
217    }
218});
219
220L$1.Symbol.dash = function (options) {
221    return new L$1.Symbol.Dash(options);
222};
223
224L$1.Symbol.ArrowHead = L$1.Class.extend({
225    options: {
226        polygon: true,
227        pixelSize: 10,
228        headAngle: 60,
229        pathOptions: {
230            stroke: false,
231            weight: 2
232        }
233    },
234
235    initialize: function initialize(options) {
236        L$1.Util.setOptions(this, options);
237        this.options.pathOptions.clickable = false;
238    },
239
240    buildSymbol: function buildSymbol(dirPoint, latLngs, map, index, total) {
241        return this.options.polygon ? L$1.polygon(this._buildArrowPath(dirPoint, map), this.options.pathOptions) : L$1.polyline(this._buildArrowPath(dirPoint, map), this.options.pathOptions);
242    },
243
244    _buildArrowPath: function _buildArrowPath(dirPoint, map) {
245        var d2r = Math.PI / 180;
246        var tipPoint = map.project(dirPoint.latLng);
247        var direction = -(dirPoint.heading - 90) * d2r;
248        var radianArrowAngle = this.options.headAngle / 2 * d2r;
249
250        var headAngle1 = direction + radianArrowAngle;
251        var headAngle2 = direction - radianArrowAngle;
252        var arrowHead1 = L$1.point(tipPoint.x - this.options.pixelSize * Math.cos(headAngle1), tipPoint.y + this.options.pixelSize * Math.sin(headAngle1));
253        var arrowHead2 = L$1.point(tipPoint.x - this.options.pixelSize * Math.cos(headAngle2), tipPoint.y + this.options.pixelSize * Math.sin(headAngle2));
254
255        return [map.unproject(arrowHead1), dirPoint.latLng, map.unproject(arrowHead2)];
256    }
257});
258
259L$1.Symbol.arrowHead = function (options) {
260    return new L$1.Symbol.ArrowHead(options);
261};
262
263L$1.Symbol.Marker = L$1.Class.extend({
264    options: {
265        markerOptions: {},
266        rotate: false
267    },
268
269    initialize: function initialize(options) {
270        L$1.Util.setOptions(this, options);
271        this.options.markerOptions.clickable = false;
272        this.options.markerOptions.draggable = false;
273    },
274
275    buildSymbol: function buildSymbol(directionPoint, latLngs, map, index, total) {
276        if (this.options.rotate) {
277            this.options.markerOptions.rotationAngle = directionPoint.heading + (this.options.angleCorrection || 0);
278        }
279        return L$1.marker(directionPoint.latLng, this.options.markerOptions);
280    }
281});
282
283L$1.Symbol.marker = function (options) {
284    return new L$1.Symbol.Marker(options);
285};
286
287var isCoord = function isCoord(c) {
288    return c instanceof L$1.LatLng || Array.isArray(c) && c.length === 2 && typeof c[0] === 'number';
289};
290
291var isCoordArray = function isCoordArray(ll) {
292    return Array.isArray(ll) && isCoord(ll[0]);
293};
294
295L$1.PolylineDecorator = L$1.FeatureGroup.extend({
296    options: {
297        patterns: []
298    },
299
300    initialize: function initialize(paths, options) {
301        L$1.FeatureGroup.prototype.initialize.call(this);
302        L$1.Util.setOptions(this, options);
303        this._map = null;
304        this._paths = this._initPaths(paths);
305        this._bounds = this._initBounds();
306        this._patterns = this._initPatterns(this.options.patterns);
307    },
308
309    /**
310    * Deals with all the different cases. input can be one of these types:
311    * array of LatLng, array of 2-number arrays, Polyline, Polygon,
312    * array of one of the previous.
313    */
314    _initPaths: function _initPaths(input, isPolygon) {
315        var _this = this;
316
317        if (isCoordArray(input)) {
318            // Leaflet Polygons don't need the first point to be repeated, but we do
319            var coords = isPolygon ? input.concat([input[0]]) : input;
320            return [coords];
321        }
322        if (input instanceof L$1.Polyline) {
323            // we need some recursivity to support multi-poly*
324            return this._initPaths(input.getLatLngs(), input instanceof L$1.Polygon);
325        }
326        if (Array.isArray(input)) {
327            // flatten everything, we just need coordinate lists to apply patterns
328            return input.reduce(function (flatArray, p) {
329                return flatArray.concat(_this._initPaths(p, isPolygon));
330            }, []);
331        }
332        return [];
333    },
334
335    // parse pattern definitions and precompute some values
336    _initPatterns: function _initPatterns(patternDefs) {
337        return patternDefs.map(this._parsePatternDef);
338    },
339
340    /**
341    * Changes the patterns used by this decorator
342    * and redraws the new one.
343    */
344    setPatterns: function setPatterns(patterns) {
345        this.options.patterns = patterns;
346        this._patterns = this._initPatterns(this.options.patterns);
347        this.redraw();
348    },
349
350    /**
351    * Changes the patterns used by this decorator
352    * and redraws the new one.
353    */
354    setPaths: function setPaths(paths) {
355        this._paths = this._initPaths(paths);
356        this._bounds = this._initBounds();
357        this.redraw();
358    },
359
360    /**
361    * Parse the pattern definition
362    */
363    _parsePatternDef: function _parsePatternDef(patternDef, latLngs) {
364        return {
365            symbolFactory: patternDef.symbol,
366            // Parse offset and repeat values, managing the two cases:
367            // absolute (in pixels) or relative (in percentage of the polyline length)
368            offset: parseRelativeOrAbsoluteValue(patternDef.offset),
369            endOffset: parseRelativeOrAbsoluteValue(patternDef.endOffset),
370            repeat: parseRelativeOrAbsoluteValue(patternDef.repeat)
371        };
372    },
373
374    onAdd: function onAdd(map) {
375        this._map = map;
376        this._draw();
377        this._map.on('moveend', this.redraw, this);
378    },
379
380    onRemove: function onRemove(map) {
381        this._map.off('moveend', this.redraw, this);
382        this._map = null;
383        L$1.FeatureGroup.prototype.onRemove.call(this, map);
384    },
385
386    /**
387    * As real pattern bounds depends on map zoom and bounds,
388    * we just compute the total bounds of all paths decorated by this instance.
389    */
390    _initBounds: function _initBounds() {
391        var allPathCoords = this._paths.reduce(function (acc, path) {
392            return acc.concat(path);
393        }, []);
394        return L$1.latLngBounds(allPathCoords);
395    },
396
397    getBounds: function getBounds() {
398        return this._bounds;
399    },
400
401    /**
402    * Returns an array of ILayers object
403    */
404    _buildSymbols: function _buildSymbols(latLngs, symbolFactory, directionPoints) {
405        var _this2 = this;
406
407        return directionPoints.map(function (directionPoint, i) {
408            return symbolFactory.buildSymbol(directionPoint, latLngs, _this2._map, i, directionPoints.length);
409        });
410    },
411
412    /**
413    * Compute pairs of LatLng and heading angle,
414    * that define positions and directions of the symbols on the path
415    */
416    _getDirectionPoints: function _getDirectionPoints(latLngs, pattern) {
417        var _this3 = this;
418
419        if (latLngs.length < 2) {
420            return [];
421        }
422        var pathAsPoints = latLngs.map(function (latLng) {
423            return _this3._map.project(latLng);
424        });
425        return projectPatternOnPointPath(pathAsPoints, pattern).map(function (point) {
426            return {
427                latLng: _this3._map.unproject(L$1.point(point.pt)),
428                heading: point.heading
429            };
430        });
431    },
432
433    redraw: function redraw() {
434        if (!this._map) {
435            return;
436        }
437        this.clearLayers();
438        this._draw();
439    },
440
441    /**
442    * Returns all symbols for a given pattern as an array of FeatureGroup
443    */
444    _getPatternLayers: function _getPatternLayers(pattern) {
445        var _this4 = this;
446
447        var mapBounds = this._map.getBounds().pad(0.1);
448        return this._paths.map(function (path) {
449            var directionPoints = _this4._getDirectionPoints(path, pattern)
450            // filter out invisible points
451            .filter(function (point) {
452                return mapBounds.contains(point.latLng);
453            });
454            return L$1.featureGroup(_this4._buildSymbols(path, pattern.symbolFactory, directionPoints));
455        });
456    },
457
458    /**
459    * Draw all patterns
460    */
461    _draw: function _draw() {
462        var _this5 = this;
463
464        this._patterns.map(function (pattern) {
465            return _this5._getPatternLayers(pattern);
466        }).forEach(function (layers) {
467            _this5.addLayer(L$1.featureGroup(layers));
468        });
469    }
470});
471/*
472 * Allows compact syntax to be used
473 */
474L$1.polylineDecorator = function (paths, options) {
475    return new L$1.PolylineDecorator(paths, options);
476};
477
478})));

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.