Polar Data Lands in the Wrong Place

On this page

Problem statement

You load a polar grid β€” sea-ice concentration, an ice-sheet DEM, a snow product β€” and it does not sit where it should. The ice edge is rotated a quarter turn around the pole, or appears over Antarctica, or sits a few tens of kilometres too far from the pole all round. Polar stereographic grids come in several near-identical versions that differ in central meridian, latitude of true scale, ellipsoid and hemisphere, and a file whose CRS was lost or overwritten can be assigned the wrong one without any error.

This page takes NSIDC's September 2025 Arctic sea-ice grid, deliberately assigns it the wrong polar CRSs that are most often confused, measures where known places end up, and shows how to tell which mistake you are looking at.

Quick answer

Push a few known places through the right CRS and back through the suspect one, and measure the error. The pattern names the mistake:

for label, wrong in WRONG.items():
    to_wrong = Transformer.from_crs(wrong, 4326, always_xy=True)
    errs = []
    for name, (lon, lat) in PLACES.items():
        x, y = to_right.transform(lon, lat)                  # where the grid really puts this place
        lon2, lat2 = to_wrong.transform(x, y)                 # where a mislabelled grid says it is
        errs.append(f"{name} {geod.inv(lon, lat, lon2, lat2)[2] / 1000:,.0f} km")
    print(f"{label:30} " + " | ".join(errs))
EPSG:3413 (WGS 84 twin)        Utqiagvik 0 km | Longyearbyen 0 km | Tiksi 0 km | Nuuk 0 km
EPSG:3995 (meridian 0)         Utqiagvik 1,572 km | Longyearbyen 999 km | Tiksi 1,544 km | Nuuk 2,139 km
true scale at the pole         Utqiagvik 62 km | Longyearbyen 39 km | Tiksi 61 km | Nuuk 84 km
EPSG:3031 (Antarctic)          Utqiagvik 17,046 km | Longyearbyen 18,390 km | Tiksi 18,071 km | Nuuk 17,272 km
error pattern likely mistake
none the right CRS, or EPSG:3413 for EPSG:3411
hundreds to thousands of km, rotated about the pole wrong central meridian (e.g. EPSG:3995 for EPSG:3411)
tens of km, all towards or away from the pole wrong latitude of true scale
the other hemisphere north grid labelled with a south CRS
Three Arctic maps with correct coastlines: the September 2025 ice cells placed by EPSG:3411 fill the Arctic Ocean; labelled EPSG:3995 the same cells are rotated 45 degrees and spill over Greenland and Canada; with true scale assumed at the pole they sit slightly too far from the pole. Three Arctic maps with correct coastlines: the September 2025 ice cells placed by EPSG:3411 fill the Arctic Ocean; labelled EPSG:3995 the same cells are rotated 45 degrees and spill over Greenland and Canada; with true scale assumed at the pole they sit slightly too far from the pole.
The same grid under three CRS labels. NSIDC Sea Ice Index; coastlines Natural Earth.

Read the CRS, compare candidates, spot rotation or scale

Fixing a polar grid with the wrong CRS label. fix the label, not the dataread file CRSbefore assigningtest known placesor the land maskmatch the patternrotate, scale, flipassign the rightCRSno resamplingLand mask check: 100% on land under 3411, 46% under 3995 spatialworkflow.io
The fix is always to the label.

1. Read the CRS the file actually has

The NSIDC GeoTIFF carries its CRS. Look at it before assigning anything:

with rasterio.open("data/N_202509_concentration_v4.0.tif") as src:
    print(src.crs.to_epsg(), src.crs.to_proj4())
3411 +proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +x_0=0 +y_0=0 +a=6378273 +rf=298.279411123064 +units=m +no_defs=True

Polar stereographic north, origin at the pole, true scale at 70Β° N, central meridian 45Β° W, on the Hughes 1980 ellipsoid. Four parameters, each of which differs between common polar CRSs.

2. Compare the candidates

for code in (3411, 3413, 3995, 3031):
    print(code, CRS.from_epsg(code).name, "|", CRS.from_epsg(code).to_proj4().replace("+no_defs +type=crs", ""))
