Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Leaflet Polyline Arrows

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.

like image 830
Liam Avatar asked Nov 14 '18 19:11

Liam


1 Answers

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);
like image 124
nAviD Avatar answered Sep 19 '22 12:09

nAviD