2025, Sep 17 01:00

How to fix a squashed India choropleth in Plotly: use district-level join keys and sane scaling

Fix a distorted India choropleth in Plotly by joining on district fields (locations + featureidkey) and setting a sensible projection_scale for accurate maps.

Fixing a distorted India choropleth in Plotly: getting the join key right

When a choropleth of India looks squashed or the state outlines appear oddly small, the problem often isn’t the projection. In this case, the map was drawn from district-level geometry, but the data was matched on state names. That mismatch caused the layer to render incorrectly and the visualization to feel wrong at a glance.

Corrected example

Below is a minimal setup that fixes the join between your dataframe and the GeoJSON. The key change is to connect features by district rather than by state. Additionally, the duplicate centering was removed and the projection scale was set to a more reasonable value.

import plotly.express as px
map_df = geo_df
shape_src = geojson_data
chart = px.choropleth(
    map_df,
    geojson=shape_src,
    locations="district",
    featureidkey="properties.district",
    color="Accidents",
    center=dict(lat=22, lon=80)
)
chart.update_geos(projection_scale=3)
chart.show()

What went wrong and why it happens

The GeoJSON features describe districts, and each feature carries a properties.district value. The dataframe also holds district-level rows with an Accidents column. When the map is instructed to join on a state field like st_nm, Plotly tries to match every district-shaped feature to a single state label. Multiple features share the same state name, so the many-to-one link produces a misleading rendering, which is why the shapes end up looking off and the visual scale feels wrong. In other words, the geometry is detailed per district, but the key used for matching is aggregated at the state level.

The fix

Align the join keys with the geometry granularity. Use the district field in both the dataframe and the GeoJSON: set locations to "district" and featureidkey to "properties.district". This makes the mapping unambiguous and allows Plotly to color and render each district polygon correctly. While at it, keep only a single center definition and choose a projection scale that frames India appropriately; a value like 3 is a practical starting point for a country-scale view.

Why this detail matters

Choropleths depend on a reliable feature join. If the key mismatches the geometry level, the map can appear distorted, duplicate regions might end up with the same color without representing true values, and the entire visual narrative becomes misleading. Ensuring that the attribute key aligns with the polygon level is essential for trustworthy geospatial analysis and for communicating patterns such as accident counts across districts.

Takeaways

Always verify that your locations column matches the granularity of your GeoJSON’s featureidkey. For district polygons, join by district; for state polygons, join by state. Keep map configuration minimal and intentional: one center, a sensible projection_scale, and attributes that exist in both the dataframe and the geometry source. If, after fixing the join, boundaries still look inaccurate, consider sourcing a more precise district GeoJSON before drawing conclusions from the visualization.

The article is based on a question from StackOverflow by Ryan_Brusseau and an answer by pixel-process.