Tutorial

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 Polygon or MultiPolygon

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:

  1. groups rows by the specified field
  2. unions polygon geometries within each group
  3. 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 MultiPolygon if 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.

Keep exploring with more guides in this category.