Transform Longitude Values#

Introduction#

Many climate datasets have longitude coordinates from 0 to 360. This is not suitable to be used in other raster based geoprocessing systems. This notebook shows how to convert such datasets to span longitude from -180 to +180 and save it as a GeoTIFF file suitable for analysis in GIS software.

Overview of the Task#

We take a NetCDF file of Monthly Precipitation by ECMWF ERA5-Land Monthly Average dataset. This file has global monthly precipitation for the month of July, 2021. We reproject the longitude coordinates and save the result as a GeoTIFF file.

Input Layers:

  • era5-land-precipitation.nc: A NetCDF file containing monthly average precipitation with longitude coordinates from 0-360.

Output:

  • precipitation.tif: A reprojected GeoTiff file of total monthly precpitation with longitudes from -180 to +180.

Data Credit:

  • Muñoz Sabater, J., (2019): ERA5-Land monthly averaged data from 1981 to present. Copernicus Climate Change Service (C3S) Climate Data Store (CDS). (Accessed on < 05-Aug-2022 >), 10.24381/cds.68d2bb3

Setup and Data Download#

The following blocks of code will install the required packages and download the datasets to your Colab environment.

%%capture
if 'google.colab' in str(get_ipython()):
    !pip install rioxarray netcdf4 cartopy
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import os
import pandas as pd
import rioxarray as rxr
import xarray as xr
data_folder = 'data'
output_folder = 'output'

if not os.path.exists(data_folder):
    os.mkdir(data_folder)
if not os.path.exists(output_folder):
    os.mkdir(output_folder)
def download(url):
    filename = os.path.join(data_folder, os.path.basename(url))
    if not os.path.exists(filename):
        from urllib.request import urlretrieve
        local, _ = urlretrieve(url, filename)
        print('Downloaded ' + local)

filename = 'era5-land-precipitation.nc'
data_url = 'https://github.com/spatialthoughts/geopython-tutorials/releases/download/data/'

download(data_url + filename)

Procedure#

Open the input file with XArray.

input_path = os.path.join(data_folder, filename)
ds = xr.open_dataset(input_path)
ds
<xarray.Dataset> Size: 52MB
Dimensions:    (longitude: 3600, latitude: 1801, time: 1)
Coordinates:
  * longitude  (longitude) float32 14kB 0.0 0.1 0.2 0.3 ... 359.7 359.8 359.9
  * latitude   (latitude) float32 7kB 90.0 89.9 89.8 89.7 ... -89.8 -89.9 -90.0
  * time       (time) datetime64[ns] 8B 2021-07-01
Data variables:
    tp         (time, latitude, longitude) float64 52MB ...
Attributes:
    Conventions:  CF-1.6
    history:      2022-08-05 17:45:55 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...

Notice the longitude coordinates have have values from 0 to 360. We convert the longitude to range from -180 to +180. Many downstream packages expect the coordinates to be sorted in ascening order, so we also sort the resulting longitude values.

ds.coords['longitude'] = (ds.coords['longitude'] + 180) % 360 - 180
ds = ds.sortby(ds.longitude)
ds
<xarray.Dataset> Size: 52MB
Dimensions:    (latitude: 1801, time: 1, longitude: 3600)
Coordinates:
  * latitude   (latitude) float32 7kB 90.0 89.9 89.8 89.7 ... -89.8 -89.9 -90.0
  * time       (time) datetime64[ns] 8B 2021-07-01
  * longitude  (longitude) float32 14kB -180.0 -179.9 -179.8 ... 179.8 179.9
Data variables:
    tp         (time, latitude, longitude) float64 52MB ...
Attributes:
    Conventions:  CF-1.6
    history:      2022-08-05 17:45:55 GMT by grib_to_netcdf-2.25.1: /opt/ecmw...

Notice the longitude coordinates have have values from -180 to +180. The dataset is now ready to be saved. But before saving, we can also convert the units to be more usable. The input dataset’s units are meters/day, so we multiply by 1000 and the number of days in the month to get total precipitation in mm/month. [reference]

Calculate the number of days in the month using the Pandas pd.Period.

time = ds.time.values[0]
ts = pd.to_datetime(time).strftime('%Y-%m-%d')
days = pd.Period(ts).days_in_month
print(f'Number of days in month of {ts}: {days}')
Number of days in month of 2021-07-01: 31

Select the variable tp (total precipitation) and convert the units.

da = ds['tp']
da_prcp = da * 1000 * days

We have just 1 value of time, so use squeeze() to remove the empty time dimension and get a 2D array.

da_prcp = da_prcp.squeeze()

Visualize the results using CartoPy on an Equal Earth projection.

projection = ccrs.EqualEarth()
fig, ax = plt.subplots(1, 1, subplot_kw={'projection': projection})
fig.set_size_inches(10,5)

# Plot the data
plot = da_prcp.plot(
    ax=ax,
    transform=ccrs.PlateCarree(),  # Data is in PlateCarree
    cmap='GnBu',
    vmin=0, vmax=500,
    add_colorbar=False)

# Add a custom colorbar
cbar = fig.colorbar(plot, ax=ax, orientation='horizontal', shrink=0.5, pad=0.05)
cbar.set_label('Precipitation (mm)')
# Add coastlines and gridlines
ax.coastlines()
ax.gridlines()

# Set title
ax.set_title('Global Precipitation - July 2021')

# Show the plot
plt.show()
../_images/6a66ec1a6ef8aabe4230c2e52dd9847cfdfb01713c401244d7cf54d6839c3826.png

Finally save the results as a GeoTIFF file.

output_file = 'precipitation.tif'
output_path = os.path.join(output_folder, output_file)
da_prcp.rio.to_raster(output_path, compress='LZW')

If you want to give feedback or share your experience with this tutorial, please comment below. (requires GitHub account)