Downscaling a climate grid with terrain rests on a long-standing
idea: a climate variable often tracks elevation through a roughly linear
relationship, so a high-resolution elevation model can carry the
variable to a finer grid through a regression. This is the
regression-on-elevation step behind high-resolution climate surfaces
such as CHELSA (Karger et al. 2017). topocast generalizes
the predictor from elevation alone to any set of aligned terrain
covariates, fits the relationship locally rather than globally, and
keeps the per-cell cost independent of the neighborhood size.
This vignette describes the method: the local regression, the summed-area tables that make it fast, the numerical choices, and what the output does and does not preserve.
A global regression of the coarse response \(y\) on \(k\) predictors \(x_1, \dots,
x_k\) assumes one relationship over the whole map.
Terrain-climate relationships are not that stable: a lapse rate on a
windward slope differs from one in a rain shadow. topocast
instead fits a separate ordinary least squares regression in a square
window around every coarse cell. For the cell at row \(r\), column \(c\), the window of radius \(\rho\) holds the cells within \(\rho\) rows and columns, and the fit
solves
\[ \min_{\beta} \sum_{(i,j) \in W_{rc}} \Big( y_{ij} - \beta_0 - \sum_{m=1}^{k} \beta_m \, x_{m,ij} \Big)^2 . \]
The result is a grid of intercepts \(\hat\beta_0(r,c)\) and a grid of slopes
\(\hat\beta_m(r,c)\) for each
predictor, all at the coarse resolution. These coefficient grids are the
locally varying relationship between the response and the terrain, and
topocast(..., coefficients = TRUE) returns them on the fine
grid alongside the fitted layer.
The downscaling step resamples each coefficient grid to the fine resolution and evaluates the regression on the fine predictors:
\[ \hat y(f) = \hat\beta_0(f) + \sum_{m=1}^{k} \hat\beta_m(f)\, x_m(f), \]
where \(f\) indexes fine cells and the coefficients are the resampled coarse grids. The fine-scale structure of the output comes entirely from the fine predictors; the coefficients carry the relationship.
A naive moving window re-sums every cell in the neighborhood for
every center, so its cost grows with the window area. For a radius of 20
that is over 1,600 cells touched per output cell. topocast
avoids this with summed-area tables, also called integral images.
The normal equations for the window fit are \(A \beta = b\), where \(A\) holds the within-window sums \(\sum 1\), \(\sum x_m\), and \(\sum x_m x_{m'}\), and \(b\) holds \(\sum y\) and \(\sum x_m y\). Each of these is a sum of a fixed quantity over the window. For any grid \(g\), the summed-area table \(S\) stores the sum over the rectangle from the top-left corner to each cell:
\[ S(r,c) = \sum_{i \le r,\, j \le c} g_{ij}. \]
With \(S\) built once in a single pass, the sum over any rectangular window is four lookups:
\[ \sum_{(i,j)\in W} g_{ij} = S(r_1,c_1) - S(r_0-1,c_1) - S(r_1,c_0-1) + S(r_0-1,c_0-1). \]
topocast builds one table for the cell count, one for
\(\sum y\), one per predictor for \(\sum x_m\) and \(\sum x_m y\), and one per predictor pair
for \(\sum
x_m x_{m'}\). Every window then assembles \(A\) and \(b\) from four lookups each, solves a \((k{+}1) \times (k{+}1)\) system, and stores
the coefficients. The total cost is proportional to the number of cells
and the small per-cell solve, with no dependence on the radius.
The radius below changes the smoothness of the slope grid but not the runtime.
set.seed(1)
g <- rast(nrows = 80, ncols = 80, xmin = 0, xmax = 8000, ymin = 0, ymax = 8000,
crs = "EPSG:32632")
elev <- setValues(g, 1000 + 500 * sin(crds(g)[, 1] / 1500))
prec <- 800 - 0.15 * elev + setValues(g, rnorm(ncell(g), 0, 15))
y <- as.matrix(prec, wide = TRUE)
x <- as.matrix(elev, wide = TRUE)
small <- window_regression(y, x, radius = 3)
large <- window_regression(y, x, radius = 15)
op <- par(mfrow = c(1, 2), mar = c(2, 2, 2, 4))
plot(setValues(elev, as.vector(t(small$slope[[1]]))), main = "slope, radius 3")
plot(setValues(elev, as.vector(t(large$slope[[1]]))), main = "slope, radius 15")Global centering. Before accumulation the response and each predictor are centered on their global mean over the valid cells. Centering keeps the entries of \(A\) small even when elevation runs into the thousands, which conditions the solve. The intercept is mapped back to the raw response and predictor scale on output, so the returned coefficients are on the original units.
Complete-case masking. A cell contributes to a window only where the response and every predictor are finite. A separate count table tracks how many valid cells fall in each window, and invalid cells are set to zero in the centered copies so they drop out of the sums without distorting them.
Degenerate windows. A cell is returned as
NA in three cases: the window holds fewer valid cells than
the model needs (k + 1, raised by min_cells);
a predictor has within-window variance below min_variance,
so its slope is not identified; or the assembled system is singular.
These guards keep a flat or data-starved neighborhood from producing a
meaningless coefficient.
The same sufficient statistics give the local quality of each fit at no extra window cost. One more summed-area table, of the squared centered response, yields the within-window total sum of squares \(\mathrm{SST} = \sum (y - \bar y_W)^2\), and the residual sum of squares follows from the fitted coefficients as \(\mathrm{SSE} = y'y - \hat\beta' b\), where \(b\) is the response side of the normal equations. Their ratio is the coefficient of determination,
\[ R^2(r,c) = 1 - \frac{\mathrm{SSE}}{\mathrm{SST}}, \]
returned by diagnostics = TRUE as an
r.squared grid on the fine resolution. It maps where the
terrain explains the response and where the downscaled field carries
little more than the coarse level.
The same statistics also give the residual standard deviation, \(\hat\sigma(r,c) = \sqrt{\mathrm{SSE} / (n - k -
1)}\), in the response’s own units rather than R-squared’s
unitless, variance-relative scale, and the window’s valid cell count
\(n\) itself.
diagnostics = TRUE returns both, as
residual.sd and n.valid. A low R-squared is
ambiguous on its own: it can mean the terrain-response relationship is
genuinely weak in that neighborhood, or that the window barely cleared
the minimum valid-cell count and the fit is just noisy from too little
data. n.valid tells the two apart. Because the valid-cell
mask is complete-case across every response fit in one call,
n.valid is one shared grid rather than one per
response.
A climatology is one field. A time series shares the terrain
relationship across many periods, so refitting the regression each
period would be wasteful and would let the spatial pattern drift between
steps. topocast instead downscales the baseline once and
carries each period’s coarse anomaly onto the fine baseline.
For a period with coarse value \(v\), coarse baseline \(b\), and fine baseline \(\hat y_b\), the ratio path returns
\[ \hat y = \hat y_b \cdot \frac{v}{b}, \]
and the additive path returns \(\hat y =
\hat y_b + (v - b)\). The ratio suits non-negative variables such
as precipitation; the additive form suits variables such as temperature
where a difference is the natural anomaly. Both carry the coarse anomaly
verbatim, so the downscaled series tracks the coarse values over time
even though the baseline itself is a regression surface. A zero or
missing baseline in the ratio path returns NA rather than
dividing by zero.
The downscaled field is the regression evaluated on the fine predictors. Two consequences are worth stating plainly.
The output is not mass conserving. Averaging the fine field back to
the coarse grid does not reproduce the coarse input, because the coarse
residual, the part of \(y\) the
regression did not explain, is discarded. For a smoothed climate product
this is usually acceptable, and the anomaly path restores
fidelity to the coarse values for the time-varying part.
The slopes are applied to the full fine-predictor range. Where the
fine elevation reaches values the coarse window never saw, the
regression extrapolates linearly. The fine output is only as trustworthy
as a linear relationship over that extended range, which is a reason to
keep predictors that stay roughly linear with the response and to
inspect the result where the terrain is extreme. Setting
clamp = TRUE bounds the downscaled field to the observed
range of the coarse response, capping such extrapolation at the values
the variable actually takes.
Karger, D. N., Conrad, O., Bohner, J., Kawohl, T., Kreft, H., Soria-Auza, R. W., Zimmermann, N. E., Linder, H. P., and Kessler, M. (2017). Climatologies at high resolution for the earth’s land surface areas. Scientific Data 4, 170122. doi:10.1038/sdata.2017.122