How to Dissolve Polygons in Python (GeoPandas)
A common GIS task is merging many polygon features into larger areas based on a shared attribute. For example:
Problem statement
A common GIS task is merging many polygon features into larger areas based on a shared attribute. For example:
- parcel polygons merged into zoning areas
- census tracts merged into counties
- land use polygons merged by class
- district polygons merged into regions
In GIS, this operation is called a dissolve. It combines polygon geometries that share the same value in a field, such as region, county, or zone. It can also summarize other attribute columns at the same time.
This is useful when you need to:
- create higher-level boundaries
- simplify a polygon dataset
- prepare data for mapping or reporting
- aggregate polygon data before analysis
If you need to dissolve polygons in Python, the standard approach is to use GeoPandas.
Quick answer
Use GeoDataFrame.dissolve().
You need:
- a polygon
GeoDataFrame - a field to group by
- optional aggregation rules for other columns
Example:
import geopandas as gpd
gdf = gpd.read_file("data/admin_areas.shp")
dissolved = gdf.dissolve(by="region").reset_index()
This creates one merged polygon or multipart geometry for each unique region value. If you want to summarize other fields at the same time, pass aggregation functions with aggfunc.
Step-by-step solution
Load polygon data into GeoPandas
Start by reading a polygon layer from a shapefile, GeoJSON, or GeoPackage.
import geopandas as gpd
gdf = gpd.read_file("data/zones.shp")
print(gdf.head())
print(gdf.geom_type.unique())
print(gdf.crs)
Check that:
- the file loaded correctly
- the geometry column exists
- the layer contains polygon geometries such as
PolygonorMultiPolygon
A quick validation check:
if not all(geom_type in ["Polygon", "MultiPolygon"] for geom_type in gdf.geom_type.unique()):
raise ValueError("Input layer must contain polygon geometries.")
Inspect the attribute used for dissolving
Before running a dissolve, inspect the field you want to group by.
print(gdf.columns)
print(gdf["region"].value_counts(dropna=False))
Look for common problems:
- null values
- inconsistent spelling like
North,north,NORTH - extra spaces
- unexpected category values
Clean the field if needed:
gdf["region"] = gdf["region"].str.strip().str.title()
If the grouping field contains nulls, decide how to handle them before dissolving.
gdf = gdf.dropna(subset=["region"])
Dissolve polygons by attribute
To merge polygons with the same attribute value, use dissolve(by="column_name").
dissolved = gdf.dissolve(by="region")
print(dissolved.head())
This does two main things:
- groups rows by
region - unions the polygon geometries within each group
The result has one row per group.
Control how non-geometry fields are aggregated
If your input has other fields, GeoPandas can summarize them during the dissolve.
For example, if you want to dissolve by county, sum population, and keep one representative state_name value:
dissolved = gdf.dissolve(
by="county",
aggfunc={
"population": "sum",
"state_name": "first"
}
)
Common aggregation functions include:
"sum""mean""first""min""max""count"
Choose functions that match the meaning of each field. For example:
- sum total population
- keep the first text label when it is identical within each group
- average a numeric field only if that makes sense for your workflow
Reset the index after dissolve
By default, the dissolve field becomes the index in the output.
print(dissolved.index)
This is normal, but it is usually easier to inspect and export the data after resetting the index:
dissolved = dissolved.reset_index()
print(dissolved.head())
Save the dissolved output
Export the result to a format you can use in QGIS or another GIS tool.
GeoJSON:
dissolved.to_file("output/dissolved_regions.geojson", driver="GeoJSON")
Shapefile:
dissolved.to_file("output/dissolved_regions.shp")
GeoPackage:
dissolved.to_file("output/dissolved_regions.gpkg", layer="regions", driver="GPKG")
If you use shapefiles, remember that they have field name and format limitations. GeoPackage or GeoJSON is often easier for modern workflows.
Code examples
Example 1: Dissolve polygons by a single attribute
This example reads a polygon shapefile and dissolves features by region.
import geopandas as gpd
gdf = gpd.read_file("data/admin_units.shp")
dissolved = gdf.dissolve(by="region").reset_index()
print(dissolved[["region", "geometry"]].head())
This creates one output feature for each unique region.
Example 2: Dissolve polygons and aggregate attribute values
This example dissolves by county, sums population, and keeps a county name field.
import geopandas as gpd
gdf = gpd.read_file("data/census_blocks.geojson")
dissolved = gdf.dissolve(
by="county",
aggfunc={
"population": "sum",
"county_name": "first"
}
).reset_index()
print(dissolved[["county", "county_name", "population"]].head())
This is a common pattern when building higher-level administrative boundaries from smaller polygons.
Example 3: Export the dissolved layer to a new file
This example writes the dissolved result to GeoJSON.
import geopandas as gpd
gdf = gpd.read_file("data/land_use.shp")
dissolved = gdf.dissolve(by="class").reset_index()
dissolved.to_file("output/land_use_dissolved.geojson", driver="GeoJSON")
You can open that file directly in QGIS to verify the result.
Explanation
When you dissolve polygons in GeoPandas, GeoPandas does three practical things:
- groups rows by the specified field
- unions polygon geometries within each group
- aggregates non-geometry columns using the functions you provide
This is different from other GIS operations:
- Spatial join adds attributes based on spatial relationships
- Clip cuts features using another boundary
- Append stacks features without merging geometry
A dissolve removes internal boundaries between polygons in the same group. The output may be:
- a single merged polygon
- a
MultiPolygonif grouped parts are separate in space
That means dissolving polygons in GeoPandas does not guarantee one simple polygon per group. If polygons in the same category are disconnected, the result is often multipart geometry.
If you want to merge all polygons into one feature instead of grouping by a field, use a constant field first:
gdf["group_all"] = 1
all_merged = gdf.dissolve(by="group_all").reset_index(drop=True)
Edge cases or notes
Mixed or invalid geometries
Invalid polygons can produce bad dissolve results or geometry errors. Check validity before dissolving:
print(gdf.is_valid.value_counts())
If needed, fix invalid features before processing. See How to Fix Invalid Geometries in GeoPandas.
Null or inconsistent group values
If your dissolve field has missing or inconsistent values, the output may not match your expected groups. Clean the field first:
gdf["zone"] = gdf["zone"].str.strip().str.upper()
gdf = gdf.dropna(subset=["zone"])
Dissolving all polygons into one feature
If you do not want grouped output and only need a single merged boundary, dissolve on a constant value as shown earlier. This is often used to build a study area outline.
CRS considerations
Dissolve does not require reprojection to merge polygons, but the CRS should still be correct. If the layer has the wrong CRS metadata, downstream analysis will also be wrong.
Check CRS first:
print(gdf.crs)
If you plan to calculate area or distance afterward, reproject to an appropriate local projected CRS for your dataset.
gdf = gdf.to_crs("EPSG:32633")
Performance with large datasets
A polygon dissolve can be slow on large or complex layers. Practical ways to reduce issues:
- fix invalid geometries first
- remove unnecessary columns
- test on a subset
- use GeoPackage instead of shapefile for intermediate outputs
Internal links
If you are working with polygon workflows more broadly, see How to Reproject Spatial Data in Python (GeoPandas).
Related tasks:
Troubleshooting:
FAQ
How do I dissolve polygons by attribute in GeoPandas?
Use dissolve(by="field_name") on a polygon GeoDataFrame.
dissolved = gdf.dissolve(by="region").reset_index()
This groups polygons by the chosen field and merges their geometries.
What is the difference between dissolve and unary union in GeoPandas?
dissolve() groups features by an attribute and can aggregate attributes. A unary union merges geometries without preserving grouped attribute structure. Use dissolve when you need one output feature per category.
Why does dissolve() make my grouping field become the index?
That is the default GeoPandas behavior. After dissolving, run:
dissolved = dissolved.reset_index()
This moves the grouping field back into a normal column.
How do I keep or summarize attribute columns after dissolving polygons?
Pass aggfunc with aggregation rules:
dissolved = gdf.dissolve(
by="county",
aggfunc={"population": "sum", "name": "first"}
).reset_index()
Use functions that match the meaning of each field.
Related articles
Keep exploring with more guides in this category.
How to Fix Invalid Geometries in Python (GeoPandas)
How to find and fix invalid geometries in GeoPandas using buffer(0), make_valid, and geometry validation checks.
Read article →
How to Read a Shapefile in Python with GeoPandas
Step-by-step guide to reading a shapefile in Python using GeoPandas, with examples and common issues covered.
Read article →
How to Reproject Spatial Data in Python (GeoPandas)
How to reproject spatial data in Python using GeoPandas to_crs(), with examples for common coordinate systems.
Read article →