Leaflet – How to Update Filter Option for GeoJSON Layer in Leaflet

filtergeojsonleaflet

I got this Leaflet sample from the Leaflet documentation for testing how the filter option works. Then I've created the following code (jsfiddle sample here):

var freeBus = {/*check the jsfiddle sample*/}
var map = L.map('map').setView([39.74739, -105], 13);

L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
    maxZoom: 18,
    attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, ' +
    'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
    id: 'mapbox/light-v9',
    tileSize: 512,
    zoomOffset: -1
}).addTo(map);

function filter(feature, layer) {
    if (feature.properties) {
        if ( feature.properties.underConstruction === undefined || feature.properties.underConstruction === false ) {
            return false
        } else {
            return true
        }
    }
}

const layer = L.geoJSON(freeBus, {
    filter: filter,
}).addTo(map);

freeBus.features[2].properties.underConstruction = true
layer.options.filter

Basically, there's a GeoJSON with three polyline features on this code, and I'm using the filter option to select which polylines I want to show on my map, if underConstruction equals to true, then it appears on the map, otherwise, it doesn't. This code works as I expect until the following lines:

freeBus.features[2].properties.underConstruction = true
layer.options.filter

With those lines I tried to change a false value from my geoJSON to true, and then I tried to rerun the filter on my layer to update the polylines that are being showed on the map. However, that didn't happen in practice… I know that running map.removeLayer(layer) and then drawing the polylines again with L.geoJSON(freeBus, {filter: filter, onEachFeature: onEachFeature }).addTo(map) would solve the problem. But I think that this is not the best way of doing it since I'm erasing everything and drawing all the values again (if my GeoJSON was big with a lot of features that wouldn't be an efficient way of doing it). What am I missing? Is it possible to run the filter option again to update the feature values after I've already drawn my features once?

Best Answer

If you look at the Leaflet docs for GeoJSON layer at https://leafletjs.com/reference-1.7.1.html#geojson, you'll notice the following note for filter option:

Note: dynamically changing the filter option will have effect only on newly added data. It will not re-evaluate already included features.

This means you have to reload data if you change the filter. This can be done with the .clearLayers and .addData methods.

Code could then look something like this:

var includeUnderConstruction = false;
function filter(feature, layer) {
  if (feature.properties) {
    if (feature.properties.underConstruction === undefined || feature.properties.underConstruction === includeUnderConstruction) {
      return false;
    } else {
      return true;
    }
  }
  return true;
}

const layer = L.geoJSON(freeBus, {
  filter: filter,
}).addTo(map);

// Change filter
includeUnderConstruction = true;
layer.clearLayers();
layer.addData(freeBus);

You can avoid reloading data with the use of GeoJSON layer style option. In the style function you simply set undesired lines not to be displayed. When you change your filter, you just have to set style again with the .setStyle method.

In this case code could look something like this:

var includeUnderConstruction = false;   

function filter(feature, layer) {
  if (feature.properties) {
    if (feature.properties.underConstruction === undefined || feature.properties.underConstruction === includeUnderConstruction) {
      return false;
    } else {
      return true;
    }
  }
  return true;
}

function busStyle(feature) {
  return {
    stroke: filter(feature)
  };
}

const layer = L.geoJSON(freeBus, {
  style: busStyle
}).addTo(map);

// Change style
includeUnderConstruction = true;
layer.setStyle(busStyle);