Value-suppressing maps
The package has functions to produce value-suppressing uncertainty palettes.
using SpeciesDistributionToolkit
const SDT = SpeciesDistributionToolkit
import Statistics
using CairoMakieA note about legends
Makie does not currently make it easy (possible?) to define custom legends from recipes. For this reason, the legends for VSUP plots are provided as plots. Note that Makie recipes also (as far as we can tell) do not set default axis properties, so getting the legends is a little more awkward than we'd like.
Getting data
pol = getpolygon(PolygonData(OpenStreetMap, Places); place = "Corsica");
spatialextent = SDT.boundingbox(pol; padding = 0.1)(left = 8.434716606140137, right = 9.660012817382812, bottom = 41.23319091796875, top = 43.127706146240236)We will get a series of layers here for illustrations:
provider = RasterData(CHELSA2, BioClim)
L = SDMLayer{Float32}[
SDMLayer(
provider;
layer = i,
spatialextent...,
) for i in layers(provider)
]
mask!(L, pol)19-element Vector{SDMLayer{Float32}}:
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)
🗺️ A 229 × 148 layer (13685 Float32 cells)VSUP
Value Suppressing Uncertainty Palettes (VSUPs) are a way to downgrade a color palette towards a specific color in order to mask values that are increasingly uncertain. We will illustrate this by showing the prediction of an SDM, but also convey some information about uncertainty (through bootstrap).
base_model = SDM(RawData, Logistic, SDeMo.__demodata()...)
variables!(base_model, ForwardSelection)
model = Bagging(base_model, 20)
train!(model){RawData → Logistic → P(x) ≥ 0.542} × 20We get two layers: one for the prediction, and one for the uncertainty.
val = predict(base_model, L; threshold = false)
unc = predict(model, L; threshold = false, consensus = iqr)🗺️ A 229 × 148 layer with 13685 Float64 cells
Spatial Reference System: +proj=longlat +datum=WGS84 +no_defsThis is the default output of a VSUP:
vsup(val, unc; axis = (; aspect = DataAspect()))Because VSUP are binary partitions, the number of uncertainty bins n will determine the number of value bins. In our experience, a number of bins between 4 (8 classes for value) and 5 (16 classes for values) is ideal. Larger number of bins give a smoother output, but can make interpretation less obvious:
vsup(val, unc; axis = (; aspect = DataAspect()), bins = 10)We can change the color associated to uncertain areas:
vsup(val, unc; axis = (; aspect = DataAspect()), color = colorant"#ff0000")We can also change the color map used to display the value:
vsup(
val,
unc;
axis = (; aspect = DataAspect()),
colormap = [:cadetblue, :tan3],
color = colorant"#d0d0d0",
)The legend of a VSUP is presented as a wedge, where increasingly uncertain cells are merged together, and averaged towards the color for uncertain values. It must be added to a polar axis, which has two key parameters: the direction towards which it points, and the angle covered by the values:
kwargs = (bins = 4, colormap = [:purple, :skyblue], color = colorant"#f0f0f0")
vsuplegend(val, unc; kwargs..., direction = π / 4, span = π / 2)Note that this is returned as a default polar axis, and so needs a little bit of work. We can start by setting the correct limits:
direction, angle = π / 5, π / 2.5
f, ax, pl = vsuplegend(val, unc; kwargs..., direction = direction, span = angle)
autolimits!(ax)
tightlimits!(ax)
fWe also need to change the ticks for the uncertainty dimension. The order of arguments for the vsuplegendticks function is the layer, the desired number of ticks, and then the axis limits (for the uncertainty, this goes from the number of bins to 0):
ax.rticks = vsuplegendticks(unc, 7, 5, 0)
fWe can do the same thing for the value ticks
ax.thetaticks = vsuplegendticks(val, 10, direction - angle / 2, direction + angle / 2)
fPutting everyhing together, we can get to a fairly good result:
f = Figure(; size = (400, 600))
ax = Axis(f[1, 1]; aspect = DataAspect())
le = PolarAxis(f[1, 1];
width = Relative(0.69),
height = Relative(0.235),
halign = 0.0,
valign = 1.0,
thetaticks = vsuplegendticks(val, 5, direction - angle / 2, direction + angle / 2),
rticks = vsuplegendticks(unc, 3, 5, 0),
rticklabelrotation = pi / 2,
)
vsup!(ax, val, unc; kwargs...)
lines!(ax, pol; color = :black)
vsuplegend!(le, val, unc; kwargs..., direction = direction, span = angle)
autolimits!(le)
tightlimits!(le)
hidespines!(ax)
hidedecorations!(ax)
current_figure()