⚠️ Our 0.1 release refactored several early-development functions for long-term stability, to update your code see here. ⚠️
Future changes will come with deprecation warnings! 🙂

The georeferenced mask (Mask)#

Below, a summary of the Mask object and its methods.

Object definition and attributes#

A Mask is a subclass of Raster that contains three main attributes:

  1. a numpy.ma.MaskedArray as data of boolean dtype,

  2. an affine.Affine as transform, and

  3. a pyproj.crs.CRS as crs.

A Mask also inherits the same derivative attributes as a Raster.

Note

There is no nodata value defined in a Mask, as it only take binary values. However, the numpy.ma.MaskedArray still has a mask for invalid values.

Open and save#

Masks can be created from files through instantiation with Mask, and inherit the save() method from Raster.

Important

Most raster file formats such a GeoTIFFs do not support bool array dtype on-disk, and most of Rasterio functionalities also do not support bool dtype.

To address this, during opening, saving and geospatial handling operations, Masks are automatically converted to and from numpy.uint8. The nodata of a Mask can now be defined to save to a file, and defaults to 255.

On opening, all data will be forced to a bool numpy.dtype.

import geoutils as gu

# Instantiate a mask from a filename on disk
filename_mask = gu.examples.get_path("exploradores_aster_dem")
mask = gu.Mask(filename_mask, load_data=True)
mask
Mask(
  data=[[True True True ... True True True]
        [True True True ... True True True]
        [True True True ... True True True]
        ...
        [True True True ... True True True]
        [True True True ... True True True]
        [True True True ... True True True]]
  transform=| 30.00, 0.00, 627175.00|
            | 0.00,-30.00, 4852085.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:32718)

Cast from Raster#

Masks are automatically cast by a logical comparison operation performed on a Raster with either another Raster, a ndarray or a number.

# Instantiate a raster from disk
filename_rast = gu.examples.get_path("exploradores_aster_dem")
rast = gu.Raster(filename_rast, load_data=True)

# Which pixels are below 1500 m?
rast < 1500
Mask(
  data=[[True True True ... True True True]
        [True True True ... True True True]
        [True True True ... True True True]
        ...
        [False False False ... True True True]
        [False False False ... True True True]
        [False False False ... True True True]]
  transform=| 30.00, 0.00, 627175.00|
            | 0.00,-30.00, 4852085.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:32718)

See Support of pythonic operators for more details.

Create from ndarray#

The class method geoutils.Raster.from_array(), inherited by the Mask subclass, automatically casts to a Mask if the input ndarray is of bool dtype.

import rasterio as rio
import pyproj
import numpy as np

# Create a random 3 x 3 masked array
np.random.seed(42)
arr = np.random.randint(0, 2, size=(3, 3), dtype="bool")
mask = np.random.randint(0, 2, size=(3, 3), dtype="bool")
ma = np.ma.masked_array(data=arr, mask=mask)

# Cast to a mask from a boolean array through the Raster class
mask = gu.Raster.from_array(
        data = ma,
        transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3),
        crs = pyproj.CRS.from_epsg(4326),
        nodata = 255
    )
mask
Mask(
  data=[[-- -- True]
        [False -- --]
        [True -- --]]
  transform=| 0.33, 0.00, 0.00|
            | 0.00,-0.33, 1.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:4326)

When creating with geoutils.Mask.from_array(), any input ndarray will be forced to a bool numpy.dtype.

# Create a mask from array directly from the Mask class
mask = gu.Mask.from_array(
        data = ma.astype("float32"),
        transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3),
        crs = pyproj.CRS.from_epsg(4326),
        nodata = 255
    )
mask
Mask(
  data=[[-- -- True]
        [False -- --]
        [True -- --]]
  transform=| 0.33, 0.00, 0.00|
            | 0.00,-0.33, 1.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:4326)

Create from Vector#

Masks can also be created from a Vector using create_mask, which rasterizes all input geometries to a boolean array through rasterize.

Georeferencing attributes to create the Mask can also be passed individually, using bounds, crs, xres and yres.

# Open a vector of glacier outlines
filename_vect = gu.examples.get_path("exploradores_rgi_outlines")
vect = gu.Vector(filename_vect)

# Create mask using the raster as reference to match for bounds, resolution and projection
mask_outlines = vect.create_mask(rast)
mask_outlines
Mask(
  data=[[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]]
  transform=| 30.00, 0.00, 627175.00|
            | 0.00,-30.00, 4852085.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:32718)

See Match-reference functionality for more details on the match-reference functionality.

Arithmetic#

Masks support Python’s logical bitwise operators (~, &, |, ^) with other Masks, and always output a Mask.

# Combine masks
~mask | mask
Mask(
  data=[[-- -- True]
        [True -- --]
        [True -- --]]
  transform=| 0.33, 0.00, 0.00|
            | 0.00,-0.33, 1.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:4326)

Indexing and assignment#

Masks can be used for indexing and index assignment operations ([], []=) with Rasters.

Important

When indexing, a flattened MaskedArray is returned with the indexed values of the Mask excluding those masked in its data’s MaskedArray.

# Index raster values on mask
rast[mask_outlines]
masked_array(data=[1234.8963623046875, 1238.497314453125,
                   1246.9481201171875, ..., 1199.0155029296875,
                   1187.132080078125, 1176.7186279296875],
             mask=[False, False, False, ..., False, False, False],
       fill_value=-9999.0,
            dtype=float32)

See Indexing a Raster with a Mask for more details.

Polygonize (overloaded from Raster)#

Masks have simplified class methods overloaded from Rasters when one or several attributes of the methods become implicit in the case of bool data.

The polygonize() function is one of those, implicitly applying to the True values of the mask as target pixels. It outputs a Vector of the input mask.

# Polygonize mask
mask.polygonize()
Vector(
  ds=   New_ID                                           geometry  raster_value
       0       0  POLYGON ((0.66667 1.00000, 0.66667 0.66667, 1....           1.0
       1       1  POLYGON ((0.00000 0.33333, 0.00000 0.00000, 0....           1.0
  crs=GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
  bounds=BoundingBox(left=0.0, bottom=0.0, right=1.0, top=1.0))

Proximity (overloaded from Raster)#

The proximity() function is another overloaded method of Raster implicitly applying to the True values of the mask as target pixels. It outputs a Raster of the distances to the input mask.

# Proximity to mask
mask.proximity()
Raster(
  data=[[0.66666667 0.33333333 0.        ]
        [0.33333333 0.47140452 0.33333333]
        [0.         0.33333333 0.66666667]]
  transform=| 0.33, 0.00, 0.00|
            | 0.00,-0.33, 1.00|
            | 0.00, 0.00, 1.00|
  crs=EPSG:4326
  nodata=None)