Choropleth map error: Empty map

I’m trying to replicate this map for Sweden (See US unemployment Choropleth map): https://plotly.com/python/mapbox-county-choropleth/

I use geojson file for Swedish municipalities from this link:

The identifier is ID (a four-digit number) and I’m trying to map a number of households per municipality. I get empty map (with no layers by municipalities and number of households). Anybody knows why this might happen?

import pandas as pd
import json

df1 = pd.read_csv("households.csv",engine='python')
df1['id'] = df1['region'].astype(str).str[0:4]
df1=df1[['id','Antal hushåll 2019']]
with open('sverige-kommuner-municipalities-of-sweden.geojson') as f:
    counties = json.load(f)

import plotly.express as px

fig = px.choropleth_mapbox(df1, geojson=counties, locations='id', color='Antal hushåll 2019',
                           color_continuous_scale="Viridis",
                           range_color=(0, 12),
                           mapbox_style="carto-positron",
                           zoom=3, center = {"lat": 57.6952, "lon": 11.9149},
                           opacity=0.5,
                           labels={'Antal hushåll 2019':'# of households'}
                          )
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()

Hi @parvizalizada,

As long as I cannot see your df1 file I cannot express any opinion on why your choroplethmapbox is empty

I defined a synthetic dataframe, df1, and the choroplethmapbox was generated:

import numpy as np
import json
import pandas as pd
import plotly.express as px

with open('sverige-kommuner-municipalities-of-sweden.geojson', 'r') as fp:
    jdata = json.load( fp)

print(jdata['features'][0].keys())
#dict_keys(['type', 'geometry', 'properties'])  #display the definition of a feature

for f in jdata['features'][:5]:
    print(f['properties'])

#{'lan_code': '01', 'kom_namn': 'Norrtälje', 'id': '0188', 'geo_point_2d': [59.7884877684, 18.8397393055]}
#{'lan_code': '01', 'kom_namn': 'Sigtuna', 'id': '0191', 'geo_point_2d': [59.648408222, 17.8908040774]}
#{'lan_code': '01', 'kom_namn': 'Nynäshamn', 'id': '0192', 'geo_point_2d': [58.8975887668, 17.8846309997]}
#{'lan_code': '13', 'kom_namn': 'Varberg', 'id': '1383', 'geo_point_2d': [57.1804025311, 12.3812078222]}
#{'lan_code': '23', 'kom_namn': 'Krokom', 'id': '2309', 'geo_point_2d': [63.7739425982, 14.2078490764]}

#I define new feature id to hae successive ids:

for k in range(len(jdata['features'])):
    jdata['features'][k]['id'] = k

#Synthetic df1:
df1= pd.DataFrame({'id': np.arange(290),
                  'Antal hushåll 2019': np.random.randint(0, 12, 290)})
fig = px.choropleth_mapbox(df1, geojson=jdata, locations='id', color='Antal hushåll 2019',
                           color_continuous_scale="Viridis",
                           range_color=(0, 12),
                           mapbox_style="carto-positron",
                           zoom=3, center = {"lat": 58.6952, "lon": 11.9149},
                           opacity=0.5,
                           labels={'Antal hushåll 2019':'# of households'}
                          )
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

1 Like

Thank you very much @empet!

Now I get why my code didn’t work. If I understood correctly, “id” must be an independent key in a json file for choropleth_mapbox to correctly parse it. In this case, “id” was inside ‘properties’ which choropleth_mapbox could not understand. Hence, the empty map.

@parvizalizada

