Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Leaflet custom icon resize on zoom. Performance icon vs divicon

I was trying to resize my custom icons when zooming in leaflet. I came up with two solutions for this. One using the L.Icon tag, the other one using L.divIcon. In both examples I only set 1 marker and group for readability

Method 1 using L.Icon: make groups with markers. Then on zoomend i use mygroup.eachLayer(function (layer) to change all icons for 1 layer using layer.setIcon(). I repeat this for all groups

<script>
// Setting map options
....

// Setting Icon
var normalicon = L.icon({
    iconUrl: 'icon1.jpg',
    iconSize:     [40,40],
    iconAnchor:   [20,20],
    popupAnchor:  [0,-20] 
    });


// Create a group
var normalLayer = L.layerGroup([
    L.marker([200,200], {icon:normalicon})
]).addTo(map);

// Resizing on zoom
map.on('zoomend', function() {

    // Normal icons
    var normaliconresized = L.Icon.extend({
        options: {
            iconSize: [20*(map.getZoom()+2), 20*(map.getZoom()+2)], // New size!
            iconAnchor:   [20,20],
            popupAnchor:  [0,-20]
        }
    });

    var normaliconchange = new normaliconresized({iconUrl: 'icon1.jpg'})
    normalLayer.eachLayer(function (layer) {
        layer.setIcon(normaliconchange);
    });

    .... Do the same for the other groups
});             
</script>

Method 2 using L.divIcon: I make the icons and the different groups and add some CSS for each icon with a background-image property. Then on zoomend I simply use JQuery to change the css. background-size css-property allows me to change the image size. I do this for each divIcon class I have

Css
.iconsdiv{
    width:20px; height:20px;
    background-image:url("icon2.jpg");
    background-size: 20px 20px;
}


Script
<script>
// Setting map options
....


// Setting Icon

var divicon = L.divIcon({className: 'iconsdiv', iconSize: null }); // Explicitly set to null or you will default to 12x12


// Create a group
var divLayer = L.layerGroup([
    L.marker([200,200], {icon:divicon})
]).addTo(map);


// Resizing on zoom
map.on('zoomend', function() {

    var newzoom = '' + (20*(map.getZoom()+2)) +'px';
    $('#map .iconsdiv').css({'width':newzoom,'height':newzoom,'background-size':newzoom + ' ' + newzoom}); 

     ... repeat for the other classes
});


</script>

I have barely any experience with javascript/jquery/...

Is the second option preferable as it does not require re-setting each icon? Would it improve performance when there is a large number of groups/icons?

like image 571
Pepe Avatar asked Sep 02 '17 14:09

Pepe


1 Answers

I did a test myself using performance.now(). I tested on a 1024x1180 (bounds) custom map. Once with 676 makers. Then with about half of this and lastly with 100 markers. The performance was measured inside the map.on('zoomend', function() { function.

  • For 676 markers it took 2500-2900 milliseconds for the L.Iconmethod to update. For the L.divIconthis was only 10-30 milliseconds.
  • Half the amount of markers also halved this time.
  • For about 100 markers (104) L.Icon took 300-400 milliseconds to update. L.divIcon did the same in only 4-5 milliseconds.

I also timed the performance of the initialization (L.layerGroup([...]).addTo(map)) for 676 markers. L.Icon took 2200-2400 milliseconds. L.divIcon did the same in 80-95 milliseconds.

L.divIcon clearly does a lot better (as expected). While it is a bit of a cheat, I guess I would prefer using this method. I can not directly think of reasons why the L.Iconmethod would be preferred in case we want zooming

Edit: I noticed that according to Leaflet Documentation 'Icon' you can also assign a className to the Icons. Using css-properties width and height the same can be done as I did earlier for the divIcons, thus saving you a lot of loading time, yet allowing you to use all options linked to L.Icon. Your initialisation time will still be longer though.

like image 60
Pepe Avatar answered Oct 27 '22 06:10

Pepe