How to Read a CSV with Coordinates as a GeoDataFrame

Problem statement

A CSV file with latitude and longitude columns is not spatial data by itself. It is just tabular data until you create a geometry column and assign a coordinate reference system (CRS).

A common GIS task is to take a CSV of site locations, inspection points, geocoded customer addresses, or sensor readings with coordinates, and turn it into a GeoDataFrame for mapping, spatial joins, distance analysis, or export.

Typical coordinate column patterns include:

  • latitude and longitude
  • lat and lon
  • x and y
  • projected coordinates such as easting and northing

The goal is to load the CSV, create point geometries from the coordinate columns, set the correct CRS, and return a valid GeoDataFrame.

Quick answer

The shortest working way to read a CSV with coordinates into a GeoDataFrame is to read the CSV with pandas, create point geometry with GeoPandas, and assign the correct CRS.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("points.csv")

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

print(gdf.head())
print(gdf.crs)

Key requirements:

  • identify the correct coordinate columns
  • use longitude as x and latitude as y
  • assign the correct CRS

For most CSV files where longitude and latitude are stored in decimal degrees in WGS84, EPSG:4326 is the correct CRS.

Step-by-step solution

Step 1: Read the CSV into a pandas DataFrame

Start by loading the CSV with pandas and checking the coordinate columns.

import pandas as pd

df = pd.read_csv("points.csv")

print(df.head())
print(df.columns)

Confirm that the expected coordinate columns are present before creating geometry.

You should also check for missing or invalid values:

print(df[["longitude", "latitude"]].isna().sum())

If the coordinates are stored as text, convert them to numeric:

df["longitude"] = pd.to_numeric(df["longitude"], errors="coerce")
df["latitude"] = pd.to_numeric(df["latitude"], errors="coerce")

Then remove rows with missing coordinates:

df = df.dropna(subset=["longitude", "latitude"])

Step 2: Create point geometry from coordinate columns

Use geopandas.points_from_xy() to build point geometries.

import geopandas as gpd

geometry = gpd.points_from_xy(df["longitude"], df["latitude"])

The order matters:

  • longitude = x
  • latitude = y

This is one of the most common mistakes when creating points from CSV coordinates.

Step 3: Convert the DataFrame to a GeoDataFrame

Wrap the DataFrame in gpd.GeoDataFrame() and pass the geometry.

gdf = gpd.GeoDataFrame(df, geometry=geometry)

At this stage, you have a geometry column, but the data still needs a CRS to be spatially meaningful.

Step 4: Assign the correct CRS

If the CSV contains geographic coordinates in latitude and longitude, use EPSG:4326.

gdf = gdf.set_crs("EPSG:4326")

If the CSV uses projected coordinates such as UTM easting/northing, assign the projected CRS instead. For example, UTM Zone 33N:

gdf = gdf.set_crs("EPSG:32633")

Do not guess the CRS. If you assign the wrong one, your points will appear in the wrong place and later analysis will be wrong.

Step 5: Verify the result

Check the first rows, geometry type, CRS, and bounds.

print(gdf.head())
print(gdf.geom_type.unique())
print(gdf.crs)
print(gdf.total_bounds)

You can also make a quick plot:

gdf.plot()

This is a simple way to confirm that the points were created correctly.

Code examples

Example 1: Read a latitude/longitude CSV into a GeoDataFrame

This is the standard case.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("store_locations.csv")

df["longitude"] = pd.to_numeric(df["longitude"], errors="coerce")
df["latitude"] = pd.to_numeric(df["latitude"], errors="coerce")
df = df.dropna(subset=["longitude", "latitude"])

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

print(gdf.head())
print(gdf.crs)

This creates point geometry from longitude and latitude and assigns WGS84.

Example 2: Read a CSV with custom x/y column names

Not every file uses standard names. This example uses x_coord and y_coord.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("survey_points.csv")

df["x_coord"] = pd.to_numeric(df["x_coord"], errors="coerce")
df["y_coord"] = pd.to_numeric(df["y_coord"], errors="coerce")
df = df.dropna(subset=["x_coord", "y_coord"])

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

print(gdf.head())

If these columns are longitude and latitude in decimal degrees, EPSG:4326 is appropriate. If they are projected coordinates, assign the projected CRS instead.