No, the id can be inside, too, but it must be inserted in px.choropleth_mapbox as `featureidkey=“properties.id”.

Because I didnt know whether the geojson file or your df1 dataframe caused the empty map, for me it was simpler to check if geojson file is well defined with the id defined outside properties . Otherwise I had to copy in the df1 a lot of id strings from your geojson.

@empet featureidkey=“properties.id” was super useful. To be honest, I didn’t fully understand how choropleth_mapboxworks. From your explanation, I understand that we can pass id as an argument in featureidkey. Now I also upgraded Plotly version to 4.6 from 4.4.1 and it worked smoothly. Your help is very much appreciated.

@parvizalizada

Because a lot of users are complaining about getting an empty choroplethmapbox I explain here again the details on preparing data for its definition:

A choroplethmapbox is defined by a geojson file and a data frame.

First of all (i.e. as the first STEP) you must read the geojson file as a dict, jdata. Then inspect its structure as I did in my first answer above.

There can be three cases:

  1. No id is identified in the geojson dict (neither outside nor inside feature properties or other position). In this case define yourself an id for each feature as follows:
 for k in range(len(jdata['features'])):
    jdata['features'][k]['id'] = k
    
  1. Each feature in jdata['features'] has a key 'id' that can be identified
    from displaying:

jdata['features][0].keys()
In this case you’ll get:

dict_keys(['type', 'id', 'geometry', 'properties', 'othekey'])

This is the best case because 'id' is in the default position and the featureidkey can be omitted in the definition of go.Choroplethmapbox or px.choropleth_mapbox.

  1. Inside feature['properties'] or eventually feature['otherkey'] there is a key called either 'id' or other name, let us say 'region', that identifies uniquely a geographic region defined by feature['geometry'].

Second STEP: Based on the jdata definition you’ll create a dataframe, df, that has a column (with a name at your choice, but 'ids is the most suggestive), consisting in all ids or a part of them for the cases 1 and 2, above, or in the case 3, all or a subset of the ids recorded in feature['properties']['id'] or feature['otherkey']['region'].
Note that the list(df['ids']) can be a permutation of the jdata ids or a part/subset of a permutation.

The second column, let us say df['vals'] is a numerical one, and it can contain the population in each geographical region, represented by feature['geometry'] or unemployment percent, etc.

With these data you can define a trace of type choroplethmapbox as follows:

Case 1 and 2:

trace= go.Choroplethmapbox(geojson=jdata,
                           locations=df['ids'],
                           z=df['vals'],
                           colorscale='deep_r',
                           colorbar_thickness=20,
                           hoverinfo='all',
                        )

Case 3:

trace= go.Choroplethmapbox(geojson=jdata,
                           locations=df['ids'],
                           z=df['vals'],
                           featureidkey='properties.id', #or 'otherkey.region'  #HERE YOU SPECIFY WHERE IN jdata CAN BE READ  THE REGION ID
                           colorscale='deep_r',
                           colorbar_thickness=20,
                           hoverinfo='all',
                        )
                        

for more information on go.Choroplethmapbox
print help(go.Choroplethmapbox)

Beside trace definition you have to define the map layout:

layout = go.Layout(title_text= 'Choroplethmapbox',
                   title_x=0.5,
                   margin = ...
                   mapbox = dict(center= dict(lat=39.913818,  lon=102.363625),
                                 accesstoken= mapbox_token, #only when you define a special mapbox style
                                 zoom=2.650,
                               ))

‘px.choropleth_mapbox’ has as parameters all you want to set at once to get a go.Chroplethmapbox and its layout.

To get more info print
help(px.choropleth_mapbox)

From pedagogical point of view any user must know and understand the definition of the class go.Choroplethmapbox, because behind the scene
px.choropleth_mapbox instantiates it. Moreover, after getting the corresponding figure, all trace and layout updates are performed on an instance of this class , respectively go.layout.Mapbox.

1 Like

@empet, good night,

I have the same problem, an empty map. the code doesn’t show any error, however a new text file appears in the directory and show the following message:

[1011/194839.522:ERROR:directory_reader_win.cc(43)] FindFirstFile: the system can’t find the specified path. (0x3)

data = open('DAIUPZ.geojson')
local = json.load(data)
dataframe = pd.read_csv(r'C:/Users/LENOVO/PycharmProjects/GeoJson_csv_xlsx/data_final.csv', dtype={"OBJECTID": int})

fig = go.Figure(go.Choroplethmapbox(geojson=local, locations=dataframe['OBJECTID'], z=dataframe['Sec_value'],
                                    colorscale="Viridis", zmin=0, zmax=300,
                                    marker_opacity=0.5, marker_line_width=1))
fig.update_layout(mapbox_style="open-street-map",
                  mapbox_zoom=3, mapbox_center = {"lat": 37.0902, "lon": -95.7129})
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()

I try everything about the dataframe and the json file.

this is a sample of my .csv and my json file

{"type": "FeatureCollection", "features": [{"type": "Feature", "id": 10, "geometry": {"type": "Polygon", "coordinates": [[[-74.15508929100002, 4.631828144999986], [-74.15514039800001, 4.631831463000026], [-74.15517193900001, 4.632034920000024], [-74.15518234299998, 4.63220714900001], [-74.15520314999998, 4.6322906539999735], [-74.15526556600003, 4.632478542000001], [-74.155299727, 4.6325764800000115], [-74.155338385, 4.632687304000001], [-74.15535398999998, 4.632786466000027], [-74.15541640599997, 4.633010887000012], [-74.15545281599998, 4.633177896999996], [-74.15546321900001, 4.633313593000025], [-74.15545801899998, 4.633438850999994], [-74.15545632800001, 4.633449519999999], [-74.15542161000002, 4.633668489999991], [-74.15538914199999, 4.633742956999981], [-74.155348791, 4.633835501000021], [-74.155301979, 4.633945101999984], [-74.15528469600002, 4.633966605000012], [-74.155171945, 4.634106894000013], [-74.155119931, 4.634159083999975], [-74.15473817399999, 4.634546582999974], [-74.15429046100002, 4.6350010279999765], [-74.15386160000003, 4.635418307999998], [-74.15389450499998, 4.635494659000017], [-74.15406953299998, 4.6359007870000255], [-74.15431645400002, 4.636409347999972], [-74.15456337500001, 4.636917906999997], [-74.15497676500001, 4.637689614999999], [-74.15512219700003, 4.6379611059999775], [-74.15516067099998, 4.638038556000026], [-74.156161871, 4.64005401999998], [-74.15630051099998, 4.640317197999991], [-74.15841576899999, 4.6386829059999855], [-74.15926878800002, 4.637933824000015], [-74.16004882099998, 4.637245052000026], [-74.160659657, 4.6367011950000006], [-74.16127006900001, 4.636159597000017], [-74.16174647600002, 4.635683536999977], [-74.16220127499997, 4.6352593120000165], [-74.16255275499998, 4.634943490000012], [-74.16288487999998, 4.634645396999986], [-74.16312860599999, 4.634430845999987], [-74.16341498600002, 4.63418197499999], [-74.163715228, 4.633921236999981], [-74.164826108, 4.632940086000019], [-74.16538654499999, 4.632460772000002], [-74.16603157700001, 4.631890993000013], [-74.16711063899999, 4.630939356999988], [-74.16784934399999, 4.63029273799998], [-74.16787292100003, 4.630272115000025], [-74.16849127699999, 4.629776063000008], [-74.16887336799999, 4.629491240999982], [-74.169161642, 4.629287705000024], [-74.169226955, 4.629242576000024], [-74.16965971899998, 4.628943769999978], [-74.16991273500003, 4.628771308000012], [-74.170644611, 4.628272566000021], [-74.17035203400002, 4.628063747999988], [-74.170143274, 4.627680987000019], [-74.17012321599998, 4.6276441250000175], [-74.169918048, 4.627256208000006], [-74.169720762, 4.626852402999987], [-74.169505103, 4.626252765999993], [-74.16908399599998, 4.625340073000018], [-74.16887340900001, 4.624952721999989], [-74.16880907900003, 4.624829490000025], [-74.16865114299998, 4.624694538000028], [-74.16854585099998, 4.624577095999996], [-74.16835863799997, 4.624453867], [-74.16819486100002, 4.624348223000027], [-74.16799603300001, 4.624195613999973], [-74.16784393900002, 4.62406652300001], [-74.16767431900001, 4.623872813999981], [-74.16719937800002, 4.623049806999973], [-74.16718135899998, 4.623020429999997], [-74.16681164099998, 4.622399680000001], [-74.16649273899998, 4.621858164999992], [-74.16647063900001, 4.621820666000019], [-74.16600203600001, 4.621042433000014], [-74.16546532199999, 4.621252255999991], [-74.165423094, 4.6212776799999915], [-74.16486547800002, 4.6216134919999945], [-74.16402563899999, 4.621934477000025], [-74.16350574799998, 4.622335752000026], [-74.16290583400001, 4.622696985999994], [-74.162345964, 4.62301796700001], [-74.161966055, 4.623258716000009], [-74.16158614900002, 4.623700172999975], [-74.16085814299998, 4.624368041000025], [-74.15966654800002, 4.625786896000022], [-74.15891742500003, 4.626608223000005], [-74.15884753799997, 4.626684848000025], [-74.15883008399999, 4.6267058220000195], [-74.15880172099997, 4.626739932000021], [-74.158664058, 4.626904974000013], [-74.158409775, 4.627209987000015], [-74.15800642800002, 4.627676935000011], [-74.15796821200001, 4.627721142999974], [-74.15749272300002, 4.628215210000008], [-74.15703426800002, 4.628743455999995], [-74.15664365600003, 4.629186532999995], [-74.15623608200002, 4.6295954999999935], [-74.15584554100002, 4.6300214859999755], [-74.15542100599998, 4.630464421999989], [-74.15506438699998, 4.631043797000018], [-74.155047427, 4.631606149999982], [-74.155083252, 4.63179611999999], [-74.15508929100002, 4.631828144999986]]]}, "properties": {"OBJECTID": 10, "CMIUUPLA": "UPZ80", "CMNOMUPLA": "Corabastos", "CMMES": "Ene-Dic", "CMH18CONT": 23, "CMH19CONT": 20, "CMH20CONT": 22, "CMHVAR": 10, "CMHTOTAL": 543, "CMLP18CONT": 376, "CMLP19CONT": 358, "CMLP20CONT": 115, "CMLPVAR": -67.88, "CMLPTOTAL": 8111, "CMHP18CONT": 848, "CMHP19CONT": 846, "CMHP20CONT": 374, "CMHPVAR": -55.79, "CMHPTOTAL": 45440, "CMHR18CONT": 79, "CMHR19CONT": 77, "CMHR20CONT": 43, "CMHRVAR": -44.160000000000004, "CMHRTOTAL": 3889, "CMHA18CONT": 24, "CMHA19CONT": 25, "CMHA20CONT": 8, "CMHAVAR": -68, "CMHATOTAL": 1581, "CMHB18CONT": 85, "CMHB19CONT": 107, "CMHB20CONT": 78, "CMHBVAR": -27.1, "CMHBTOTAL": 5864, "CMHC18CONT": 138, "CMHC19CONT": 95, "CMHC20CONT": 23, "CMHCVAR": -75.79, "CMHCTOTAL": 5660, "CMHCE18CONT": 543, "CMHCE19CONT": 391, "CMHCE20CONT": 197, "CMHCEVAR": -49.620000000000005, "CMHCETOTAL": 25506, "CMHM18CONT": 61, "CMHM19CONT": 37, "CMHM20CONT": 16, "CMHMVAR": -56.76, "CMHMTOTAL": 1643, "CMHF18CONT": 0, "CMHF19CONT": 0, "CMHF20CONT": 0, "CMHFVAR": 0, "CMHFTOTAL": 55, "CMDS18CONT": 60, "CMDS19CONT": 57, "CMDS20CONT": 24, "CMDSVAR": -57.89, "CMDSTOTAL": 55, "CMVI18CONT": 360, "CMVI19CONT": 460, "CMVI20CONT": 203, "CMVIVAR": -55.870000000000005, "CMVITOTAL": 19756, "SHAPE.AREA": 0.000150267673363048, "SHAPE.LEN": 0.0530865588970009}}
OBJECTID,Sec_value
10,101.15
11,50.8
12,60.7
13,41.05
14,57.55
15,256.85
16,87.75
17,92.25
18,105.15
19,85.0
20,81.65
21,146.35000000000005
22,135.20000000000002

any suggestion to solve the problem?. and finally thanks for your help.

1 Like

@juan95barrero Where you able to solve the issue?

I got the same issue and spent 3 hours more!!

finally I tried to use the sample code below
first)
with urlopen(‘https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json’) as response:
counties = json.load(response)

import pandas as pd
df = pd.read_csv(“https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv”,
dtype={“fips”: str})

import plotly.express as px

fig = px.choropleth(df, geojson=counties, locations=‘fips’, color=‘unemp’,
color_continuous_scale=“Viridis”,
range_color=(0, 12),
scope=“usa”,
labels={‘unemp’:‘unemployment rate’}
)
fig.update_layout(margin={“r”:0,“t”:0,“l”:0,“b”:0})

second)
df = px.data.election()
geojson = px.data.election_geojson()

fig = px.choropleth(df, geojson=geojson, color=“Bergeron”,
locations=“district”, featureidkey=“properties.district”,
projection=“mercator”
)
fig.update_geos(fitbounds=“locations”, visible=False)
fig.update_layout(margin={“r”:0,“t”:0,“l”:0,“b”:0})

HOWEVER! I got empty map… help me !

I am using the latest version Dash.