Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GeoJSON issues with Plotly choropleth_mapbox

My approach to creating a choropleth map via plotly seems pretty straightforward--load in the DataFrame, load in the geojson, assign the necessary features to the custom polygons, and plot.

Obviously there is a missed step somewhere when referencing of the custom polygons as only a blank map appears after a lengthy loading time.

One major thing to note is that about half of the polygons are located within states, but are their own custom polygon within the states. Therefore to my knowledge, choropleth_mapbox is the more suitable solution.

A sample of the image, showing custom polygons within the states: enter image description here

The code:

import pandas as pd
import plotly.express as px
import geopandas as gpd
from geojson import Polygon
import json

# reading in the dataframe
path = '/path/to/csv'
df = pd.read_csv(path)
geo_df = gpd.GeoDataFrame(df)

# reading in the geospatial data
with open('/path/to/geojson') as f:
    geojson = json.load(f)
    
# create the plot
fig = px.choropleth_mapbox(geo_df[0:50], #slicing for quick loading
                           geojson=geojson, 
                           color="MALL",
                           locations="MWS_ID", 
                           featureidkey="properties.MWS_ID",
                           center={"lat": 39, 
                                   "lon": -95},
                           mapbox_style="carto-positron", 
                           zoom=3)

fig.show()

The output:

enter image description here

Obviously data is missing.

I think the issue is in the geojson file. The only thing I can see that might be off about the geojson structure is that the coordinates of my geojson are in two brackets, whereas the geojson from the documentation's coordinates are in three brackets.

Below is my geojson.

{'type': 'FeatureCollection',
 'features': [{'type': 'Feature',
   'geometry': {'type': 'Polygon',
    'coordinates': [[-89.299965, 36.508405],
     [-89.414355, 36.499866],
     [-89.424498, 36.476321],
     .....
   'properties': {'MWS_ID': 'TN_1'}},

  {'type': 'Feature',
   'geometry': {'type': 'Polygon',
    'coordinates': [[-111.043999, 44.139903],
     [-111.040171, 42.227952],
     [-111.040773, 41.820698],
     .....

And below is the documentation's geojson

{'type': 'FeatureCollection',
 'features': [{'type': 'Feature',
   'geometry': {'type': 'MultiPolygon',
    'coordinates': [[[[-73.6363215300962, 45.5759177646435],
       [-73.6362833815582, 45.5758266113331],
       .....
       [-73.6363215300962, 45.5759177646435]]],
     [[[-73.6561004885273, 45.5841347974261],
       .....
       [-73.6561004885273, 45.5841347974261]]]]},
   'properties': {'district': '11-Sault-au-Récollet'},
   'id': '11'},

  {'type': 'Feature',
   'geometry': {'type': 'Polygon',
    'coordinates': [[[-73.6217484540132, 45.5544783077209],
      [-73.6235005117779, 45.5536358848324],
      [-73.6278096771011, 45.5513024018691],

Here is how I created the geojson

# Create the GeoJSON
names_merged = names_1 + names_2
geoms_merged = geoms_1 + geoms_2

geojson = {'type':'FeatureCollection', 'features':[]}

for i in range(len(names_merged)):
    feature = Feature(geometry=geoms_merged[i])
    geojson['features'].append(feature)
    geojson['features'][i]['properties']['MWS_ID'] = names_merged[i]
    
with open('/path/to/geojson', 'w') as f:
   dump(geojson, f)

Where names_merged is a list of MWS_IDs in str format and geoms_merged is a list of polygons in the geojson.geometry.Polygon format.

Verifying the dataframe and geojson have the same keys.

print(geo_df['MWS_ID'][3])
print(geojson["features"][28]['properties']["MWS_ID"])

The output

AZ_2
AZ_2

But the map is still blank.

Thank you for your continued help S.O. community.

like image 700
Jkiefn1 Avatar asked May 03 '21 02:05

Jkiefn1


1 Answers

Your output is not a choropleth_mapbox, it is a choropleth. Are you missing showing us any layout code? Without seeing all of your code, it's hard to determine the root cause of your issue so instead I'll show you a simple working example of how to connect a geojson to a dataframe and display it as a choropleth_mapbox below.

import pandas as pd
import plotly.express as px

df = pd.DataFrame({'letter':['A','B','C'],'name':['AZ_1','BV_2','CD_3'],'value':[2,5,7]})


gj = {
    'type': 'FeatureCollection',
    'features': [
        {
            'type': 'Feature',
            'geometry': {
                'type': 'Polygon',
                'coordinates':[[
                    [-82.899205,33.653817],
                    [-82.771500,32.516301],
                    [-80.914171,32.133138],
                    [-79.710831,33.171969],
                    [-82.899205,33.653817]
                ]]
            },
            'properties': {'unique_id': 'AZ_1', 'name': 'shape A'}
        },
                {
            'type': 'Feature',
            'geometry': {
                'type': 'Polygon',
                'coordinates':[[
                    [-80.883651,33.080687],
                    [-80.835692,33.797411],
                    [-82.095711,34.396734],
                    [-82.945897,33.168320],
                    [-80.883651,33.080687]
                ]]
            },
            'properties': {'unique_id': 'BV_2', 'name': 'shape B'}
        },
                {
            'type': 'Feature',
            'geometry': {
                'type': 'Polygon',
                'coordinates':[[
                    [-79.471035,33.255865],
                    [-78.202296,34.086771],
                    [-78.629569,34.855954],
                    [-81.446082,34.698384],
                    [-79.471035,33.255865]
                ]]
            },
            'properties': {'unique_id': 'CD_3', 'name': 'shape C'}
        },
    ]
}

fig = px.choropleth_mapbox(df, geojson=gj,color=df.value,
                           locations=df.name, featureidkey="properties.unique_id",
                           center={"lat": 33.33012299999999, "lon": -81.08463033333332},
                           mapbox_style="carto-positron", zoom=5)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()

When using a geojson with custom Polygons, you just need to insure that the featureidkey matches the locations dataframe column so that you can apply other dataframe values to the Polygon (like color, text, etc.).

The simple code above should output:enter image description here This is what a choropleth_mapbox should output- a tile-based map. Your output is not tile-based which is why I'm asking if you have additional layout code that is changing the type of map displayed. Even if your choropleth_mapbox is malformed, the output won't be what you have.

Editing answer

Your geojson is malformed- a Polygon coordinates array should be a 3-dimensional array not 2-dimensional. Run this code to update your coordinates array:

old_features = [feature for feature in geojson['features']]
new_features = [feature for feature in geojson['features']]
for i in range(len(old_features)):
    new_features[i]['geometry']['coordinates'] = [new_features[i]['geometry']['coordinates']]
new_geojson = {
    'type': 'FeatureCollection',
    'features': new_features

Now creating the map should work:

fig = px.choropleth_mapbox(df, geojson=new_geojson,color=df.MALL,
                           locations=df.MWS_ID, featureidkey="properties.MWS_ID",
                           center={"lat": 39, "lon": -95},
                           mapbox_style="carto-positron", zoom=3)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
}

Here is the output I get with your csv and geojson:

enter image description here

like image 77
zerOpRiME Avatar answered Oct 06 '22 11:10

zerOpRiME