Converting longitude from 0-360 to -180-180 (wrap_longitude)¶
Many gridded datasets — NetCDF climate files in particular — store longitude on a 0 to 360 scale instead of the
GIS-standard -180 to 180 scale centered on the Prime Meridian. On a 0-360 grid the eastern and western hemispheres
are effectively swapped, so the map looks shifted and won't line up with standard basemaps or vector layers. In this
tutorial you will load such a raster, see the problem on a plot, fix it with Dataset.wrap_longitude(), and save the
corrected file.
The problem: 0-360 vs -180-180¶
Standard GIS longitude runs from -180 to 180 (centered on the Prime Meridian), and latitude from -90 to 90
(centered on the Equator). Some files — especially NetCDF — instead number longitude from 0 to 360, which places
the Prime Meridian at the left edge and the antimeridian in the middle of the grid. wrap_longitude rewraps the columns
and rewrites the geotransform so the grid is expressed on the conventional -180 to 180 range.
Setup¶
We only need the Dataset class. The sample file is a NOAH daily-precipitation GeoTIFF stored on a 0-360 longitude
grid — exactly the case wrap_longitude is meant to fix. The path is written relative to this notebook's own location.
# NBVAL_IGNORE_OUTPUT
import tempfile
from pathlib import Path
from pyramids.dataset import Dataset
%matplotlib inline
path = r"../../../examples/data/geotiff/noah-precipitation-1979.tif"
2026-07-11 14:38:07 | INFO | pyramids.base.config | Logging is configured.
Read the raster file¶
Dataset.read_file opens the GeoTIFF and returns a Dataset object wired up with the geo-metadata (extent, CRS, band
info). The pixel values are not pulled into memory until we actually ask for the array.
dataset = Dataset.read_file(path)
Printing the dataset shows its summary metadata. Look at the Top Left Corner and dimensions to get a feel for the grid before we inspect the longitude values.
print(dataset)
Top Left Corner: (0.0, 90.0)
Cell size: 0.5
Dimension: 360 * 720
EPSG: 4326
Number of Bands: 4
Band names: ['Band_1', 'Band_2', 'Band_3', 'Band_4']
Band colors: {0: 'gray_index', 1: 'undefined', 2: 'undefined', 3: 'undefined'}
Band units: ['', '', '', '']
Scale: [1.0, 1.0, 1.0, 1.0]
Offset: [0, 0, 0, 0]
Mask: -9.969209968386869e+36
Data type: float32
File: ../../../examples/data/geotiff/noah-precipitation-1979.tif
Confirm the longitude range¶
The clearest symptom of the 0-360 convention is the longitude bounds, so let's read the minimum and maximum longitude of the grid.
print(f"Min longitude: {min(dataset.lon)}")
print(f"Max longitude: {max(dataset.lon)}")
Min longitude: 0.25 Max longitude: 359.75
The longitudes span roughly 0 to 360 rather than -180 to 180 — confirming this file uses the non-standard convention.
Plot the first band (before wrapping)¶
Plotting band 0 makes the shifted geography obvious: because longitude starts at 0, the map is centered on the
antimeridian and the continents appear rolled halfway across the frame. vmax=30 clips the colour scale so heavy
rainfall cells don't wash out the rest of the map.
array_glyph = dataset.plot(
band=0,
figsize=(10, 5),
title="NOAH daily Precipitation 1979-01-01",
cbar_label="Rainfall mm/day",
vmax=30,
cbar_length=0.85,
)
Notice how the map is centered on the antimeridian rather than the Prime Meridian — the western hemisphere is pushed to the right-hand side of the image. This is the visual signature of a 0-360 grid.
Wrap the longitude to -180-180¶
wrap_longitude() returns a new Dataset whose columns are reordered and whose geotransform is rewritten so
longitude runs from -180 to 180. The original dataset is left untouched.
| aspect | meaning |
|---|---|
| return value | a new Dataset; the original is not modified in place |
| effect | grid columns rolled so the Prime Meridian is centered |
| when to use | any raster whose longitude spans 0-360 instead of -180-180 |
new_dataset = dataset.wrap_longitude()
Re-plotting the wrapped dataset should now show the familiar world layout, centered on the Prime Meridian.
new_dataset.plot(
band=0,
figsize=(10, 5),
title="NOAH daily Precipitation 1979-01-01",
cbar_label="RainFall mm/day",
vmax=30,
cbar_length=0.85,
)
<cleopatra.array_glyph.ArrayGlyph at 0x7fc690b85810>
The continents are now in their conventional positions — the map is centered on longitude 0 and reads left-to-right from -180 to 180, matching standard basemaps.
Verify the new longitude range¶
As a numeric check, read the longitude bounds again — they should now fall within -180 to 180.
print(f"Min longitude: {min(new_dataset.lon)}")
print(f"Max longitude: {max(new_dataset.lon)}")
Min longitude: -179.75 Max longitude: 179.75
Save the corrected raster¶
Finally, write the wrapped dataset to a new GeoTIFF so downstream tools receive it on the standard longitude grid.
out_path = Path(tempfile.mkdtemp()) / "noah-precipitation-1979-corrected.tif"
new_dataset.to_file(str(out_path))