Note

This tutorial was generated from an IPython notebook that can be downloaded here.

Create 3D region masks

In this tutorial we will show how to create 3D boolean masks for arbitrary latitude and longitude grids. It uses the same algorithm to determine if a gridpoint is in a region as for the 2D mask. However, it returns a xarray.Dataset with shape region x lat x lon, gridpoints that do not fall in a region are False, the gridpoints that fall in a region are True.

Import regionmask and check the version:

import regionmask

regionmask.__version__
'0.5.0+dev'

Load xarray and numpy:

import xarray as xr
import numpy as np

Creating a mask

We define a lon/ lat grid with a 1° grid spacing, where the points define the center of the grid.

lon = np.arange(-179.5, 180)
lat = np.arange(-89.5, 90)

We create a mask with the SREX regions (Seneviratne et al., 2012).

regionmask.defined_regions.srex
<regionmask.Regions>
Name:     SREX
Source:   Seneviratne et al., 2012 (https://www.ipcc.ch/site/assets/uploads/2...

Regions:
  1  ALA        Alaska/N.W. Canada
  2  CGI      Canada/Greenl./Icel.
  3  WNA          W. North America
  4  CNA          C. North America
  5  ENA          E. North America
..   ...                       ...
 22  EAS                   E. Asia
 23  SAS                   S. Asia
 24  SEA                 S.E. Asia
 25  NAU              N. Australia
 26  SAU  S. Australia/New Zealand

[26 regions]

The function mask_3D determines which gripoints lie within the polygon making up the each region:

mask = regionmask.defined_regions.srex.mask_3D(lon, lat)
mask
Show/Hide data repr Show/Hide attributes
xarray.DataArray
'region'
  • region: 26
  • lat: 180
  • lon: 360
  • False False False False False False ... False False False False False
    array([[[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           ...,
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]]])
    • lon
      (lon)
      float64
      -179.5 -178.5 ... 178.5 179.5
      array([-179.5, -178.5, -177.5, ...,  177.5,  178.5,  179.5])
    • lat
      (lat)
      float64
      -89.5 -88.5 -87.5 ... 88.5 89.5
      array([-89.5, -88.5, -87.5, -86.5, -85.5, -84.5, -83.5, -82.5, -81.5, -80.5,
             -79.5, -78.5, -77.5, -76.5, -75.5, -74.5, -73.5, -72.5, -71.5, -70.5,
             -69.5, -68.5, -67.5, -66.5, -65.5, -64.5, -63.5, -62.5, -61.5, -60.5,
             -59.5, -58.5, -57.5, -56.5, -55.5, -54.5, -53.5, -52.5, -51.5, -50.5,
             -49.5, -48.5, -47.5, -46.5, -45.5, -44.5, -43.5, -42.5, -41.5, -40.5,
             -39.5, -38.5, -37.5, -36.5, -35.5, -34.5, -33.5, -32.5, -31.5, -30.5,
             -29.5, -28.5, -27.5, -26.5, -25.5, -24.5, -23.5, -22.5, -21.5, -20.5,
             -19.5, -18.5, -17.5, -16.5, -15.5, -14.5, -13.5, -12.5, -11.5, -10.5,
              -9.5,  -8.5,  -7.5,  -6.5,  -5.5,  -4.5,  -3.5,  -2.5,  -1.5,  -0.5,
               0.5,   1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,   9.5,
              10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5,  18.5,  19.5,
              20.5,  21.5,  22.5,  23.5,  24.5,  25.5,  26.5,  27.5,  28.5,  29.5,
              30.5,  31.5,  32.5,  33.5,  34.5,  35.5,  36.5,  37.5,  38.5,  39.5,
              40.5,  41.5,  42.5,  43.5,  44.5,  45.5,  46.5,  47.5,  48.5,  49.5,
              50.5,  51.5,  52.5,  53.5,  54.5,  55.5,  56.5,  57.5,  58.5,  59.5,
              60.5,  61.5,  62.5,  63.5,  64.5,  65.5,  66.5,  67.5,  68.5,  69.5,
              70.5,  71.5,  72.5,  73.5,  74.5,  75.5,  76.5,  77.5,  78.5,  79.5,
              80.5,  81.5,  82.5,  83.5,  84.5,  85.5,  86.5,  87.5,  88.5,  89.5])
    • region
      (region)
      int64
      1 2 3 4 5 6 7 ... 21 22 23 24 25 26
      array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
             19, 20, 21, 22, 23, 24, 25, 26])
    • abbrev
      (region)
      <U3
      'ALA' 'CGI' 'WNA' ... 'NAU' 'SAU'
      array(['ALA', 'CGI', 'WNA', 'CNA', 'ENA', 'CAM', 'AMZ', 'NEB', 'WSA',
             'SSA', 'NEU', 'CEU', 'MED', 'SAH', 'WAF', 'EAF', 'SAF', 'NAS',
             'WAS', 'CAS', 'TIB', 'EAS', 'SAS', 'SEA', 'NAU', 'SAU'],
            dtype='<U3')
    • name
      (region)
      <U24
      'Alaska/N.W. Canada' ... 'S. Australia/New Zealand'
      array(['Alaska/N.W. Canada', 'Canada/Greenl./Icel.', 'W. North America',
             'C. North America', 'E. North America', 'Central America/Mexico',
             'Amazon', 'N.E. Brazil', 'Coast South America',
             'S.E. South America', 'N. Europe', 'C. Europe',
             'S. Europe/Mediterranean', 'Sahara', 'W. Africa', 'E. Africa',
             'S. Africa', 'N. Asia', 'W. Asia', 'C. Asia', 'Tibetan Plateau',
             'E. Asia', 'S. Asia', 'S.E. Asia', 'N. Australia',
             'S. Australia/New Zealand'], dtype='<U24')

