SoftEngStudent
SoftEngStudent

Reputation: 159

Adding data from URL to a leaflet map

I am trying to add covid Data from a URL to a leaflet map. The issue is that my map is loading and drawing before the data has fully loaded. I have a file of JSON data which I call statesData. Then, I use the p5.js loadJSON() function to grab covid data from a URL and another function to format that data.

 // GET THE COVID DATA
    function setup(){
        loadJSON("https://disease.sh/v3/covid-19/states",gotData);
    }
    function gotData(data){
        covid = data;

        statesData.features[1].properties.casesPerOneMillion = covid[1].casesPerOneMillion;
        // add covid cases to states data
        for (let i = 0; i < statesData.features.length; i++) {
            for (let j = 0; j < statesData.features.length; j++) {
                if (statesData.features[i].properties.name === covid[j].state) {
                    statesData.features[i].properties.casesPerOneMillion = covid[j].casesPerOneMillion;
                }
            }
        }
    };

Here's an example of what the data looks like when I inspect the page and print it to the console: enter image description here

Here is how I'm adding the color layers:

    // GET CHLORO COLORS BASED ON CASES PER MIL NUM
    function getColor(d) {
        return d > 30000 ? '#800026' :
            d > 25000  ? '#BD0026' :
                d > 20000  ? '#E31A1C' :
                    d > 15000  ? '#FC4E2A' :
                        d > 5000   ? '#FD8D3C' :
                            d > 2000   ? '#FEB24C' :
                                d > 1000   ? '#FED976' :
                                    '#FFEDA0';
    }
    
    // CREATE FUNCTION TO STYLE AND APPLY GET COLOR
    function style(feature) {
        return {
            // apply get color
            fillColor: getColor(feature.properties.casesPerOneMillion),
            weight: 2,
            opacity: 1,
            color: 'white',
            dashArray: '3',
            fillOpacity: 0.7
        }
    }
    // doesn't work
    L.geoJson(statesData, {style: style}).addTo(map);

I know it isn't an issue with the code, because this (5000 being a random num I used just to be sure my style code was working) :

statesData.features[0].properties.casesPerOneMillion = 5000;

and it does work that way. The problem is that my map is loading before the data has finished downloading/has gone through the gotData() function that formats it. I thought the obvious solution would be to throw the L.geoJson(statesData, {style: style}).addTo(map); into an async function, but I guess leaflet doesn't allow you to do that? I always get the error "t.addLayer()" isn't a function when I try... Anyone know how to asynchronously add data to a leaflet map? I'm familiar with javaScript but am a novice when it comes back to async functions, callbacks, etc

Upvotes: 3

Views: 1104

Answers (3)

SoftEngStudent
SoftEngStudent

Reputation: 159

With the help of answers above, I determined my original problem was that I was trying to define geo and add it to map at the same time. geo needed to be defined and added to the map outside of the gotData() function. Inside the gotData() function, the addData() function solved the problem.

 geo = L.geoJson(statesData, {
        style: style,
        onEachFeature: onEachFeature,

    });

// *** GET THE COVID DATA *** //
function setup(){
    loadJSON("https://disease.sh/v3/covid-19/states",gotData);
}

// *** FORMAT THE DATA *** //
// adds casesPerMillion from queried data to states data
function gotData(data){
    covid = data;
    statesData.features[1].properties.casesPerOneMillion = covid[1].casesPerOneMillion;
    // add covid cases to states data
    for (let i = 0; i < statesData.features.length; i++) {
        for (let j = 0; j < statesData.features.length; j++) {
            if (statesData.features[i].properties.name === covid[j].state) {
                // add cases per million to statesData
                statesData.features[i].properties.casesPerOneMillion = covid[j].casesPerOneMillion;
                // add tests per million to statesData
                statesData.features[i].properties.testsPerOneMillion = covid[j].testsPerOneMillion;
                break;
            }
        }
    }
    geo.addData(statesData); // another part of the solution - addData function

};

geo.addTo(map);

Upvotes: 0

Christian Fritz
Christian Fritz

Reputation: 21364

You don't need to use async/await for asynchronous code. The default is still to use the, now somewhat old-school, callback tree. So just put your L.geoJson call inside the gotData callback.

This should work. I've allowed myself to replace the getColor if-then-else monster into a lookup table. Just for style points :-)

const colorMap = {
  30000: '#800026',
  25000: '#BD0026',
  20000: '#E31A1C',
  15000: '#FC4E2A',
  5000: '#FD8D3C',
  2000: '#FEB24C',
  1000: '#FED976',
};

// GET CHLORO COLORS BASED ON CASES PER MIL NUM
const getColor = (d) => colorMap(d) || '#FFEDA0';

// CREATE FUNCTION TO STYLE AND APPLY GET COLOR
function style(feature) {
  return {
    // apply get color
    fillColor: getColor(feature.properties.casesPerOneMillion),
    weight: 2,
    opacity: 1,
    color: 'white',
    dashArray: '3',
    fillOpacity: 0.7
  }
}

// GET THE COVID DATA
function setup(){
  loadJSON("https://disease.sh/v3/covid-19/states", gotData);
}

function gotData(statesData){
  covid = statesData;
  
  statesData.features[1].properties.casesPerOneMillion = covid[1].casesPerOneMillion;
  // add covid cases to states data
  for (let i = 0; i < statesData.features.length; i++) {
    for (let j = 0; j < statesData.features.length; j++) {
      if (statesData.features[i].properties.name === covid[j].state) {
        statesData.features[i].properties.casesPerOneMillion = covid[j].casesPerOneMillion;
      }
    }
  }
  
  // now it should work
  L.geoJson(statesData, {style: style});
};

Update: As Falke Design points out, there was another bug in gotData: data needed to be statesData. I changed that.

Upvotes: 3

Falke Design
Falke Design

Reputation: 11338

Your function gotData will not work, because you never loop over data, you loop twice over statesData.features.length

You can do this in two ways.

1. define L.geoJSON after loading data

function gotData(data) {
    var covid = data;
    // add covid cases to states data
    for (let j = 0; j < data.length; j++) {
        for (let i = 0; i < statesData.features.length; i++) {
            if (statesData.features[i].properties.name === covid[j].state) {
                statesData.features[i].properties.casesPerOneMillion = covid[j].casesPerOneMillion;
                break; // break the loop and go to the next state (outer loop), because you have already found the correct state entry
            }
        }
    }

    L.geoJSON(statesData, {
        style: style
    }).addTo(map);
};

2. define L.geoJSON before and add only the loaded data

var geo = L.geoJSON(null, {
    style: style
}).addTo(map);

function gotData(data) {
    var covid = data;
    // add covid cases to states data
    for (let i = 0; i < data.length; i++) {
        for (let j = 0; j < statesData.features.length; j++) {
            if (statesData.features[i].properties.name === covid[j].state) {
                statesData.features[i].properties.casesPerOneMillion = covid[j].casesPerOneMillion;
                break; // break the loop and go to the next state (outer loop), because you have already found the correct state entry
            }
        }
    }

    geo.addData(statesData);
};

Upvotes: 1

Related Questions