Choropleth mapbox of COVID-19 deaths - not working

Hi all, greeting from Brasil!

I’m trying to create a dash app to display information of covid-related deaths in Brasil, not only in the whole country but also in its states, intermediate regions and cities, so as population could be well informed before taking decisions.

I’m struggling to display information. So far I’'ve been able to compile all the information and produce a map, but such map is blank.

Here below the code:


import pandas as pd
import geopandas as gpd
import plotly.graph_objects as go
import plotly.offline as pyo
import json

Reading shapefile

path_zip_BR_estados = ‘zip://Deployment/Maps/br_unidades_da_federacao.zip’ #unidades da federação = estados = states
mapa_estados = gpd.read_file(path_zip_BR_estados)

Reading data about covid in Brasil.

data_covidbr = pd.read_csv(“https://brasil.io/dataset/covid19/caso?format=csv”,
dtype = {‘city_ibge_code’: ‘object’},
parse_dates = [‘date’] )

Organizing data to reach only state-related numbers

data_covidbr = data_covidbr[data_covidbr[‘place_type’] == ‘city’] ##removing summarized info about states.
last = data_covidbr[data_covidbr[‘is_last’]] #take only the most up-to-date values
df_states = last.groupby(‘state’).sum().reset_index()[[‘state’, ‘confirmed’, ‘deaths’]] ##summarizing from city level to state level: more accurate data

Converting shapefile to geojson

mapa_estados_WGS84 = mapa_estados.to_crs(epsg=4326)
mapa_estados_WGS84.to_file(‘area_states_json’, driver = “GeoJSON”)
with open(‘area_states_json’) as geofile:
area_states_json = json.load(geofile)

All geojson and data in place, let’s create the choropleth mapbox

trace = go.Choroplethmapbox(geojson=area_states_json,
featureidkey = ‘SIGLA_UF’,
locations=df_states[‘state’],
z=df_states[‘deaths’],
colorscale=“RdPu”,
zauto = True,
marker_opacity=0.5,
marker_line_width=0, visible = True)

data = [trace]

layout = go.Layout(title = ‘COVID-BR deaths in BRA states’)

fig = go.Figure(data = data, layout = layout)

fig.update_layout(mapbox_style=“carto-positron”,
mapbox_zoom=3,
mapbox_center = {“lat”: -15,
“lon”: -60})

fig.update_layout(margin={“r”:0,“t”:0,“l”:0,“b”:0})

pyo.plot(fig)


The output is an .html file in which we can see a nice map of South America but without no data on it.

What am I doing wrong?

Here below I’ll share the links for covid data and shapefile used:
covid br data: https://brasil.io/dataset/covid19/caso?format=csv (the same used in code).
states shapefile: https://www.ibge.gov.br/geociencias/organizacao-do-territorio/estrutura-territorial/15774-malhas.html?=&t=downloads (please look for br_unidades_da_federacao.zip file).

Many thanks in advance!

Fábio

Hi @ffernandes_BR,
Welcome to Plotly forum!
Mapbox supports epsg=3857 (Mercator projection).
Could you please display or paste here the following information:

area_states_json.keys()

and

area_states_json['features'][0].keys()

area_states_json['type'] must be 'FeatureCollection',
and for each feat in area_states_json['features'], feat['geometry']['type'] should be 'Polygon' or 'MultiPolygon'].

1 Like

Hi @empet! Many thanks for your kind answer.

First: I solved the espg=3857 using
mapa_estados = gpd.read_file(path_zip_BR_estados).to_crs(epsg=3857).

Now when I call
geodf_states.crs

I got:

Name: WGS 84 / Pseudo-Mercator
Axis Info [cartesian]:

  • X[east]: Easting (metre)
  • Y[north]: Northing (metre)
    Area of Use:
  • name: World - 85°S to 85°N
  • bounds: (-180.0, -85.06, 180.0, 85.06)
    Coordinate Operation:
  • name: Popular Visualisation Pseudo-Mercator
  • method: Popular Visualisation Pseudo Mercator
    Datum: World Geodetic System 1984
  • Ellipsoid: WGS 84
  • Prime Meridian: Greenwich

But the json part seems not working. When I call:
area_states_json.keys()

I got:

AttributeError Traceback (most recent call last)
in
----> 1 area_states_json.keys()

AttributeError: ‘str’ object has no attribute ‘keys’

And:
area_states_json['features'][0].keys()

leads to:

TypeError Traceback (most recent call last)
in
----> 1 area_states_json[‘features’][0].keys()

TypeError: string indices must be integers

So ite really seems the problem now is this json.

Any suggestion?

Many thanks again!

Fábio

Hi @empet ! I’ve news.

### Producing a json of area of States
x = mapa_estados.to_json().replace("'", "\"")
area_states_json = json.loads(x)

now we have:
area_states_json.keys()
dict_keys([‘type’, ‘features’])

area_states_json['type']
‘FeatureCollection’

and

area_states_json['features'][0].keys()
dict_keys([‘id’, ‘type’, ‘properties’, ‘geometry’])

The chart is still in blank.

Thanks!

@ffernandes_BR

After reading the shapefile try to display the geodataframe head:

mapa_estados.head()

If you can see the dataframe header, then insert the following lines of code:

mapa_estados.to_file('Brasil.json', driver = 'GeoJSON')
with open('Brasil.json') as geofile:
    area_states_json = json.load(geofile)  

and now try again to display the required keys.
If it doesn’t work yet, please give a link to the shapefile to try myself to download, and convert it to a geojson file.

Hi man.
Worked fine to generate the json.

mapa_estados.to_file('Brasil.json', driver = 'GeoJSON')
with open('Brasil.json') as geofile:
    area_states_json = json.load(geofile)