As mentioned, mask is a boolean xarray.Dataset with shape region x lat x lon. It contains region as dimension coordinate and abbrev and name as non-dimension coordinates (see the xarray docs for the details on the terminology). The four first layers look as follows:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
from matplotlib import colors as mplc

cmap1 = mplc.ListedColormap(["none", "#9ecae1"])

fg = mask.isel(region=slice(4)).plot(
    subplot_kws=dict(projection=ccrs.PlateCarree()),
    col="region",
    col_wrap=2,
    transform=ccrs.PlateCarree(),
    add_colorbar=False,
    aspect=1.5,
    cmap=cmap1,
)

for ax in fg.axes.flatten():
    ax.coastlines()

fg.fig.subplots_adjust(hspace=0, wspace=0.1);
../_images/mask_3D_13_0.png

Working with a 3D mask

masks can be used to select data in a certain region and to calculate regional averages - let’s illustrate this with a ‘real’ dataset:

airtemps = xr.tutorial.load_dataset("air_temperature")

The example data is a temperature field over North America. Let’s plot the first time step:

# choose a good projection for regional maps
proj = ccrs.LambertConformal(central_longitude=-100)

ax = plt.subplot(111, projection=proj)

airtemps.isel(time=1).air.plot.pcolormesh(ax=ax, transform=ccrs.PlateCarree())

ax.coastlines();
../_images/mask_3D_17_0.png

An xarray object can be passed to the mask_3D function:

