[GIS] Is it possible to check incoming GeoJSON to see if any of the features are identical to existing ones before adding to map

geojsongoogle mapsleafletpostgis

I am working on a Rails/PostGIS app and doing a typical ajax call to retrieve GeoJSON within the map viewport bounds. Whenever the map is shifted or zoomed all of the markers are removed and then added again according to the new bounds. The problem I have with this is that it creates a flicker effect, as well as closes popups(especially annoying when a marker is close to the bounds and readjusts them when the popup opens, closing it immediately). I am wondering if there is a standard approach to filtering out duplicate features. This way nonidentical markers can simply be added to the GeoJSON layer instead of recreating the entire layer each time, and existing markers that are now outside of the bounds can be removed. I am having a hard time looking for examples as I am not familiar enough with Javascript to write a solution. I had originally been using Google Maps API but switched to Leaflet hoping it would be easier to figure out. http://labs.easyblog.it/maps/leaflet-layerjson/ is an example of the ideal behavior, but I had a hard time trying to implement this with GeoJSON.

The code:

var geojsonLayer;

var addGeoJSONLayer = function(geoJSON) {

if(geojsonLayer) {
  geojsonLayer.clearLayers();
}

geojsonLayer = new L.GeoJSON(geoJSON, {
    onEachFeature: function(feature, layer){
        layer.bindPopup("<a data-no-turbolink='true' href="+ feature.properties.id + ">" + feature.properties.name + "</a>");
        }

}).addTo(map);

geojsonLayer.addData(geoJSON);

};

$.getJSON('/summits.geojson/?bbox=' + map.getBounds().toBBoxString() + '', addGeoJSONLayer);

map.on('moveend', function(event){
    if(map.getZoom() >= 10) {
        $.getJSON('/summits.geojson/?bbox=' + map.getBounds().toBBoxString() + '', addGeoJSONLayer);
    }
});

I have also used the leaflet-ajax plugin hoping it would solve this, but it produces the same results as above. I know it has a "refilter" method but I am not sure how to use it, or if it can even be used to solve the problem. Any help whatsoever would be greatly appreciated!

Best Answer

The way I dealt with this a long time ago (back before ArcGIS Feature Server) was to create a simple unique integer hash for each feature from a specific source (can be as simple as a unique identifier) and then store information in an array indexed on that unique integer hash. If the incoming feature is not stored in the array already at its position, then you add it to the map and store it at the position. If it is already stored, then it is already on the map and you do nothing with it. When you remove a feature from the map, you remove the object occupying that feature's position in the array.

This reduces a potential search to a much less costly array index lookup (especially if your hash function is as simple as accessing one property).

Sample code here (mapdijits.GraphicsManagerOpt.add):
http://maps.stlouisco.com/police/beta/mapdijits/GraphicsManagerOpt.js

Obviously this code is for ArcGIS javascript API, but the concept should be adaptable to Leaflet.

Edit: Just thought I would add that databinding in D3 might make this problem much easier. If you are inserting the GeoJSON into an array based on an integer hash, you can then bind just data bind that array as path information and your map updates. Here is a crude example that takes a long time to load because I am bringing in a big hunk of GeoJSON to run it:
http://maptest.stlouisco.com/d3/map.html