[GIS] Styling GeoJSON in leaflet based on properties

geojsonjavascriptleaflet

I'm new to Leaflet, Javascript and GIS Stackexchange. I've been working through various tutorials to try and produce what I need but I'm missing something.

Essentially I would like to produce a leaflet map with a drop down selector that changes the styling and popups of my geoJSON based on different attributes.

Here is my code:

var map = new L.Map('map', { zoomControl:false });

    map.dragging.disable();
    map.touchZoom.disable();
    map.doubleClickZoom.disable();
    map.scrollWheelZoom.disable();
    map.boxZoom.disable();
    map.keyboard.disable();

    // Create control for dropdown selector

    var selector = L.control({
        position: 'topright'
    });

    // Add content to the control

    selector.onAdd = function(map) {

    //create div container  

    var div = L.DomUtil.create('div', 'hazard_select');

    //create select element within container (with id, so it can be referred to later
    div.innerHTML = '<select id="hazard_select"><option value ="FL">River flood</option><option value ="EQ">Earthquake</option><option value ="DR">Water scarcity</option><option value ="CY">Cyclone</option><option value ="CF">Coastal flood</option><option value ="TS">Tsunami</option><option value ="VO">Volcano</option><option value ="LS">Landslide</option></select>';
      return div; 

    };

    // Add the selector to the map
     selector.addTo(map);

    var geojson, style_change, hazard;

    // Define a style

    function restylemap(feature, hazard) {


      if (feature.properties.FL_SUB !== null) {
          return {
          weight: 1.2,
          color: '#0072bc',
          fillColor: '#08519c',
          lineJoin: 'bevel',
          opacity: 1.0,
          fillOpacity: 1.0
          };
      }
      else if (feature.properties.FL_NAT !== null) {
          return {
          weight: 1.2,
          color: '#659ad2',
          fillColor: '#3182bd',
          lineJoin: 'bevel',
          opacity: 1.0,
          fillOpacity: 1.0
          };
      } 
      else if (feature.properties.FL_REG !== null) {
          return {
          weight: 1.2,
          color: '#659ad2',
          fillColor: '#6baed6',
          lineJoin: 'bevel',
          opacity: 1.0,
          fillOpacity: 1.0
          };
      } 
      else {
          return {
          weight: 1,
          color: '#afafaf',
          fillColor: '#bdd7e7',
          lineJoin: 'bevel',
          opacity: 1.0,
          fillOpacity: 1.0
          };
      }

    }

    // Get json through jquery

    $.getJSON("js/dataholdings.geojson", function(data) {
    geojson = L.geoJson(data, {
      style: restylemap,
      onEachFeature: function (feature, layer) {
       layer.bindPopup("Name: " + feature.properties.ADM0_NAME);
      },

    });

    // Add event listeners


    style_change = L.DomUtil.get("hazard_select");
    L.DomEvent.addListener(style_change, 'click', function(e) {
      L.DomEvent.stopPropagation(e);
    });

    L.DomEvent.addListener(style_change, 'change', changeHandler);


    function changeHandler(e) {
      geojson = L.geoJson(data,
      restylemap(geojson,e.target.value)  
      )
    }


    map.fitBounds(geojson.getBounds()); 
    geojson.addTo(map);
    });

This works to initially style the map, but not when the drop down is changed. I think the problem is with my changeHandler function, it passes on the Hazard ID to restylemap but then has the error "TypeError: undefined is not an object (evaluating 'feature.properties.FL_SUB')". As hazard is replacing feature?

I wanted to pass the Hazard variable into the 'Feature.properties' somehow, is this possible? i.e (feature.properties[hazard]& '_REG' !== null). Would this work?

Any advice gratefully received!

Best Answer

You guessed almost right, your restylemap function does not like receiving geojson,e.target.value as arguments, instead of its specified feature, hazard signature (where actually just feature is used).

So your error comes from passing geojson full Leaflet layer group in place of the expected feature plain single GeoJSON feature object. It tries to read the properties key of your geojson argument, but finds none, so it is undefined ("not an object"). Then it tries to read the FL_SUB key, but there is no key on undefined.

Furthermore, within your select change listener changeHandler, you re-assign your geojson variable but do not add it to map. Therefore it would duplicate your layers, without actually adding the new ones to the map.

For the above issue, you should rather use the .setStyle() method on your geojson variable within changeHandler instead of creating a new layer through L.geoJson.

Then you are left with specifying a new style function that depends on your select (drop-down) chosen value. For this, you should realize that you may need an array of styling functions, or a function that works on a 2-dimensional scale, since you seem to need 2 variables (your selected value and the layers feature properties). In your current restylemap, you use only the layers feature properties.