topocast

Lifecycle: experimental License: MIT R-CMD-check

coarse climate grids at the resolution of your terrain

Downscale a coarse raster onto fine terrain by moving-window regression.

A coarse blocky precipitation grid beside the same field downscaled onto fine terrain, sharing one color scale.

plot of chunk downscale

Give topocast a coarse variable and a fine predictor it tracks. In a window around every cell it learns how the variable depends on the predictor, then evaluates that local relationship on the fine predictor. A 1 km precipitation grid and a 100 m elevation model become a 100 m precipitation grid.

library(topocast)
library(terra)

names(prec_1km) <- "prec"      # coarse variable, what you want at high resolution
names(dem_100m) <- "elev"      # fine predictor it tracks

prec_100m <- topocast(prec ~ elev, data = prec_1km, onto = dem_100m, radius = 15)

prec_100m is precipitation on the elevation model’s grid. Coarse to fine is one call: name the response and the predictor in a formula, pass the coarse grid as data and the fine grid as onto.

What goes in, what comes out

When the only fine layer you have is the predictor itself, as in the example above, topocast derives the coarse predictor from onto for you, so a single coarse climate layer and a DEM are enough to start.

How it works

The relationship is fit locally, in a square window around every coarse cell, with summed-area tables: each window fit reduces to four lookups per sufficient statistic, so a radius of 30 costs the same as a radius of 3. The fitted intercept and slope grids are resampled to the fine grid and combined with the fine predictors as fitted = intercept + sum(slope * predictor), so the output carries the fine-scale structure of the terrain with locally varying coefficients. This is the regression step behind high-resolution climate surfaces such as CHELSA (Karger et al. 2017); topocast runs it locally and takes any number of named predictors.

What’s in the box

Installation

# install.packages("pak")
pak::pak("gcol33/topocast")

Usage

Several predictors, matched by name between the two grids:

coarse <- c(prec_1km, elev_1km, twi_1km, slope_1km)
names(coarse) <- c("prec", "elev", "twi", "slope")
terrain <- c(elev_100m, twi_100m, slope_100m)
names(terrain) <- c("elev", "twi", "slope")

prec_100m <- topocast(prec ~ elev + twi + slope, data = coarse, onto = terrain,
                      radius = 15)

Several responses that share the terrain, downscaled in one pass and returned as one layer each:

climate_100m <- topocast(cbind(prec, tmin, tmax) ~ elev, data = coarse,
                         onto = terrain, radius = 15)

A monthly series sharing one terrain relationship across periods:

series <- topocast(prec ~ elev, data = coarse, onto = terrain, radius = 15,
                   anomaly = prec_monthly_1km, type = "ratio")

Downscaling straight to plot locations, returned as a column on the points:

at_plots <- topocast(prec ~ elev, data = coarse, onto = plots_sf, radius = 15)

The local coefficient grids, to read the fitted lapse rate:

coefs <- topocast(prec ~ elev, data = coarse, onto = terrain, radius = 15,
                  coefficients = TRUE)

Documentation

Support

“Software is like sex: it’s better when it’s free.” — Linus Torvalds

I’m a PhD student who builds R packages in my free time because I believe good tools should be free and open. I started these projects for my own work and figured others might find them useful too.

If this package saved you some time, buying me a coffee is a nice way to say thanks. It helps with my coffee addiction.

Buy Me A Coffee

License and citation

MIT. If you use topocast in published work, please cite it:

@software{topocast,
  author = {Colling, Gilles},
  title  = {topocast: Moving-Window Regression Downscaling of Raster Data},
  year   = {2026},
  url    = {https://github.com/gcol33/topocast}
}