Skip to content

... mask a layer?

The process of masking refers to turning cells on a layer's grid to off, which will result in them being excluded from the analysis/display.

There are two ways to mask a layer – using the nodata! approach, and using the mask! approach. We use nodata! when working from a single layer, and mask! when using data stored in a second layer. Note that both approaches have a non-mutating version (nodata and mask, that return a modified copy of their layer).

julia
using SpeciesDistributionToolkit
using Dates, Statistics
using CairoMakie

We will illustrate both approaches using the CHELSA2 temperature data for the month of September.

julia
spatial_extent = (left = 8.412, bottom = 41.325, right = 9.662, top = 43.060)
temp2 =
    SDMLayer(
        RasterData(CHELSA2, AverageTemperature);
        month = Month(9),
        spatial_extent...,
    )
SDM Layer with 31559 UInt16 cells
	Proj string: +proj=longlat +datum=WGS84 +no_defs
	Grid size: (209, 151)

Code for the figure
julia
heatmap(temp2, colormap=:navia, axis=(;aspect=DataAspect())) # hide

Using nodata!

When using nodata!, we can either indicate a value to remove from the layer, or pass a function. For example, we can mask the layer to remove all cells where the temperature is in the upper and lower 5%:

julia
m, M = Statistics.quantile(values(temp2), [0.05, 0.95])
nodata(temp2, v -> !(m <= v <= M))
SDM Layer with 28449 UInt16 cells
	Proj string: +proj=longlat +datum=WGS84 +no_defs
	Grid size: (209, 151)

Code for the figure
julia
heatmap(nodata(temp2, v -> !(m <= v <= M)), colormap=:navia, axis=(;aspect=DataAspect())) # hide

The function given as the second argument must return true for a point that will be excluded from the layer. In other words, this behaves as the opposite of filter!.

Using mask!

When using mask!, the first layer will be modified so that only the cells that are also valued in the second layer are used. For example, we can use the fact that the CHELSA1 layers do not have values outside of land, to mask the CHELSA2 data:

julia
temp1 =
    SDMLayer(
        RasterData(CHELSA1, AverageTemperature);
        month = Month(9),
        spatial_extent...,
    )
SDM Layer with 14432 Int16 cells
	Proj string: +proj=longlat +datum=WGS84 +no_defs
	Grid size: (209, 151)
julia
mask(temp2, temp1)
SDM Layer with 14432 UInt16 cells
	Proj string: +proj=longlat +datum=WGS84 +no_defs
	Grid size: (209, 151)

Code for the figure
julia
heatmap(mask(temp2, temp1), colormap=:navia, axis=(;aspect=DataAspect())) # hide
SimpleSDMLayers.nodata! Function
julia
nodata!(layer::SDMLayer{T}, nodata::T) where {T}

Changes the value of the layer representing no data. This modifies the layer passed as its first argument.

source

julia
nodata!(layer::SDMLayer{T}, f)

Removes the data matching a function

source

SimpleSDMLayers.nodata Function
julia
nodata(layer::SDMLayer, args...)

Makes a copy and calls nodata! on it

source

SimpleSDMLayers.mask! Function
julia
mask!(layer::SDMLayer, template::SDMLayer)

Updates the positions in the first layer to be those that have a value in the second layer.

source

julia
SimpleSDMLayers.mask!(layer::SDMLayer, multipolygon::GeoJSON.MultiPolygon)

Turns of fall the cells outside the polygon (or within holes in the polygon). This modifies the object.

source

SimpleSDMLayers.mask Function
julia
mask(layer::SDMLayer, template::SDMLayer)

Returns a copy of the first layer masked according to the second layer. See also mask!.

source

julia
SimpleSDMLayers.mask(occ::T, multipolygon::GeoJSON.MultiPolygon) where {T <: AsbtractOccurrenceCollection}

Returns a copy of the occurrences that are within the polygon.

source

A note about how this works

The SDMLayer type stores a BitMatrix (in the indices field) that tracks which cells in the raster are visible. This costs a little more memory, but allows to rapidly turn pixels on and off.