Search code examples
javascriptleafletinteractivegeochoropleth

Leaflet - Interactive choropleth map over time


I have data of, say, density over 30 districts from 2000 to 2010. I'd like to make an interactive choropleth map for each year and then either use a slider (ideally) or a radio button to select between years.

I can get interactivity on the first year, but NOT on the layers for other years. You can see a working example here, but let me put some details below:

For simplicity, consider just two years. blocks1995 has the non-overlapping polygons BlockA and BlockB (the two districts) and blocks1996 has the same blocks. They have a property called density that produces the choropleth:

var blocks1995 = {
    "type": "FeatureCollection",
    "crs": {
        "type": "name",
        "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" }
    },
    "features": [{
        "type": "Feature",
        "properties": { "time": 1995, "density": 3.1, "nameB": "BlockA" },
        "geometry": {
            "type": "Polygon",
            "coordinates": [[[50.0, 29.0],[50.0, 29.99],[50.51, 29.99],[50.0, 29.0]]]
        }
    }, {
        "type": "Feature",
        "properties": { "time": 1995, "density": 1.1, "nameB": "BlockB" },
        "geometry": {
            "type": "Polygon",
            "coordinates": [[[50.01, 30.0],[50.52, 30.0],[50.52, 30.5]]]
        }
    }]
};

var blocks1996 = {
    "type": "FeatureCollection",
    "crs": {
        "type": "name",
        "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" }
    },
    "features": [{
        "type": "Feature",
        "properties": {"year": 1996, "density": 2.2, "name": "BlockA" },
        "geometry": {
            "type": "Polygon",
            "coordinates": [[[50.0, 29.0],[50.0, 29.99],[50.51, 29.99]]]
        }
    }, {
        "type": "Feature",
        "properties": {"year": 1996,"density": 1.2,"name": "BlockB"},
        "geometry": {
            "type": "Polygon",
            "coordinates": [[[50.01, 30.0],[50.52, 30.0],[50.52, 30.5]]]
        }
    }]
};

I've tried adding them to an OverlayLayer

var blocks1995Layer = L.layerGroup([ L.geoJson(blocks1995)]),
    blocks1996Layer = L.layerGroup([ L.geoJson(blocks1996)]);


var overlayMaps = {
    "Blocks 1995": blocks1995Layer,
    "Blocks 1996": blocks1996Layer
};

var map = new L.map('map', {layers:[blocks1995Layer]})
    .setView([29, 50], 7);

I put the boilerplate interactivity code found in this Leaflet interactive choropleth tutorial and then I add back to the map:

geojson = L.geoJson(blocks1995, {
    style: density_style,
    onEachFeature: addToFeature
}).addTo(map);

L.control.layers(null, overlayMaps).addTo(map);

The problem is I'm adding interactivity to blocks1995 only, but I haven't been able to add it to overlayMaps.

I'm OK using a Leaflet plug-in (I tried TimeSlider but also couldn't figure it out).

Another possibility is to combine the two block1995and block1996 variables into one with an extra feature year or time is that makes things easier. The idea would be for Leaflet to query by time (say, when a slider moves) and produce the interactive choropleth per year.

Thanks!


Solution

  • Basically, you're not adding layers to control properly. Currently, you're doing this

    var blocks1995Layer = L.layerGroup([ L.geoJson(blocks1995)]),
        blocks1996Layer = L.layerGroup([ L.geoJson(blocks1996)]);
    
    var overlayMaps =  {
        "Blocks 1995": blocks1995Layer,
        "Blocks 1996": blocks1996Layer
    };
    
    geojson = L.geoJson(blocks1995, {
        style: density_style,
        onEachFeature: addToFeature
    }).addTo(map);
    

    Instead, try this

    geojson = L.geoJson(blocks1995, {
        style: density_style,
        onEachFeature: addToFeature
    }).addTo(map);
    
    geojson1 = L.geoJson(blocks1996, {
        style: density_style,
        onEachFeature: addToFeature
    }).addTo(map);
    
    var overlayMaps =  {
        "Blocks 1995": geojson,
        "Blocks 1996": geojson1
    };
    

    Here is a working example

    Here is another example where I've implemented radio buttons instead of checkboxes using this plugin

    Edited

    As per your comment, I've created a example using this leaflet time slider plugin. Here is the part of the code.

    //I've created 5 geojson layers, in order the slider to look appropriate.
    geojson = L.geoJson(blocks1995, {
        style: density_style,
        onEachFeature: addToFeature,
        time: "1995" //this is for labeling, you may alter this value if required
    });
    
    geojson1 = L.geoJson(blocks1996, {
        style: density_style,
        onEachFeature: addToFeature,
        time: "1996"
    });
    
    geojson2 = L.geoJson(blocks1997, {
        style: density_style,
        onEachFeature: addToFeature,
        time: "1997"
    });
    
    geojson3 = L.geoJson(blocks1998, {
        style: density_style,
        onEachFeature: addToFeature,
        time: "1998"
    });
    
    geojson4 = L.geoJson(blocks1999, {
        style: density_style,
        onEachFeature: addToFeature,
        time: "1999"
    });
    
    //now add each geojson layer to a single layer group, as the slider take only one layer
    var layerGroup = L.layerGroup([geojson, geojson1, geojson2, geojson3, geojson4 ]);
    
    //initiate slider, follow = 1 means, show one feature at a time
    var sliderControl = L.control.sliderControl({layer:layerGroup, follow: 1});
    map.addControl(sliderControl);//add slider to map
    sliderControl.startSlider();//starting slider
    

    Here is the working example