Leaflet – Filtering and Adding GeoJSON Features Using Function in Leaflet

functiongeojsonleaflet

I have a Leaflet map where I am adding the features from a GeoJSON file. I am able to filter the features depending on the properties in the GeoJSON.

I am writing out the same giant block of code each time I add a layer and just changing the variables. I leave off the .addTo(map) and load it later from the layerControl by clicking on a checkbox.

I wrote a function that filters and adds the layers. But it only works if I add it to the map on load — addTo(map) inside the function. Which loads it with initial paint of the map. I want to add it later, using the checkboxes in the L.control.layers menu.

I've tried several different approaches, removing the .addTo(map) from the function and adding it to the end of the function call. I've also tried calling the function from within the L.control.layers group. I left some comments in the snippet and some comments on what I have tried after the snippet. I may be going about this all wrong.

Here is what I have:

var grayscale = new L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}', {attribution: 'Tiles © Esri — Esri, DeLorme, NAVTEQ', maxZoom: 16 })
var mapOptions = {
     center: [44.2457, -116.9228],
     zoom: 14,
                 layers: [grayscale]
 }

var dog1 = new L.marker([44.2441, -116.9228]).bindPopup('This is just a placeholder.')

map = new L.Map("map", mapOptions);

// ========  function call
var fence2 = newFeature('red', 'fence')
//console.log(fence2) returns 'undefined' but it appears on the map?!
var dog = newFeature('red', 'dog')
// var dog4 = newFeature('blue', 'dog').addTo(map) // with .addTo(map) removed in the function -- nope!

// layercontrol
var baseMaps = {
"Grayscale": grayscale,
};
var overlayMaps = {
"new L.marker dog": dog1,
"not from function": groups.dogs,
"add function feature here after load": dog1   
};
L.control.layers(baseMaps, overlayMaps, { collapsed:false }).addTo(map);
function newFeature(eColor, group){
L.geoJson(myGeoJson, {
 filter: function(feature, layer) {
   return (feature.properties.farm === "Kerner" && feature.properties.group === group);
 },
 onEachFeature: function(feature, layer) {
 layer.bindPopup('farm: ' + feature.properties.farm + '<br />Notes: ' +                                    
feature.properties.description)},

style: function (feature) {
        return {
            color: eColor,
            weight: 3,
            opacity: 1,
            dashArray: 4
        }
  }
    // !!!only works if I add to map here inside the function
    // !!! adds the layer on load
  }).addTo(map)
  //})
  }

If I remove the .addTo(map) from the function and add it when I call the function:

var dog = newFeature('red', 'dog').addTo(map) 

results in this error:

Uncaught TypeError: Cannot read properties of undefined (reading 'addTo')"

Even if the function generates a visible layer, if I add the var to a L.control.layer it breaks the layer control.

var overlayMaps = {

"kerner": fence2 //  the L.control disappears from the map.
 // console - Uncaught TypeError: Cannot read properties of undefined (reading 'setZIndex')

All fine with

.addTo(map)

called like this:

var dog = newFeature('red', 'dog')

inside the function. But that is not the result I am looking for.

Here is a working, but not as needed, concept jsfiddle

and a sample of the geojson. No surprises there.

var myGeoJson = [{
"type": "FeatureCollection",
"features": [
    {
        "type": "Feature",
        "geometry": {
            "type": "LineString",
            "coordinates": [
                [
                    -116.921847,
                    44.245915
                ],
                [
                    -116.921986,
                    44.245442
                ],
                [
                    -116.922721,
                    44.245423
                ]
            ]
        },
        "properties": {
            "name": "Calf Pasture",
            "farm": "Kerner",
            "description": "pasture fence N end.",
            "group": "fence"
        }
    },{
        "type": "Feature",
        "geometry": {
            "type": "LineString",
            "coordinates": [
                [
                    -116.915847,
                    44.243915
                ],
                [
                    -116.915986,
                    44.243442
                ],
                [
                    -116.916721,
                    44.243423
                ]
            ]
        },
        "properties": {
            "name": "Other Pasture",
            "farm": "Kerner",
            "description": "other pasture fence S end.",
            "group": "fence"
        }
    },{
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": 
                [-116.9210, 44.2450]
        },
        "properties": {
            "name": "foodo",
            "farm": "Kerner",
            "description": "another dog yet.",
            "group": "dog"
        }
    }
    ]
    }];

Best Answer

There is just one small mistake in your code.

In the function newFeature(eColor, group) you are not returning created layer, so any use of the function result fails.

Code should look something like this:

function newFeature(eColor, group){
  var vectorLayer = L.geoJson(myGeoJson, {
    filter: function(feature, layer) {
      return (feature.properties.farm === "Kerner" && feature.properties.group === group);
    },
    onEachFeature: function(feature, layer) {
      layer.bindPopup('farm: ' + feature.properties.farm + '<br />Notes: ' +                                    
      feature.properties.description)
    },
    style: function (feature) {
      return {
        color: eColor,
        weight: 3,
        opacity: 1,
        dashArray: 4
      }
    }
  });
  return(vectorLayer);  // added function return value
}

var dog = newFeature('red', 'dog').addTo(map);
var fence2 = newFeature('red', 'fence');

var overlayMaps = {
  "kerner": fence2
};

L.control.layers(baseMaps, overlayMaps, {collapsed: false}).addTo(map);
Related Question