3411 NSIDC Sea Ice Polar Stereographic North | +proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +x_0=0 +y_0=0 +a=6378273 +b=6356889.449 +units=m 
3413 WGS 84 / NSIDC Sea Ice Polar Stereographic North | +proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +x_0=0 +y_0=0 +datum=WGS84 +units=m 
3995 WGS 84 / Arctic Polar Stereographic | +proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m 
3031 WGS 84 / Antarctic Polar Stereographic | +proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m 

EPSG:3413 is EPSG:3411 on WGS 84; EPSG:3995 has its central meridian at 0Β° and true scale at 71Β° N; EPSG:3031 is the Antarctic grid. Only the first pair is interchangeable at the scale of a 25 km cell.

3. Recognise a rotation

A different central meridian rotates the whole grid about the pole by the difference β€” 45Β° between EPSG:3411 and EPSG:3995. Places keep their latitude and lose their longitude:

x, y = to_right.transform(*PLACES["Utqiagvik"])
print("Utqiagvik read as EPSG:3995:", [round(v, 1) for v in Transformer.from_crs(3995, 4326, always_xy=True).transform(x, y)])
Utqiagvik read as EPSG:3995: [-111.8, 71.3]

4. Recognise a scale error

A different latitude of true scale shrinks or stretches the grid radially. Assuming true scale at the pole instead of at 70Β° N puts every point slightly too far south β€” tens of kilometres at the ice edge β€” without rotating anything. It is the hardest of the mistakes to see on a map and the easiest to measure with a known place.

5. Fix it by assigning, not reprojecting

If the CRS label is wrong, the coordinates are right and only the label must change. Reprojecting from the wrong label would bake the error into the data:

with rasterio.open("data/N_202509_concentration_v4.0.tif") as src:
    profile = src.profile | {"crs": CRS.from_epsg(3411)}
    with rasterio.open("data/fixed.tif", "w", **profile) as dst:
        dst.write(src.read())
print(rasterio.open("data/fixed.tif").crs.to_epsg())
3411

Diagnosing an unknown label from the data

The data used on this page

NSIDC Sea Ice Index, version 4 (G02135), September 2025 monthly concentration GeoTIFF, Northern Hemisphere, from NSIDC / NOAA.

import warnings
from pathlib import Path

import numpy as np
import rasterio
import requests
from pyproj import CRS, Geod, Transformer

Path("data").mkdir(exist_ok=True)
UA = {"User-Agent": "spatialworkflow.io guide (https://www.spatialworkflow.io)"}
f = Path("data/N_202509_concentration_v4.0.tif")
if not f.exists():
    f.write_bytes(requests.get("https://noaadata.apps.nsidc.org/NOAA/G02135/north/monthly/geotiff/09_Sep/" + f.name,
                               headers=UA, timeout=120).content)
PLACES = {"Utqiagvik": (-156.79, 71.29), "Longyearbyen": (15.63, 78.22), "Tiksi": (128.87, 71.64), "Nuuk": (-51.72, 64.18)}
WRONG = {"EPSG:3413 (WGS 84 twin)": "EPSG:3413", "EPSG:3995 (meridian 0)": "EPSG:3995",
         "true scale at the pole": "+proj=stere +lat_0=90 +lat_ts=90 +lon_0=-45 +a=6378273 +rf=298.279411123064 +units=m",
         "EPSG:3031 (Antarctic)": "EPSG:3031"}
to_right = Transformer.from_crs(4326, 3411, always_xy=True)
geod = Geod(ellps="WGS84")
warnings.filterwarnings("ignore", message="You will likely lose")   # PROJ-string export is for display only
print(f.stat().st_size, "bytes")
666553 bytes

Example 1: diagnose an unknown label from the data itself

If you do not know which places should be where, the grid's own land mask is a check. NSIDC marks land as 2540; those cells should fall on land in any coastline dataset:

import geopandas as gpd
from shapely.geometry import Point
if not Path("data/ne_110m_land.zip").exists():
    Path("data/ne_110m_land.zip").write_bytes(requests.get("https://naciscdn.org/naturalearth/110m/physical/ne_110m_land.zip", headers=UA, timeout=120).content)
