How to Calculate Area and Distance in GeoPandas (Correctly)
Problem statement
A common GIS mistake is to calculate polygon area or feature distance directly from data in a geographic CRS such as EPSG:4326.
This usually produces wrong results because geographic coordinates are stored in degrees, not meters. GeoPandas will still return numeric values from .area, .length, or .distance(), but those values are based on the coordinate units of the geometry. If your data is in latitude/longitude, the numbers are not reliable for real-world measurement.
In practice, users are usually trying to do one of these tasks:
- calculate polygon area
- calculate line length
- calculate distance between points or other features
The practical fix is to reproject the data to a suitable projected CRS first, then run the measurement.
Quick answer
To calculate area in GeoPandas or measure distance correctly:
- Check the current CRS with
.crs - Reproject to a suitable projected CRS with
.to_crs() - Use:
.areafor polygon area.lengthfor line length.distance()for distances between geometries
import geopandas as gpd
gdf = gpd.read_file("parcels.shp")
# Reproject to a projected CRS with suitable linear units
gdf_proj = gdf.to_crs("EPSG:32633")
# Polygon area in square meters if the CRS uses meters
gdf_proj["area_m2"] = gdf_proj.area
The output units come from the CRS units. In many projected CRSs those units are meters, but some projected CRSs use other units such as feet.
Reproject before you measure
Step-by-step solution
Check the current CRS
Start by checking whether your layer is in a geographic CRS like EPSG:4326.
import geopandas as gpd
gdf = gpd.read_file("parcels.shp")
print(gdf.crs)
If you see something like EPSG:4326, your coordinates are in degrees. That is fine for storage and web mapping, but not for accurate area and distance measurements.
Choose a suitable projected CRS
You need a projected CRS that fits your study area and task.
Common practical choices:
- Local UTM zone for city or regional data
- National projected CRS for country-specific workflows
- Equal-area CRS when area accuracy is the main priority
Examples:
EPSG:32633— UTM zone 33N (only appropriate for data in that zone)EPSG:27700— British National GridEPSG:5070— NAD83 / Conus Albers for US-wide area analysis
For small to medium study areas, a local UTM CRS is often a good default. For large regions, CRS choice matters more because one projected CRS may not preserve both area and distance equally well everywhere.
Reproject the GeoDataFrame
Use .to_crs() to create a projected copy.
gdf_proj = gdf.to_crs("EPSG:32633")
This keeps the original gdf unchanged and gives you a new GeoDataFrame for measurement.
Calculate polygon area
After reprojection, use .area.
gdf_proj["area_m2"] = gdf_proj.area
If the CRS uses meters, the result is square meters.
You can also convert to hectares or square kilometers:
gdf_proj["area_ha"] = gdf_proj["area_m2"] / 10_000
gdf_proj["area_km2"] = gdf_proj["area_m2"] / 1_000_000
Calculate line length
For roads, rivers, or utility lines, use .length after reprojection.
lines = gpd.read_file("roads.shp")
lines_proj = lines.to_crs("EPSG:32633")
lines_proj["length_m"] = lines_proj.length
lines_proj["length_km"] = lines_proj["length_m"] / 1000
Calculate distance between two geometries
Use .distance() when comparing geometries in the same projected CRS.
points = gpd.read_file("sites.shp").to_crs("EPSG:32633")
distance_m = points.geometry.iloc[0].distance(points.geometry.iloc[1])
print(distance_m)
For two layers, both must use the same CRS:
schools = gpd.read_file("schools.shp").to_crs("EPSG:32633")
hospitals = gpd.read_file("hospitals.shp").to_crs("EPSG:32633")
schools["dist_to_first_hospital_m"] = schools.geometry.distance(
hospitals.geometry.iloc[0]
)
Code examples
Example 1: Calculate polygon area in square meters
import geopandas as gpd
parcels = gpd.read_file("data/parcels.shp")
print("Original CRS:", parcels.crs)
# Reproject to a projected CRS with meter units
parcels_proj = parcels.to_crs("EPSG:32633")
# Calculate area
parcels_proj["area_m2"] = parcels_proj.area
print(parcels_proj[["area_m2"]].head())
# Optional: save result
parcels_proj.to_file("output/parcels_with_area.shp")
Example 2: Convert area to hectares and square kilometers
parcels_proj["area_ha"] = parcels_proj["area_m2"] / 10_000
parcels_proj["area_km2"] = parcels_proj["area_m2"] / 1_000_000
print(parcels_proj[["area_m2", "area_ha", "area_km2"]].head())
Example 3: Calculate line length in meters
import geopandas as gpd
roads = gpd.read_file("data/roads.shp")
roads_proj = roads.to_crs("EPSG:32633")
roads_proj["length_m"] = roads_proj.length
print(roads_proj[["length_m"]].head())
Example 4: Calculate distance between points
import geopandas as gpd
origins = gpd.read_file("data/origins.shp").to_crs("EPSG:32633")
destinations = gpd.read_file("data/destinations.shp").to_crs("EPSG:32633")
origins["distance_to_first_dest_m"] = origins.geometry.distance(
destinations.geometry.iloc[0]
)
print(origins[["distance_to_first_dest_m"]].head())
Example 5: Calculate nearest-feature distance
A practical pattern is to find the nearest feature in another layer, then measure the distance.
import geopandas as gpd
buildings = gpd.read_file("data/buildings.shp").to_crs("EPSG:32633")
bus_stops = gpd.read_file("data/bus_stops.shp").to_crs("EPSG:32633")
nearest = gpd.sjoin_nearest(
buildings,
bus_stops,
how="left",
distance_col="nearest_dist_m"
)
print(nearest[["nearest_dist_m"]].head())
Note: gpd.sjoin_nearest() requires a recent GeoPandas version and a working spatial index backend in your environment.
Explanation
The key issue is the difference between a geographic CRS and a projected CRS.
A geographic CRS stores coordinates on the earth using angular units such as latitude and longitude. EPSG:4326 is the most common example. In this CRS, coordinates are in degrees, so geometry calculations based on raw coordinate values do not represent meters or square meters.
A projected CRS transforms the curved earth surface into a flat coordinate system. Its units are often meters or feet. That makes it suitable for planar measurements such as area, line length, and distance between features.
GeoPandas does not automatically decide whether your CRS is appropriate for measurement. It simply uses the geometry coordinates as they are:
.areareturns area in CRS units squared.lengthreturns length in CRS units.distance()returns distance in CRS units
So if your data is in degrees, your output is also degree-based, which is usually not what you want.
For accurate GeoPandas area calculation and distance measurement, reproject first, then measure.
Edge cases and notes
Data in EPSG:4326 gives misleading results
You can run .area or .distance() on data in EPSG:4326, but the results are not suitable for real-world measurement. The values may look valid because they are numeric, but they are based on degrees.
Missing CRS metadata
If gdf.crs is None, do not use .to_crs() yet. First assign the correct existing CRS with .set_crs(), then reproject if needed.
gdf = gdf.set_crs("EPSG:4326")
gdf_proj = gdf.to_crs("EPSG:32633")
Both layers must use the same CRS for distance calculations
If you calculate distance between two layers, align both to the same projected CRS first.
layer1 = layer1.to_crs("EPSG:32633")
layer2 = layer2.to_crs("EPSG:32633")
Multipolygon and multiline geometries
Multipart geometries still work with .area and .length. The result is the total area or total length of the full geometry.
Large study areas need careful CRS selection
A single local projected CRS may work well for one city or region but perform poorly across a continent. If you are measuring over large areas, choose a CRS designed for that extent and purpose. For some large-extent workflows, a geodesic approach may be more appropriate than using one projected CRS for everything.
Geometry validity can affect results
Invalid polygons can produce unreliable area values. If results look wrong, check geometry validity.
invalid = ~gdf.geometry.is_valid
print(gdf[invalid])
If needed, repair invalid geometries before measurement.
Internal links
- How to Read a Shapefile in Python with GeoPandas
- How to Export GeoJSON in Python with GeoPandas
- How to Reproject Spatial Data in Python (GeoPandas)
FAQ
How do I calculate area in GeoPandas in square meters?
Reproject your polygon layer to a projected CRS that uses meters, then use .area.
gdf_proj = gdf.to_crs("EPSG:32633")
gdf_proj["area_m2"] = gdf_proj.area
Why is GeoPandas giving incorrect area or distance values?
The most common reason is that your data is in a geographic CRS such as EPSG:4326. In that case, coordinates are in degrees, so area and distance calculations are not in meters.
What CRS should I use to calculate distance in GeoPandas?
Use a projected CRS appropriate for your study area. For regional work, a local UTM zone is often a practical choice. For country-scale or area-focused analysis, a national or equal-area CRS may be better.
Do both GeoDataFrames need the same CRS for .distance()?
Yes. Both layers should be in the same projected CRS before running .distance() or nearest-distance workflows.
Can I calculate area directly in EPSG:4326?
You can, but the result will not be a reliable real-world area measurement. For correct values, reproject first.
How do I confirm what linear units my projected CRS uses?
Check the axis units on the CRS object with gdf.crs.axis_info[0].unit_name. Most projected CRSs such as UTM zones (EPSG:32633) and EPSG:5070 report metre, but some national grids use feet, in which case .area and .length return feet-based values.
Why do my UTM areas drift from the true area at the edge of my dataset?
Each UTM zone is only accurate within its own narrow band, and distortion grows toward the zone edges and away from the central meridian. If your data spans more than one zone or a wide region, switch to an equal-area CRS such as EPSG:5070 for area, so the measurement stays consistent across the whole extent.
How do I compute pairwise distances between every feature in one layer and another?
Reproject both layers to the same projected CRS, then use gpd.sjoin_nearest() with distance_col= to get each feature's nearest neighbour and its distance in one step. For an explicit all-pairs matrix you would loop or use a spatial index, but nearest-distance covers most real tasks.
Is .area the planar area or the surface area following terrain?
It is the planar (2D) area in the CRS coordinate plane; GeoPandas does not account for elevation or terrain slope. The value reflects the flat footprint of the polygon, which is what most GIS area calculations expect.
Why is my distance zero when I expected a positive value?
.distance() returns 0 when the two geometries touch or overlap, because that is the minimum separating distance. If you expected a centre-to-centre or boundary distance instead, measure between .centroid points or .boundary geometries explicitly.