mask_full = regionmask.defined_regions.srex.mask_3D(airtemps)
mask_full
Show/Hide data repr Show/Hide attributes
xarray.DataArray
'region'
  • region: 26
  • lat: 25
  • lon: 53
  • False False False False False False ... False False False False False
    array([[[False, False, False, ..., False, False, False],
            [ True,  True,  True, ..., False, False, False],
            [ True,  True,  True, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ...,  True,  True,  True],
            [False, False, False, ...,  True,  True,  True],
            [False, False, False, ...,  True,  True,  True],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           ...,
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]]])
    • lon
      (lon)
      float32
      200.0 202.5 205.0 ... 327.5 330.0
      array([200. , 202.5, 205. , 207.5, 210. , 212.5, 215. , 217.5, 220. , 222.5,
             225. , 227.5, 230. , 232.5, 235. , 237.5, 240. , 242.5, 245. , 247.5,
             250. , 252.5, 255. , 257.5, 260. , 262.5, 265. , 267.5, 270. , 272.5,
             275. , 277.5, 280. , 282.5, 285. , 287.5, 290. , 292.5, 295. , 297.5,
             300. , 302.5, 305. , 307.5, 310. , 312.5, 315. , 317.5, 320. , 322.5,
             325. , 327.5, 330. ], dtype=float32)
    • lat
      (lat)
      float32
      75.0 72.5 70.0 ... 20.0 17.5 15.0
      array([75. , 72.5, 70. , 67.5, 65. , 62.5, 60. , 57.5, 55. , 52.5, 50. , 47.5,
             45. , 42.5, 40. , 37.5, 35. , 32.5, 30. , 27.5, 25. , 22.5, 20. , 17.5,
             15. ], dtype=float32)
    • region
      (region)
      int64
      1 2 3 4 5 6 7 ... 21 22 23 24 25 26
      array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
             19, 20, 21, 22, 23, 24, 25, 26])
    • abbrev
      (region)
      <U3
      'ALA' 'CGI' 'WNA' ... 'NAU' 'SAU'
      array(['ALA', 'CGI', 'WNA', 'CNA', 'ENA', 'CAM', 'AMZ', 'NEB', 'WSA',
             'SSA', 'NEU', 'CEU', 'MED', 'SAH', 'WAF', 'EAF', 'SAF', 'NAS',
             'WAS', 'CAS', 'TIB', 'EAS', 'SAS', 'SEA', 'NAU', 'SAU'],
            dtype='<U3')
    • name
      (region)
      <U24
      'Alaska/N.W. Canada' ... 'S. Australia/New Zealand'
      array(['Alaska/N.W. Canada', 'Canada/Greenl./Icel.', 'W. North America',
             'C. North America', 'E. North America', 'Central America/Mexico',
             'Amazon', 'N.E. Brazil', 'Coast South America',
             'S.E. South America', 'N. Europe', 'C. Europe',
             'S. Europe/Mediterranean', 'Sahara', 'W. Africa', 'E. Africa',
             'S. Africa', 'N. Asia', 'W. Asia', 'C. Asia', 'Tibetan Plateau',
             'E. Asia', 'S. Asia', 'S.E. Asia', 'N. Australia',
             'S. Australia/New Zealand'], dtype='<U24')

Per default this creates a mask containing one layer for each individual region - even if no gridpoint is contained in this region (all points are False). As the example data only has values over Northern America most layers will only contain False. To only obtain those layers containing (at least) one gridpoint specify drop=True:

mask_drop = regionmask.defined_regions.srex.mask_3D(airtemps, drop=True)
mask_drop
Show/Hide data repr Show/Hide attributes
xarray.DataArray
'region'
  • region: 6
  • lat: 25
  • lon: 53
  • False False False False False False ... False False False False False
    array([[[False, False, False, ..., False, False, False],
            [ True,  True,  True, ..., False, False, False],
            [ True,  True,  True, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ...,  True,  True,  True],
            [False, False, False, ...,  True,  True,  True],
            [False, False, False, ...,  True,  True,  True],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]],
    
           [[False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            ...,
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False],
            [False, False, False, ..., False, False, False]]])
    • lon
      (lon)
      float32
      200.0 202.5 205.0 ... 327.5 330.0
      array([200. , 202.5, 205. , 207.5, 210. , 212.5, 215. , 217.5, 220. , 222.5,
             225. , 227.5, 230. , 232.5, 235. , 237.5, 240. , 242.5, 245. , 247.5,
             250. , 252.5, 255. , 257.5, 260. , 262.5, 265. , 267.5, 270. , 272.5,
             275. , 277.5, 280. , 282.5, 285. , 287.5, 290. , 292.5, 295. , 297.5,
             300. , 302.5, 305. , 307.5, 310. , 312.5, 315. , 317.5, 320. , 322.5,
             325. , 327.5, 330. ], dtype=float32)
    • lat
      (lat)
      float32
      75.0 72.5 70.0 ... 20.0 17.5 15.0
      array([75. , 72.5, 70. , 67.5, 65. , 62.5, 60. , 57.5, 55. , 52.5, 50. , 47.5,
             45. , 42.5, 40. , 37.5, 35. , 32.5, 30. , 27.5, 25. , 22.5, 20. , 17.5,
             15. ], dtype=float32)
    • region
      (region)
      int64
      1 2 3 4 5 6
      array([1, 2, 3, 4, 5, 6])
    • abbrev
      (region)
      <U3
      'ALA' 'CGI' 'WNA' 'CNA' 'ENA' 'CAM'
      array(['ALA', 'CGI', 'WNA', 'CNA', 'ENA', 'CAM'], dtype='<U3')
    • name
      (region)
      <U22
      'Alaska/N.W. Canada' ... 'Central America/Mexico'
      array(['Alaska/N.W. Canada', 'Canada/Greenl./Icel.', 'W. North America',
             'C. North America', 'E. North America', 'Central America/Mexico'],
            dtype='<U22')

