Handling Antimeridian and Polar CRS Edge Cases
Work in a projected CRS in which the area is contiguous, and split only the geographic output:
from rasterio.warp import transform_bounds
w, s, e, n = transform_bounds(src.crs, "EPSG:4326", *src.bounds, densify_pts=21)
crosses_antimeridian = w > e # e.g. w=178.9, e=-178.4
near_pole = max(abs(s), abs(n)) > 84
The failures on this page are rare, which is exactly why they are not handled until a scene from Fiji, Chukotka or Antarctica breaks a pipeline. This page belongs to mastering CRS transformations in rasterio in Core Raster Fundamentals & STAC Mapping.
What Goes Wrong at 180°
The consequence in a catalog is that such an item matches every spatial search on Earth, and the consequence in a mosaic is that it tries to allocate a raster spanning the globe at full resolution. Both are the same bug: treating longitude as a linear quantity where it wraps.
Environment & Setup
| Package | Version pin | Used for |
|---|---|---|
rasterio |
>=1.3.0 |
Bounds transforms and reprojection |
pyproj |
>=3.6 |
CRS definitions and area-of-use checks |
shapely |
>=2.0 |
Splitting footprints at the antimeridian |
pip install "rasterio>=1.3.0" "pyproj>=3.6" "shapely>=2.0"
Complete Working Example
import rasterio
from rasterio.warp import transform_bounds
from shapely.geometry import MultiPolygon, box
def geographic_footprint(path: str):
"""WGS84 footprint that is correct across the antimeridian."""
with rasterio.open(path) as src:
w, s, e, n = transform_bounds(src.crs, "EPSG:4326", *src.bounds, densify_pts=41)
if w <= e:
return box(w, s, e, n), [w, s, e, n]
# Crosses 180°: two boxes, and a STAC-style bbox with west > east
footprint = MultiPolygon([box(w, s, 180.0, n), box(-180.0, s, e, n)])
return footprint, [w, s, e, n]
def working_crs_for(lon: float, lat: float) -> str:
"""A projected CRS in which an area around (lon, lat) is contiguous."""
if lat >= 60:
return "EPSG:3413" # NSIDC Arctic polar stereographic
if lat <= -60:
return "EPSG:3031" # Antarctic polar stereographic
zone = int((lon + 180) // 6) % 60 + 1
return f"EPSG:{(32600 if lat >= 0 else 32700) + zone}"
The STAC specification handles the antimeridian by allowing a bbox whose west value is greater than its east value, which is why the function returns [w, s, e, n] unchanged in that case rather than “fixing” it. Search clients that follow the specification interpret that correctly; code that sorts the values to force w < e recreates the whole-world bug.
Polar Regions
Polar products — sea ice, ice sheet velocity, snow cover — are almost always distributed in polar stereographic grids for exactly these reasons, and the safest approach is to keep them there for all processing. Reprojecting polar data to geographic coordinates for analysis produces pixels whose ground size varies by a factor of five or more across a single scene, which silently biases every area calculation.
Tile servers are the awkward exception, since web maps expect Web Mercator and Web Mercator stops at about 85°. For display, either serve the polar data through a polar-projected web map, or accept that the region above 85° is simply not shown — a limitation of the display, not of the data. The display-side choices are covered in preparing rasters for the web.
Mosaicking Across the Line
Mosaicking scenes on both sides of the antimeridian in WGS84 is where most pipelines finally fail, because the merged extent computed from min and max longitudes is the whole globe. The fix is to do the mosaic in a projected CRS centred on the area — a UTM zone that contains it, or a custom transverse Mercator with its central meridian at 180° — where the scenes are adjacent and the extent is small.
from rasterio.crs import CRS
# A transverse Mercator centred on the antimeridian: contiguous for Fiji, Chukotka, the Aleutians
tm180 = CRS.from_proj4("+proj=tmerc +lon_0=180 +k=0.9996 +x_0=500000 +datum=WGS84 +units=m")
Reproject each scene into that CRS, merge with the usual tools from merging tiles with rasterio merge, and only derive a geographic footprint at the very end, using the split logic above.
Verification
footprint, bbox = geographic_footprint("S2B_01KAV_20260714.tif")
w, s, e, n = bbox
width = (e - w) % 360
assert width < 10, f"footprint {width:.1f} degrees wide — wrap not handled"
assert footprint.area < 50, "footprint area implausible for one scene"
A single satellite scene is at most a few degrees across even at high latitude, so any computed width above that is a wrap bug rather than a large scene. That assertion belongs in any ingest step that writes catalog items.
Common Errors
A catalog item matches every search
Its bbox spans −180 to 180 because the antimeridian crossing was flattened. Emit the STAC-style bbox with west greater than east.
Mosaic allocation fails with a huge array
The merge extent was computed in WGS84 across the line. Mosaic in a projected CRS centred on the area.
Reprojected polar data has streaks
The warp crossed the pole or the dateline in geographic space. Keep polar data in a polar stereographic CRS.
transform_bounds returns a box missing part of the footprint
Edges were not densified. Pass densify_pts=21 or more so curved edges are sampled.
Frequently Asked Questions
Q: Why does my bounding box span the whole world? Because the raster straddles 180 degrees, so its western edge is near plus 179 and its eastern edge near minus 179. Taking the minimum and maximum gives minus 179 to plus 179 — nearly the entire globe — rather than the narrow strip that actually crosses the meridian.
Q: Which CRS should I use near the poles? A polar stereographic projection — EPSG 3413 or 3995 for the Arctic, 3031 for the Antarctic. Geographic coordinates and Web Mercator both break down there, and UTM zones converge to slivers that are awkward to mosaic.
Q: Does reprojection across the antimeridian work at all? Into a projected CRS centred on the area, yes, and cleanly. Into WGS84 it produces either a raster that wraps the globe or two pieces, depending on the tool, so keep processing in a projected CRS and convert only the final footprint.
Q: How common are these cases in practice? Rarer than most bugs and more damaging when they occur. Pacific island nations, the Russian Far East, Alaska’s Aleutians and both polar regions all trigger them, and a global pipeline will meet all of them eventually — which is why handling them once, centrally, is worth the effort.
Related
- Mastering CRS Transformations in rasterio — the parent topic.
- Transforming Point Coordinates with pyproj — the coordinate operations underneath.
- Filtering STAC Items with CQL2 — spatial queries that must cope with wrapped bboxes.
- Assigning a CRS to a Raster That Has None — the other CRS repair on this topic.