Example 3: Read a CSV with projected coordinates

This is common for engineering, utilities, or local government data. Do not treat projected x/y values as lat/lon.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("assets_utm.csv")

gdf = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df["easting"], df["northing"]),
    crs="EPSG:32632"
)

print(gdf.head())
print(gdf.crs)

This example uses UTM Zone 32N. Replace the CRS with the correct one for your data.

Example 4: Reproject the GeoDataFrame after loading

After loading the CSV, you may need another CRS for web maps or spatial analysis.

import pandas as pd
import geopandas as gpd

df = pd.read_csv("monitoring_sites.csv")

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

gdf_web = gdf.to_crs("EPSG:3857")
gdf_utm = gdf.to_crs("EPSG:32618")

print(gdf_web.crs)
print(gdf_utm.crs)

Use .to_crs() only after the original CRS has been assigned correctly.

Explanation

A pandas DataFrame is not enough for GIS work because it has no spatial geometry and no CRS metadata. You can store coordinate columns in a DataFrame, but GeoPandas needs a geometry column to perform mapping and spatial operations.

A GeoDataFrame becomes spatial when it has:

  • a geometry column containing Shapely geometries such as Point
  • a CRS that describes what those coordinates mean

This is why converting a CSV with coordinates into a GeoDataFrame always has two main steps: make geometry, then assign CRS.

It is also important to distinguish between assigning a CRS and reprojecting:

  • Assigning a CRS means telling GeoPandas what CRS the current coordinates already use
  • Reprojecting means converting coordinates from one CRS to another with .to_crs()

If your CSV already contains WGS84 longitude and latitude, assign EPSG:4326. If you need Web Mercator or UTM later, reproject after that.

Coordinate order is another frequent source of errors. For point creation:

  • x = longitude
  • y = latitude

If you reverse them, your points may end up in the wrong country or fail visual checks entirely.

Edge cases and notes

CSV has latitude and longitude stored as text

If coordinate fields contain spaces, commas, or mixed text, convert them first:

df["longitude"] = pd.to_numeric(df["longitude"], errors="coerce")
df["latitude"] = pd.to_numeric(df["latitude"], errors="coerce")

Rows that fail conversion become NaN.

CSV contains missing coordinate values

Drop rows with null coordinates before creating geometry:

df = df.dropna(subset=["longitude", "latitude"])

Longitude and latitude are reversed

If points appear in the wrong place, check whether you passed latitude as x and longitude as y. For point creation in GeoPandas, the correct order is always x then y.

CSV uses a projected coordinate system

If your file contains easting/northing values, do not assign EPSG:4326. That CRS is for geographic longitude/latitude coordinates in degrees, not projected coordinates in meters.

CSV uses a different delimiter or encoding

Some GIS exports use semicolons or non-UTF-8 encodings:

df = pd.read_csv("points.csv", sep=";", encoding="latin1")

This is common with CSV files exported from Excel or local GIS tools.

If you need a refresher on CRS concepts, see What Is a Coordinate Reference System (CRS) in GeoPandas?.

For the next step after loading data, see How to Reproject Spatial Data in Python (GeoPandas).

To save your points for use in other tools, see How to Export GeoJSON in Python with GeoPandas.

If your points are not showing where you expect, check Why Are My Points Showing in the Wrong Location in GeoPandas?.

FAQ

How do I convert a CSV with latitude and longitude to a GeoDataFrame in Python?

Read the CSV with pandas.read_csv(), create point geometry with gpd.points_from_xy(), and wrap the result in gpd.GeoDataFrame() with the correct CRS.

What CRS should I use for a CSV with lat/lon columns?

In most cases, use EPSG:4326 if the coordinates are longitude and latitude in decimal degrees in WGS84.

What is the difference between assigning a CRS and reprojecting a GeoDataFrame?

Assigning a CRS tells GeoPandas what CRS the existing coordinates already use. Reprojecting converts those coordinates into a different CRS using .to_crs().

Why are my CSV points appearing in the wrong place on the map?

The most common causes are:

  • latitude and longitude columns were swapped
  • the wrong CRS was assigned
  • projected coordinates were incorrectly treated as WGS84
  • coordinate values were malformed or read as text