Note how there are only 6 layers.

Calculate weighted regional averages

Using the 3-dimensional mask it is possible to calculate weighted averages of all regions in one go, using the weighted method (requires xarray 0.15.1 or later). As proxy of the grid cell area we use cos(lat):

weights = np.cos(np.deg2rad(airtemps.lat))

ts_airtemps_regional = airtemps.weighted(mask_drop * weights).mean(dim=("lat", "lon"))
ts_airtemps_regional
Show/Hide data repr Show/Hide attributes
xarray.Dataset
    • region: 6
    • time: 2920
    • time
      (time)
      datetime64[ns]
      2013-01-01 ... 2014-12-31T18:00:00
      standard_name :
      time
      long_name :
      Time
      array(['2013-01-01T00:00:00.000000000', '2013-01-01T06:00:00.000000000',
             '2013-01-01T12:00:00.000000000', ..., '2014-12-31T06:00:00.000000000',
             '2014-12-31T12:00:00.000000000', '2014-12-31T18:00:00.000000000'],
            dtype='datetime64[ns]')
    • region
      (region)
      int64
      1 2 3 4 5 6
      array([1, 2, 3, 4, 5, 6])
    • abbrev
      (region)
      <U3
      'ALA' 'CGI' 'WNA' 'CNA' 'ENA' 'CAM'
      array(['ALA', 'CGI', 'WNA', 'CNA', 'ENA', 'CAM'], dtype='<U3')
    • name
      (region)
      <U22
      'Alaska/N.W. Canada' ... 'Central America/Mexico'
      array(['Alaska/N.W. Canada', 'Canada/Greenl./Icel.', 'W. North America',
             'C. North America', 'E. North America', 'Central America/Mexico'],
            dtype='<U22')
    • air
      (time, region)
      float32
      255.46873 255.63712 ... 295.03595
      array([[255.46873, 255.63712, 272.55185, 271.25925, 279.89474, 295.0278 ],
             [255.18092, 254.97868, 270.3979 , 269.2749 , 279.28098, 293.58374],
             [255.03746, 255.30022, 269.289  , 267.79205, 279.26285, 292.88605],
             ...,
             [260.47092, 253.83435, 268.54593, 261.13162, 275.96317, 293.93817],
             [258.43375, 254.55461, 269.3965 , 262.11487, 275.55936, 293.37787],
             [256.5124 , 254.48091, 270.21487, 266.05923, 276.97205, 295.03595]],
            dtype=float32)

The regionally-averaged time series can be plotted:

ts_airtemps_regional.air.plot(col="region", col_wrap=3);
../_images/mask_3D_27_0.png

Restrict the mask to land points

Combining the mask of the regions with a land-sea mask we can create a land-only mask. We use the natural_earth.land_110 regions to create a land-sea mask:

land_110 = regionmask.defined_regions.natural_earth.land_110

land_sea_mask = land_110.mask_3D(airtemps)

and plot it

proj = ccrs.LambertConformal(central_longitude=-100)

ax = plt.subplot(111, projection=proj)

land_sea_mask.squeeze().plot.pcolormesh(
    ax=ax, transform=ccrs.PlateCarree(), cmap=cmap1, add_colorbar=False
)

ax.coastlines();
../_images/mask_3D_31_0.png

To create the combined mask we multiply the two:

mask_lsm = mask_drop * land_sea_mask.squeeze(drop=True)

Note the squeeze(drop=True). This is required to remove the regions dimension from land_sea_mask.

Finally we compare the original mask with the one restricted to land points:

f, axes = plt.subplots(1, 2, subplot_kw=dict(projection=proj))


ax = axes[0]
mask_drop.sel(region=2).plot(
    ax=ax, transform=ccrs.PlateCarree(), add_colorbar=False, cmap=cmap1
)
ax.coastlines()
ax.set_title("Regional mask: all points")

ax = axes[1]
mask_lsm.sel(region=2).plot(
    ax=ax, transform=ccrs.PlateCarree(), add_colorbar=False, cmap=cmap1
)
ax.coastlines()
ax.set_title("Regional mask: land only");
../_images/mask_3D_35_0.png

References