land = gpd.read_file("/vsizip/data/ne_110m_land.zip").union_all()
with rasterio.open(f) as src:
    a, tr = src.read(1), src.transform
r, c = np.nonzero(a == 2540)
sel = np.random.default_rng(0).choice(len(r), 400, replace=False)
xs, ys = rasterio.transform.xy(tr, r[sel], c[sel])
for label, code in (("EPSG:3411", "EPSG:3411"), ("EPSG:3995", "EPSG:3995")):
    lon, lat = Transformer.from_crs(code, 4326, always_xy=True).transform(np.array(xs), np.array(ys))
    on_land = np.mean([land.contains(Point(x, y)) for x, y in zip(lon, lat)])
    print(f"{label}: {on_land:.0%} of the grid's land cells fall on Natural Earth land")
EPSG:3411: 100% of the grid's land cells fall on Natural Earth land
EPSG:3995: 46% of the grid's land cells fall on Natural Earth land

Example 2: the southern grid

The Antarctic files use EPSG:3412 β€” polar stereographic south, true scale at 70Β° S, central meridian 0Β°. Assign it to a northern file and everything moves to the other end of the Earth, which is at least easy to spot; assign EPSG:3411 to a southern file and the error is just as large but the map can look plausible over the Arctic Ocean.

What defines a polar stereographic grid

A polar stereographic projection is defined by the pole it is centred on, the latitude at which its scale is true, the meridian that points down the page, and the ellipsoid. Data producers choose these differently: NSIDC's sea-ice grids use 70Β° and 45Β° W on the Hughes ellipsoid, the European Arctic grid EPSG:3995 uses 71Β° and 0Β° on WGS 84, Antarctic products use their own. The coordinates in a file are meaningless without the right set.

The parameters of four polar stereographic CRSs often confused with each other. four polar grids, four parameter setsEPSGpoletrue scalemeridian downellipsoid3411north70Β° N45Β° WHughes 19803413north70Β° N45Β° WWGS 843995north71Β° N0Β°WGS 843031south71Β° S0Β°WGS 84Only the first two are interchangeable for 25 km data spatialworkflow.io
Each parameter moves the data in its own way if wrong.

Each parameter, if wrong, moves the data in a characteristic way. A wrong meridian rotates; a wrong latitude of true scale changes the radius; a wrong ellipsoid shifts by metres to hundreds of metres; a wrong hemisphere moves everything to the other pole. Measuring known places β€” or the data's own land mask β€” against those patterns identifies the mistake without guessing, and the fix is always to correct the label rather than to reproject.

EPSG 3411 versus 3413, and assign not reproject

Diagnosing a wrong polar stereographic CRS from how known places move. how did the data move?Push known places through, measure theerrorrotated about the polewrong meridian3995 for 3411: 1,000–2,100 kmtowards or away from polewrong true-scale latitude39–84 kmother hemispheresouth CRS on north data17,000+ kmEPSG:3413 for EPSG:3411 moved nothing measurable spatialworkflow.io
The size and direction of the error name the mistake.
  • EPSG:3411 and EPSG:3413 differ by ellipsoid only; interchangeable for 25 km grids, not for metre-scale data.
  • Rotation about the pole means a wrong central meridian.
  • Radial error means a wrong latitude of true scale.
  • Assign, do not reproject, when the label is the problem.
  • Old NSIDC binary files have no georeferencing at all; supply EPSG:3411 and the grid's corner coordinates.
  • Check with the data's own land mask when you have no reference points.

FAQ

Why is my polar data rotated?

The CRS has the wrong central meridian. NSIDC grids use 45Β° W; EPSG:3995 uses 0Β°, so a mix-up rotates the data 45Β° about the pole.

What is the difference between EPSG:3411 and EPSG:3413?

The ellipsoid: Hughes 1980 for 3411, WGS 84 for 3413. For 25 km sea-ice cells the difference is negligible.

My data is slightly too far from the pole everywhere β€” why?

Probably a wrong latitude of true scale, which stretches or shrinks the grid radially by tens of kilometres at the ice edge.

How do I fix a wrong CRS on a GeoTIFF?

Rewrite the file with the correct CRS in its profile (or use gdal_edit.py -a_srs). Do not reproject from the wrong CRS.