Skip to content

LeafletfeatureGroups`

A feature group, which is Leaflet’s container for dealing with multiple groups of data layers.

In our js/init.js file we will add some variables to store our groups in:

let vaccinated = L.featureGroup();
let nonVaccinated = L.featureGroup();

Important!

Important!!! These variables have to be OUTSIDE the function, because Leaflet needs to be able to use them. Remember: A variable outside of a function is said to be in the global scope.

Adding L.featureGroups() in our addMarker() function

Next we will need to change our addMarker function to add each marker to their respective groups instead of just directly onto the map:

// our Leaflet feature group layers waiting for content!
let vaccinated = L.featureGroup();
let nonVaccinated = L.featureGroup();

function addMarker(data){
    if(data['Have you been vaccinated?'] == "Yes"){
        vaccinated.addLayer(L.marker([data.lat,data.lng]).addTo(map).bindPopup(`<h2>Vaccinated</h2>`))
        createButtons(data.lat,data.lng,data['What zip code do you live in?'])
        }
    else{
        nonVaccinated.addLayer(L.marker([data.lat,data.lng]).addTo(map).bindPopup(`<h2>Unvaccinated</h2>`))
        createButtons(data.lat,data.lng,data['What zip code do you live in?'])
    }
    return data
}

Awesome! Now let’s see our beautiful layers!

Cleaning up the addMarker() function

We also need to delete the line .addTo(map) after each marker, because we will add the featureGroup to the map instead of individual markers:

// our Leaflet feature group layers waiting for content!
let vaccinated = L.featureGroup();
let nonVaccinated = L.featureGroup();

function addMarker(data){
    if(data['Have you been vaccinated?'] == "Yes"){
        vaccinated.addLayer(L.marker([data.lat,data.lng]).bindPopup(`<h2>Vaccinated</h2>`))
        createButtons(data.lat,data.lng,data['What zip code do you live in?'])
        }
    else{
        nonVaccinated.addLayer(L.marker([data.lat,data.lng]).bindPopup(`<h2>Unvaccinated</h2>`))
        createButtons(data.lat,data.lng,data['What zip code do you live in?'])
    }
    return data
}
Wait… our markers dispearred after we did that, why?

Since we removed addTo(map), we now need to add the group layers to to map!!

Adding our group layers to the Leaflet map

As alluded to in the answer above, we need to add the grouped layers to our Leaflet map. To do so, we will add the layers to the map, so we need to add this code somewhere:

vaccinated.addTo(map)
nonVaccinated.addTo(map)

Where to add the groupLayers()?

If you answered, at the end of the processData() function, you are correct (and sneaky if you read ahead during the lab!)!

function processData(results){
    console.log(results)
    results.data.forEach(data => {
        console.log(data)
        addMarker(data)
    })
    vaccinated.addTo(map) // add our layers after markers have been made
    nonVaccinated.addTo(map) // add our layers after markers have been made  
}

Why?

We have to add our layers after the forEach loop finishes, otherwise the code will run only for the first marker.

Layer Controls

One big benefit of using Leaflet FeatureGroups is that we can add controls to the map which allows us to turn on and off layers.

Let’s add our list of layers as an object:

// define layers
let layers = {
    "Vaccinated Respondent": vaccinated,
    "Unvaccinated Respondent": nonVaccinated
}

The first property name is our alias or what the users will see, and the property key is the Leaflet FeatureGroup that we created earlier.

Next we will add a Leaflet controlGroup right after the map is initalized.

// add layer control box
L.control.layers(null,layers).addTo(map)

You can actually turn on and off layers using buttons outside of Leaflet controls by creating a function to add and remove layers.

We can also add a fitBounds to our layers to make sure our map zooms into our layer:

// make the map zoom to the extent of markers
map.fitBounds(nonVaccinated.getBounds());

Hmm, that only works for one layer group, so let’s create a new feature group that contains all of our layers:

let allLayers = L.featureGroup([vaccinated,nonVaccinated]);
map.fitBounds(allLayers.getBounds());     

That code should go at the end of our processData function so it fits all of our layers and markers.

Our processData function should look like the following:

function processData(results){
    console.log(results)
    results.data.forEach(data => {
        console.log(data)
        addMarker(data)
    })
    vaccinated.addTo(map) // add our layers after markers have been made
    nonVaccinated.addTo(map) // add our layers after markers have been made  
    let allLayers = L.featureGroup([vaccinated,nonVaccinated]);
    map.fitBounds(allLayers.getBounds());
}

Now that we have the pop-up change based on our fields, let’s actually change how the marker looks!

🏁Check point

Before moving on, check to see if your JavaScript looks like the following:

js/init.js
// declare variables
let mapOptions = {'center': [34.0709,-118.444],'zoom':5}

let vaccinated = L.featureGroup();
let nonVaccinated = L.featureGroup();

let layers = {
    "Vaccinated Respondent": vaccinated,
    "Unvaccinated Respondent": nonVaccinated
}

const dataUrl = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSNq8_prhrSwK3CnY2pPptqMyGvc23Ckc5MCuGMMKljW-dDy6yq6j7XAT4m6GG69CISbD6kfBF0-ypS/pub?output=csv"

// define the leaflet map
const map = L.map('the_map').setView(mapOptions.center, mapOptions.zoom);

// add layer control box
L.control.layers(null,layers).addTo(map)

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

function addMarker(data){
    if(data['Have you been vaccinated?'] == "Yes"){
        vaccinated.addLayer(L.marker([data.lat,data.lng]).bindPopup(`<h2>Vaccinated</h2>`))
        createButtons(data.lat,data.lng,data['What zip code do you live in?'])
        }
    else{
        nonVaccinated.addLayer(L.marker([data.lat,data.lng]).bindPopup(`<h2>Unvaccinated</h2>`))
        createButtons(data.lat,data.lng,data['What zip code do you live in?'])
    }
    return data
}

function createButtons(lat,lng,title){
    const newButton = document.createElement("button"); // adds a new button
    newButton.id = "button"+title; // gives the button a unique id
    newButton.innerHTML = title; // gives the button a title
    newButton.setAttribute("lat",lat); // sets the latitude 
    newButton.setAttribute("lng",lng); // sets the longitude 
    newButton.addEventListener('click', function(){
        map.flyTo([lat,lng]); //this is the flyTo from Leaflet
    })
    const spaceForButtons = document.getElementById('placeForButtons')
    spaceForButtons.appendChild(newButton);//this adds the button to our page.
}

function loadData(url){
    Papa.parse(url, {
        header: true,
        download: true,
        complete: results => processData(results)
    })
}

function processData(results){
    console.log(results)
    results.data.forEach(data => {
        console.log(data)
        addMarker(data)
    })
    vaccinated.addTo(map) // add our layers after markers have been made
    nonVaccinated.addTo(map) // add our layers after markers have been made  
    let allLayers = L.featureGroup([vaccinated,nonVaccinated]);
    map.fitBounds(allLayers.getBounds());
}

loadData(dataUrl)