Search code examples
pythonfolium

Getting the following error when I try and create a Folium - Choropleth map: ValueError: key_on `'id'` not found in GeoJSON


ere's my code:

folium.Choropleth(

geo_data=geojson,

data=df,

columns=['Location ','Charging devices per 100,000 population'],

key_on='feature.id',

fill_color='YlGnBu',

fill_opacity=0.7,

line_opacity=0.2,

legend_name='Charging devices per 100,000 population'

).add_to(map1)

My Geojson file:

https://martinjc.github.io/UK-GeoJSON/json/eng/topo_lad.json

My Csv:

https://maps.dft.gov.uk/ev-charging-map/index.html

I renamed ONS to ID.

Please help.


Solution

    • you are using topojson not geojson as your source geometry
    • this means you need to also specify topojson parameter
    • full solution is mostly dealing with fact that spreadsheet published by https://maps.dft.gov.uk/ev-charging-map/index.html mixes data dictionary and data together that requires clean up.
    • a simpler alternative solution if you have it installed is with geopandas

    full solution

    import pandas as pd
    import requests
    import folium
    
    # source data, requires https://pypi.org/project/odfpy/
    df = pd.read_excel(
        "https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/1048354/electric-vehicle-charging-device-statistics-january-2022.ods",
        engine="odf",
        sheet_name=1,
        header=[6, 7],
    )
    
    # cleanup description records...
    df = df.loc[df.iloc[:, 0].str.match(r"^[A-Z][0-9]+").fillna(False)]
    
    # reshape to much nicer shape..
    df = (
        df.set_index(
            pd.MultiIndex.from_arrays(
                df.iloc[:, 0:2].T.values, names=df.columns.get_level_values(0)[0:2]
            )
        )
        .iloc[:, 2:]
        .stack(0)
        .reset_index()
    ).rename(columns={"level_2": "Date"})
    
    # cleanup columns
    df = df.rename(columns={c:c.strip() for c in df.columns})
    df["Total devices"] = pd.to_numeric(df["Total devices"], errors="coerce")
    df["per 100,000 population"] = pd.to_numeric(df["per 100,000 population"], errors="coerce")
    
    # just most recent data
    df = (
        df.sort_values(["LA / Region Code", "Date"])
        .groupby(["LA / Region Code"], as_index=False)
        .last()
    )
    
    geojson = requests.get(
        "https://martinjc.github.io/UK-GeoJSON/json/eng/topo_lad.json"
    ).json()
    
    map1 = folium.Map(location=(52.83791067797496, -2.336002422471461), zoom_start=6, height=300, width=500)
    
    folium.Choropleth(
        geo_data=geojson,
        topojson="objects.lad",
        data=df,
        columns=["LA / Region Code", "per 100,000 population"],
        key_on="feature.id",
        fill_color="YlGnBu",
        fill_opacity=0.7,
        line_opacity=0.2,
        legend_name="Charging devices per 100,000 population",
    ).add_to(map1)
    
    map1
    

    enter image description here

    alternative

    import geopandas as gpd
    
    gdf = gpd.read_file("https://martinjc.github.io/UK-GeoJSON/json/eng/topo_lad.json").set_crs("epsg:4326").merge(
        df.drop(columns="Date"), left_on="id", right_on="LA / Region Code"
    )
    
    
    gdf.explore(column="per 100,000 population", cmap="YlGnBu", scheme="NaturalBreaks", height=300, width=500)