I'm currently using leaflet to draw a bunch of lines on a leaflet map using the canvas. However, I'm having issue draw arrows on the lines using canvas.
I have found items such as polyline decorator but it very very slow and I only want the arrows to be visible at specified zoom distance (>=13).
How would one do this using leaflet please? I don't have to have repeatable arrows but just a way to show poyline direction.
Thanks.
I know this answer is a little too late but It works with a good performance and without any plugin.
You can change the icon(which currently is a triangle arrow(▶)) to any thing you want and its initial direction should be toward right(the zero degree angle).
You can also change the number of arrows to be shown(in this example it's 5)
CSS code :
.arrow-icon {
width: 14px;
height: 14px;
}
.arrow-icon > div {
margin-left: -1px;
margin-top: -3px;
transform-origin: center center;
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
And here is the Javascript code.The getArrows
function accepts array of coordinates of polyline(and optional parameter color and optional parameter number of arrows per line) and returns an array of markers(just copy and paste everything) :
function getArrows(arrLatlngs, color, arrowCount, mapObj) {
if (typeof arrLatlngs === undefined || arrLatlngs == null ||
(!arrLatlngs.length) || arrLatlngs.length < 2)
return [];
if (typeof arrowCount === 'undefined' || arrowCount == null)
arrowCount = 1;
if (typeof color === 'undefined' || color == null)
color = '';
else
color = 'color:' + color;
var result = [];
for (var i = 1; i < arrLatlngs.length; i++) {
var icon = L.divIcon({ className: 'arrow-icon', bgPos: [5, 5], html: '<div style="' + color + ';transform: rotate(' + getAngle(arrLatlngs[i - 1], arrLatlngs[i], -1).toString() + 'deg)">▶</div>' });
for (var c = 1; c <= arrowCount; c++) {
result.push(L.marker(myMidPoint(arrLatlngs[i], arrLatlngs[i - 1], (c / (arrowCount + 1)), mapObj), { icon: icon }));
}
}
return result;
}
function getAngle(latLng1, latlng2, coef) {
var dy = latlng2[0] - latLng1[0];
var dx = Math.cos(Math.PI / 180 * latLng1[0]) * (latlng2[1] - latLng1[1]);
var ang = ((Math.atan2(dy, dx) / Math.PI) * 180 * coef);
return (ang).toFixed(2);
}
function myMidPoint(latlng1, latlng2, per, mapObj) {
if (!mapObj)
throw new Error('map is not defined');
var halfDist, segDist, dist, p1, p2, ratio,
points = [];
p1 = mapObj.project(new L.latLng(latlng1));
p2 = mapObj.project(new L.latLng(latlng2));
halfDist = distanceTo(p1, p2) * per;
if (halfDist === 0)
return mapObj.unproject(p1);
dist = distanceTo(p1, p2);
if (dist > halfDist) {
ratio = (dist - halfDist) / dist;
var res = mapObj.unproject(new Point(p2.x - ratio * (p2.x - p1.x), p2.y - ratio * (p2.y - p1.y)));
return [res.lat, res.lng];
}
}
function distanceTo(p1, p2) {
var x = p2.x - p1.x,
y = p2.y - p1.y;
return Math.sqrt(x * x + y * y);
}
function toPoint(x, y, round) {
if (x instanceof Point) {
return x;
}
if (isArray(x)) {
return new Point(x[0], x[1]);
}
if (x === undefined || x === null) {
return x;
}
if (typeof x === 'object' && 'x' in x && 'y' in x) {
return new Point(x.x, x.y);
}
return new Point(x, y, round);
}
function Point(x, y, round) {
this.x = (round ? Math.round(x) : x);
this.y = (round ? Math.round(y) : y);
}
Then simply draw leaflet polyline(just works for array of latLng
coordinates not array of array of coordinates):
// array of coordinates
var mylatlngs = [
[55.555, 33.33],
[..., ...],
[..., ...],
[..., ...],
[..., ...],
...
];
var polyline = L.polyline(mylatlngs, { color: 'red' }).addTo(map);
// draw 5 arrows per line
L.featureGroup(getArrows(mylatlngs, 'red', 5,map)).addTo(map);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With