Alexey
Alexey

Reputation: 2812

How to load/unload markers based on visible tiles on Leaflet.js?

According to the documentation I can pre-define the set of markers and other objects while creating the map, which then will be displayed. I do not wish to put all the possible markers/images/rectangles/etc in the JS code. I've read that people calculate the visible area on every tiles moving/zooming, do a HTTP request and server returns needed markers.

However, I would like to use another way, as it is a little more efficient:

1. For example, currently Leaflet automatically asks for the tiles 0:0 and 0:1;

2. In addition it could make a HTTP request and ask the server: "Hey, give me also the markers for the tiles 0:0 and 0:1".

3. Completely remove markers which are on the tiles which have become invisible.

Are the steps 2-3 possible and how if Yes?

Upvotes: 2

Views: 1349

Answers (1)

IvanSanchez
IvanSanchez

Reputation: 19069

What you're asking looks pretty similar to how Leaflet.VectorGrid works: each "tile" request is a request for vector data, not for a raster image. In fact, perhaps using protobuffer vector tiles is the right approach for your scenario.

VectorGrid relies on the logic implemented by L.GridLayer to handle the tile loading/unloading logic.

If you insist on doing this yourself, I suggest reading the Leaflet tutorials on creating plugins first; have a look to the source code for Leaflet's L.GridLayer and for VectorGrid to see how those work, and then something like:

L.GridLayer.MarkerLoader = L.GridLayer.extend({

    initialize: funcion(url, options) {
        this._url = url;
        this._markerStore = {};
        L.GridLayer.prototype.initialize.call(this, options);
    },

    createTile: function(coords, done) {
        var key = this._tileCoordsToKey(coords);
        var data = {
            s: this._getSubdomain(coords),
            x: coords.x,
            y: coords.y,
            z: coords.z
        };
        var tileUrl = L.Util.template(this._url, L.extend(data, this.options));

        fetch(tileUrl).then(function(response){
            // Parse the response, with either response.json()
            // or response.text() or response.blob().
            // See https://developer.mozilla.org/en-US/docs/Web/API/Response

            // Create a bunch of markers based on the parsed response
            // The specific syntax depends on the format of the data structure
            var markers = data.map(function(point){
                return L.marker(point);
            });

            // Add those markers to a L.LayerGroup, add that LayerGroup
            // to a dictionary (to remove it later), and finally add it to the map
            var group = L.layerGroup(markers);
            this._markerStore[key] = group;

            // Mark the tile as ready
            done();
        });

        // Return an empty <div> as a tile. Real data will be loaded async and
        // put in LayerGroups anyway.
        return L.DomUtil.createElement('div');
    },

    _removeTile: function(key) {
        // Whenever a tile is pruned, remove the corresponding LayerGroup
        // from the map and from the _markerStore dictionary
        this._markerStore[key].remove();
        delete this._markerStore[key];
        
        return L.GridLayer.prototype._removeTile.call(this, key);
    }
});

Upvotes: 3

Related Questions