[GIS] Leaflet populate L.divIcon marker’s html option from feature properties

labelingleafletmarkers

I am using Leaflet.js to make a timeline-based playback of marker locations, using a Leaflet Playback listed on Leaflet Plugins.

I am defining an L.divIcon earlier in my code:

var busIcon = L.divIcon({className: 'bus-div-icon', html:"1"});

And later on I have to pass it to the plugin's function:

var playbackOptions = {
  //...
  marker: {
    icon : busIcon,
    getPopup: function(featureData) {
      // returns something using featureData.properties.name, so here a function works
    }
  },
  //...

I want to populate the html variable in the divIcon dynamically, similarly to how getPopup has been defined (this code I have copied and customized from the plugin's documentation example).

But when I try to pass a function to the icon:

icon: function(featureData) {
  return L.divIcon({className: 'bus-div-icon', html: 
  featureData.properties.busnum });
},

It did not work; I got this error in my console:

Uncaught TypeError: t.icon.createIcon is not a function
at e._initIcon (leaflet.js:5)
at e.onAdd (leaflet.js:5)
at e._layerAdd (leaflet.js:5)
at e.whenReady (leaflet.js:5)
at e.addLayer (leaflet.js:5)
at e.addTo (leaflet.js:5)
at e.addTrack (LeafletPlayback.min.js:1)
at e.addData (LeafletPlayback.min.js:1)
at e.setData (LeafletPlayback.min.js:1)
at e.initialize (LeafletPlayback.min.js:1)

I tried another strategy, and no errors this time:

icon: L.divIcon({
  className: 'bus-div-icon',
  html: function(featureData) {
    console.log("Hello!");
    return '1';
  }
}),

.. but the results were hilarious!

Imgur

What happened here is that instead of executing the function, it has displayed that whole code as text on the map! See screenshot above.

How to populate html dynamically?

Best Answer

The beauty of Leaflet is that can "easily" customize the behaviour of its classes, including for plugins which follow Leaflet classes conventions.

Therefore you can customize how LeafletPlayback plugin creates its MoveableMarker, so that it uses a new "getIcon" function that you would pass in the options, similarly to the getPopup option that you mention:

L.Playback.MoveableMarker.include({
  _originalInitialize: L.Playback.MoveableMarker.prototype.initialize,
  initialize: function(startLatLng, options, feature) {
    // Perform some operations before doing the normal initialization.
    var marker_options = options.marker || {};

    // "Dynamically" assign the icon.
    if (marker_options.getIcon) {
      marker_options.icon = marker_options.getIcon(feature);
    }

    // Now perform the normal initialization.
    this._originalInitialize.call(this, startLatLng, options, feature);
  }
});

Now you can pass marker.getIcon option to new L.Playback, which will receive the feature data as parameter, so that you can build your icon accordingly.

Example based on the plugin example 0: https://plnkr.co/edit/8GQHHfzE9qkvPEb20pGn?p=preview

EDIT: changed marker_options.getIcon(this.feature); to marker_options.getIcon(feature); as that's the one carrying the feature data.