Prakhar
Prakhar

Reputation: 3536

Updating layers in Leaflet / Mapbox

I'm trying to make a mapping visualization in realtime, where I keep getting new points via websockets. The initial plotting these markers on the map seems simple, but I'm not sure what's the right way of updating a layer on Mapbox.

As of now, whenever I get a new point, I remove the old layer, create a new one and then add it on the map. The problem with this approach is that it is slow and for high number of points (>5000) it starts lagging.

    // remove layer
    if (this.pointsLayer != null) {
        map.removeLayer(this.pointsLayer);
    }

    // build geoJSON
    var geoJSON = { "type": "FeatureCollection", "features": [] };
    geoJSON["features"] = tweets.map(function(tweet) {
        return this.getGeoPoint(tweet);
    }.bind(this));

    // add geoJSON to layer
    this.pointsLayer = L.mapbox.featureLayer(geoJSON, {
        pointToLayer: function(feature, latlon) {
            return L.circleMarker(latlon,  {
                fillColor: '#AA5042',
                fillOpacity: 0.7,
                radius: 3,
                stroke: false
            });
        }
    }).addTo(map);

Is there a better way?

Upvotes: 4

Views: 6629

Answers (3)

nathansnider
nathansnider

Reputation: 2873

You can create an empty GeoJSON layer by passing it a false instead of real data:

//create empty layer
this.pointsLayer = L.mapbox.featureLayer(false, {
    pointToLayer: function(feature, latlon) {
        return L.circleMarker(latlon,  {
            fillColor: '#AA5042',
            fillOpacity: 0.7,
            radius: 3,
            stroke: false
        });
    }
}).addTo(map);

then use .addData to update it as new tweets come in. Something like:

// build geoJSON
var geoJSON = { "type": "FeatureCollection", "features": [] };
geoJSON["features"] = /**whatever function you use to build a single tweet's geoJSON**/

// add geoJSON to layer
this.pointsLayer.addData(geoJSON);

For a single tweet, I guess you could just create a Feature instead of a FeatureCollection, though I don't know whether that extra layer of abstraction would make any difference in terms of performance.

EDIT: Here is an example fiddle showing the .addData method at work:

http://jsfiddle.net/nathansnider/4mwrwo0t/

It does slow down noticeably if you add 10,000 points, and for 15,000 points, it's really sluggish, but I suspect that has less to do with how the points are added that the demands of rendering so many circleMarkers.

If you aren't already, you may want to try using the new Leaflet 1.0 beta, which redraws vector layers faster and is generally much more responsive with large datasets. Compare this 15,000-point example using Leaflet 0.7.5 to the same code using Leaflet 1.0.0b2. Not everything is fixed (popups take a long time to open in both), but the difference in lag time when trying to drag the map is pretty dramatic.

Upvotes: 4

iH8
iH8

Reputation: 28638

I would take a look at Leaflet Realtime:

Put realtime data on a Leaflet map: live tracking GPS units, sensor data or just about anything.

https://github.com/perliedman/leaflet-realtime

Upvotes: 2

Steve Bennett
Steve Bennett

Reputation: 126095

There's no reason to go through the intermediate step of construction a GeoJSON object just so you can add it to the map. Depending on your exact needs, you can do something like this:

tweets.forEach(function(t) {
    L.marker(this.getGeoPoint(t)).addTo(map);
}, this);

You should manage the tweets object so it only contains points that are not already visible on the map, though. Deleting all the old markers, just so you can add them again, is of course going to be very slow.

Upvotes: 1

Related Questions