3

I would like to filter the markers on my (geojson) layer according to two features: year and type of event, ideally using both filters (year and eventType) as check-boxes without using layerGroup at all. I would like to be able to click on a checkbox and update dynamically the markers by filtering them according to feature.properties.eventType too, in addition to the year displayed, something like

  <input type="checkbox" id="all" name="all" value="all">
  <label for="all">All</label>
  <input type="checkbox" id="funfair" name="funfair" value="newsletter">
  <label for="funfair">Funfair</label>
  <input type="checkbox" id="trade" name="trade" value="trade">
  <label for="trade">Trade</label>
  <input type="checkbox" id="exhibition" name="exhibition" value="exhibition">
  <label for="exhibition">Paid exhibition</label>

etc... which would trigger a

filter: function(feature, layer) {
  return feature.properties.eventType == value-of-checkbox;
}

I have five types of eventType. So, for instance, if 1890 is checked, the map is showing all 1890 events, no matter what kind of event it is (because by default it would show all events). If the user checks the 'funfair' and the 'trade' checkbox, though, I want the map to show only the 1890 events of the funfair and trade kind, and not other kind of events.

This is my complete script, which so far is working fine with filtering by year.

var oldmap = L.tileLayer('address', {
  maxZoom: 12
}),

var modernmap = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
  maxZoom: 12,
  id: 'mapbox.streets',
  accessToken: 'mytoken'
});

var map = L.map('map', {
  center: [40.1857864,-4.5536861],
  zoom: 7,
  layers: [oldmap, modernmap]
});

var baseMaps = {
  "Old map": oldmap,
  "Modern map": modernmap
};

var jsontest = {"type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates":  [ -16.5471268,28.4136726 ]
      },
      "properties": {
        "Name":"Point1",
        "year":'1892',
        "eventType":"funfair",
        "notes":""
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates":  [ 2.1753895,41.3767925 ]
      },
      "properties": {
        "Name":"Point2",
        "year":'1890',
        "eventType":"trade demonstration",
        "notes":""
      }
    }
  ]};

var mapLayerGroupsYear = [];

function onEachFeature(feature, layer) {
  var year = mapLayerGroupsYear[feature.properties.year];
  if (year === undefined) {
    year = new L.layerGroup();

    //add the layer to the map
    year.addTo(map);

    //store layer
    mapLayerGroupsYear[feature.properties.year] = year;
  }

  //add the feature to the layer
  year.addLayer(layer);   
}


var myLayer = L.geoJSON(jsontest, {
  onEachFeature: onEachFeature
})

L.control.layers(baseMaps, mapLayerGroupsYear).addTo(map);

I am a beginner with leaflet so please forgive the dumb question, in case it is.

3

I propose to have <input> checkboxes for both the years and the event types outside the map container. The filter function in the L.GeoJson definition rejects every feature that does not have both the respective checkboxes for its year and for its event type checked.

const geojsonLayer = L.geoJSON(null,{
    filter: (feature) => {
        const isYearChecked = checkboxStates.years.includes(feature.properties.year)
        const isEventTypeChecked = checkboxStates.eventTypes.includes(feature.properties.eventType)
        return isYearChecked && isEventTypeChecked //only true if both are true
    }
}).addTo(map)

Now every time the state of a checkbox changes, all markers are removed, checkboxStates is updated and then data is added again, going through the filter.

for (let input of document.querySelectorAll('input')) {
    //Listen to 'change' event of all inputs
    input.onchange = (e) => {
        geojsonLayer.clearLayers()
        updateCheckboxStates()
        geojsonLayer.addData(jsontest)   
    }
}

The utility function updateCheckboxStates is here. It assumes that the <input> elements have a class of either event-type or year.

function updateCheckboxStates() {
    checkboxStates = {
        years: [],
        eventTypes: []
    }

    for (let input of document.querySelectorAll('input')) {
        if(input.checked) {
            switch (input.className) {
                case 'event-type': checkboxStates.eventTypes.push(input.value); break
                case 'year': checkboxStates.years.push(input.value); break
            }
        }
    }
}

Here is a working jsfiddle: https://jsfiddle.net/newluck77/rk9v0uyo/


Edit: And three screenshots comparing behaviour:

All events

All checkboxes ticked

Trade demonstrations and events of 1892 excluded

No trade demonstrations and no events of 1892

Events of 1892 added again (trade demonstrations stay excluded)

No trade demonstrations

  • Hi @Stefan, thanks so much for replying and the fiddle! I was able to reach a similar result using the filter. My issues begin when I combine the eventType filtering with the year filtering. I can't find the year layering in your code/fiddle. Could you please confirm if/how/ this would work nicely with year filtering as well? – user134763 Jan 9 at 10:27
  • You have year in your layers control, right? If you uncheck the year there, all markers with that year will disappear. – Stefan Jan 9 at 12:16
  • Oh and if you have a working solution, post it as answer so that others can learn from it. – Stefan Jan 9 at 12:18
  • 1
    Thanks so much @Stefan, that's definitely getting close. It's still misbehaving, though. For instance, if you go here: tinyurl.com/ycju8lqd; deselect all the checkboxes and leave only 'funfair'; deselect all the years; check only one year (say, 1890), you get all the markers from that year, regardless of their eventType. I guess this is because there's no "onchange" check on the year checkboxes, is that correct? – user134763 Jan 9 at 16:21
  • 1
    Sorry, my bad - I CAN'T BELIEVE IT, IT'S WORKING!!!! :D This is just fantastic, thank you so much. I'll take my time to test it properly tomorrow. You rock! – user134763 Jan 9 at 23:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.