I try to set up a filterable "shot chart" like http://graphics.latimes.com/kobe-every-shot-ever/ with leaflet. I've a geojson file thats stores all the shots data, I've them showing up in a layer.


  "type": "Feature",
  "properties": {
    "ID": 2,
    "SEASON_SHO": "1998-1999",
    "GAME_TYPE": "Regular",
    "PERIOD": 1,
    "MINUTES_RE": "06:25:00",
    "ACTION_TYP": "Jump Shot",
    "SHOT_TYPE": "2-PT-Wurf",
    "SHOT_DISTA": 15,
    "GAME_DATE": "05.02.1999",
    "HEIM": "SEA",
    "GAST": "DAL",
    "GEGNER": "Seattle SuperSonics",
    "SHOT_ATTEM": 1,
    "SHOT_MADE_": 0
  "geometry": {
    "type": "Point",
    "coordinates": [

I've set up an layer for all shots and a layergroup to filter the markers.

var url = 'data/Wuerfe.geojson';
var allewuerfe;

allewuerfe = L.geoJson(null, {

    onEachFeature: onEachFeature,
    style: style,
    pointToLayer: function (feature, latlng) {
        return L.circleMarker(latlng, allewuerfe);

$.getJSON(url, function (data) {

var data = L.layerGroup([

I already have some filters-buttons for single events, like "reset".

<button class="btn reset-btn" id="all">Reset</button>

document.getElementById("all").addEventListener('click', function (event) {

But what I'd like to set up is an dropdown-filter for "shots missed", "shots hit" and "all shots".

           <select name="shot-result" class="custom-select" id="shotresult">
                <option value="all">All Shots</option>
                <option value="Made">Made</option>
                <option value="Missed">Missed</option>

The data-info for that is stored in the geojson at "SHOT_MADE_", 1 means shot was made, 0 shot was missed.

I would like to get an code-example of how to get the filter work.


Here is a working example of filtering, I only show 2 options and start with one, then depending on which radio button selected, the data changes. http://www.gistechsolutions.com/leaflet/DEMO/filter/filter.html

You basically have a layergroup, and on change, clear it and remove it from the map. Then


Then your layer has the filter in it's onEachFeature function:

    filter: function(feature, layer) {   
         return (feature.properties.SHOT_MADE_,  == 1 );

Then you just add the layer to the layergroup and add it to the map.

  • Thanks Bill! this works just fine to filter a couple of items, but I would also like to filter mulitple option-categorys like all teams (there are 30+ of them I'd like to store in a dropdown menu) - so for that, I need a different solution, right? – bvboisseree Feb 19 at 15:08

With 30 some teams and several conditions using 2 select dropdowns, something like this may be better.

This is mostly pseudo code, two selects call one "Team", the other "Conditions", set the conditions to fire onchange="SelectPoints();"

//Using a Layer Group to add/ remove data from the map.
//initial setup your layer as "myLayer" and add it to the grouplayer.
var myData =  L.layerGroup([]);

var selPts = [];

function SelectPoints(){
    var team = document.getElementById("Team").value;
    var cond= document.getElementById("Conditions").value;

    //Clear existing data 

    selPts.length =0;  //Reset the array if selecting new points

    myLayer.eachLayer(function (layer) {

        // query  (Rough example/ something like this)
        if (  GEGNER  == team &&  SHOT_MADE_ ==cond  ) {

    //Symbolize the Selected Points
         geojsonLayer = L.geoJson(selPts, {

            pointToLayer: function(feature, latlng) {
                return L.circleMarker(latlng, {
                radius: 4, //expressed in pixels circle size
                color: "green", //or whatever
                stroke: true,
                weight: 7,      //outline width  increased width to look like a filled circle.
                fillOpcaity: 1
        //Add selected points back into map as green circles.

For this particular scenario, I'd use the GeoJSON data to create several instances of L.GeoJSON (one per filter) and then use a L.Control.Layers to switch them off and on, something like:

.then(function(response){ return response.json() })

  var missedShots = L.geoJson(data, { 
    filter: function(feature) { return feature.properties.SHOT_MADE_ == 0 }

  var madeShots = L.geoJson(data, { 
    filter: function(feature) { return feature.properties.SHOT_MADE_ == 1 }

  var allShots = L.layerGroup([madeShots, missedShots]);


    "All shots": allShots,
    "Made shots": madeShots,
    "Missed shots": missedShots


Note that this method parses the GeoJSON data only once.

If you really want a drop-down menu instead of the default L.Control.Layers, then I encourage you to check out the layer switchers section of the Leaflet plugins list.

Using this technique over a lot of categories is also possible (pseudo-code follows, as I'm not sure how the teams would be structured:

var allTeams = L.layerGroup().addTo(map);
var teamLayers = { "All teams": allTeams };

for (var team in teams) {
   var teamLayer = L.geoJson(data, {
     filter: function(feat) { return feat.properties.team === team; }
   var teamName = 'Team ' + team;
   teamLayers[ teamName ] = teamLayer;


Note how this is functionally the same as the other code block above, only using data structures that grow instead of hard-coded data structures.

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.