How to Look Up the Right EPSG Code for Your Region
To find the correct EPSG code, first decide whether you need display, distance, or area, then let pyproj resolve the UTM zone or national grid for your area of interest and verify the units and axis order before you reproject.
Jump to heading Why this matters
Picking a CRS by guessing — or copying one from an old script — is how layers end up in the wrong place and area statistics come out wrong. The right code depends on both the region and the task: a dashboard covering France wants EPSG:2154 for measurement but EPSG:3857 for its tile map, and a dataset over central Europe wants a different UTM zone than one over the UK. Rather than memorise every zone, you can query the code programmatically for whatever bounding box your data covers, which is far more reliable than eyeballing a zone map. This task sits directly under the CRS & coordinate systems reference, which explains the geographic-versus-projected distinction the lookup depends on.
Jump to heading Prerequisites
- Python 3.10+ with
pyproj>=3.5andgeopandas>=0.14 - A rough bounding box or centre point for your data, in WGS 84 degrees
- Familiarity with the
.crsattribute on aGeoDataFrame, covered in the GeoDataFrame schema reference
Jump to heading Step-by-step solution
Jump to heading Step 1 — Decide what the code is for
The lookup branches on the task. If you only need to draw the data on a web map, you want EPSG:3857 regardless of region and can stop here. If you need distance, you want a local UTM zone or national grid. If you need area, you want an equal-area CRS. Write down the task first, because it changes which of the next steps applies.
task = "distance" # one of: "display", "distance", "area"
Jump to heading Step 2 — Query the UTM zone for your area of interest
For distance work, pyproj can resolve the UTM zone directly from a bounding box. This is the reliable way to get the right zone without reading a map.
from pyproj.database import query_utm_crs_info
from pyproj.aoi import AreaOfInterest
# Bounding box around Berlin (lon/lat degrees)
aoi = AreaOfInterest(
west_lon_degree=13.0, south_lat_degree=52.3,
east_lon_degree=13.8, north_lat_degree=52.7,
)
utm_list = query_utm_crs_info(datum_name="WGS 84", area_of_interest=aoi)
epsg = int(utm_list[0].code)
print(epsg, utm_list[0].name) # 32633 WGS 84 / UTM zone 33N
query_utm_crs_info returns the zones intersecting the bounding box, northern hemisphere first. For a compact area you take the first result.
Jump to heading Step 3 — Resolve and inspect a candidate code
Whether the code came from the UTM query, a national-grid choice, or an equal-area choice, load it with CRS.from_user_input (which accepts EPSG integers, strings, and PROJ) and inspect what you got before trusting it.
from pyproj import CRS
crs = CRS.from_user_input(epsg)
print(crs.name) # WGS 84 / UTM zone 33N
print(crs.axis_info[0].unit_name) # metre
print(crs.is_projected) # True
print(crs.area_of_use.bounds) # (12.0, 0.0, 18.0, 84.0)
The area_of_use.bounds check is the important one: confirm your data actually falls inside that longitude/latitude box, or the projection will distort.
Jump to heading Step 4 — Verify units and axis order, then reproject
Before using the code in the dashboard, confirm the units are metres for measurement work and reproject a copy to sanity-check the coordinate ranges.
import geopandas as gpd
gdf = gpd.read_file("berlin_districts.geojson") # EPSG:4326
metric = gdf.to_crs(epsg)
assert crs.axis_info[0].unit_name == "metre"
# UTM eastings sit near 500,000 at the central meridian
minx, _, maxx, _ = metric.total_bounds
assert 100_000 < minx < 900_000, "eastings out of UTM range — wrong zone?"
gdf["area_km2"] = metric.area / 1_000_000
Jump to heading Region → recommended EPSG
For quick reference, these are sound defaults for measurement in common regions. Display always uses EPSG:3857.
| Region | Recommended EPSG | Name | Notes |
|---|---|---|---|
| United Kingdom | 27700 | OSGB36 / British National Grid | National grid, whole country |
| France (metropolitan) | 2154 | RGF93 / Lambert-93 | National grid |
| Germany / central EU | 25832 | ETRS89 / UTM zone 32N | 6°E–12°E |
| Eastern EU / Poland | 32633 | WGS 84 / UTM zone 33N | 12°E–18°E |
| Europe (area stats) | 3035 | ETRS89-extended / LAEA Europe | Equal-area |
| Contiguous US (area) | 5070 | NAD83 / Conus Albers | Equal-area |
| Global (area stats) | 6933 | WGS 84 / EASE-Grid 2.0 | Equal-area |
Jump to heading Verification
Confirm the code round-trips cleanly and produces measurements in the right order of magnitude:
from pyproj import CRS
crs = CRS.from_epsg(epsg)
assert not crs.is_deprecated, f"EPSG:{epsg} is deprecated — migrate to its replacement"
assert crs.is_projected, "expected a projected CRS for distance/area work"
# A Berlin district area should be single- to double-digit km², never < 0.01
assert gdf["area_km2"].between(0.01, 10_000).all()
Jump to heading Edge cases and gotchas
- UTM zone boundaries. A point at 12.0°E sits exactly on the boundary between zones 32N and 33N.
query_utm_crs_infomay return both; pick the zone that contains the bulk of your data, not a single edge point. - Cross-zone areas. If your data spans more than one 6° UTM band, do not force a single zone — distortion grows fast at the edges. Switch to a national grid or the regional equal-area code from the table above.
- Deprecated codes. Older datasets sometimes carry superseded codes.
CRS.from_epsg(code).is_deprecatedflags them; migrate to the replacement the warning names so you do not inherit an old datum definition.
Jump to heading FAQ
How do I find the UTM zone for a coordinate in Python?
Use pyproj.database.query_utm_crs_info with an AreaOfInterest bounding box around your point. It returns the matching UTM CRS records, and you read the EPSG code from the first result’s code attribute. UTM zones are 6° of longitude wide, so a bounding box around a single point resolves to exactly one zone.
What EPSG code should I use if my region spans two UTM zones?
Do not force a single UTM zone across a wide region — distortion grows quickly outside a zone’s 6° band. Use a national grid such as EPSG:27700 for the UK, or a regional equal-area projection such as EPSG:3035 for Europe, that is designed to cover the whole area at acceptable distortion.
How do I know if an EPSG code is deprecated?
Build the CRS with pyproj.CRS.from_epsg and check its is_deprecated attribute. Deprecated codes still resolve for backward compatibility, but pyproj flags them and you should migrate to the current replacement code to avoid subtle datum differences.
Back to CRS & Coordinate Systems Reference
Related
- Reprojecting Between EPSG:4326 and EPSG:3857 — the display conversion once you have chosen your codes
- CRS & Coordinate Systems Reference — geographic vs projected, axis order, and the full EPSG table
- Dynamic Spatial Filtering — using a resolved CRS in a live, filterable map