GeoPandas "No Geometry Column" Error: How to Fix It

Problem statement

A common GeoPandas problem happens when you call a geospatial method on an object that has no active geometry column. In practice, this usually shows up in one of these ways:

  • AttributeError: No geometry data set yet (expected in column 'geometry')
  • AttributeError: You are calling a geospatial method on a GeoDataFrame, but the active geometry column ('geometry') is not present
  • KeyError: 'geometry'
  • gdf.plot(), gdf.to_crs(...), gdf.geometry, or gpd.sjoin(...) fails even though the data looks correct

This matters because GeoPandas needs a defined geometry column to run spatial operations. Methods like to_crs(), plot(), buffer(), and sjoin() all read from the active geometry column. If that column is missing, renamed, or never set, GeoPandas cannot find the geometry and raises an error.

A missing geometry column is usually caused by one of these situations:

  • the active geometry column was dropped or renamed
  • the object is a plain DataFrame, not a GeoDataFrame, for example after pd.read_csv() or a merge()
  • the geometry lives in a column with a different name that was never set as active
  • geometry was created but never assigned to the GeoDataFrame
  • coordinates exist as lon/lat numbers or WKT strings that were never converted to geometry

Quick answer

If you have a column that holds geometry but it is not active, set it with set_geometry():

import geopandas as gpd

# The geometry lives in a column named "geom", not "geometry"
gdf = gdf.set_geometry("geom")

print(gdf.geometry.name)  # "geom"

If you have a plain DataFrame with coordinate columns, build a GeoDataFrame from them:

import geopandas as gpd

gdf = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df["lon"], df["lat"]),
    crs="EPSG:4326",
)

Use set_geometry() when the geometry already exists. Build a new GeoDataFrame when you only have raw coordinates or WKT strings.

Choosing the repair path

Flowchart — Choosing how to restore an active geometry column.
Choosing how to restore an active geometry column.

Step-by-step solution

Step 1: Confirm whether the object is a GeoDataFrame

A frequent cause is that you are working with a plain DataFrame, not a GeoDataFrame. Check the type first.

import geopandas as gpd

print(type(gdf))
print(isinstance(gdf, gpd.GeoDataFrame))

If isinstance(gdf, gpd.GeoDataFrame) returns False, the object lost its GeoDataFrame status somewhere, often after pd.read_csv(), a merge(), or a pd.concat(). In that case you need to rebuild a GeoDataFrame, not just set the geometry.

Step 2: Find the geometry column

If the object is a GeoDataFrame, check which column is active and list the columns.

print(gdf.columns.tolist())

# This raises if no active geometry column is set
try:
    print(gdf.geometry.name)
except AttributeError as e:
    print("No active geometry:", e)

Look for a column that holds geometry but is not named geometry, such as geom, the_geom, shape, or wkt. The active geometry column name is available through gdf.geometry.name once one is set.

Step 3: Set the geometry column as active

If a geometry column exists under another name, make it active with set_geometry().

gdf = gdf.set_geometry("geom")

print(gdf.geometry.name)
print(isinstance(gdf, gpd.GeoDataFrame))

If you want the active column to be called geometry, rename it with rename_geometry():

gdf = gdf.rename_geometry("geometry")
print(gdf.geometry.name)  # "geometry"

set_geometry() only works when the column already contains geometry objects. If the column holds numbers or strings, go to the next step.

Step 4: Rebuild geometry from coordinates

If you only have coordinate columns such as lon and lat, create point geometry with points_from_xy() and construct a GeoDataFrame.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("data/sites.csv")  # columns: name, lon, lat

gdf = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df["lon"], df["lat"]),
    crs="EPSG:4326",
)

print(gdf.geometry.name)
print(gdf.crs)

Note the argument order: points_from_xy(x, y) expects longitude first, then latitude. Passing latitude first puts points in the wrong place.

Step 5: Verify the fix

After setting or rebuilding the geometry, confirm that GeoPandas now sees it.

print(isinstance(gdf, gpd.GeoDataFrame))
print(gdf.geometry.name)
print(gdf.geometry.head())
print(gdf.crs)

Check that:

  • isinstance(gdf, gpd.GeoDataFrame) is True
  • gdf.geometry.name returns a column name without error
  • gdf.geometry.head() shows geometry objects, not numbers or strings
  • a spatial method such as gdf.total_bounds runs without error

Code examples

Example 1: A plain DataFrame after pd.read_csv()

A CSV has no geometry, so pd.read_csv() always returns a plain DataFrame. Convert it.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("data/sites.csv")  # name, lon, lat
print(isinstance(df, gpd.GeoDataFrame))  # False

gdf = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df["lon"], df["lat"]),
    crs="EPSG:4326",
)

print(isinstance(gdf, gpd.GeoDataFrame))  # True
print(gdf.geometry.head())

Example 2: Geometry column with a different name

A GeoPackage or PostGIS export may store geometry in a column named geom.

import geopandas as gpd

gdf = gpd.read_file("data/parcels.gpkg")
print(gdf.columns.tolist())

# Geometry is in "geom", not "geometry"
gdf = gdf.set_geometry("geom")

print(gdf.geometry.name)      # "geom"
gdf = gdf.rename_geometry("geometry")
print(gdf.geometry.name)      # "geometry"

Example 3: Rebuild geometry from WKT strings

If geometry was stored as Well-Known Text, parse it before setting it active.

