leaflet-polylinedecorator
Advanced tools
Comparing version 1.3.0 to 1.3.2
{ | ||
"name": "leaflet-polylinedecorator", | ||
"main": "leaflet.polylineDecorator.js", | ||
"version": "1.3.0", | ||
"version": "1.3.2", | ||
"authors": [ | ||
@@ -6,0 +6,0 @@ "Benjamin Becquet" |
@@ -7,142 +7,117 @@ (function (global, factory) { | ||
function computeAngle(a, b) { | ||
var computeSegmentHeading = function computeSegmentHeading(a, b) { | ||
return Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI + 90; | ||
} | ||
}; | ||
function getPointPathPixelLength(pts) { | ||
var getPointPathPixelLength = function getPointPathPixelLength(pts) { | ||
return pts.reduce(function (distance, pt, i) { | ||
return i === 0 ? 0 : distance + pt.distanceTo(pts[i - 1]); | ||
}, 0); | ||
} | ||
}; | ||
function getPixelLength(pl, map) { | ||
var latLngs = pl instanceof L.Polyline ? pl.getLatLngs() : pl; | ||
var points = latLngs.map(function (latLng) { | ||
return map.project(latLng); | ||
}); | ||
return getPointPathPixelLength(points); | ||
var asRatioToPathLength = function asRatioToPathLength(_ref, totalPathLength) { | ||
var value = _ref.value, | ||
isInPixels = _ref.isInPixels; | ||
return isInPixels ? value / totalPathLength : value; | ||
}; | ||
function parseRelativeOrAbsoluteValue(value) { | ||
if (typeof value === 'string' && value.indexOf('%') !== -1) { | ||
return { | ||
value: parseFloat(value) / 100, | ||
isInPixels: false | ||
}; | ||
} | ||
var parsedValue = value ? parseFloat(value) : 0; | ||
return { | ||
value: parsedValue, | ||
isInPixels: parsedValue > 0 | ||
}; | ||
} | ||
/** | ||
* path: array of L.LatLng | ||
* ratios is an object with the following fields: | ||
* offset: the ratio of the total pixel length where the pattern will start | ||
* endOffset: the ratio of the total pixel length where the pattern will end | ||
* repeat: the ratio of the total pixel length between two points of the pattern | ||
* map: the map, to access the current projection state | ||
*/ | ||
function projectPatternOnPath(path, ratios, map) { | ||
var pathAsPoints = path.map(function (latLng) { | ||
function projectPatternOnPath(latLngs, pattern, map) { | ||
var pathAsPoints = latLngs.map(function (latLng) { | ||
return map.project(latLng); | ||
}); | ||
var pathPixelLength = getPointPathPixelLength(pathAsPoints); | ||
// project the pattern as pixel points | ||
var pattern = projectPatternOnPointPath(pathAsPoints, ratios); | ||
// and convert it to latlngs; | ||
pattern.forEach(function (point) { | ||
point.latLng = map.unproject(point.pt); | ||
var ratios = { | ||
offset: asRatioToPathLength(pattern.offset, pathPixelLength), | ||
endOffset: asRatioToPathLength(pattern.endOffset, pathPixelLength), | ||
repeat: asRatioToPathLength(pattern.repeat, pathPixelLength) | ||
}; | ||
return projectPatternOnPointPath(pathAsPoints, ratios).map(function (point) { | ||
return { | ||
latLng: map.unproject(point.pt), | ||
heading: point.heading | ||
}; | ||
}); | ||
} | ||
return pattern; | ||
function pointsToSegments(pts) { | ||
var segments = []; | ||
var a = void 0, | ||
b = void 0, | ||
distA = 0, | ||
distAB = void 0; | ||
for (var i = 1, l = pts.length; i < l; i++) { | ||
a = pts[i - 1]; | ||
b = pts[i]; | ||
distAB = a.distanceTo(b); | ||
segments.push({ | ||
a: a, | ||
b: b, | ||
distA: distA, // distances from the start of the polyline | ||
distB: distA + distAB, | ||
heading: computeSegmentHeading(a, b) | ||
}); | ||
distA += distAB; | ||
} | ||
return segments; | ||
} | ||
function projectPatternOnPointPath(pts, _ref) { | ||
var offset = _ref.offset, | ||
endOffset = _ref.endOffset, | ||
repeat = _ref.repeat; | ||
function projectPatternOnPointPath(pts, _ref2) { | ||
var offset = _ref2.offset, | ||
endOffset = _ref2.endOffset, | ||
repeat = _ref2.repeat; | ||
var positions = []; | ||
// 1. compute the absolute interval length in pixels | ||
var repeatIntervalLength = getPointPathPixelLength(pts) * repeat; | ||
// 2. find the starting point by using the offset and find the last pixel using endOffset | ||
var previous = interpolateOnPointPath(pts, offset); | ||
var endOffsetPixels = endOffset > 0 ? getPointPathPixelLength(pts) * endOffset : 0; | ||
// 1. split the path as segment infos | ||
var segments = pointsToSegments(pts); | ||
var nbSegments = segments.length; | ||
positions.push(previous); | ||
if (repeat > 0) { | ||
// 3. consider only the rest of the path, starting at the previous point | ||
var remainingPath = pts; | ||
remainingPath = remainingPath.slice(previous.predecessor); | ||
if (nbSegments === 0) { | ||
return []; | ||
} | ||
remainingPath[0] = previous.pt; | ||
var remainingLength = getPointPathPixelLength(remainingPath); | ||
var totalPathLength = segments[nbSegments - 1].distB; | ||
var repeatIntervalPixels = totalPathLength * repeat; | ||
var startOffsetPixels = offset > 0 ? totalPathLength * offset : 0; | ||
var endOffsetPixels = endOffset > 0 ? totalPathLength * endOffset : 0; | ||
// 4. project as a ratio of the remaining length, | ||
// and repeat while there is room for another point of the pattern | ||
// 2. generate the positions of the pattern as offsets from the polygon start | ||
var positionOffsets = []; | ||
var positionOffset = startOffsetPixels; | ||
do { | ||
positionOffsets.push(positionOffset); | ||
positionOffset += repeatIntervalPixels; | ||
} while (repeatIntervalPixels > 0 && positionOffset < totalPathLength - endOffsetPixels); | ||
while (repeatIntervalLength <= remainingLength - endOffsetPixels) { | ||
previous = interpolateOnPointPath(remainingPath, repeatIntervalLength / remainingLength); | ||
positions.push(previous); | ||
remainingPath = remainingPath.slice(previous.predecessor); | ||
remainingPath[0] = previous.pt; | ||
remainingLength = getPointPathPixelLength(remainingPath); | ||
// 3. projects offsets to segments | ||
var segmentIndex = 0; | ||
var segment = segments[0]; | ||
return positionOffsets.map(function (positionOffset) { | ||
// find the segment matching the offset, | ||
// starting from the previous one as offsets are ordered | ||
while (positionOffset > segment.distB && segmentIndex < nbSegments - 1) { | ||
segmentIndex++; | ||
segment = segments[segmentIndex]; | ||
} | ||
} | ||
return positions; | ||
} | ||
/** | ||
* pts: array of L.Point | ||
* ratio: the ratio of the total length where the point should be computed | ||
* Returns null if ll has less than 2 LatLng, or an object with the following properties: | ||
* latLng: the LatLng of the interpolated point | ||
* predecessor: the index of the previous vertex on the path | ||
* heading: the heading of the path at this point, in degrees | ||
*/ | ||
function interpolateOnPointPath(pts, ratio) { | ||
var nbVertices = pts.length; | ||
if (nbVertices < 2) { | ||
return null; | ||
} | ||
// easy limit cases: ratio negative/zero => first vertex | ||
if (ratio <= 0) { | ||
var segmentRatio = (positionOffset - segment.distA) / (segment.distB - segment.distA); | ||
return { | ||
pt: pts[0], | ||
predecessor: 0, | ||
heading: computeAngle(pts[0], pts[1]) | ||
pt: interpolateBetweenPoints(segment.a, segment.b, segmentRatio), | ||
heading: segment.heading | ||
}; | ||
} | ||
// ratio >=1 => last vertex | ||
if (ratio >= 1) { | ||
return { | ||
pt: pts[nbVertices - 1], | ||
predecessor: nbVertices - 1, | ||
heading: computeAngle(pts[nbVertices - 2], pts[nbVertices - 1]) | ||
}; | ||
} | ||
// 1-segment-only path => direct linear interpolation | ||
if (nbVertices == 2) { | ||
return { | ||
pt: interpolateBetweenPoints(pts[0], pts[1], ratio), | ||
predecessor: 0, | ||
heading: computeAngle(pts[0], pts[1]) | ||
}; | ||
} | ||
var pathLength = getPointPathPixelLength(pts); | ||
var a = pts[0], | ||
b = a, | ||
ratioA = 0, | ||
ratioB = 0, | ||
distB = 0; | ||
// follow the path segments until we find the one | ||
// on which the point must lie => [ab] | ||
var i = 1; | ||
for (; i < nbVertices && ratioB < ratio; i++) { | ||
a = b; | ||
ratioA = ratioB; | ||
b = pts[i]; | ||
distB += a.distanceTo(b); | ||
ratioB = distB / pathLength; | ||
} | ||
// compute the ratio relative to the segment [ab] | ||
var segmentRatio = (ratio - ratioA) / (ratioB - ratioA); | ||
return { | ||
pt: interpolateBetweenPoints(a, b, segmentRatio), | ||
predecessor: i - 2, | ||
heading: computeAngle(a, b) | ||
}; | ||
}); | ||
} | ||
@@ -155,4 +130,4 @@ | ||
function interpolateBetweenPoints(ptA, ptB, ratio) { | ||
if (ptB.x != ptA.x) { | ||
return L.point(ptA.x * (1 - ratio) + ratio * ptB.x, ptA.y * (1 - ratio) + ratio * ptB.y); | ||
if (ptB.x !== ptA.x) { | ||
return L.point(ptA.x + ratio * (ptB.x - ptA.x), ptA.y + ratio * (ptB.y - ptA.y)); | ||
} | ||
@@ -222,7 +197,2 @@ // special case where points lie on the same vertical axis | ||
// enable rotationAngle and rotationOrigin support on L.Marker | ||
/** | ||
* Defines several classes of symbol factories, | ||
* to be used with L.PolylineDecorator | ||
*/ | ||
L.Symbol = L.Symbol || {}; | ||
@@ -235,4 +205,2 @@ | ||
L.Symbol.Dash = L.Class.extend({ | ||
isZoomDependant: true, | ||
options: { | ||
@@ -271,4 +239,2 @@ pixelSize: 10, | ||
L.Symbol.ArrowHead = L.Class.extend({ | ||
isZoomDependant: true, | ||
options: { | ||
@@ -313,4 +279,2 @@ polygon: true, | ||
L.Symbol.Marker = L.Class.extend({ | ||
isZoomDependant: false, | ||
options: { | ||
@@ -325,3 +289,2 @@ markerOptions: {}, | ||
this.options.markerOptions.draggable = false; | ||
this.isZoomDependant = L.Browser.ie && this.options.rotate; | ||
}, | ||
@@ -426,16 +389,2 @@ | ||
_parseRelativeOrAbsoluteValue: function _parseRelativeOrAbsoluteValue(value) { | ||
if (typeof value === 'string' && value.indexOf('%') !== -1) { | ||
return { | ||
value: parseFloat(value) / 100, | ||
isInPixels: false | ||
}; | ||
} | ||
var parsedValue = value ? parseFloat(value) : 0; | ||
return { | ||
value: parsedValue, | ||
isInPixels: parsedValue > 0 | ||
}; | ||
}, | ||
/** | ||
@@ -449,5 +398,5 @@ * Parse the pattern definition | ||
// absolute (in pixels) or relative (in percentage of the polyline length) | ||
offset: this._parseRelativeOrAbsoluteValue(patternDef.offset), | ||
endOffset: this._parseRelativeOrAbsoluteValue(patternDef.endOffset), | ||
repeat: this._parseRelativeOrAbsoluteValue(patternDef.repeat) | ||
offset: parseRelativeOrAbsoluteValue(patternDef.offset), | ||
endOffset: parseRelativeOrAbsoluteValue(patternDef.endOffset), | ||
repeat: parseRelativeOrAbsoluteValue(patternDef.repeat) | ||
}; | ||
@@ -479,9 +428,2 @@ }, | ||
_asRatioToPathLength: function _asRatioToPathLength(_ref, totalPathLength) { | ||
var value = _ref.value, | ||
isInPixels = _ref.isInPixels; | ||
return isInPixels ? value / totalPathLength : value; | ||
}, | ||
/** | ||
@@ -498,10 +440,3 @@ * Select pairs of LatLng and heading angle, | ||
var pathPixelLength = getPixelLength(latLngs, this._map); | ||
var ratios = { | ||
offset: this._asRatioToPathLength(pattern.offset, pathPixelLength), | ||
endOffset: this._asRatioToPathLength(pattern.endOffset, pathPixelLength), | ||
repeat: this._asRatioToPathLength(pattern.repeat, pathPixelLength) | ||
}; | ||
return projectPatternOnPath(latLngs, ratios, this._map); | ||
return projectPatternOnPath(latLngs, pattern, this._map); | ||
}, | ||
@@ -546,4 +481,5 @@ | ||
this._patterns.forEach(function (pattern) { | ||
var layers = _this5._getPatternLayers(pattern); | ||
this._patterns.map(function (pattern) { | ||
return _this5._getPatternLayers(pattern); | ||
}).forEach(function (layers) { | ||
_this5.addLayer(L.layerGroup(layers)); | ||
@@ -550,0 +486,0 @@ }); |
@@ -13,15 +13,10 @@ | ||
// --- Arrow, with animation to demonstrate the use of setPatterns --- | ||
// --- Simple arrow --- | ||
var arrow = L.polyline([[57, -19], [60, -12]], {}).addTo(map); | ||
var arrowHead = L.polylineDecorator(arrow).addTo(map); | ||
var arrowHead = L.polylineDecorator(arrow, { | ||
patterns: [ | ||
{offset: '100%', repeat: 0, symbol: L.Symbol.arrowHead({pixelSize: 15, polygon: false, pathOptions: {stroke: true}})} | ||
] | ||
}).addTo(map); | ||
var arrowOffset = 0; | ||
var anim = window.setInterval(function() { | ||
arrowHead.setPatterns([ | ||
{offset: arrowOffset+'%', repeat: 0, symbol: L.Symbol.arrowHead({pixelSize: 15, polygon: false, pathOptions: {stroke: true}})} | ||
]) | ||
if(++arrowOffset > 100) | ||
arrowOffset = 0; | ||
}, 100); | ||
// --- Polygon, with an inner ring --- | ||
@@ -28,0 +23,0 @@ var polygon = L.polygon([[[54, -6], [55, -7], [56, -2], [55, 1], [53, 0]], [[54, -3], [54, -2], [55, -1], [55, -5]]], {color: "#ff7800", weight: 1}).addTo(map); |
{ | ||
"name": "leaflet-polylinedecorator", | ||
"version": "1.3.0", | ||
"version": "1.3.2", | ||
"repository": "bbecquet/Leaflet.PolylineDecorator", | ||
@@ -5,0 +5,0 @@ "main": "./dist/leaflet.polylineDecorator.js", |
@@ -10,5 +10,5 @@ # Leaflet PolylineDecorator | ||
The development version of the plugin (on the `master` branch) is targeted at the 1.x version of Leaflet. | ||
**The current version of the plugin (on the `master` branch) works only with versions 1.\* of Leaflet**. | ||
For a version of the plugin compatible with the 0.7.x Leaflet release, use the `leaflet-0.7.2` branch. | ||
For a version of the plugin compatible with the older 0.7.* Leaflet releases, use the `leaflet-0.7.2` branch. But this branch is not maintained anymore and Leaflet 1.* has been around for a while, so you should definitely update. | ||
@@ -79,6 +79,8 @@ ## npm / bower | ||
## Performance note | ||
## Performance note/alternatives | ||
Please note that this library is in an early stage, and many operations could still be optimized. | ||
Moreover, as it requires a lot of (re-)computations, and each pattern symbol is an actual `L.ILayer` object, it can have an impact on the responsiveness of your map, especially if used on many objects. | ||
In cases where it's applicable (dash patterns), you should probably use instead the `dashArray` property of `L.Path`, as it's natively drawn by the browser. | ||
This plugin creates actual `L.Layer` objects (markers, polyline, etc.) to draw the pattern symbols. This is extra customizable as you can define your own symbols, but it may have an impact on the responsiveness of your map if you have to draw a lot of symbols on many large polylines. | ||
Here are two light-weight alternatives for simpler cases: | ||
- the [`dashArray` property of `L.Path`](http://leafletjs.com/reference-1.1.0.html#path-dasharray), f you only need to draw simple patterns (dashes, dots, etc.). | ||
- the [`Leaflet.TextPath`](https://github.com/makinacorpus/Leaflet.TextPath) plugin, which is based on SVG. |
import { | ||
getPixelLength, | ||
projectPatternOnPath, | ||
} from './utils.js'; | ||
parseRelativeOrAbsoluteValue, | ||
} from './patternUtils.js'; | ||
import './L.Symbol.js'; | ||
@@ -91,16 +91,2 @@ | ||
_parseRelativeOrAbsoluteValue: function(value) { | ||
if (typeof value === 'string' && value.indexOf('%') !== -1) { | ||
return { | ||
value: parseFloat(value) / 100, | ||
isInPixels: false, | ||
}; | ||
} | ||
const parsedValue = value ? parseFloat(value) : 0; | ||
return { | ||
value: parsedValue, | ||
isInPixels: parsedValue > 0, | ||
}; | ||
}, | ||
/** | ||
@@ -114,5 +100,5 @@ * Parse the pattern definition | ||
// absolute (in pixels) or relative (in percentage of the polyline length) | ||
offset: this._parseRelativeOrAbsoluteValue(patternDef.offset), | ||
endOffset: this._parseRelativeOrAbsoluteValue(patternDef.endOffset), | ||
repeat: this._parseRelativeOrAbsoluteValue(patternDef.repeat), | ||
offset: parseRelativeOrAbsoluteValue(patternDef.offset), | ||
endOffset: parseRelativeOrAbsoluteValue(patternDef.endOffset), | ||
repeat: parseRelativeOrAbsoluteValue(patternDef.repeat), | ||
}; | ||
@@ -142,6 +128,2 @@ }, | ||
_asRatioToPathLength: function({ value, isInPixels }, totalPathLength) { | ||
return isInPixels ? value / totalPathLength : value; | ||
}, | ||
/** | ||
@@ -158,10 +140,3 @@ * Select pairs of LatLng and heading angle, | ||
const pathPixelLength = getPixelLength(latLngs, this._map); | ||
const ratios = { | ||
offset: this._asRatioToPathLength(pattern.offset, pathPixelLength), | ||
endOffset: this._asRatioToPathLength(pattern.endOffset, pathPixelLength), | ||
repeat: this._asRatioToPathLength(pattern.repeat, pathPixelLength), | ||
}; | ||
return projectPatternOnPath(latLngs, ratios, this._map); | ||
return projectPatternOnPath(latLngs, pattern, this._map); | ||
}, | ||
@@ -199,6 +174,5 @@ | ||
_draw: function () { | ||
this._patterns.forEach(pattern => { | ||
const layers = this._getPatternLayers(pattern); | ||
this.addLayer(L.layerGroup(layers)); | ||
}); | ||
this._patterns | ||
.map(pattern => this._getPatternLayers(pattern)) | ||
.forEach(layers => { this.addLayer(L.layerGroup(layers)); }); | ||
} | ||
@@ -205,0 +179,0 @@ }); |
@@ -16,4 +16,2 @@ // enable rotationAngle and rotationOrigin support on L.Marker | ||
L.Symbol.Dash = L.Class.extend({ | ||
isZoomDependant: true, | ||
options: { | ||
@@ -55,4 +53,2 @@ pixelSize: 10, | ||
L.Symbol.ArrowHead = L.Class.extend({ | ||
isZoomDependant: true, | ||
options: { | ||
@@ -107,4 +103,2 @@ polygon: true, | ||
L.Symbol.Marker = L.Class.extend({ | ||
isZoomDependant: false, | ||
options: { | ||
@@ -119,3 +113,2 @@ markerOptions: { }, | ||
this.options.markerOptions.draggable = false; | ||
this.isZoomDependant = (L.Browser.ie && this.options.rotate); | ||
}, | ||
@@ -122,0 +115,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1948831
17
42449
85