Dr.YSG
Dr.YSG

Reputation: 7591

in-place Update Leaflet GeoJSON feature

I was hoping that GeoJSON.addData() would return the newly created subLayer of the GeoJSON object, but it does not. Why Do I need something like this?

(currently using Leaflet 1.0Beta2)

I am using Leaflet GeoJson to show live data in a GeoJSON (point, line, polygon). It is a CRUD interface (Create, Update and Delete). I receive WebSocket messages with GeoJSON data, each one with a GUID.

I the case of a CREATE I just do a GeoJSon.AddData() to the appropriate layer.

But for the UPDATE and DELETE I want a handle for the layer that was added to the GeoJSON so that I can update its location, or update the Geometry. addData is not giving me this handle. And it is really hard to get it from onEachFeature() or from pointToLayer()

Currently, I do have a way that works but ugly. I have to do is search the entire layer with GeoJSon.eachLayer(fn) whenever an update or delete occurs. It seems a bit expensive.

{even if Leaflet is not truly engineered for this live r/t display of data, it is working, and it seems sad if you cannot use it for watching a lot of sensor data, IoT) as we are doing.

this.delete = function (layerName, feature) {
    if (!(layerName in self.myLayers)) {
        alert("live.display: Missing Layer: " + layerName);
        return;
    }
    var layerInfo = Live.myLayers[layerName];
    var base = layerInfo.layer;
    var id = feature.properties.objectID;
    this.find(layerName, id, function (layer) {
        this.removeLayer(layer);
        Tools.debug(16, "live.delete:", "killed object: " + id);
    });
}
this.find = function (layerName, id, action) {
    var base = Live.myLayers[layerName].layer;
    base.eachLayer(function (feature) {
        if (!("objectID" in feature.feature.properties)) { return; }
        var objectID = feature.feature.properties.objectID;
        if (objectID == id) {
            action.call(base, feature);
        }
    });
}

Upvotes: 6

Views: 13666

Answers (3)

Tek Kshetri
Tek Kshetri

Reputation: 2337

If someone still looking for another short method to update GeoJSON, you can try something like this,


//function to clear the previous geojson feature
function clearMap() {
    map.eachLayer(function(layer){
        if(layer.myTag && layer.myTag === 'previousLayer'){
            lmap.removeLayer(layer);
        }
    });
}

function geojsonUpdate(geojsonData){
 var geojsonLayer = L.geoJson(geojsonData, {
    onEachFeature: function (feature, layer) {
        layer.myTag = 'previousLayer'
      );
    },
  });
 geojsonLayer.addTo(lmap);
 lmap.fitBounds(geojsonLayer.getBounds());
}

//function call
clearMap();
geojsonUpdate(data);

Upvotes: 1

iH8
iH8

Reputation: 28638

If you want to store references to the actual layers being created, you can access them from the layeradd event on your L.GeoJSON instance:

var geojson = new L.GeoJSON().on(
    'layeradd', function (e) {
        console.log(e.layer);
    }
).addTo(map);

// This addData call will fire the handler above twice
// because it adds two features.
geojson.addData({
    "type": "FeatureCollection",
    "features": [{
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type": "Point",
            "coordinates": [90, 0]
        }
    }, {
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type": "Point",
            "coordinates": [-90, 0]
        }
    }]
});

Upvotes: 3

ghybs
ghybs

Reputation: 53215

Instead (or in parallel) of "merging" all created GeoJSON features into a single Leaflet GeoJSON layer group (which you do with addData), why not creating first each feature in its own Leaflet GeoJSON layer, so that it gives you the handle you are looking for (then you could simply record this handle in an object / mapping with the key being your objectID for example)?

If desired, you could even still merge the individual layers into your single GeoJSON layer group after that.

var myGeoJsonLayerGroup = L.geoJson().addTo(map);
var myFeaturesMap = {};

function addNewFeatureToGeoJsonLayerGroup(newGeoJsonData) {
  var newGeoJSONfeature = L.geoJson(newGeoJsonData);
  myFeaturesMap[newGeoJsonData.properties.objectID] = newGeoJSONfeature;
  myGeoJsonLayerGroup.addLayer(newGeoJSONfeature);
}

function updateFeature(updatedGeoJsonData) {
  var updatedFeature = myFeaturesMap[updatedGeoJsonData.properties.objectID];
  updatedFeature.clearLayers(); // Remove the previously created layer.
  updatedFeature.addData(updatedGeoJsonData); // Replace it by the new data.
}

function deleteFeature(deletedGeoJsonData) {
  var deletedFeature = myFeaturesMap[deletedGeoJsonData.properties.objectID];
  myGeoJsonLayerGroup.removeLayer(deletedFeature);
}

Demo (not using GeoJSON): http://jsfiddle.net/ve2huzxw/94/


EDIT:

A slightly more simple solution would be to store the reference to each individual layer through the onEachFeature function of the GeoJSON layer group:

var myFeaturesMap = {};

var myGeoJsonLayerGroup = L.geoJson({
    onEachFeature: function (feature, layer) {
        myFeaturesMap[feature.properties.objectID] = layer;
    }
}).addTo(map);

function addNewFeatureToGeoJsonLayerGroup(newGeoJsonData) {
    myGeoJsonLayerGroup.addData(newGeoJsonData);
}

function updateFeature(updatedGeoJsonData) {
    deleteFeature(updatedGeoJsonData); // Remove the previously created layer.
    addNewFeatureToGeoJsonLayerGroup(updatedGeoJsonData); // Replace it by the new data.
}

function deleteFeature(deletedGeoJsonData) {
    var deletedFeature = myFeaturesMap[deletedGeoJsonData.properties.objectID];
    myGeoJsonLayerGroup.removeLayer(deletedFeature);
}

Upvotes: 8

Related Questions