area_states_json.keys()
dict_keys([‘type’, ‘crs’, ‘features’])

area_states_json['features'][0].keys()
dict_keys([‘type’, ‘properties’, ‘geometry’])

area_states_json['type']
‘FeatureCollection’

area_states_json['features'][0]['type']
‘Feature’

area_states_json['features'][0]['geometry']
{‘type’: ‘Polygon’,
‘coordinates’: [[[-7278235.240100951, -1167423.6371186797],
[-7278083.193595216, -1166879.4836885305],
[-7278058.654771943, -1166454.1797676787],
[-7277993.706082956, -1166089.9899131104],
[-7277974.689708303, -1165845.332167231],
[-7277966.087939928, -1165151.1409410737],

[-7428798.478967099, -1091369.573713407],
[-7427836.40683577, -1091327.4550955116],
…]]}

@empet I think we are doing great progress. I understand the CRS and json issues are solved. Nevertheless the chart is still blank.

I’m wondering if the problem lies in the variables used to connect the json and dataframe. Look:

GeoJSON:
area_states_json['features'][18]['properties']['SIGLA_UF']
’RJ’

DataFrame:
df_states['state'].unique()
array([‘AC’, ‘AL’, ‘AM’, ‘AP’, ‘BA’, ‘CE’, ‘DF’, ‘ES’, ‘GO’, ‘MA’, ‘MG’,
‘MS’, ‘MT’, ‘PA’, ‘PB’, ‘PE’, ‘PI’, ‘PR’, ‘RJ’, ‘RN’, ‘RO’, ‘RR’,
‘RS’, ‘SC’, ‘SE’, ‘SP’, ‘TO’], dtype=object)

The trace is:

 trace = go.Choroplethmapbox(geojson = x_json, 
                            featureidkey = 'SIGLA_UF',
                            locations = df_states['state'],
                            z=df_states['deaths'],
                            colorscale = "RdPu",
                            zauto = True,
                            marker_opacity = 0.5, 
                            marker_line_width = 0, 
                            visible = True)

So I’m wondering if the trace call will read inside [‘features’][n][‘properties’][‘SIGLA_UF’] where n is each one of the features, or if ‘SIGLA_UF’ should be placed in another place.

What do you think?

Thanks!

Fábio

@empet I realized that there was a mistake in featureidkey. Now I corrected and made a much simpler code. Anyhow still not working.


import pandas as pd
import geopandas as gpd
import plotly.graph_objects as go
import plotly.offline as pyo
import json

path_zip_BR_estados = 'zip://Deployment/Maps/br_unidades_da_federacao.zip'
mapa_estados = gpd.read_file(path_zip_BR_estados).to_crs(epsg=3857)


### Converting to GeoJSON
mapa_estados.to_file('Brasil.json', driver = 'GeoJSON')
with open('Brasil.json') as geofile:
    area_states_json = json.load(geofile) 


### Reading data from web and adjusting it
data_covidbr = pd.read_csv("https://brasil.io/dataset/covid19/caso?format=csv", 
                      dtype = {'city_ibge_code': 'object'},
                      parse_dates = ['date'] )

### Keeping only the needed info
data_covidbr = data_covidbr[data_covidbr['place_type'] == 'state']
df_states = data_covidbr[data_covidbr['is_last']] #take only the most up-to-date values
df_states = df_states[['state', 'deaths']] 

### Plotting
fig = go.Figure(go.Choroplethmapbox(geojson = area_states_json,
                                    featureidkey = 'properties.SIGLA_UF',
                                    locations = df_states['state'], 
                                    z = df_states['deaths'],
                                    colorscale = "RdPu", 
                                    zmin = 0, 
                                    zmax = 10000,
                                    marker_opacity=0.5, 
                                    marker_line_width=1))
fig.update_layout(mapbox_style="carto-positron",
                  mapbox_zoom=3, mapbox_center = {"lat": -15, "lon": -60})
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()

states shapefile: https://www.ibge.gov.br/geociencias/organizacao-do-territorio/estrutura-territorial/15774-malhas.html?=&t=downloads (please look for br_unidades_da_federacao.zip file).

Thanks!

Hi @ffernandes_BR,

Because you posted the link to your shapefile only at the end of your post I thought it isn’t provided and I downloaded another Brasil shape file from the WEB, and got the choroplethmapbox : https://chart-studio.plotly.com/~empet/15685.

The right steps to read and convert a shapefile to a geojson are as follows:

gdf = gpd.read_file("shapefile_name.shp", encoding='utf-8')
gdf.to_file('brasil_1.json', driver = 'GeoJSON')
with open('brasil_1.json') as geofile:
    jdataBra = json.load(geofile)  
jdataBra.keys()

In your initial post you had this line:

mapa_estados_WGS84 = mapa_estados.to_crs(epsg=4326)]

and I told you that mapbox supports epsg=3857, and did not say that you must perform a change of epsg, but just to display the keys in your geojson dict.

This line:
mapa_estados = gpd.read_file(path_zip_BR_estados).to_crs(epsg=3857) .
led to big point coordinates in the Polygon definition.
This line must be removed, as you can see from the above notebook, because:

gdf.to_file('brasil_1.json', driver = 'GeoJSON')

does the conversion to a geojson, that can be read by mapbox.

1 Like

Hi @empet! Just to let you know that worked!! Many thanks.

I just made some arrangements:

I didn’t understand this:
locations = [ f'BR.{st}' for st in df_states['state']]

And also this:
featureidkey='properties.HASC_1'

So I just left:
locations = [ f'{st}' for st in df_states['state']]

and

featureidkey='properties.SIGLA_UF'

and all worked fine.

Many thanks for you huge support and attention.

All the best,

Fábio