Leaflet – Implementing Canvas Text Labels in Leaflet

labelingleaflet

I have some text labels in Leaflet which I'm building from JSON data using L.divIcon. They're looking like this:

lbl_coord = feature.properties.coord;

lbl_text = L.divIcon({
        className: /*...*/,
        iconAnchor: [0,0],
        popupAnchor: /*...*/,
        html: "<b>" + feature.properties.name + "</b>"
      });

  var label = L.marker(lbl_coord, {icon: lbl_text});
  label.addTo(labelLayer);

Well, this works but the performance is rather bad because each marker is rendered as individual DOM element. It becomes very slow on panning operations. I'd like to load 10 000 labels at some point and am struggling to make it use canvas. I couldn't get the Canvas Markers Plugin to work as I'm experiencing the same issue that is discussed here (The HTMLImageElement provided is in the 'broken' state). However, the workaround proposed there won't work for me (error: Layer is not defined). Besides, there is no license given, so I don't know if I might use this at all for my project. Here's another Plugin but this wouldn't work with divIcon functionality.

At this point, I am wondering why this functionality is not part of Leaflet core. I know >10 000 markers is a lot but performance is already quite bad with 100 markers. I hope somebody's got a solution.

Best Answer

Leaflet Canvas-Markers plugin expects regular L.icon icons that are based on images. Trick to get text into images dynamically is using svg images, defined with text descriptor.

Code for example below is taken from Canvas-Markers demo. Text of each icon is serial number of icon. Loading of 10.000 icons lasts about 3 seconds. When map is zoomed out so that all icons are displayed, things get a bit slow.

var map = L.map('map').setView([59.9578,30.2987], 10);
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
  preferCanvas: true
}).addTo(map);

var ciLayer = L.canvasIconLayer({}).addTo(map);

function svgText(txt) {
  return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="30"><text x="0" y="30" style="font-size: 14px;">' + txt + '</text></svg>';      
}

var icon;
var img;
var markers = [];

for (var i = 0; i < 10000; i++) {
  img = 'data:image/svg+xml,' + encodeURIComponent(svgText(i));
  icon = L.icon({
    iconUrl: img,
    iconSize: [40, 30],
    iconAnchor: [20, 15]
  });
  var marker = L.marker([58.5578 + Math.random()*1.8, 29.0087 + Math.random()*3.6], {icon: icon}).bindPopup("I Am "+i);
  markers.push(marker);
}

ciLayer.addLayers(markers);