import pandas as pd
import geopandas as gpd
from shapely import wkt  # noqa: F401  (confirms shapely is available)

df = pd.read_csv("data/zones.csv")  # columns: zone_id, wkt
print(isinstance(df, gpd.GeoDataFrame))  # False

df["geometry"] = gpd.GeoSeries.from_wkt(df["wkt"])

gdf = gpd.GeoDataFrame(df, geometry="geometry", crs="EPSG:4326")
print(gdf.geometry.name)
print(gdf.geometry.head())

Example 4: Geometry lost after a merge

Joining attributes can return a plain DataFrame, which drops the active geometry.

import pandas as pd
import geopandas as gpd

gdf = gpd.read_file("data/regions.gpkg")        # has geometry
stats = pd.read_csv("data/region_stats.csv")    # region_id, population

merged = gdf.merge(stats, on="region_id")
print(type(merged))  # may be a plain DataFrame

# Restore the GeoDataFrame and the active geometry
merged = gpd.GeoDataFrame(merged, geometry="geometry", crs=gdf.crs)
print(isinstance(merged, gpd.GeoDataFrame))
print(merged.geometry.name)

Explanation

A GeoDataFrame is a DataFrame with one extra concept: an active geometry column. This is the column that spatial methods read from. When you call gdf.plot(), gdf.to_crs(), gdf.buffer(), or gpd.sjoin(), GeoPandas looks up the active geometry column by name and operates on it.

By default that column is named geometry, but it does not have to be. A GeoDataFrame can hold geometry in any column name, and even hold more than one geometry column. Only one column is active at a time, and gdf.geometry.name tells you which one it is.

The "No geometry data set" error means GeoPandas tried to find the active geometry column and could not. Either no column was ever set as active, the active column was dropped or renamed, or the object is a plain DataFrame that has no geometry concept at all.

This is why the fix depends on the situation. If geometry exists in a column, you make that column active with set_geometry(). If the object is a plain DataFrame, you rebuild a GeoDataFrame. If you only have coordinates or WKT, you create geometry first, then assign it.

Edge cases or notes

A merge() often returns a DataFrame

When you call gdf.merge(other), pandas may return a plain DataFrame, which has no active geometry. Rebuild it with gpd.GeoDataFrame(merged, geometry="geometry", crs=gdf.crs) and keep the original CRS.

Reading a CSV never gives you geometry

pd.read_csv() returns a DataFrame with text and number columns only. Coordinates in lon/lat columns are just numbers until you call gpd.points_from_xy() and construct a GeoDataFrame.

Multiple geometry columns

A GeoDataFrame can hold several geometry columns, for example centroid and boundary. Only the active one is used by spatial methods. Switch the active column with gdf.set_geometry("centroid") when you need a different one.

WKT strings need parsing

If a column holds text like POINT (12.49 41.89), it is not geometry yet. Parse it with gpd.GeoSeries.from_wkt(df["wkt"]) (or from shapely import wkt for single values) before calling set_geometry().

Dropping the geometry column

Selecting columns with gdf[["name", "value"]] returns a DataFrame without the geometry column. Keep the geometry column in the selection, or set the geometry again afterward.

Renaming the active column with rename()

Using gdf.rename(columns={"geometry": "geom"}) can detach the active geometry. Prefer gdf.rename_geometry("geom"), which renames the column and keeps it active.

For the underlying concept, read What Is a GeoDataFrame? Structure and Key Concepts.

Related task guides:

If geometry quality is the next problem, see How to Remove Null and Empty Geometries in GeoPandas.

FAQ

Why does GeoPandas say "No geometry data set" when my data has coordinates?

Coordinates in lon/lat columns are just numbers. GeoPandas needs an actual geometry column. Build one with gpd.points_from_xy() and construct a GeoDataFrame, or set an existing geometry column active with set_geometry().

How do I check which column is the active geometry?

Use gdf.geometry.name, which returns the name of the active geometry column. If it raises an AttributeError, no geometry column is set yet.

Why did my GeoDataFrame turn into a plain DataFrame after a merge?

A merge() can return a pandas DataFrame, which drops the active geometry. Rebuild it with gpd.GeoDataFrame(merged, geometry="geometry", crs=gdf.crs).

What is the difference between set_geometry() and building a new GeoDataFrame?

set_geometry() activates a column that already contains geometry objects. Building a new GeoDataFrame is for when you only have raw coordinates or WKT strings and need to create the geometry first.

How do I keep the GeoDataFrame type after a pd.concat()?

pd.concat() can return a plain DataFrame, dropping the active geometry. Either use pd.concat([gdf1, gdf2]) where the first object is a GeoDataFrame, or rebuild it afterward with gpd.GeoDataFrame(combined, geometry="geometry", crs=gdf1.crs).

Why does gpd.sjoin() complain about no geometry on the right frame?

gpd.sjoin() needs an active geometry column on both inputs. If the right side came from a merge or column selection that lost its geometry, rebuild it as a GeoDataFrame before the join, or call set_geometry() on the column that still holds geometry.

Can a GeoDataFrame have geometry but no active geometry column set?

Yes. A GeoDataFrame can hold geometry objects in a column while having no column marked active, which still raises the "No geometry data set" error. Activate one with gdf.set_geometry("geom").

How do I convert a GeoDataFrame back to a plain DataFrame on purpose?

Drop the geometry column and cast the type: pd.DataFrame(gdf.drop(columns="geometry")). This is useful when you only need the attribute table for a CSV export or a tabular merge.