✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚾️ It's finally Baseball season! Root for the home team... & Register for our Sports Analytics Webinar!

Choropleth maps with custom geojson

Based on several threads here and elsewhere I think I have almost strung together an example of plotting a Choropleth map with custom geojson. Code runs, and the color bar reflects the fact that the data ranges from 4 – 15, but there are no polygons drawn.

This link

Tips to get a right geojson dict

Says there may be an issue with the ID, I have chased that thread and not found anything obvious

import geopandas as gpd
import pandas as pd
import shapely.geometry
from shapely.geometry import Polygon
import json

import plotly.graph_objects as go

p1 = Polygon([(0, 0), (1, 0), (1, 1)])
p2 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
p3 = Polygon([(2, 0), (3, 0), (3, 1), (2, 1)])

g = gpd.GeoSeries([p1, p2, p3])

poly_json = g.to_json()

data = [[0,10],[1,4],[2,15]]
df = pd.DataFrame(data, columns = ['id','color'])

fig = go.Figure(data = go.Choropleth(
    geojson=poly_json,
    locations=df['id'].astype(str),
    z = df['color'].astype(float)
))
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.layout.template= None
fig.show()

Thanks in advance.

Hi @BrianWalsh

Welcome to Plotly forum!!!

First a few comments:

  • you must set your mapbox token within the dict fig.layout.mapbox.
    To get it you need a Mapbox account. From that account you’ll get a public Mapbox Access Token.
  • your conversion, g.to_json(), yields a string, not a dict:
'{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {}, "geometry": {"type": "Polygon", "coordinates": [[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]}, "bbox": [0.0, 0.0, 1.0, 1.0]}, {"id": "1", "type": "Feature", "properties": {}, "geometry": {"type": "Polygon", "coordinates": [[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]]}, "bbox": [0.0, 0.0, 1.0, 1.0]}, {"id": "2", "type": "Feature", "properties": {}, "geometry": {"type": "Polygon", "coordinates": [[[2.0, 0.0], [3.0, 0.0], [3.0, 1.0], [2.0, 1.0], [2.0, 0.0]]]}, "bbox": [2.0, 0.0, 3.0, 1.0]}], "bbox": [0.0, 0.0, 3.0, 1.0]}'

To convert this string to a geojson type dict, define:

poly_json = json.loads(g.to_json())
  • go.Chroplethmapbox plots your polygons on a map in a region on the globe around the longitude and latitude read from your polygon coordinates:
import geopandas as gpd
import pandas as pd
import shapely.geometry
from shapely.geometry import Polygon
import json

import plotly.graph_objects as go
mapboxt = open(".mapbox_token").read().rstrip() #my mapbox_access_token 

p1 = Polygon([(0, 0), (1, 0), (1, 1)])
p2 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
p3 = Polygon([(2, 0), (3, 0), (3, 1), (2, 1)])

g = gpd.GeoSeries([p1, p2, p3])

poly_json = json.loads(g.to_json())

data = [[0,10],[1,4],[2,15]]
df = pd.DataFrame(data, columns = ['id','color'])

fig = go.Figure(data=[go.Choroplethmapbox(
                                    geojson=poly_json,
                                    colorscale='matter_r',
                                    locations=df['id'].astype(str),
                                    z = df['color'].astype(float))])

fig.update_layout(width=1000, height=800,
                  mapbox = dict(center= dict(lat=1.5, lon=1.5),  #lat=37, lon=-95          
                                 accesstoken= mapboxt,
                                 zoom=2.5,
                                 style='light'
                               ))
fig.show()

I suspect that you wanted only to plot some filled Shapely polygons, but not located on a map. If this is a case then Choroplethmapbox is not the right choice for this purpose.

@empet,

Thank your for the quick response, and this does work but… You guessed correctly. I am trying to repurpose the choropleth / Choroplethmapbox to plot a set of arbitrary polygons. Coming from a SAS background I was looking for the analog of their sgplot polygon procedure that has the ability to plot data against a predefined defined map.

I am still planning on poking around with this but, perhaps another tack I can explore is getting clever with plotly’s scatter or add_shape to replicate the same effect. I do want to stay in plotly because this will eventually be apart of a dash based dashboard.

Thanks!

1 Like

I am trying to do something very similar (I think), but my polygons don’t show.

Instead of manually defining polygons, I download them from a URL.

Then I follow all the same steps as outlined in your post:

# plot
from urllib.request import urlopen
import json

with urlopen(
    "https://geodata.nationaalgeoregister.nl/cbspostcode4/wfs?request=GetFeature&service=WFS&version=2.0.0&typeName=postcode42017&propertyName=(postcode)&outputFormat=json&count=2&startindex=0"
) as response:
    poly_json = json.load(response)

# import config as cfg  # here I have defined my mapbox token.
import plotly.graph_objects as go


data = [[1011, 4], [1012, 10]]
df = pd.DataFrame(data, columns=["id", "color"])

fig = go.Figure(
    data=[
        go.Choroplethmapbox(
            geojson=poly_json,
            colorscale="matter_r",
            featureidkey="properties.postcode",
            locations=df["id"].astype(str),
            z=df["color"].astype(float),
        )
    ]
)

fig.update_layout(
    mapbox=dict(
        center=dict(lat=52.36, lon=4.9),  # lat=37, lon=-95
        accesstoken=cfg.mapbox_token,
        zoom=12,
        style="light",
    ),
)
fig.show()

Do you maybe know what else I could try to show the geojson on a map?

For context, these are postal code regions in The Netherlands.

Doh moment: the loaded geojson had its CRS set to EPSG 28992. Converting it to EPSG 4326 solved the issue.