✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

Shape files are not being rendered by go.Choroplethmapbox

Hey everyone. I have been trying to render shapefiles from the UK’s Open Geography Portal specifically here https://geoportal.statistics.gov.uk/datasets/local-authority-districts-december-2019-boundaries-uk-bfc.

I have been able to use mapshaper.org and QGis to successfully render these polygons, but I would really would prefer to do this in Python within JupyterLab. I have followed many tutorials on how to convert .shp files to geoJson and have been able to successfully render the examples provided by @empet such as https://plotly.com/~empet/15238/tips-to-get-a-right-geojson-dict-to-defi/ and https://plotly.com/~empet/15237/choroplethmapbox-with-dropdown-menu/#/. After trying these methods to no avail I tried different ways of inserting the id key but again they didn’t render.

I have tried both creating an artificial ‘id’ and using the plotly featurekeyid field but neither of them are working. When I do use the id key, I have checked that the id key is in the correct location.

Sometimes the base mapbox layer will render but no polygons and others the code will run and then hang. Please see below code as to what I have been implementing. This is an example of trying to render Local Authority Boundaries:


import geopandas as gpd
from shapely.geometry import LineString, MultiLineString
import plotly.graph_objs as go

# load in shp files
lad_shp = gpd.read_file('zip://../../data/external/Local_Authority_Districts_(December_2019)_Boundaries_UK_BFC-shp.zip', encoding='utf-8')

# using empet code to convert .shp to geoJSON
def shapefile_to_geojson(gdf, index_list, tolerance=0.025): 
    # gdf - geopandas dataframe containing the geometry column and values to be mapped to a colorscale
    # index_list - a sublist of list(gdf.index)  or gdf.index  for all data
    # tolerance - float parameter to set the Polygon/MultiPolygon degree of simplification
    # returns a geojson type dict 
   
    geo_names = list(gdf[f'lad19nm']) # name of authorities
    geojson = {'type': 'FeatureCollection', 'features': []}
    for index in index_list:
        geo = gdf['geometry'][index].simplify(tolerance)
    
        if isinstance(geo.boundary, LineString):
            gtype = 'Polygon'
            bcoords = np.dstack(geo.boundary.coords.xy).tolist()
    
        elif isinstance(geo.boundary, MultiLineString):
            gtype = 'MultiPolygon'
            bcoords = []
            for b in geo.boundary:
                x, y = b.coords.xy
                coords = np.dstack((x,y)).tolist() 
                bcoords.append(coords) 
        else: pass
        
        
       
        feature = {'type': 'Feature', 
                   'id' : index,
                   'properties': {'name': geo_names[index]},
                   'geometry': {'type': gtype,
                                'coordinates': bcoords},
                    }
                                
        geojson['features'].append(feature)
    return geojson

geojsdata = shapefile_to_geojson(lad_shp, list(lad_shp.index))

# length to generate synthetic data for z attribute
L = len(geojsdata['features'])

# check id key is there
geojsdata['features'][0].keys()
>> dict_keys(['type', 'id', 'properties', 'geometry'])

# example of authroity name
geojsdata['features'][0]['properties']['name']
>> 'Hartlepool'

# check id
k=5
geojsdata['features'][k]['id']
>> '5'

trace = go.Choroplethmapbox( z = np.random.randint(10, 75,  size=L), #synthetic data
                            locations = [geojsdata['features'][k]['id'] for k in range(L)], 
                            colorscale = 'Viridis',
                            colorbar = dict(thickness=20, ticklen=3),
                            geojson = geojsdata,
                            text = regions,
                            marker_line_width=0.1, marker_opacity=0.7)
layout = go.Layout(title_text ='UK LAD Choropleth Demo', title_x =0.5, width=750, height=700,
                   mapbox = dict(center= dict(lat=54, lon=-2),            
                                 accesstoken= mapbox_access_token,
                                 zoom=3
                               ))

fig=go.Figure(data=[trace], layout =layout)
fig.show()

This does not render as I say. I have tried using a list of values from a different df too not just synthetic data with the corresponding locations field from that df too matching the id key. What I am expecting are the following polygons on top of the Mapbox base layer. This image was generated on mapshaper:

I have also tried reducing the size of the .shp file using mapshaper’s various algorithms then saving that to geoJSON format and skipping the conversion step in Python from .shp to geoJSON but again to no avail. Also changing the tolerance in the shapely manipulation does not seem to change the output.

My mapbox access token is valid.

Does anyone have any idea what could be causing the issue? I’m assuming the .shp files are fine as they are rendered fine on mapshaper and QGis. Any help would be greatly appreciated.

Thank you.

Hi @TheDataFox,

PLease provide your resulted geojson file or at least geojsondata['features'][k] for k =0, 1, 2, 3,
to inspect its contents and to identify why the corresponding go.Choroplethmapbox doesn’t work. The conversion function was written for a custom choropleth, when Plotly had not yet defined the go.Choroplethmapbox class.

Thank you for the reply. Here is the geojson as requested: https://www.dropbox.com/s/vuf3jtrr2boq5eg/lad19-geo.json?dl=0. This is the full size geojson straight from the .shp file with id added as per above.

I forgot to mention that I had tried id as a int64 and string.

Thanks.

@TheDataFox Your spatial data is not in the right projection system, plotly mapbox only allows WGS84. To reproject your data, use:

lad_shp = lad_shp.to_crs(epsg=4326)

before converting to geojson. Check that the coordinates in your geojson data look like latitude and longitude values. For the UK, latitude should be between 50 and 60 and longitude should be between -10 and 2.