├── .github ├── .gitignore ├── workflows │ ├── R-CMD-check.yaml │ ├── test-coverage.yaml │ ├── pkgdown.yaml │ └── progress-tracker.yml └── template.R ├── R ├── env.R ├── crs.R ├── checks.R ├── write.R ├── utils_documentation.R ├── read.R ├── math.R ├── plot.R ├── conversion.R ├── matrix.R ├── print.R ├── installation.R ├── summary.R ├── WhiteboxClasses.R ├── terra.R ├── system.R ├── dimensions.R ├── hillshade.R └── zzz.R ├── vignettes └── articles │ ├── .gitignore │ ├── tutorials.Rmd │ ├── FAQ.Rmd │ └── Tutorial-2.Rmd ├── LICENSE ├── .gitignore ├── inst ├── extdata │ ├── dem.tif │ └── dem.tif.aux.xml └── wbw_helpers.py ├── man ├── figures │ ├── logo.png │ ├── README-terra-1.png │ └── src │ │ └── wbw_social_preview.png ├── rd_example.Rd ├── rd_wbw_link.Rd ├── rd_input_raster.Rd ├── wbw_version.Rd ├── wbw_get_name.Rd ├── checks.Rd ├── print.wbw-colon-colon-WhiteboxRaster.Rd ├── wbw_ext.Rd ├── resolution.Rd ├── plot.wbw-colon-colon-WhiteboxRaster.Rd ├── dimensions.Rd ├── xy.coords.wbw-colon-colon-WhiteboxRaster.Rd ├── WhiteboxExtent.Rd ├── as_wbw_raster.Rd ├── io.Rd ├── as_rast.Rd ├── matrix.Rd ├── wbw_to_degrees.Rd ├── wbw_to_radians.Rd ├── WhiteboxRaster.Rd ├── print_geotiff_tags.Rd ├── datatype.Rd ├── wbw_max_procs.Rd ├── wbw_write_raster.Rd ├── wbw_gaussian_filter.Rd ├── vector.Rd ├── wbw_range_filter.Rd ├── wbw_random_sample.Rd ├── wbw_install.Rd ├── wbw_maximum_filter.Rd ├── wbw_minimum_filter.Rd ├── wbw_ruggedness_index.Rd ├── wbw_majority_filter.Rd ├── wbw_total_filter.Rd ├── wbw_download_sample_data.Rd ├── wbw_high_pass_filter.Rd ├── wbw_olympic_filter.Rd ├── wbw_adaptive_filter.Rd ├── wbw_standard_deviation_filter.Rd ├── wbw_slope.Rd ├── wbw_mean_filter.Rd ├── wbw_high_pass_median_filter.Rd ├── summarize.Rd ├── wbw_aspect.Rd ├── wbw_conservative_smoothing_filter.Rd ├── wbw_bilateral_filter.Rd ├── wbw_fill_missing_data.Rd ├── wbw_hillshade.Rd ├── wbw_median_filter.Rd ├── wbw_plan_curvature.Rd ├── wbw_multidirectional_hillshade.Rd ├── wbw_percentile_filter.Rd ├── wbw_mean_curvature.Rd ├── wbw_gaussian_curvature.Rd ├── wbw_maximal_curvature.Rd ├── wbw_minimal_curvature.Rd └── wbw_profile_curvature.Rd ├── pkgdown ├── favicon │ ├── favicon.ico │ ├── favicon-96x96.png │ ├── apple-touch-icon.png │ ├── web-app-manifest-192x192.png │ ├── web-app-manifest-512x512.png │ └── site.webmanifest └── index.md ├── tests ├── tinytest │ ├── test_zzz.R │ ├── test_conversions.R │ ├── test_system.R │ ├── test_math.R │ ├── test_crs.R │ ├── setup.R │ ├── test_print.R │ ├── test_dims.R │ ├── test_checks.R │ ├── test_terra.R │ ├── test_utils_documentation.R │ ├── test_io.R │ ├── test_primitives.R │ ├── test_geomorphometry.R │ ├── test_curvature.R │ └── test_filter.R └── test.R ├── .Rbuildignore ├── NEWS.md ├── codecov.yml ├── LICENSE.md ├── DESCRIPTION ├── NAMESPACE ├── _pkgdown.yml └── CONTRIBUTING.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /R/env.R: -------------------------------------------------------------------------------- 1 | wbw_env <- new.env() 2 | -------------------------------------------------------------------------------- /vignettes/articles/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2025 2 | COPYRIGHT HOLDER: wbw authors 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | /playground/ 3 | docs 4 | README.Rmd -------------------------------------------------------------------------------- /inst/extdata/dem.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/inst/extdata/dem.tif -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/man/figures/logo.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /man/figures/README-terra-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/man/figures/README-terra-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/pkgdown/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /man/figures/src/wbw_social_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/man/figures/src/wbw_social_preview.png -------------------------------------------------------------------------------- /pkgdown/favicon/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/pkgdown/favicon/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /pkgdown/favicon/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsyplenkov/wbw/HEAD/pkgdown/favicon/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /tests/tinytest/test_zzz.R: -------------------------------------------------------------------------------- 1 | # Test version string format 2 | version <- wbw_version() 3 | expect_true(is.character(version)) 4 | expect_true(grepl("^\\d+\\.\\d+\\.\\d+$", version)) 5 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^wbw\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^playground$ 5 | ^playground/ 6 | /playground/ 7 | ^README\.Rmd$ 8 | ^\.github$ 9 | ^codecov\.yml$ 10 | ^CONTRIBUTING\.md$ 11 | ^_pkgdown\.yml$ 12 | ^docs$ 13 | ^pkgdown$ 14 | ^vignettes/articles$ 15 | ^man/figures/src$ -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # wbw 0.0.2 (2024-01-09) 2 | 3 | * Swithced from `{testthat}` to `{tinytest}` to 4 | reduce dependencies 5 | 6 | # wbw 0.0.2 (2024-01-07) 7 | 8 | * Documentation updates: 9 | - Added FAQ 10 | - Tutorial No. 2 11 | - Benchmarks 12 | 13 | # wbw 0.0.1 (2024-12-26) 14 | 15 | * MVP 16 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /man/rd_example.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils_documentation.R 3 | \name{rd_example} 4 | \alias{rd_example} 5 | \title{Create basic example} 6 | \usage{ 7 | rd_example(foo, args = NULL) 8 | } 9 | \description{ 10 | Create basic example 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /man/rd_wbw_link.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils_documentation.R 3 | \name{rd_wbw_link} 4 | \alias{rd_wbw_link} 5 | \title{Create reference tags for docs} 6 | \usage{ 7 | rd_wbw_link(fun_name) 8 | } 9 | \description{ 10 | Links to the Whitebox Workflows for Python manual 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /man/rd_input_raster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils_documentation.R 3 | \name{rd_input_raster} 4 | \alias{rd_input_raster} 5 | \title{Create input parameter tag} 6 | \usage{ 7 | rd_input_raster(param) 8 | } 9 | \description{ 10 | Description of input \link{WhiteboxRaster} object 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /man/wbw_version.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/zzz.R 3 | \name{wbw_version} 4 | \alias{wbw_version} 5 | \title{\code{wbw_version()}: Gets the \verb{Whitebox Workflows} version} 6 | \usage{ 7 | wbw_version() 8 | } 9 | \value{ 10 | character. Version Number. 11 | } 12 | \description{ 13 | \code{wbw_version()}: Gets the \verb{Whitebox Workflows} version 14 | } 15 | -------------------------------------------------------------------------------- /tests/test.R: -------------------------------------------------------------------------------- 1 | # Run package tests 2 | if (requireNamespace("tinytest", quietly = TRUE)) { 3 | # Initialize session 4 | have_wbw <- reticulate::py_module_available("whitebox_workflows") 5 | have_numpy <- reticulate::py_module_available("numpy") 6 | if (!have_wbw & !have_numpy) { 7 | wbw_install(system = TRUE) 8 | } 9 | 10 | tinytest::test_package("wbw", pattern = "^test_.*\\.[rR]$") 11 | } -------------------------------------------------------------------------------- /man/wbw_get_name.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/read.R 3 | \name{wbw_get_name} 4 | \alias{wbw_get_name} 5 | \title{Get File Name from Path} 6 | \usage{ 7 | wbw_get_name(path) 8 | } 9 | \arguments{ 10 | \item{path}{\code{character}, file path} 11 | } 12 | \value{ 13 | \code{character}, file name 14 | } 15 | \description{ 16 | Extracts the file name from a full path string. 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /tests/tinytest/test_conversions.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | slope_deg <- wbw_slope(x, units = "d") 4 | slope_rad <- wbw_slope(x, units = "r") 5 | deg_to_rad <- wbw_to_radians(slope_deg) 6 | rad_to_deg <- wbw_to_degrees(slope_rad) 7 | 8 | expect_inherits(deg_to_rad, c("wbw::WhiteboxRaster", "S7_object")) 9 | expect_inherits(rad_to_deg, c("wbw::WhiteboxRaster", "S7_object")) 10 | expect_equal(mean(slope_rad), mean(deg_to_rad)) 11 | expect_equal(mean(slope_deg), mean(rad_to_deg)) 12 | -------------------------------------------------------------------------------- /inst/extdata/dem.tif.aux.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YES 5 | 360.84683227539 6 | 167.16138323474 7 | 64.347480773926 8 | 49.205704393024 9 | 100 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/tinytest/test_system.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test invalid max_procs settings 4 | expect_error(wbw_max_procs(0)) 5 | expect_error(wbw_max_procs(-20)) 6 | expect_error(wbw_max_procs(1.1)) 7 | 8 | # Test performance with different max_procs settings 9 | wbw_max_procs(-1) 10 | t_parallel <- system.time(wbw_slope(x)) 11 | 12 | wbw_max_procs(1) 13 | t_1 <- system.time(wbw_slope(x)) 14 | 15 | # Check that parallel processing is faster 16 | expect_true(t_1[3] >= t_parallel[3]) 17 | -------------------------------------------------------------------------------- /tests/tinytest/test_math.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test random sample generation 4 | expect_inherits(wbw_random_sample(x), c("wbw::WhiteboxRaster", "S7_object")) 5 | expect_inherits( 6 | wbw_random_sample(x, num_samples = 1), 7 | c("wbw::WhiteboxRaster", "S7_object") 8 | ) 9 | 10 | # Test error conditions 11 | expect_error(wbw_random_sample(x, num_samples = -1)) 12 | expect_error(wbw_random_sample(x, num_samples = runif(1))) 13 | expect_error(wbw_random_sample(x, num_samples = x@source$num_cells() + 1)) 14 | -------------------------------------------------------------------------------- /tests/tinytest/test_crs.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test extent extraction 4 | ext <- wbw_ext(x) 5 | expect_inherits(ext, c("wbw::WhiteboxExtent", "S7_object")) 6 | 7 | # Test individual components 8 | expect_identical(ext@west, x@source$configs$west) 9 | expect_identical(ext@east, x@source$configs$east) 10 | expect_identical(ext@south, x@source$configs$south) 11 | expect_identical(ext@north, x@source$configs$north) 12 | 13 | # Test error cases 14 | expect_error(wbw_ext("x")) 15 | expect_error(wbw_ext(NULL)) 16 | -------------------------------------------------------------------------------- /pkgdown/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/web-app-manifest-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png", 9 | "purpose": "maskable" 10 | }, 11 | { 12 | "src": "/web-app-manifest-512x512.png", 13 | "sizes": "512x512", 14 | "type": "image/png", 15 | "purpose": "maskable" 16 | } 17 | ], 18 | "theme_color": "#ffffff", 19 | "background_color": "#ffffff", 20 | "display": "standalone" 21 | } -------------------------------------------------------------------------------- /man/checks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/checks.R 3 | \name{check_package} 4 | \alias{check_package} 5 | \alias{check_env} 6 | \alias{check_input_file} 7 | \title{Check if package is installed} 8 | \usage{ 9 | check_package(package) 10 | 11 | check_env(env = wbe) 12 | 13 | check_input_file(file_name, type) 14 | } 15 | \description{ 16 | Check if package is installed 17 | 18 | Check if whitebox environment is present 19 | 20 | Check input file extension 21 | } 22 | \keyword{internal} 23 | -------------------------------------------------------------------------------- /man/print.wbw-colon-colon-WhiteboxRaster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{print.wbw::WhiteboxRaster} 4 | \alias{print.wbw::WhiteboxRaster} 5 | \title{Print Method for WhiteboxRaster} 6 | \usage{ 7 | \method{print}{`wbw::WhiteboxRaster`}(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{\link{WhiteboxRaster} object to print} 11 | 12 | \item{...}{additional arguments passed to print method} 13 | } 14 | \description{ 15 | Print Method for WhiteboxRaster 16 | } 17 | \keyword{methods} 18 | -------------------------------------------------------------------------------- /man/wbw_ext.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crs.R 3 | \name{wbw_ext} 4 | \alias{wbw_ext} 5 | \title{Get WhiteboxExtent} 6 | \usage{ 7 | wbw_ext(x) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | } 12 | \description{ 13 | Get WhiteboxExtent 14 | } 15 | \examples{ 16 | f <- system.file("extdata/dem.tif", package = "wbw") 17 | wbw_read_raster(f) |> 18 | wbw_ext() 19 | } 20 | \keyword{crs} 21 | -------------------------------------------------------------------------------- /tests/tinytest/setup.R: -------------------------------------------------------------------------------- 1 | library(tinytest) 2 | library(wbw) 3 | 4 | # Load New Zealand DEM 5 | raster_path <- system.file("extdata/dem.tif", package = "wbw") 6 | x <- wbw_read_raster(raster_path) 7 | 8 | # Path to terra's files 9 | if (requireNamespace("terra", quietly = TRUE)) { 10 | library(terra) 11 | f <- system.file("ex/elev.tif", package = "terra") 12 | } 13 | 14 | # Helper functions 15 | skip_if_not_installed <- function(pkg) { 16 | if (!requireNamespace(pkg, quietly = TRUE)) { 17 | exit_file("Package", pkg, "not available") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /man/resolution.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dimensions.R 3 | \name{wbw_res} 4 | \alias{wbw_res} 5 | \alias{wbw_xres} 6 | \alias{wbw_yres} 7 | \title{Get WhiteboxRaster resolution (x and y)} 8 | \usage{ 9 | wbw_res(x) 10 | 11 | wbw_xres(x) 12 | 13 | wbw_yres(x) 14 | } 15 | \arguments{ 16 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 17 | } 18 | \value{ 19 | \code{double} Vector containing x and y resolution 20 | } 21 | \description{ 22 | Get WhiteboxRaster resolution (x and y) 23 | } 24 | \keyword{utils} 25 | -------------------------------------------------------------------------------- /vignettes/articles/tutorials.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tutorials" 3 | output: 4 | rmarkdown::html_vignette: 5 | toc: false 6 | vignette: > 7 | %\VignetteIndexEntry{Tutorials} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | A list of the original [Whitebox Workflows for Python](https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tutorials.html) tutorials, rewritten in R with the `{wbw}` and `{terra}` packages. 13 | 14 | ◽ Tutorial 1: Hydrological Analysis
15 | ◽ [Tutorial 2: Geomorphometric Analysis](Tutorial-2.html)
16 | ◽ Tutorial 3: Mapping Building Footprints from LiDAR
17 | -------------------------------------------------------------------------------- /man/plot.wbw-colon-colon-WhiteboxRaster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot.R 3 | \name{plot.wbw::WhiteboxRaster} 4 | \alias{plot.wbw::WhiteboxRaster} 5 | \title{Plot WhiteboxRaster} 6 | \usage{ 7 | \method{plot}{`wbw::WhiteboxRaster`}(x, col = grDevices::terrain.colors(100), add = FALSE, ...) 8 | } 9 | \arguments{ 10 | \item{x}{WhiteboxRaster object} 11 | 12 | \item{col}{Colors to use for plotting} 13 | 14 | \item{add}{Logical, whether to add to existing plot} 15 | 16 | \item{...}{Additional arguments passed to plot} 17 | } 18 | \description{ 19 | Plot WhiteboxRaster 20 | } 21 | \keyword{methods} 22 | -------------------------------------------------------------------------------- /R/crs.R: -------------------------------------------------------------------------------- 1 | #' Get WhiteboxExtent 2 | #' @keywords crs 3 | #' 4 | #' @eval rd_input_raster("x") 5 | #' 6 | #' @examples 7 | #' f <- system.file("extdata/dem.tif", package = "wbw") 8 | #' wbw_read_raster(f) |> 9 | #' wbw_ext() 10 | #' @export 11 | wbw_ext <- S7::new_generic( 12 | name = "wbw_ext", 13 | dispatch_args = "x", 14 | fun = function(x) { 15 | # Add some input validation 16 | S7::S7_dispatch() 17 | } 18 | ) 19 | 20 | S7::method(wbw_ext, WhiteboxRaster) <- function(x) { 21 | # Checks 22 | conf <- x@source$configs 23 | WhiteboxExtent( 24 | west = conf$west, 25 | east = conf$east, 26 | south = conf$south, 27 | north = conf$north 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /man/dimensions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dimensions.R 3 | \name{num_cells} 4 | \alias{num_cells} 5 | \alias{wbw_rows} 6 | \alias{wbw_cols} 7 | \title{Get dimensions of a WhiteboxRaster or WhiteboxVector object} 8 | \usage{ 9 | num_cells(x) 10 | 11 | wbw_rows(x) 12 | 13 | wbw_cols(x) 14 | } 15 | \arguments{ 16 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 17 | } 18 | \value{ 19 | \code{integer} Number of cells in the raster 20 | } 21 | \description{ 22 | Get dimensions of a WhiteboxRaster or WhiteboxVector object 23 | } 24 | \keyword{utils} 25 | -------------------------------------------------------------------------------- /man/xy.coords.wbw-colon-colon-WhiteboxRaster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot.R 3 | \name{xy.coords.wbw::WhiteboxRaster} 4 | \alias{xy.coords.wbw::WhiteboxRaster} 5 | \title{Convert WhiteboxRaster to x-y coordinates} 6 | \usage{ 7 | `xy.coords.wbw::WhiteboxRaster`(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{WhiteboxRaster object} 11 | 12 | \item{...}{additional arguments (not used)} 13 | } 14 | \value{ 15 | A list with NULL x and y components 16 | } 17 | \description{ 18 | This is an internal method used by the plotting system. 19 | It returns NULL coordinates to prevent default plotting behavior. 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /man/WhiteboxExtent.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/WhiteboxClasses.R 3 | \name{WhiteboxExtent} 4 | \alias{WhiteboxExtent} 5 | \title{WhiteboxExtent Class} 6 | \usage{ 7 | WhiteboxExtent( 8 | west = numeric(0), 9 | east = numeric(0), 10 | south = numeric(0), 11 | north = numeric(0) 12 | ) 13 | } 14 | \arguments{ 15 | \item{west}{\code{double} Western boundary coordinate} 16 | 17 | \item{east}{\code{double} Eastern boundary coordinate} 18 | 19 | \item{south}{\code{double} Southern boundary coordinate} 20 | 21 | \item{north}{\code{double} Northern boundary coordinate} 22 | } 23 | \description{ 24 | Defines the spatial extent of a raster dataset using coordinates for 25 | the west, east, south, and north boundaries. 26 | } 27 | \keyword{class} 28 | -------------------------------------------------------------------------------- /man/as_wbw_raster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/terra.R 3 | \name{as_wbw_raster} 4 | \alias{as_wbw_raster} 5 | \title{Convert SpatRaster to WhiteboxRaster} 6 | \usage{ 7 | as_wbw_raster(x) 8 | } 9 | \arguments{ 10 | \item{x}{SpatRaster object} 11 | } 12 | \value{ 13 | \link{WhiteboxRaster} object 14 | } 15 | \description{ 16 | Convert SpatRaster to WhiteboxRaster 17 | } 18 | \examples{ 19 | \dontrun{ 20 | library(terra) 21 | f <- system.file("extdata/dem.tif", package = "wbw") 22 | rast(f) |> 23 | as_wbw_raster() 24 | } 25 | } 26 | \seealso{ 27 | \code{\link[=WhiteboxRaster]{WhiteboxRaster()}} 28 | } 29 | \keyword{Converts} 30 | \keyword{SpatRaster} 31 | \keyword{[WhiteboxRaster]} 32 | \keyword{a} 33 | \keyword{object} 34 | \keyword{terra} 35 | \keyword{to} 36 | -------------------------------------------------------------------------------- /man/io.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/read.R 3 | \name{wbw_read_raster} 4 | \alias{wbw_read_raster} 5 | \alias{wbw_read_vector} 6 | \title{Read Raster File as WhiteboxRaster} 7 | \usage{ 8 | wbw_read_raster(file_name) 9 | 10 | wbw_read_vector(file_name) 11 | } 12 | \arguments{ 13 | \item{file_name}{\code{character}, path to ESRI shapefile} 14 | } 15 | \value{ 16 | WhiteboxRaster object 17 | 18 | WhiteboxVector object 19 | } 20 | \description{ 21 | Creates a new WhiteboxRaster object by reading raster data from a file 22 | into memory. 23 | 24 | Creates a new WhiteboxVector object by reading vector data from an ESRI 25 | shapefile into memory. 26 | } 27 | \examples{ 28 | f <- system.file("extdata/dem.tif", package = "wbw") 29 | wbw_read_raster(f) 30 | 31 | } 32 | \keyword{io} 33 | -------------------------------------------------------------------------------- /man/as_rast.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/terra.R 3 | \name{as_rast} 4 | \alias{as_rast} 5 | \title{Convert WhiteboxRaster to SpatRaster} 6 | \usage{ 7 | as_rast(x) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | } 12 | \value{ 13 | SpatRaster object 14 | } 15 | \description{ 16 | Convert WhiteboxRaster to SpatRaster 17 | } 18 | \examples{ 19 | \dontrun{ 20 | f <- system.file("extdata/dem.tif", package = "wbw") 21 | wbw_read_raster(f) |> 22 | wbw_slope(units = "r") |> 23 | as_rast() 24 | } 25 | } 26 | \seealso{ 27 | \code{\link[=WhiteboxRaster]{WhiteboxRaster()}} 28 | } 29 | \keyword{Converts} 30 | \keyword{SpatRaster} 31 | \keyword{[WhiteboxRaster]} 32 | \keyword{a} 33 | \keyword{object} 34 | \keyword{terra} 35 | \keyword{to} 36 | -------------------------------------------------------------------------------- /man/matrix.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/matrix.R 3 | \name{as_matrix} 4 | \alias{as_matrix} 5 | \title{Convert WhiteboxRaster to Matrix} 6 | \usage{ 7 | as_matrix(x, raw = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{raw}{logical. Should the raw data be returned (\code{raw = TRUE}) or should 13 | NoData values be transformed to \code{NA} (\code{raw = FALSE})?} 14 | } 15 | \value{ 16 | matrix containing raster values 17 | } 18 | \description{ 19 | Converts a WhiteboxRaster object to a matrix. The output maintains the same 20 | dimensions and cell values as the input raster. 21 | } 22 | \examples{ 23 | f <- system.file("extdata/dem.tif", package = "wbw") 24 | wbw_read_raster(f) |> 25 | as_matrix(raw = TRUE) 26 | } 27 | \keyword{transform} 28 | -------------------------------------------------------------------------------- /tests/tinytest/test_print.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Reading geotiff tags 4 | expected_out <- c( 5 | "+-----------------------------------------------+ ", 6 | "| WhiteboxRaster |", 7 | "| dem.tif |", 8 | "|...............................................| ", 9 | "| bands : 1 |", 10 | "| dimensions : 726, 800 (nrow, ncol) |", 11 | "| resolution : 5.002392, 5.000243 (x, y) |", 12 | "| EPSG : 2193 (Linear_Meter) |", 13 | "| extent : 1925449 1929446 5582091 5585717 |", 14 | "| min value : 63.698193 |", 15 | "| max value : 361.020721 |", 16 | "+-----------------------------------------------+ " 17 | ) 18 | 19 | expect_equal( 20 | paste(utils::capture.output(print(x)), collapse = "\n"), 21 | paste(expected_out, collapse = "\n") 22 | ) 23 | -------------------------------------------------------------------------------- /man/wbw_to_degrees.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conversion.R 3 | \name{wbw_to_degrees} 4 | \alias{wbw_to_degrees} 5 | \title{Convert to degrees} 6 | \usage{ 7 | wbw_to_degrees(x) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | } 12 | \value{ 13 | \link{WhiteboxRaster} object in degrees 14 | } 15 | \description{ 16 | Convert to degrees 17 | } 18 | \examples{ 19 | f <- system.file("extdata/dem.tif", package = "wbw") 20 | wbw_read_raster(f) |> 21 | wbw_slope(units = "r") |> 22 | wbw_to_degrees() 23 | } 24 | \seealso{ 25 | \code{\link[=wbw_to_radians]{wbw_to_radians()}}, \code{\link[=wbw_slope]{wbw_slope()}} 26 | } 27 | \keyword{Converts} 28 | \keyword{[WhiteboxRaster]} 29 | \keyword{a} 30 | \keyword{conversions} 31 | \keyword{degrees} 32 | \keyword{from} 33 | \keyword{radians} 34 | \keyword{to} 35 | -------------------------------------------------------------------------------- /man/wbw_to_radians.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conversion.R 3 | \name{wbw_to_radians} 4 | \alias{wbw_to_radians} 5 | \title{Convert to radians} 6 | \usage{ 7 | wbw_to_radians(x) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | } 12 | \value{ 13 | \link{WhiteboxRaster} object in radians 14 | } 15 | \description{ 16 | Convert to radians 17 | } 18 | \examples{ 19 | f <- system.file("extdata/dem.tif", package = "wbw") 20 | wbw_read_raster(f) |> 21 | wbw_slope(units = "d") |> 22 | wbw_to_radians() 23 | } 24 | \seealso{ 25 | \code{\link[=wbw_to_degrees]{wbw_to_degrees()}}, \code{\link[=wbw_slope]{wbw_slope()}} 26 | } 27 | \keyword{Converts} 28 | \keyword{[WhiteboxRaster]} 29 | \keyword{a} 30 | \keyword{conversions} 31 | \keyword{degrees} 32 | \keyword{from} 33 | \keyword{radians} 34 | \keyword{to} 35 | -------------------------------------------------------------------------------- /man/WhiteboxRaster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/WhiteboxClasses.R 3 | \name{WhiteboxRaster} 4 | \alias{WhiteboxRaster} 5 | \title{WhiteboxRaster Class} 6 | \usage{ 7 | WhiteboxRaster(name = character(0), source = NULL) 8 | } 9 | \arguments{ 10 | \item{name}{\code{character} Name of the raster} 11 | 12 | \item{source}{\code{any} Source data for the raster} 13 | } 14 | \description{ 15 | Represents a raster dataset in Whitebox Workflows for Python (WbW) format. 16 | Provides access to raster properties including name, Python pointer, summary 17 | statistics, and min/max values. 18 | } 19 | \section{Properties}{ 20 | 21 | \describe{ 22 | \item{stats}{\code{character} Summary statistics for the raster} 23 | \item{min}{\code{numeric} Minimum value for the raster} 24 | \item{max}{\code{numeric} Maximum value for the raster} 25 | \item{extent}{\code{WhiteboxExtent} Spatial extent of the raster} 26 | } 27 | } 28 | 29 | \keyword{class} 30 | -------------------------------------------------------------------------------- /man/print_geotiff_tags.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{print_geotiff_tags} 4 | \alias{print_geotiff_tags} 5 | \title{Print GeoTIFF Tags} 6 | \usage{ 7 | print_geotiff_tags(file_name) 8 | } 9 | \arguments{ 10 | \item{file_name}{\code{character}, path to raster file} 11 | } 12 | \description{ 13 | Displays the tags contained within a GeoTIFF file. This is useful when 14 | importing GeoTIFF files into different software environments. The tool prints 15 | tag information to the console. Tags containing more than 100 values are 16 | truncated in the output. GeoKeys are interpreted according to the GeoTIFF 17 | specification. 18 | } 19 | \examples{ 20 | \dontrun{ 21 | raster_path <- system.file("extdata/dem.tif", package = "wbw") 22 | print_geotiff_tags(raster_path) 23 | } 24 | 25 | } 26 | \references{ 27 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#print_geotiff_tags} 28 | } 29 | \keyword{utils} 30 | -------------------------------------------------------------------------------- /man/datatype.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dimensions.R 3 | \name{wbw_data_type} 4 | \alias{wbw_data_type} 5 | \alias{wbw_is_int} 6 | \alias{wbw_is_float} 7 | \alias{wbw_is_rgb} 8 | \title{Get WhiteboxRaster data type} 9 | \usage{ 10 | wbw_data_type(x) 11 | 12 | wbw_is_int(x) 13 | 14 | wbw_is_float(x) 15 | 16 | wbw_is_rgb(x) 17 | } 18 | \arguments{ 19 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 20 | } 21 | \value{ 22 | \code{character} String representing the raster data type 23 | } 24 | \description{ 25 | Get WhiteboxRaster data type 26 | } 27 | \examples{ 28 | f <- system.file("extdata/dem.tif", package = "wbw") 29 | wbw_read_raster(f) |> 30 | wbw_data_type() 31 | f <- system.file("extdata/dem.tif", package = "wbw") 32 | wbw_read_raster(f) |> 33 | wbw_is_int() 34 | f <- system.file("extdata/dem.tif", package = "wbw") 35 | wbw_read_raster(f) |> 36 | wbw_is_float() 37 | f <- system.file("extdata/dem.tif", package = "wbw") 38 | wbw_read_raster(f) |> 39 | wbw_is_rgb() 40 | } 41 | \keyword{utils} 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2025 wbw authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/tinytest/test_dims.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | skip_if_not_installed("terra") 3 | 4 | r <- terra::rast(raster_path) 5 | 6 | # Test dimension functions 7 | expect_equal(num_cells(x), terra::ncell(r)) 8 | expect_equal(wbw_cols(x), terra::ncol(r)) 9 | expect_equal(wbw_rows(x), terra::nrow(r)) 10 | expect_equal(wbw_res(x), terra::res(r)) 11 | expect_equal(wbw_yres(x), terra::yres(r)) 12 | expect_equal(wbw_xres(x), terra::xres(r)) 13 | 14 | # Test data type detection 15 | r2 <- terra::rast(f) 16 | x2 <- wbw_read_raster(f) 17 | 18 | # Compare with terra 19 | expect_equal(wbw_is_int(x), terra::is.int(r)) 20 | expect_equal(wbw_is_int(x2), terra::is.int(r2)) 21 | expect_equal(wbw_data_type(x2), "RasterDataType.I16") 22 | expect_equal(wbw_data_type(x), "RasterDataType.F32") 23 | 24 | # Check output class 25 | expect_true(is.logical(wbw_is_int(x))) 26 | expect_true(is.logical(wbw_is_float(x))) 27 | expect_true(is.logical(wbw_is_rgb(x))) 28 | expect_true(is.character(wbw_data_type(x))) 29 | 30 | # Check input validation 31 | expect_error(wbw_is_int(r)) 32 | expect_error(wbw_is_float(r)) 33 | expect_error(wbw_is_rgb(r)) 34 | expect_error(wbw_data_type(r)) 35 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: wbw 2 | Title: Whitebox Workflows for R 3 | Version: 0.0.3 4 | URL: https://wbw.anatolii.nz 5 | BugReports: https://github.com/atsyplenkov/wbw/issues 6 | Authors@R: c(person(given = "Anatoly", family = "Tsyplenkov", role = c("aut", "cre"), email = "atsyplenkov@fastmail.com", comment = c(ORCID = "0000-0003-4144-8402"))) 7 | Maintainer: Anatoly Tsyplenkov 8 | Description: Provides R bindings to the proprietary Whitebox Workflows Python library, developed by Whitebox Geospatial Inc. 'Whitebox Workflows' is an advanced geospatial data analysis library for Geographic Information Systems (GIS) and remote sensing analysis, as well as manipulation of raster, vector, and LiDAR data. 9 | License: MIT + file LICENSE 10 | Encoding: UTF-8 11 | Language: en-US 12 | Roxygen: list(markdown = TRUE) 13 | RoxygenNote: 7.3.2 14 | Imports: 15 | cli, 16 | checkmate, 17 | reticulate, 18 | S7 (>= 0.2.0), 19 | graphics, 20 | grDevices, 21 | stats 22 | Suggests: 23 | tinytest, 24 | terra 25 | SystemRequirements: Python (>= 3.8.0), numpy, whitebox-workflows (>= v1.3.3) 26 | Config/Needs/website: rmarkdown, waldo, bench, whitebox, ggplot2, tidyr 27 | -------------------------------------------------------------------------------- /man/wbw_max_procs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/system.R 3 | \name{wbw_max_procs} 4 | \alias{wbw_max_procs} 5 | \title{Set Maximum Parallel Processors} 6 | \usage{ 7 | wbw_max_procs(max_procs = -1) 8 | } 9 | \arguments{ 10 | \item{max_procs}{\code{integer} Number of processors to use. Use -1 for all 11 | available processors, or a positive integer to limit processor usage.} 12 | } 13 | \description{ 14 | Determines the number of processors used by functions that are parallelized. 15 | If set to -1 (\code{max_procs=-1}), the default, all available processors will be 16 | used. To limit processing, set \code{max_procs} to a positive whole number less 17 | than the number of system processors. 18 | } 19 | \examples{ 20 | \dontrun{ 21 | raster_path <- system.file("extdata/dem.tif", package = "wbw") 22 | x <- wbw_read_raster(raster_path) 23 | 24 | # Use 1 processor 25 | wbw_max_procs(1) 26 | system.time(wbw_slope(x)) 27 | 28 | # Use all available processors 29 | wbw_max_procs(-1) 30 | system.time(wbw_slope(x)) 31 | } 32 | } 33 | \references{ 34 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#max_procs} 35 | } 36 | \keyword{system} 37 | -------------------------------------------------------------------------------- /man/wbw_write_raster.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/write.R 3 | \name{wbw_write_raster} 4 | \alias{wbw_write_raster} 5 | \title{Writes an in-memory WhiteboxRaster object to file.} 6 | \usage{ 7 | wbw_write_raster(x, file_name, compress = TRUE) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{file_name}{\code{character} Path to output file} 13 | 14 | \item{compress}{\code{logical} Whether to compress the output file} 15 | } 16 | \description{ 17 | Writes an in-memory WhiteboxRaster object to a file in a supported raster 18 | format. 19 | } 20 | \details{ 21 | Supported raster formats: 22 | \itemize{ 23 | \item GeoTIFF (*.tif, *.tiff) 24 | \item Big GeoTIFF (*.tif, *.tiff) 25 | \item SAGA Binary (*.sdat, *.sgrd) 26 | \item Idrisi (*.rst, *.rdc) 27 | \item Surfer (*.grd) 28 | \item ESRI Binary (*.flt) 29 | \item ESRI BIL (*.bil) 30 | } 31 | 32 | The tool can read GeoTIFFs compressed using PackBits, DEFLATE, or LZW 33 | methods. 34 | 35 | When writing GeoTIFFs, use \code{compress=TRUE} to enable DEFLATE compression. 36 | } 37 | \references{ 38 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#write_raster} 39 | } 40 | \keyword{io} 41 | -------------------------------------------------------------------------------- /R/checks.R: -------------------------------------------------------------------------------- 1 | #' Check if package is installed 2 | #' @rdname checks 3 | #' @keywords internal 4 | check_package <- function(package) { 5 | if (!requireNamespace(package, quietly = TRUE)) { 6 | stop(paste(package, "is required but not installed.")) 7 | } 8 | } 9 | 10 | #' Check if whitebox environment is present 11 | #' @rdname checks 12 | #' @keywords internal 13 | check_env <- function(env = wbe) { 14 | checkmate::assert_class( 15 | env, 16 | classes = c( 17 | "whitebox_workflows.WbEnvironment", 18 | "python.builtin.WbEnvironmentBase", 19 | "python.builtin.object" 20 | ) 21 | ) 22 | } 23 | 24 | #' Check input file extension 25 | #' @rdname checks 26 | #' @keywords internal 27 | check_input_file <- function(file_name, type) { 28 | type <- checkmate::matchArg(type, c("vector", "raster")) 29 | 30 | if (type == "vector") { 31 | checkmate::assertFileExists( 32 | file_name, 33 | access = "r", 34 | extension = c(".shp") 35 | ) 36 | } else if (type == "raster") { 37 | checkmate::assertFileExists( 38 | file_name, 39 | access = "r", 40 | extension = c( 41 | ".tif", 42 | ".tiff", 43 | ".sdat", 44 | ".sgrd", 45 | ".rst", 46 | ".rdc", 47 | ".grd", 48 | ".flt", 49 | ".bil" 50 | ) 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /man/wbw_gaussian_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_gaussian_filter} 4 | \alias{wbw_gaussian_filter} 5 | \title{Gaussian Filter} 6 | \usage{ 7 | wbw_gaussian_filter(x, sigma = 0.75) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{sigma}{\code{double}, standard deviation distance parameter in 13 | \strong{units of grid cells}. Should be in the range 0.5-20.} 14 | } 15 | \value{ 16 | \link{WhiteboxRaster} object containing filtered values 17 | } 18 | \description{ 19 | Applies a Gaussian smoothing filter to reduce noise while better preserving 20 | image features compared to a simple mean filter. 21 | } 22 | \details{ 23 | The filter applies a 2D Gaussian kernel that weights pixels based on their 24 | distance from the center. This gradual weighting makes it more effective for 25 | noise reduction than the mean filter. The filter size is controlled by the 26 | sigma parameter (0.5-20 grid cells). 27 | } 28 | \examples{ 29 | f <- system.file("extdata/dem.tif", package = "wbw") 30 | wbw_read_raster(f) |> 31 | wbw_gaussian_filter(sigma = 1.5) 32 | } 33 | \references{ 34 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#gaussian_filter} 35 | } 36 | \seealso{ 37 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}} 38 | } 39 | \keyword{image_processing} 40 | -------------------------------------------------------------------------------- /R/write.R: -------------------------------------------------------------------------------- 1 | #' Writes an in-memory WhiteboxRaster object to file. 2 | #' @rdname wbw_write_raster 3 | #' @keywords io 4 | #' 5 | #' @description 6 | #' Writes an in-memory WhiteboxRaster object to a file in a supported raster 7 | #' format. 8 | #' 9 | #' @eval rd_wbw_link("write_raster") 10 | #' @eval rd_input_raster("x") 11 | #' 12 | #' @param file_name \code{character} Path to output file 13 | #' @param compress \code{logical} Whether to compress the output file 14 | #' 15 | #' @details 16 | #' Supported raster formats: 17 | #' - GeoTIFF (*.tif, *.tiff) 18 | #' - Big GeoTIFF (*.tif, *.tiff) 19 | #' - SAGA Binary (*.sdat, *.sgrd) 20 | #' - Idrisi (*.rst, *.rdc) 21 | #' - Surfer (*.grd) 22 | #' - ESRI Binary (*.flt) 23 | #' - ESRI BIL (*.bil) 24 | #' 25 | #' The tool can read GeoTIFFs compressed using PackBits, DEFLATE, or LZW 26 | #' methods. 27 | #' 28 | #' When writing GeoTIFFs, use `compress=TRUE` to enable DEFLATE compression. 29 | #' 30 | #' @export 31 | wbw_write_raster <- S7::new_generic( 32 | name = "wbw_write_raster", 33 | dispatch_args = "x", 34 | fun = function(x, file_name, compress = TRUE) { 35 | S7::S7_dispatch() 36 | } 37 | ) 38 | 39 | S7::method(wbw_write_raster, WhiteboxRaster) <- function( 40 | x, 41 | file_name, 42 | compress = TRUE 43 | ) { 44 | # Checks 45 | check_env(wbe) 46 | checkmate::assert_logical(compress, len = 1) 47 | # Write 48 | wbe$write_raster(x@source, file_name = file_name, compress = compress) 49 | } 50 | -------------------------------------------------------------------------------- /tests/tinytest/test_checks.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Package checks 4 | expect_silent(wbw:::check_package("base")) 5 | expect_error(wbw:::check_package("nonexistentpackage123")) 6 | 7 | # Environment checks 8 | mock_env <- structure( 9 | list(), 10 | class = c( 11 | "whitebox_workflows.WbEnvironment", 12 | "python.builtin.WbEnvironmentBase", 13 | "python.builtin.object" 14 | ) 15 | ) 16 | 17 | expect_silent(wbw:::check_env(mock_env)) 18 | expect_error(wbw:::check_env(list())) 19 | expect_error(wbw:::check_env(NULL)) 20 | 21 | # Vector file checks 22 | temp_shp <- tempfile(fileext = ".shp") 23 | file.create(temp_shp) 24 | 25 | expect_silent(wbw:::check_input_file(temp_shp, "vector")) 26 | expect_error(wbw:::check_input_file("nonexistent.shp", "vector")) 27 | 28 | temp_wrong <- tempfile(fileext = ".txt") 29 | file.create(temp_wrong) 30 | expect_error(wbw:::check_input_file(temp_wrong, "vector")) 31 | 32 | # Raster file checks 33 | temp_tif <- tempfile(fileext = ".tif") 34 | file.create(temp_tif) 35 | 36 | expect_silent(wbw:::check_input_file(temp_tif, "raster")) 37 | expect_error(wbw:::check_input_file("nonexistent.tif", "raster")) 38 | 39 | temp_wrong <- tempfile(fileext = ".txt") 40 | file.create(temp_wrong) 41 | expect_error(wbw:::check_input_file(temp_wrong, "raster")) 42 | 43 | # Type validation 44 | expect_error(wbw:::check_input_file(temp_file, "invalid_type")) 45 | 46 | # Cleanup 47 | unlink(temp_shp) 48 | unlink(temp_wrong) 49 | unlink(temp_tif) 50 | -------------------------------------------------------------------------------- /man/vector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/matrix.R 3 | \name{as_vector} 4 | \alias{as_vector} 5 | \alias{as_vector,WhiteboxRaster-method} 6 | \alias{as_vector,WhiteboxExtent-method} 7 | \title{Convert objects to vectors} 8 | \usage{ 9 | \S4method{as_vector}{WhiteboxRaster}(x, raw = FALSE) 10 | \S4method{as_vector}{WhiteboxExtent}(x) 11 | } 12 | \arguments{ 13 | \item{x}{Object to convert to vector. Can be: 14 | \itemize{ 15 | \item A \link{WhiteboxRaster} object 16 | \item A \link{WhiteboxExtent} object 17 | }} 18 | 19 | \item{raw}{logical. For \link{WhiteboxRaster} only: Should the raw data be 20 | returned (\code{raw = TRUE}) or should NoData values be transformed 21 | to \code{NA} (\code{raw = FALSE})?} 22 | } 23 | \value{ 24 | A vector, with type depending on the input: 25 | \itemize{ 26 | \item For \link{WhiteboxRaster}: vector containing raster values 27 | \item For \link{WhiteboxExtent}: named vector containing extent values 28 | } 29 | } 30 | \description{ 31 | Converts various Whitebox objects to vectors: 32 | \itemize{ 33 | \item For \link{WhiteboxRaster}: converts raster values to a vector in row-major 34 | order 35 | \item For \link{WhiteboxExtent}: converts extent boundaries to a named vector 36 | } 37 | } 38 | \examples{ 39 | f <- system.file("extdata/dem.tif", package = "wbw") 40 | x <- wbw_read_raster(f) 41 | 42 | # Return WhiteboxRaster's data: 43 | head(as_vector(x)) 44 | 45 | # Return WhiteboxExtent's data: 46 | as_vector(wbw_ext(x)) 47 | 48 | } 49 | \keyword{transform} 50 | -------------------------------------------------------------------------------- /man/wbw_range_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_range_filter} 4 | \alias{wbw_range_filter} 5 | \title{Range Filter} 6 | \usage{ 7 | wbw_range_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | A range filter assigns to each cell in the output grid the range 21 | (maximum - minimum) of the values contained within a moving window 22 | centred on each grid cell. 23 | } 24 | \details{ 25 | Neighbourhood size, or filter size, is specified in the x and y dimensions 26 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 27 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 28 | } 29 | \examples{ 30 | f <- system.file("extdata/dem.tif", package = "wbw") 31 | wbw_read_raster(f) |> 32 | wbw_range_filter(filter_size_x = 3L, filter_size_y = 3L) 33 | } 34 | \references{ 35 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#range_filter} 36 | } 37 | \seealso{ 38 | \code{\link[=wbw_minimum_filter]{wbw_minimum_filter()}}, \code{\link[=wbw_maximum_filter]{wbw_maximum_filter()}} 39 | } 40 | \keyword{image_processing} 41 | -------------------------------------------------------------------------------- /man/wbw_random_sample.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/math.R 3 | \name{wbw_random_sample} 4 | \alias{wbw_random_sample} 5 | \title{Random Sample} 6 | \usage{ 7 | wbw_random_sample(x, num_samples = 1000L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{num_samples}{\code{integer}, number of random samples. Must not exceed 13 | the total number of valid cells in the input raster (see \code{\link[=num_cells]{num_cells()}}).} 14 | } 15 | \value{ 16 | \link{WhiteboxRaster} object 17 | } 18 | \description{ 19 | Creates a random sample of grid cells from a raster. Uses the input 20 | WhiteboxRaster to determine grid dimensions and georeference information for 21 | the output. 22 | 23 | The output grid will contain the specified number of non-zero grid cells, 24 | randomly distributed throughout the raster. Each sampled cell will have a 25 | unique value from 1 to num_samples, with background cells set to zero. 26 | } 27 | \details{ 28 | This tool is useful for statistical analyses of raster data where a random 29 | sampling approach is needed. The sampling process only considers valid, 30 | non-NoData cells from the input raster. 31 | } 32 | \examples{ 33 | f <- system.file("extdata/dem.tif", package = "wbw") 34 | wbw_read_raster(f) |> 35 | wbw_random_sample(num_samples = 100) 36 | } 37 | \references{ 38 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#random_sample} 39 | } 40 | \keyword{math} 41 | -------------------------------------------------------------------------------- /R/utils_documentation.R: -------------------------------------------------------------------------------- 1 | #' Create reference tags for docs 2 | #' 3 | #' @description 4 | #' Links to the Whitebox Workflows for Python manual 5 | #' 6 | #' @keywords internal 7 | rd_wbw_link <- function(fun_name) { 8 | checkmate::assert_character(fun_name, min.chars = 1L) 9 | paste0( 10 | "@references For more information, see ", 11 | "" 15 | ) 16 | } 17 | 18 | #' Create input parameter tag 19 | #' 20 | #' @description 21 | #' Description of input [WhiteboxRaster] object 22 | #' 23 | #' @keywords internal 24 | rd_input_raster <- function(param) { 25 | checkmate::assert_character(param, min.chars = 1L) 26 | paste0( 27 | "@param ", 28 | param, 29 | " Raster object of class [WhiteboxRaster]. ", 30 | "See [wbw_read_raster()] for more details." 31 | ) 32 | } 33 | 34 | #' Create basic example 35 | #' 36 | #' @keywords internal 37 | rd_example <- function(foo, args = NULL) { 38 | checkmate::assert_character(foo, min.chars = 1L) 39 | checkmate::assert_vector(args, null.ok = TRUE) 40 | paste( 41 | "@examples", 42 | # "\\dontrun{", 43 | 'f <- system.file("extdata/dem.tif", package = "wbw")', 44 | "wbw_read_raster(f) |>", 45 | ifelse( 46 | is.null(args), 47 | paste0(" ", foo, "()"), 48 | paste0( 49 | " ", 50 | foo, 51 | "(", 52 | paste(args, collapse = ", "), 53 | ")" 54 | ) 55 | ), 56 | # "}", 57 | sep = "\n" 58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /man/wbw_install.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/installation.R 3 | \name{wbw_install} 4 | \alias{wbw_install} 5 | \title{Install Required Python Modules} 6 | \usage{ 7 | wbw_install(system = FALSE, force = FALSE, ...) 8 | } 9 | \arguments{ 10 | \item{system}{\code{boolean}, Use a \code{system()} call to 11 | \code{python -m pip install --user ...} 12 | instead of \code{reticulate::py_install()}. Default: \code{FALSE}.} 13 | 14 | \item{force}{\code{boolean}, Force update (uninstall/reinstall) and ignore 15 | existing installed packages? Default: \code{FALSE}. 16 | Applies to \code{system=TRUE}.} 17 | 18 | \item{...}{Additional arguments passed to \code{reticulate::py_install()}} 19 | } 20 | \value{ 21 | \code{NULL}, or \code{try-error} (invisibly) on R code execution error. 22 | } 23 | \description{ 24 | Install Required Python Modules 25 | } 26 | \details{ 27 | This function provides a basic wrapper around 28 | \code{reticulate::py_install()}, except it defaults to using the Python package 29 | manager \code{pip} and virtual environment. It creates the \code{r-wbw} 30 | virtual environment in the default location 31 | (run \code{reticulate::virtualenv_root()} to find it) and installs the 32 | required python packages. 33 | } 34 | \keyword{The} 35 | \keyword{This} 36 | \keyword{\code{numpy},} 37 | \keyword{\code{pip}} 38 | \keyword{\code{whitebox-workflows}.} 39 | \keyword{default} 40 | \keyword{for} 41 | \keyword{function} 42 | \keyword{installation.} 43 | \keyword{installs} 44 | \keyword{latest} 45 | \keyword{package} 46 | \keyword{system} 47 | \keyword{the} 48 | \keyword{uses} 49 | -------------------------------------------------------------------------------- /man/wbw_maximum_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_maximum_filter} 4 | \alias{wbw_maximum_filter} 5 | \title{Maximum Filter} 6 | \usage{ 7 | wbw_maximum_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | Assigns each cell in the output grid the maximum value in a moving window 21 | centred on each grid cell in the input raster. 22 | } 23 | \details{ 24 | Neighbourhood size, or filter size, is specified in the x and y dimensions 25 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 26 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 27 | } 28 | \examples{ 29 | f <- system.file("extdata/dem.tif", package = "wbw") 30 | wbw_read_raster(f) |> 31 | wbw_maximum_filter(filter_size_x = 3L, filter_size_y = 3L) 32 | } 33 | \references{ 34 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#maximum_filter} 35 | } 36 | \seealso{ 37 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}}, \code{\link[=wbw_high_pass_filter]{wbw_high_pass_filter()}}, 38 | \code{\link[=wbw_high_pass_median_filter]{wbw_high_pass_median_filter()}} 39 | } 40 | \keyword{image_processing} 41 | -------------------------------------------------------------------------------- /man/wbw_minimum_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_minimum_filter} 4 | \alias{wbw_minimum_filter} 5 | \title{Minimum Filter} 6 | \usage{ 7 | wbw_minimum_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | Assigns each cell in the output grid the minimum value in a moving window 21 | centred on each grid cell in the input raster. 22 | } 23 | \details{ 24 | Neighbourhood size, or filter size, is specified in the x and y dimensions 25 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 26 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 27 | } 28 | \examples{ 29 | f <- system.file("extdata/dem.tif", package = "wbw") 30 | wbw_read_raster(f) |> 31 | wbw_minimum_filter(filter_size_x = 3L, filter_size_y = 3L) 32 | } 33 | \references{ 34 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#minimum_filter} 35 | } 36 | \seealso{ 37 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}}, \code{\link[=wbw_high_pass_filter]{wbw_high_pass_filter()}}, 38 | \code{\link[=wbw_high_pass_median_filter]{wbw_high_pass_median_filter()}} 39 | } 40 | \keyword{image_processing} 41 | -------------------------------------------------------------------------------- /man/wbw_ruggedness_index.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geomorphometry.R 3 | \name{wbw_ruggedness_index} 4 | \alias{wbw_ruggedness_index} 5 | \title{Terrain Ruggedness Index (TRI)} 6 | \usage{ 7 | wbw_ruggedness_index(dem) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | } 12 | \value{ 13 | \link{WhiteboxRaster} object containing TRI values 14 | } 15 | \description{ 16 | Calculates a measure of local topographic relief. The TRI computes the 17 | root-mean-square-deviation (RMSD) for each grid cell in a DEM by calculating 18 | the residuals between a cell and its eight neighbors. 19 | } 20 | \details{ 21 | Unlike the original Riley et al. (1999) TRI, this implementation normalizes 22 | for the number of cells in the local window. This modification allows for 23 | varying numbers of neighboring cells along grid edges and areas bordering 24 | NoData cells. Note that this means output values cannot be directly compared 25 | with the index ranges (level to extremely rugged terrain) provided in Riley 26 | et al. (1999). 27 | } 28 | \examples{ 29 | f <- system.file("extdata/dem.tif", package = "wbw") 30 | wbw_read_raster(f) |> 31 | wbw_ruggedness_index() 32 | } 33 | \references{ 34 | Riley, S. J., DeGloria, S. D., and Elliot, R. (1999). Index that quantifies 35 | topographic heterogeneity. Intermountain Journal of Sciences, 5(1-4), 23-27. 36 | 37 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#ruggedness_index} 38 | } 39 | \keyword{geomorphometry} 40 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | # workflow_dispatch: 5 | push: 6 | branches: [main, master] 7 | pull_request: 8 | 9 | name: R-CMD-check.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | R-CMD-check: 15 | runs-on: ${{ matrix.config.os }} 16 | 17 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | config: 23 | - {os: macos-latest, r: 'release'} 24 | - {os: windows-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 26 | - {os: ubuntu-latest, r: 'release'} 27 | - {os: ubuntu-latest, r: 'oldrel-1'} 28 | 29 | env: 30 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 31 | R_KEEP_PKG_SOURCE: yes 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - uses: r-lib/actions/setup-pandoc@v2 37 | 38 | - uses: r-lib/actions/setup-r@v2 39 | with: 40 | r-version: ${{ matrix.config.r }} 41 | http-user-agent: ${{ matrix.config.http-user-agent }} 42 | use-public-rspm: true 43 | 44 | - uses: r-lib/actions/setup-r-dependencies@v2 45 | with: 46 | extra-packages: any::rcmdcheck 47 | needs: check 48 | 49 | - uses: r-lib/actions/check-r-package@v2 50 | with: 51 | upload-snapshots: true 52 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 53 | -------------------------------------------------------------------------------- /man/wbw_majority_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_majority_filter} 4 | \alias{wbw_majority_filter} 5 | \title{Majority Filter} 6 | \usage{ 7 | wbw_majority_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | Assigns each cell in the output grid the most frequently occurring value 21 | (mode) in a moving window centred on each grid cell in the input raster. 22 | } 23 | \details{ 24 | Neighbourhood size, or filter size, is specified in the x and y dimensions 25 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 26 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 27 | } 28 | \examples{ 29 | f <- system.file("extdata/dem.tif", package = "wbw") 30 | wbw_read_raster(f) |> 31 | wbw_majority_filter(filter_size_x = 3L, filter_size_y = 3L) 32 | } 33 | \references{ 34 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#majority_filter} 35 | } 36 | \seealso{ 37 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}}, \code{\link[=wbw_high_pass_filter]{wbw_high_pass_filter()}}, 38 | \code{\link[=wbw_high_pass_median_filter]{wbw_high_pass_median_filter()}} 39 | } 40 | \keyword{image_processing} 41 | -------------------------------------------------------------------------------- /man/wbw_total_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_total_filter} 4 | \alias{wbw_total_filter} 5 | \title{Total Filter} 6 | \usage{ 7 | wbw_total_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | A total filter assigns to each cell in the output grid the total (sum) 21 | of all values in a moving window centred on each grid cell. 22 | } 23 | \details{ 24 | Neighbourhood size, or filter size, is specified in the x and y dimensions 25 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 26 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 27 | } 28 | \examples{ 29 | f <- system.file("extdata/dem.tif", package = "wbw") 30 | wbw_read_raster(f) |> 31 | wbw_total_filter(filter_size_x = 3L, filter_size_y = 3L) 32 | } 33 | \references{ 34 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#total_filter} 35 | } 36 | \seealso{ 37 | \code{\link[=wbw_minimum_filter]{wbw_minimum_filter()}}, \code{\link[=wbw_maximum_filter]{wbw_maximum_filter()}}, 38 | \code{\link[=wbw_range_filter]{wbw_range_filter()}}, \code{\link[=wbw_majority_filter]{wbw_majority_filter()}} 39 | } 40 | \keyword{image_processing} 41 | -------------------------------------------------------------------------------- /R/read.R: -------------------------------------------------------------------------------- 1 | #' Read Raster File as WhiteboxRaster 2 | #' @rdname io 3 | #' @keywords io 4 | #' 5 | #' @description 6 | #' Creates a new WhiteboxRaster object by reading raster data from a file 7 | #' into memory. 8 | #' 9 | #' @param file_name \code{character}, path to raster file 10 | #' 11 | #' @return WhiteboxRaster object 12 | #' 13 | #' @examples 14 | #' f <- system.file("extdata/dem.tif", package = "wbw") 15 | #' wbw_read_raster(f) 16 | #' 17 | #' @export 18 | wbw_read_raster <- function(file_name) { 19 | check_env(wbe) 20 | check_input_file(file_name, "r") 21 | r <- wbe$read_raster(file_name = file_name) 22 | WhiteboxRaster( 23 | name = wbw_get_name(file_name), 24 | source = r 25 | ) 26 | } 27 | 28 | #' Read Vector File as WhiteboxVector 29 | #' @rdname io 30 | #' @keywords io 31 | #' 32 | #' @description 33 | #' Creates a new WhiteboxVector object by reading vector data from an ESRI 34 | #' shapefile into memory. 35 | #' 36 | #' @param file_name \code{character}, path to ESRI shapefile 37 | #' 38 | #' @return WhiteboxVector object 39 | #' 40 | #' @export 41 | wbw_read_vector <- function(file_name) { 42 | check_env(wbe) 43 | check_input_file(file_name, "v") 44 | wbe$read_vector(file_name = file_name) 45 | } 46 | 47 | #' Get File Name from Path 48 | #' 49 | #' @description 50 | #' Extracts the file name from a full path string. 51 | #' 52 | #' @param path \code{character}, file path 53 | #' @return \code{character}, file name 54 | #' 55 | #' @keywords internal 56 | wbw_get_name <- function(path) { 57 | checkmate::assertFile(path, access = "r") 58 | names <- strsplit(path, "/")[[1]] 59 | enc2utf8(names[length(names)]) 60 | } 61 | -------------------------------------------------------------------------------- /tests/tinytest/test_terra.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | skip_if_not_installed("terra") 4 | 5 | # Test WhiteboxRaster to SpatRaster conversion 6 | r <- terra::rast(raster_path) 7 | wbwr <- as_rast(x) 8 | 9 | # Test extent 10 | expect_identical( 11 | as.vector(terra::ext(r)), 12 | as.vector(terra::ext(wbwr)) 13 | ) 14 | 15 | # Test content 16 | expect_identical( 17 | as.vector(r), 18 | as.vector(wbwr) 19 | ) 20 | 21 | # Test resolution 22 | expect_identical( 23 | terra::res(r), 24 | terra::res(wbwr) 25 | ) 26 | 27 | # Test CRS 28 | expect_identical( 29 | terra::crs(r), 30 | terra::crs(wbwr) 31 | ) 32 | 33 | # Test data type 34 | expect_identical( 35 | terra::is.int(r), 36 | terra::is.int(wbwr) 37 | ) 38 | 39 | # Test integer data 40 | r <- terra::rast(f) 41 | r <- terra::as.int(r) 42 | wbwr <- wbw_read_raster(f) 43 | converted <- as_rast(wbwr) 44 | 45 | expect_true(terra::is.int(converted)) 46 | expect_true(terra::is.int(r)) 47 | expect_equal(as.vector(converted), as.vector(r)) 48 | 49 | # Test NA handling 50 | r <- terra::rast(f) 51 | r[r < mean(r[], na.rm = TRUE)] <- NA 52 | wbwr <- as_wbw_raster(r) 53 | converted <- as_rast(wbwr) 54 | 55 | expect_equal(sum(is.na(converted[])), sum(is.na(r[]))) 56 | expect_equal(as.vector(converted), as.vector(r)) 57 | 58 | # Test CRS conversion 59 | r <- terra::rast(f) 60 | r <- terra::project(r, "EPSG:4326") 61 | wbwr <- as_wbw_raster(r) 62 | converted <- as_rast(wbwr) 63 | 64 | expect_equal(terra::crs(converted), terra::crs(r)) 65 | expect_equal(as.vector(converted), as.vector(r)) 66 | 67 | # Test multilayer error 68 | r <- terra::rast(f) 69 | r2 <- c(r, r) 70 | expect_error(as_wbw_raster(r2)) 71 | -------------------------------------------------------------------------------- /tests/tinytest/test_utils_documentation.R: -------------------------------------------------------------------------------- 1 | # Test rd_wbw_link function 2 | expected <- paste0( 3 | "@references For more information, see ", 4 | "" 7 | ) 8 | expect_equal(wbw:::rd_wbw_link("slope"), expected) 9 | 10 | # Test with underscores 11 | expected <- paste0( 12 | "@references For more information, see ", 13 | "" 16 | ) 17 | expect_equal(wbw:::rd_wbw_link("breach_depressions"), expected) 18 | 19 | # Test rd_input_raster function 20 | expected <- paste0( 21 | "@param dem Raster object of class [WhiteboxRaster]. ", 22 | "See [wbw_read_raster()] for more details." 23 | ) 24 | expect_equal(wbw:::rd_input_raster("dem"), expected) 25 | 26 | # Test rd_example function 27 | expected <- paste( 28 | "@examples", 29 | 'f <- system.file("extdata/dem.tif", package = "wbw")', 30 | "wbw_read_raster(f) |>", 31 | " slope()", 32 | sep = "\n" 33 | ) 34 | expect_equal(wbw:::rd_example("slope"), expected) 35 | 36 | # Test with arguments 37 | expected <- paste( 38 | "@examples", 39 | 'f <- system.file("extdata/dem.tif", package = "wbw")', 40 | "wbw_read_raster(f) |>", 41 | " slope(units = 'degrees')", 42 | sep = "\n" 43 | ) 44 | expect_equal(wbw:::rd_example("slope", "units = 'degrees'"), expected) 45 | 46 | # Test error cases 47 | expect_error(wbw:::rd_wbw_link("")) 48 | expect_error(wbw:::rd_input_raster("")) 49 | expect_error(wbw:::rd_example("")) 50 | expect_error(wbw:::rd_wbw_link(NULL)) 51 | expect_error(wbw:::rd_input_raster(NULL)) 52 | expect_error(wbw:::rd_example(NULL)) 53 | -------------------------------------------------------------------------------- /man/wbw_download_sample_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/system.R 3 | \name{wbw_download_sample_data} 4 | \alias{wbw_download_sample_data} 5 | \title{Download Sample Data} 6 | \usage{ 7 | wbw_download_sample_data(data_set = NULL, path = NULL) 8 | } 9 | \arguments{ 10 | \item{data_set}{\code{character}, dataset name. See Details} 11 | 12 | \item{path}{\code{character}, path to where download sample datasets. If 13 | \code{NULL}, the currect working directory is used} 14 | } 15 | \description{ 16 | There are a number of available sample datasets that can be readily used 17 | to test Whitebox Workflows for Python. 18 | } 19 | \details{ 20 | Available datasets:\tabular{lll}{ 21 | \strong{Dataset Name} \tab \strong{Description} \tab \strong{Size} \cr 22 | Guelph_landsat \tab Landsat 5 sub-area (7 bands) \tab 10.9 MB \cr 23 | Grand_Junction \tab Small DEM in high-relief terrain \tab 5.8 MB \cr 24 | GTA_lidar \tab Airborne lidar point cloud (LAZ) \tab 54.3 MB \cr 25 | jay_brook \tab Airborne lidar point cloud (LAZ) \tab 76.3 MB \cr 26 | Jay_State_Forest \tab Lidar-derived DEM \tab 27.7 MB \cr 27 | Kitchener_lidar \tab Airborne lidar point cloud (LAZ) \tab 41.6 MB \cr 28 | London_air_photo \tab High-resolution RGB air photo \tab 87.3 MB \cr 29 | mill_brook \tab Airborne lidar point cloud (LAZ) \tab 49.9 MB \cr 30 | peterborough_drumlins \tab Lidar-derived DEM \tab 22.0 MB \cr 31 | Southern_Ontario_roads \tab Vector roads layer \tab 7.1 MB \cr 32 | StElisAk \tab Airborne lidar point cloud (LAZ) \tab 54.5 MB \cr 33 | } 34 | } 35 | \examples{ 36 | \dontrun{ 37 | # Download sample data 38 | wbw_download_sample_data(data_set = "Guelph_landsat", path = tempdir()) 39 | } 40 | } 41 | \keyword{system} 42 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | # workflow_dispatch: 5 | push: 6 | branches: [main, master] 7 | pull_request: 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | # Fail if error if not on PR, or if on PR and token is given 44 | fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} 45 | file: ./cobertura.xml 46 | plugin: noop 47 | disable_search: true 48 | token: ${{ secrets.CODECOV_TOKEN }} 49 | 50 | - name: Upload test results 51 | if: failure() 52 | uses: actions/upload-artifact@v4 53 | with: 54 | name: coverage-test-failures 55 | path: ${{ runner.temp }}/package -------------------------------------------------------------------------------- /man/wbw_high_pass_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_high_pass_filter} 4 | \alias{wbw_high_pass_filter} 5 | \title{High Pass Filter} 6 | \usage{ 7 | wbw_high_pass_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | This tool performs a high-pass filter on a raster image. High-pass filters 21 | can be used to emphasize the short-range variability in an image. 22 | The algorithm operates essentially by subtracting the value at the grid 23 | cell at the centre of the window from the average value in the surrounding 24 | neighbourhood (i.e. window.) 25 | } 26 | \details{ 27 | Neighbourhood size, or filter size, is specified in the x and y dimensions 28 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 29 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 30 | } 31 | \examples{ 32 | f <- system.file("extdata/dem.tif", package = "wbw") 33 | wbw_read_raster(f) |> 34 | wbw_high_pass_filter(filter_size_x = 3L, filter_size_y = 3L) 35 | } 36 | \references{ 37 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#high_pass_filter} 38 | } 39 | \seealso{ 40 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}}, \code{\link[=wbw_high_pass_median_filter]{wbw_high_pass_median_filter()}} 41 | } 42 | \keyword{image_processing} 43 | -------------------------------------------------------------------------------- /man/wbw_olympic_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_olympic_filter} 4 | \alias{wbw_olympic_filter} 5 | \title{Olympic Filter} 6 | \usage{ 7 | wbw_olympic_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | This filter is a modification of the \link{wbw_mean_filter}, whereby 21 | the highest and lowest values in the kernel are dropped, and the remaining 22 | values are averaged to replace the central pixel. The result is a 23 | low-pass smoothing filter that is more robust than the \link{wbw_mean_filter}, 24 | which is more strongly impacted by the presence of outlier values. 25 | It is named after a system of scoring Olympic events. 26 | } 27 | \details{ 28 | Neighbourhood size, or filter size, is specified in the x and y dimensions 29 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 30 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 31 | } 32 | \examples{ 33 | f <- system.file("extdata/dem.tif", package = "wbw") 34 | wbw_read_raster(f) |> 35 | wbw_olympic_filter(filter_size_x = 3L, filter_size_y = 3L) 36 | } 37 | \references{ 38 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#olympic_filter} 39 | } 40 | \seealso{ 41 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}} 42 | } 43 | \keyword{image_processing} 44 | -------------------------------------------------------------------------------- /R/math.R: -------------------------------------------------------------------------------- 1 | #' Random Sample 2 | #' @keywords math 3 | #' 4 | #' @description 5 | #' Creates a random sample of grid cells from a raster. Uses the input 6 | #' WhiteboxRaster to determine grid dimensions and georeference information for 7 | #' the output. 8 | #' 9 | #' The output grid will contain the specified number of non-zero grid cells, 10 | #' randomly distributed throughout the raster. Each sampled cell will have a 11 | #' unique value from 1 to num_samples, with background cells set to zero. 12 | #' 13 | #' @details 14 | #' This tool is useful for statistical analyses of raster data where a random 15 | #' sampling approach is needed. The sampling process only considers valid, 16 | #' non-NoData cells from the input raster. 17 | #' 18 | #' @eval rd_input_raster("x") 19 | #' @param num_samples \code{integer}, number of random samples. Must not exceed 20 | #' the total number of valid cells in the input raster (see [num_cells()]). 21 | #' 22 | #' @return [WhiteboxRaster] object 23 | #' 24 | #' @eval rd_wbw_link("random_sample") 25 | #' @eval rd_example("wbw_random_sample", c("num_samples = 100")) 26 | #' 27 | #' @export 28 | wbw_random_sample <- S7::new_generic( 29 | name = "wbw_random_sample", 30 | dispatch_args = "x", 31 | fun = function(x, num_samples = 1000L) { 32 | S7::S7_dispatch() 33 | } 34 | ) 35 | 36 | # !NB: 37 | # - set.seed() shouldn't work 38 | S7::method(wbw_random_sample, WhiteboxRaster) <- function( 39 | x, 40 | num_samples = 1000L 41 | ) { 42 | # Checks 43 | check_env(wbe) 44 | num_samples <- checkmate::asInteger( 45 | num_samples, 46 | lower = 1L, 47 | upper = x@source$num_cells(), 48 | len = 1L 49 | ) 50 | out <- wbe$random_sample(base_raster = x@source, num_samples = num_samples) 51 | # Return Raster 52 | WhiteboxRaster( 53 | name = x@name, 54 | source = out 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /R/plot.R: -------------------------------------------------------------------------------- 1 | #' @importFrom grDevices xy.coords 2 | NULL 3 | 4 | #' @exportS3Method xy.coords wbw::WhiteboxRaster 5 | NULL 6 | 7 | #' Convert WhiteboxRaster to x-y coordinates 8 | #' 9 | #' This is an internal method used by the plotting system. 10 | #' It returns NULL coordinates to prevent default plotting behavior. 11 | #' 12 | #' @param x WhiteboxRaster object 13 | #' @param ... additional arguments (not used) 14 | #' @return A list with NULL x and y components 15 | #' @keywords internal 16 | #' @export 17 | `xy.coords.wbw::WhiteboxRaster` <- function(x, ...) { 18 | list(x = NULL, y = NULL) 19 | } 20 | 21 | #' Plot WhiteboxRaster 22 | #' @keywords methods 23 | #' 24 | #' @param x WhiteboxRaster object 25 | #' @param col Colors to use for plotting 26 | #' @param add Logical, whether to add to existing plot 27 | #' @param ... Additional arguments passed to plot 28 | #' @export 29 | `plot.wbw::WhiteboxRaster` <- function( 30 | x, 31 | col = grDevices::terrain.colors(100), 32 | add = FALSE, 33 | ... 34 | ) { 35 | # Get raster data as matrix 36 | z <- as_matrix(x, raw = FALSE) 37 | 38 | # Get extent 39 | conf <- x@source$configs 40 | ext <- c(conf$west, conf$east, conf$south, conf$north) 41 | 42 | # Create plot 43 | if (!add) { 44 | graphics::plot.new() 45 | graphics::plot.window(ext[1:2], ext[3:4], asp = 1) 46 | } 47 | 48 | # Plot raster 49 | graphics::rasterImage( 50 | grDevices::as.raster( 51 | matrix( 52 | col[cut(z, breaks = length(col))], 53 | nrow = nrow(z), 54 | ncol = ncol(z) 55 | ) 56 | ), 57 | ext[1], 58 | ext[3], 59 | ext[2], 60 | ext[4] 61 | ) 62 | 63 | # Add axes and box if not adding to existing plot 64 | if (!add) { 65 | graphics::box() 66 | graphics::axis(1) 67 | graphics::axis(2) 68 | } 69 | 70 | invisible(x) 71 | } 72 | -------------------------------------------------------------------------------- /man/wbw_adaptive_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_adaptive_filter} 4 | \alias{wbw_adaptive_filter} 5 | \title{Adaptive Filter} 6 | \usage{ 7 | wbw_adaptive_filter(x, filter_size_x = 11L, filter_size_y = 11L, threshold = 2) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | 16 | \item{threshold}{\code{double}} 17 | } 18 | \value{ 19 | \link{WhiteboxRaster} object containing filtered values 20 | } 21 | \description{ 22 | Applies an adaptive filter to reduce random noise (shot noise) in a raster 23 | image. The filter modifies pixel values only where they differ substantially 24 | from their neighbors. 25 | } 26 | \details{ 27 | The algorithm calculates the average value in a moving window centered on 28 | each grid cell. If the absolute difference between the window mean and the 29 | center cell value exceeds the user-defined threshold, the output cell is 30 | assigned the mean value. Otherwise, it retains its original value. 31 | 32 | Neighbourhood size, or filter size, is specified in the x and y dimensions 33 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 34 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 35 | } 36 | \examples{ 37 | f <- system.file("extdata/dem.tif", package = "wbw") 38 | wbw_read_raster(f) |> 39 | wbw_adaptive_filter(filter_size_x = 3L, filter_size_y = 3L) 40 | } 41 | \references{ 42 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#adaptive_filter} 43 | } 44 | \keyword{image_processing} 45 | -------------------------------------------------------------------------------- /man/wbw_standard_deviation_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_standard_deviation_filter} 4 | \alias{wbw_standard_deviation_filter} 5 | \title{Standard Deviation Filter} 6 | \usage{ 7 | wbw_standard_deviation_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | A standard deviation filter assigns to each cell in the output grid the 21 | standard deviation, a measure of dispersion, of the values contained within 22 | a moving window centred on each grid cell. 23 | } 24 | \details{ 25 | Neighbourhood size, or filter size, is specified in the x and y dimensions 26 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 27 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 28 | } 29 | \examples{ 30 | f <- system.file("extdata/dem.tif", package = "wbw") 31 | wbw_read_raster(f) |> 32 | wbw_standard_deviation_filter(filter_size_x = 3L, filter_size_y = 3L) 33 | } 34 | \references{ 35 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#standard_deviation_filter} 36 | } 37 | \seealso{ 38 | \code{\link[=wbw_minimum_filter]{wbw_minimum_filter()}}, \code{\link[=wbw_maximum_filter]{wbw_maximum_filter()}}, 39 | \code{\link[=wbw_range_filter]{wbw_range_filter()}}, \code{\link[=wbw_majority_filter]{wbw_majority_filter()}}, \code{\link[=wbw_total_filter]{wbw_total_filter()}} 40 | } 41 | \keyword{image_processing} 42 | -------------------------------------------------------------------------------- /man/wbw_slope.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geomorphometry.R 3 | \name{wbw_slope} 4 | \alias{wbw_slope} 5 | \title{Slope} 6 | \usage{ 7 | wbw_slope(dem, units = "degrees", z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{units}{\code{character}, 13 | units of slope: "radians", "degrees", or "percent"} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object containing slope values 22 | } 23 | \description{ 24 | Calculates slope gradient (i.e., slope steepness in degrees, radians, or 25 | percent) for each grid cell in an input digital elevation model (DEM). 26 | } 27 | \details{ 28 | The tool uses Horn's (1981) 3rd-order finite difference method to estimate 29 | slope. Given the following clock-type grid cell numbering scheme (Gallant and 30 | Wilson, 2000). 31 | } 32 | \examples{ 33 | f <- system.file("extdata/dem.tif", package = "wbw") 34 | wbw_read_raster(f) |> 35 | wbw_slope(units = "radians") 36 | } 37 | \references{ 38 | Gallant, J. C., and J. P. Wilson, 2000, Primary topographic attributes, 39 | in Terrain Analysis: Principles and Applications, edited by J. P. Wilson 40 | and J. C. Gallant pp. 51-86, John Wiley, Hoboken, N.J. 41 | 42 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#slope} 43 | } 44 | \seealso{ 45 | \code{\link[=wbw_to_degrees]{wbw_to_degrees()}}, \code{\link[=wbw_to_radians]{wbw_to_radians()}}, \code{\link[=wbw_aspect]{wbw_aspect()}} 46 | } 47 | \keyword{geomorphometry} 48 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # and https://github.com/nwtgck/actions-netlify 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown.yaml 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - uses: r-lib/actions/setup-tinytex@v2 41 | 42 | - name: Build site 43 | run: pkgdown::build_site(new_process = FALSE, install = FALSE) 44 | shell: Rscript {0} 45 | 46 | - name: Deploy to Netlify 47 | uses: nwtgck/actions-netlify@v3.0 48 | with: 49 | publish-dir: 'docs' 50 | production-branch: master 51 | production-deploy: true 52 | github-token: ${{ secrets.GITHUB_TOKEN }} 53 | deploy-message: "Deploy from GitHub Actions" 54 | enable-pull-request-comment: false 55 | enable-commit-comment: true 56 | overwrites-pull-request-comment: true 57 | env: 58 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 59 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 60 | timeout-minutes: 5 61 | -------------------------------------------------------------------------------- /R/conversion.R: -------------------------------------------------------------------------------- 1 | #' Convert to radians 2 | #' @keywords conversions 3 | #' 4 | #' Converts a [WhiteboxRaster] from degrees to radians 5 | #' 6 | #' @eval rd_input_raster("x") 7 | #' 8 | #' @return [WhiteboxRaster] object in radians 9 | #' 10 | #' @seealso [wbw_to_degrees()], [wbw_slope()] 11 | #' 12 | #' @examples 13 | #' f <- system.file("extdata/dem.tif", package = "wbw") 14 | #' wbw_read_raster(f) |> 15 | #' wbw_slope(units = "d") |> 16 | #' wbw_to_radians() 17 | #' @export 18 | wbw_to_radians <- S7::new_generic( 19 | name = "wbw_to_radians", 20 | dispatch_args = "x", 21 | fun = function(x) { 22 | S7::S7_dispatch() 23 | } 24 | ) 25 | 26 | S7::method(wbw_to_radians, WhiteboxRaster) <- function(x) { 27 | # Checks 28 | check_env(wbe) 29 | # Return Raster 30 | WhiteboxRaster( 31 | name = if (grepl("\\(degrees\\)", x@name)) { 32 | sub("\\(degrees\\)", "(radians)", x@name) 33 | } else { 34 | x@name 35 | }, 36 | source = x@source$to_radians() 37 | ) 38 | } 39 | 40 | #' Convert to degrees 41 | #' @keywords conversions 42 | #' 43 | #' Converts a [WhiteboxRaster] from radians to degrees 44 | #' 45 | #' @eval rd_input_raster("x") 46 | #' 47 | #' @return [WhiteboxRaster] object in degrees 48 | #' 49 | #' @seealso [wbw_to_radians()], [wbw_slope()] 50 | #' 51 | #' @examples 52 | #' f <- system.file("extdata/dem.tif", package = "wbw") 53 | #' wbw_read_raster(f) |> 54 | #' wbw_slope(units = "r") |> 55 | #' wbw_to_degrees() 56 | #' @export 57 | wbw_to_degrees <- S7::new_generic( 58 | name = "wbw_to_degrees", 59 | dispatch_args = "x", 60 | fun = function(x) { 61 | S7::S7_dispatch() 62 | } 63 | ) 64 | 65 | S7::method(wbw_to_degrees, WhiteboxRaster) <- function(x) { 66 | # Checks 67 | check_env(wbe) 68 | # Return Raster 69 | WhiteboxRaster( 70 | name = if (grepl("\\(radians\\)", x@name)) { 71 | sub("\\(radians\\)", "(degrees)", x@name) 72 | } else { 73 | x@name 74 | }, 75 | source = x@source$to_degrees() 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /man/wbw_mean_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_mean_filter} 4 | \alias{wbw_mean_filter} 5 | \title{Mean Filter} 6 | \usage{ 7 | wbw_mean_filter(x, filter_size_x = 11L, filter_size_y = 11L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | Applies a mean filter (low-pass filter) to smooth an image by emphasizing 21 | longer-range variability and reducing noise. 22 | } 23 | \details{ 24 | Uses an efficient integral image approach (Crow, 1984) that is independent 25 | of filter size. While commonly used, mean filters can be more aggressive in 26 | their smoothing compared to edge-preserving alternatives like the 27 | \link{wbw_bilateral_filter} or \link{wbw_gaussian_filter}. 28 | 29 | Neighbourhood size, or filter size, is specified in the x and y dimensions 30 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 31 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 32 | } 33 | \examples{ 34 | f <- system.file("extdata/dem.tif", package = "wbw") 35 | wbw_read_raster(f) |> 36 | wbw_mean_filter(filter_size_x = 3L, filter_size_y = 3L) 37 | } 38 | \references{ 39 | Crow, F. C. (1984, January). Summed-area tables for texture mapping. 40 | In ACM SIGGRAPH computer graphics (Vol. 18, No. 3, pp. 207-212). ACM. 41 | 42 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#mean_filter} 43 | } 44 | \seealso{ 45 | \code{\link[=wbw_bilateral_filter]{wbw_bilateral_filter()}}, \code{\link[=wbw_gaussian_filter]{wbw_gaussian_filter()}} 46 | } 47 | \keyword{image_processing} 48 | -------------------------------------------------------------------------------- /man/wbw_high_pass_median_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_high_pass_median_filter} 4 | \alias{wbw_high_pass_median_filter} 5 | \title{High Pass Median Filter} 6 | \usage{ 7 | wbw_high_pass_median_filter( 8 | x, 9 | filter_size_x = 11L, 10 | filter_size_y = 11L, 11 | sig_digits = 2L 12 | ) 13 | } 14 | \arguments{ 15 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 16 | 17 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 18 | 19 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 20 | 21 | \item{sig_digits}{\code{integer}, default 2. Required for rounding of 22 | floating points inputs.} 23 | } 24 | \value{ 25 | \link{WhiteboxRaster} object containing filtered values 26 | } 27 | \description{ 28 | This tool performs a high-pass median filter on a raster image. 29 | High-pass filters can be used to emphasize the short-range variability in 30 | an image. The algorithm operates essentially by subtracting the value at 31 | the grid cell at the centre of the window from the median value in the 32 | surrounding neighbourhood (i.e. window.) 33 | } 34 | \details{ 35 | Neighbourhood size, or filter size, is specified in the x and y dimensions 36 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 37 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 38 | } 39 | \examples{ 40 | f <- system.file("extdata/dem.tif", package = "wbw") 41 | wbw_read_raster(f) |> 42 | wbw_high_pass_median_filter(filter_size_x = 3L, filter_size_y = 3L) 43 | } 44 | \references{ 45 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#high_pass_median_filter} 46 | } 47 | \seealso{ 48 | \code{\link[=wbw_median_filter]{wbw_median_filter()}}, \code{\link[=wbw_high_pass_filter]{wbw_high_pass_filter()}} 49 | } 50 | \keyword{image_processing} 51 | -------------------------------------------------------------------------------- /man/summarize.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/summary.R 3 | \docType{methods} 4 | \name{summary.wbw::WhiteboxRaster} 5 | \alias{summary.wbw::WhiteboxRaster} 6 | \alias{max.wbw::WhiteboxRaster} 7 | \alias{mean.wbw::WhiteboxRaster} 8 | \alias{median.wbw::WhiteboxRaster} 9 | \alias{min.wbw::WhiteboxRaster} 10 | \alias{stdev} 11 | \alias{variance} 12 | \title{Summary Statistics for WhiteboxRaster} 13 | \usage{ 14 | \method{summary}{`wbw::WhiteboxRaster`}(object, ...) 15 | 16 | \method{max}{`wbw::WhiteboxRaster`}(object, ...) 17 | 18 | \method{mean}{`wbw::WhiteboxRaster`}(x, ...) 19 | 20 | \method{median}{`wbw::WhiteboxRaster`}(x, na.rm = FALSE, ...) 21 | 22 | \method{min}{`wbw::WhiteboxRaster`}(object, ...) 23 | 24 | stdev(x) 25 | 26 | variance(x) 27 | } 28 | \arguments{ 29 | \item{object}{WhiteboxRaster object} 30 | 31 | \item{...}{additional arguments (not used)} 32 | 33 | \item{x}{WhiteboxRaster object} 34 | 35 | \item{na.rm}{logical indicating whether NA values should 36 | be stripped (not used)} 37 | } 38 | \value{ 39 | numeric value 40 | } 41 | \description{ 42 | Computes summary statistics for cells in a \link{WhiteboxRaster} object. 43 | } 44 | \examples{ 45 | f <- system.file("extdata/dem.tif", package = "wbw") 46 | wbw_read_raster(f) |> 47 | summary() 48 | f <- system.file("extdata/dem.tif", package = "wbw") 49 | wbw_read_raster(f) |> 50 | max() 51 | f <- system.file("extdata/dem.tif", package = "wbw") 52 | wbw_read_raster(f) |> 53 | mean() 54 | f <- system.file("extdata/dem.tif", package = "wbw") 55 | wbw_read_raster(f) |> 56 | median() 57 | f <- system.file("extdata/dem.tif", package = "wbw") 58 | wbw_read_raster(f) |> 59 | min() 60 | f <- system.file("extdata/dem.tif", package = "wbw") 61 | wbw_read_raster(f) |> 62 | stdev() 63 | f <- system.file("extdata/dem.tif", package = "wbw") 64 | wbw_read_raster(f) |> 65 | variance() 66 | } 67 | \references{ 68 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#raster_summary_stats} 69 | } 70 | \keyword{stats} 71 | -------------------------------------------------------------------------------- /.github/template.R: -------------------------------------------------------------------------------- 1 | #' Template {wbw} roxygen2 documentation 2 | #' @rdname template 3 | #' @keywords internal 4 | #' 5 | #' @description 6 | #' This is a template for generating documentation for new Whitebox Workflows 7 | #' for R functions. 8 | #' 9 | #' First, every function description should start with the 10 | #' function name, identical to the original Whitebox Workflows for Python. 11 | #' 12 | #' Then it should be followed by @rdname and @keywords tags. While the @rdname 13 | #' tag could be omitted in cases where you don't want to merge docs of several 14 | #' functions together, the @keywords tag is essential and should follow the 15 | #' grouping by topic, existing for the Whitebox Workflows for Python/QGIS. 16 | #' 17 | #' After @keywords and @rdname, it is essential to provide @description, which 18 | #' should align with the Whitebox Workflows documentation. 19 | #' 20 | #' There are several documentation helpers that can speed up 21 | #' documentation writing. Use them wisely: 22 | #' - @eval rd_input_raster("x") will generate a @param tag describing the input 23 | #' WhiteboxRaster 24 | #' - @eval rd_wbw_link("tool_name") will create a @reference tag with a web 25 | #' link to the original documentation website 26 | #' - @eval rd_example("wbw_ruggedness_index") will generate 27 | #' an @examples tag with a basic workflow inside the \dontrun{} tag. 28 | #' 29 | #' @eval rd_input_raster("dem") 30 | #' 31 | #' @return [WhiteboxRaster] object 32 | #' 33 | #' @eval rd_wbw_link("ruggedness_index") 34 | #' @eval rd_example("wbw_ruggedness_index") 35 | wbw_template <- 36 | S7::new_generic( 37 | name = "wbw_template", 38 | dispatch_args = "x", 39 | fun = function(x, another_arg = NULL) { 40 | S7::S7_dispatch() 41 | } 42 | ) 43 | 44 | S7::method(wbw_template, WhiteboxRaster) <- 45 | function(x, another_arg = NULL) { 46 | # Checks 47 | check_env(wbe) 48 | # Estimate slope 49 | out <- wbe$ruggedness_index(input = x@source) 50 | # Return Raster 51 | WhiteboxRaster( 52 | name = paste0("TRI"), 53 | source = out 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /tests/tinytest/test_io.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Helper function 4 | create_temp_file <- function(ext) { 5 | tmp <- tempfile(fileext = ext) 6 | file.create(tmp) 7 | return(tmp) 8 | } 9 | 10 | # Test wbw_read_raster 11 | expect_inherits( 12 | wbw_read_raster(raster_path), 13 | c("wbw::WhiteboxRaster", "S7_object") 14 | ) 15 | expect_error(wbw_read_raster("nonexistent.tif")) 16 | 17 | tmp_txt <- create_temp_file(".txt") 18 | on.exit(unlink(tmp_txt)) 19 | expect_error(wbw_read_raster(tmp_txt)) 20 | 21 | tmp_shp <- create_temp_file(".shp") 22 | on.exit(unlink(tmp_shp), add = TRUE) 23 | expect_error(wbw_read_vector("nonexistent.shp")) 24 | expect_error(wbw_read_vector(tmp_txt)) 25 | 26 | # Test wbw_write_raster 27 | expect_error(wbw_write_raster(mtcars, file_name = tempfile(fileext = ".tif"))) 28 | 29 | # Test geotiff compression 30 | tmp_tif_c <- tempfile(fileext = ".tif") 31 | tmp_tiff_c <- tempfile(fileext = ".tiff") 32 | tmp_tif <- tempfile(fileext = ".tif") 33 | tmp_tiff <- tempfile(fileext = ".tiff") 34 | on.exit( 35 | { 36 | unlink(tmp_tif_c) 37 | unlink(tmp_tiff_c) 38 | unlink(tmp_tif) 39 | unlink(tmp_tiff) 40 | }, 41 | add = TRUE 42 | ) 43 | 44 | wbw_write_raster(x, file_name = tmp_tif_c, compress = TRUE) 45 | wbw_write_raster(x, file_name = tmp_tiff_c, compress = TRUE) 46 | wbw_write_raster(x, file_name = tmp_tif, compress = FALSE) 47 | wbw_write_raster(x, file_name = tmp_tiff, compress = FALSE) 48 | 49 | expect_true(file.size(tmp_tif_c) < file.size(tmp_tif)) 50 | expect_true(file.size(tmp_tiff_c) < file.size(tmp_tiff)) 51 | 52 | # Test different raster formats 53 | formats <- c( 54 | ".tif", 55 | ".tiff", 56 | ".sgrd", 57 | ".sdat", 58 | ".rst", 59 | ".rdc", 60 | ".bil", 61 | ".flt", 62 | ".grd" 63 | ) 64 | temp_files <- vapply(formats, create_temp_file, character(1)) 65 | on.exit(unlink(temp_files), add = TRUE) 66 | 67 | for (file in temp_files) { 68 | wbw_write_raster(x, file_name = file) 69 | expect_true(file.exists(file)) 70 | expect_inherits(wbw_read_raster(file), c("wbw::WhiteboxRaster", "S7_object")) 71 | } 72 | -------------------------------------------------------------------------------- /man/wbw_aspect.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geomorphometry.R 3 | \name{wbw_aspect} 4 | \alias{wbw_aspect} 5 | \title{Aspect} 6 | \usage{ 7 | wbw_aspect(dem, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{z_factor}{\code{double}, Z conversion factor is only important 13 | when the vertical and horizontal units are not the same in the DEM. 14 | When this is the case, the algorithm will multiply each elevation in the 15 | DEM by the Z conversion factor} 16 | } 17 | \value{ 18 | \link{WhiteboxRaster} object containing aspect in degrees 19 | } 20 | \description{ 21 | Calculates slope aspect (i.e., slope orientation in degrees clockwise from 22 | north) for each grid cell in an input DEM. 23 | } 24 | \details{ 25 | For DEMs in projected coordinate systems, the tool uses the 3rd-order 26 | bivariate Taylor polynomial method described by Florinsky (2016). Based on a 27 | polynomial fit of elevations within the 5x5 neighborhood surrounding each 28 | cell, this method is more robust against outlier elevations (noise) than 29 | other methods. 30 | 31 | For DEMs in geographic coordinate systems (i.e., angular units), the tool 32 | uses the 3x3 polynomial fitting method for equal angle grids also described 33 | by Florinsky (2016). 34 | } 35 | \examples{ 36 | f <- system.file("extdata/dem.tif", package = "wbw") 37 | wbw_read_raster(f) |> 38 | wbw_aspect() 39 | } 40 | \references{ 41 | Gallant, J. C., and J. P. Wilson, 2000, Primary topographic attributes, 42 | in Terrain Analysis: Principles and Applications, edited by J. P. Wilson 43 | and J. C. Gallant pp. 51-86, John Wiley, Hoboken, N.J. 44 | 45 | Florinsky, I. (2016). Digital terrain analysis in soil science and 46 | geology. Academic Press. 47 | 48 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#aspect} 49 | } 50 | \seealso{ 51 | \code{\link[=wbw_to_degrees]{wbw_to_degrees()}}, \code{\link[=wbw_to_radians]{wbw_to_radians()}}, \code{\link[=wbw_slope]{wbw_slope()}} 52 | } 53 | \keyword{geomorphometry} 54 | -------------------------------------------------------------------------------- /tests/tinytest/test_primitives.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test as_matrix conversion 4 | m <- as_matrix(x) 5 | expect_true(is.matrix(m)) 6 | expect_equal(dim(m), c(726, 800)) 7 | 8 | # Test as_vector conversion 9 | v <- as_vector(x) 10 | expect_true(is.vector(v)) 11 | expect_equal(length(v), num_cells(x)) 12 | 13 | # Test summary stats for matrix 14 | expect_equal(max(x), max(m, na.rm = TRUE)) 15 | expect_equal(min(x), min(m, na.rm = TRUE)) 16 | expect_equal(mean(x), mean(m, na.rm = TRUE)) 17 | expect_equal(round(median(x), 4), round(median(m, na.rm = TRUE), 4)) 18 | expect_equal(round(wbw::stdev(x), 4), round(sd(m, na.rm = TRUE), 4)) 19 | 20 | # Test summary stats for vector 21 | expect_equal(max(x), max(v, na.rm = TRUE)) 22 | expect_equal(min(x), min(v, na.rm = TRUE)) 23 | expect_equal(mean(x), mean(v, na.rm = TRUE)) 24 | expect_equal(round(median(x), 4), round(median(v, na.rm = TRUE), 4)) 25 | expect_equal(round(wbw::stdev(x), 4), round(sd(v, na.rm = TRUE), 4)) 26 | expect_equal(round(variance(x), 1), round(var(v, na.rm = TRUE), 1)) 27 | 28 | # Test summary function output 29 | s <- capture.output(summary(x)) 30 | expect_true(any(grepl("minimum", s))) 31 | expect_true(any(grepl("maximum", s))) 32 | expect_true(any(grepl("average", s))) 33 | expect_true(any(grepl("standard deviation", s))) 34 | 35 | # Test NoData handling 36 | exit_if_not(requireNamespace("terra", quietly = TRUE)) 37 | r <- wbw_read_raster(f) 38 | 39 | v <- as_vector(r) 40 | v_raw <- as_vector(r, raw = TRUE) 41 | m <- as_matrix(r) 42 | m_raw <- as_matrix(r, raw = TRUE) 43 | 44 | # Check dimensions 45 | expect_equal(dim(m), dim(m_raw)) 46 | expect_equal(length(m), length(v_raw)) 47 | 48 | # Check NA values 49 | expect_true(sum(is.na(m)) != 0) 50 | expect_true(sum(is.na(m_raw)) == 0) 51 | expect_true(sum(is.na(v)) != 0) 52 | expect_true(sum(is.na(v_raw)) == 0) 53 | 54 | # Test NA handling in summary stats 55 | r <- wbw_read_raster(f) 56 | expect_true(is.numeric(max(r))) 57 | expect_true(is.numeric(min(r))) 58 | expect_true(is.numeric(mean(r))) 59 | expect_true(is.numeric(wbw::stdev(r))) 60 | expect_true(is.numeric(median(r))) 61 | expect_true(is.numeric(variance(r))) 62 | -------------------------------------------------------------------------------- /tests/tinytest/test_geomorphometry.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test slope failures 4 | expect_error(wbw_slope(dem = mtcars)) 5 | expect_error(wbw_slope(x, units = 1)) 6 | expect_error(wbw_slope(x, units = "dg")) 7 | expect_error(wbw_slope(x, z_factor = 2L)) 8 | expect_error(wbw_slope(NULL)) 9 | 10 | # Test aspect failures 11 | expect_error(wbw_aspect(dem = mtcars)) 12 | expect_error(wbw_aspect(x, z_factor = 2L)) 13 | expect_error(wbw_aspect(NULL)) 14 | 15 | # Test ruggedness index failures 16 | expect_error(wbw_ruggedness_index(dem = mtcars)) 17 | expect_error(wbw_ruggedness_index(1:10)) 18 | expect_error(wbw_ruggedness_index(NULL)) 19 | 20 | # Test fill missing data failures 21 | expect_error(wbw_fill_missing_data(x = mtcars)) 22 | expect_error(wbw_fill_missing_data(x, filter_size = 2.5)) 23 | expect_error(wbw_fill_missing_data(x, weight = "2.5")) 24 | expect_error(wbw_fill_missing_data(x, exclude_edge_nodata = "YES")) 25 | expect_error(wbw_fill_missing_data(NULL)) 26 | 27 | # Test successful returns 28 | expect_inherits(wbw_aspect(x), c("wbw::WhiteboxRaster", "S7_object")) 29 | expect_inherits(wbw_slope(x), c("wbw::WhiteboxRaster", "S7_object")) 30 | expect_inherits(wbw_ruggedness_index(x), c("wbw::WhiteboxRaster", "S7_object")) 31 | expect_inherits(wbw_fill_missing_data(x), c("wbw::WhiteboxRaster", "S7_object")) 32 | expect_inherits( 33 | wbw_multidirectional_hillshade(x), 34 | c("wbw::WhiteboxRaster", "S7_object") 35 | ) 36 | expect_inherits(wbw_hillshade(x), c("wbw::WhiteboxRaster", "S7_object")) 37 | 38 | # Test sample data download and fill missing data 39 | temp_dir <- tempdir() 40 | test_path <- wbw_download_sample_data( 41 | data_set = "Grand_Junction", 42 | path = temp_dir 43 | ) 44 | 45 | expect_true(dir.exists(test_path)) 46 | dem_path <- file.path(test_path, "DEM.tif") 47 | expect_true(file.exists(dem_path)) 48 | 49 | dem <- wbw_read_raster(dem_path) 50 | expect_inherits(dem, c("wbw::WhiteboxRaster", "S7_object")) 51 | 52 | dem_filled <- wbw_fill_missing_data(dem) 53 | expect_inherits(dem_filled, c("wbw::WhiteboxRaster", "S7_object")) 54 | 55 | # Check if fill missing data worked 56 | m <- as_matrix(dem) 57 | m_filled <- as_matrix(dem_filled) 58 | expect_true(sum(is.na(m_filled)) <= sum(is.na(m))) 59 | 60 | # Clean up 61 | unlink(file.path(temp_dir, "Grand_Junction"), recursive = TRUE) 62 | -------------------------------------------------------------------------------- /man/wbw_conservative_smoothing_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_conservative_smoothing_filter} 4 | \alias{wbw_conservative_smoothing_filter} 5 | \title{Conservative Smoothing Filter} 6 | \usage{ 7 | wbw_conservative_smoothing_filter(x, filter_size_x = 3L, filter_size_y = 3L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | } 16 | \value{ 17 | \link{WhiteboxRaster} object containing filtered values 18 | } 19 | \description{ 20 | This tool performs a conservative smoothing filter on a raster image. 21 | A conservative smoothing filter can be used to remove short-range 22 | variability in an image, effectively acting to smooth the image. 23 | It is particularly useful for eliminating local spikes and reducing the 24 | noise in an image. 25 | } 26 | \details{ 27 | The algorithm operates by calculating the minimum and maximum neighbouring 28 | values surrounding a grid cell. If the cell at the centre of the 29 | kernel is greater than the calculated maximum value, it is replaced 30 | with the maximum value in the output image. Similarly, if the cell 31 | value at the kernel centre is less than the neighbouring minimum value, 32 | the corresponding grid cell in the output image is replaced with the 33 | minimum value. 34 | 35 | Neighbourhood size, or filter size, is specified in the x and y dimensions 36 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 37 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 38 | } 39 | \examples{ 40 | f <- system.file("extdata/dem.tif", package = "wbw") 41 | wbw_read_raster(f) |> 42 | wbw_conservative_smoothing_filter(filter_size_x = 3L, filter_size_y = 3L) 43 | } 44 | \references{ 45 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#conservative_smoothing_filter} 46 | } 47 | \seealso{ 48 | \code{\link[=wbw_bilateral_filter]{wbw_bilateral_filter()}}, \code{\link[=wbw_gaussian_filter]{wbw_gaussian_filter()}}, 49 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}} 50 | } 51 | \keyword{image_processing} 52 | -------------------------------------------------------------------------------- /man/wbw_bilateral_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_bilateral_filter} 4 | \alias{wbw_bilateral_filter} 5 | \title{Bilateral Filter} 6 | \usage{ 7 | wbw_bilateral_filter(x, sigma_dist = 0.75, sigma_int = 1) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{sigma_dist}{\code{double}, standard deviation distance parameter in 13 | \strong{pixels}.} 14 | 15 | \item{sigma_int}{\code{double}, standard deviation intensity parameter, 16 | in the same units as z-units of input raster \code{x} (usually, meters).} 17 | } 18 | \value{ 19 | \link{WhiteboxRaster} object containing filtered values 20 | } 21 | \description{ 22 | Applies an edge-preserving smoothing filter that reduces noise while 23 | preserving important edges in the image. 24 | } 25 | \details{ 26 | Bilateral filtering is a non-linear technique introduced by Tomasi and 27 | Manduchi (1998). The filter combines spatial and intensity domains to 28 | preserve edges while smoothing. Unlike the Gaussian filter, the bilateral 29 | filter weights pixels based on both their spatial distance and intensity 30 | similarity to the center pixel. 31 | 32 | The size of the filter is determined by setting the standard deviation 33 | distance parameter (\code{sigma_dist}); the larger the standard deviation the 34 | larger the resulting filter kernel. 35 | The standard deviation can be any number in the range 36 | 0.5-20 and is specified in the unit of pixels. The standard deviation 37 | intensity parameter (\code{sigma_int}), specified in the same units as the 38 | z-values, determines the intensity domain contribution to kernel weightings. 39 | } 40 | \examples{ 41 | f <- system.file("extdata/dem.tif", package = "wbw") 42 | wbw_read_raster(f) |> 43 | wbw_bilateral_filter(sigma_dist = 1.5, sigma_int = 1.1) 44 | } 45 | \references{ 46 | Tomasi, C., & Manduchi, R. (1998, January). Bilateral filtering for gray 47 | and color images. In null (p. 839). IEEE. 48 | 49 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#bilateral_filter} 50 | } 51 | \seealso{ 52 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}}, \code{\link[=wbw_gaussian_filter]{wbw_gaussian_filter()}} 53 | } 54 | \keyword{image_processing} 55 | -------------------------------------------------------------------------------- /man/wbw_fill_missing_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geomorphometry.R 3 | \name{wbw_fill_missing_data} 4 | \alias{wbw_fill_missing_data} 5 | \title{Fill Missing Data} 6 | \usage{ 7 | wbw_fill_missing_data( 8 | x, 9 | filter_size = 11L, 10 | weight = 2, 11 | exclude_edge_nodata = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 16 | 17 | \item{filter_size}{\code{integer} in \strong{grid cells} is used to determine 18 | how far the algorithm will search for valid, non-NoData values. Therefore, 19 | setting a larger filter size allows for the filling of larger gaps in 20 | the input raster.} 21 | 22 | \item{weight}{\code{double}, the IDW weight.} 23 | 24 | \item{exclude_edge_nodata}{\code{boolean}, default \code{FALSE}. It can be 25 | used to exclude NoData values that are connected to the edges of the raster. 26 | It is usually the case that irregularly shaped DEMs have large regions 27 | of NoData values along the containing raster edges. This flag can be used 28 | to exclude these regions from the gap-filling operation, leaving only 29 | interior gaps for filling.} 30 | } 31 | \value{ 32 | \link{WhiteboxRaster} object 33 | } 34 | \description{ 35 | This tool can be used to fill in small gaps in a raster or digital elevation 36 | model (DEM). The gaps, or holes, must have recognized NoData values. 37 | If gaps do not currently have this characteristic, use the 38 | \code{set_nodata_value} tool and ensure that the data are stored using 39 | a raster format that supports NoData values. 40 | All valid, non-NoData values in the input raster will be assigned the same 41 | value in the output image. 42 | } 43 | \details{ 44 | The algorithm uses an inverse-distance weighted (IDW) scheme based on the 45 | valid values on the edge of NoData gaps to estimate gap values. 46 | The user must specify the filter size (\code{filter_size}), which 47 | determines the size of gap that is filled, and the IDW weight 48 | (\code{weight}). 49 | } 50 | \examples{ 51 | f <- system.file("extdata/dem.tif", package = "wbw") 52 | wbw_read_raster(f) |> 53 | wbw_fill_missing_data() 54 | } 55 | \references{ 56 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#fill_missing_data} 57 | } 58 | \keyword{geomorphometry} 59 | -------------------------------------------------------------------------------- /man/wbw_hillshade.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hillshade.R 3 | \name{wbw_hillshade} 4 | \alias{wbw_hillshade} 5 | \title{Hillshade} 6 | \usage{ 7 | wbw_hillshade(dem, azimuth = 315, altitude = 30, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{azimuth}{\code{double}, illumination source azimuth or 13 | sun direction (0 to 360 degrees)} 14 | 15 | \item{altitude}{\code{double}, the altitude of the illumination sources. 16 | i.e. the elevation of the sun above the horizon, measured as an angle from 17 | 0 to 90 degrees} 18 | 19 | \item{z_factor}{\code{double}, Z conversion factor is only important 20 | when the vertical and horizontal units are not the same in the DEM. 21 | When this is the case, the algorithm will multiply each elevation in the 22 | DEM by the Z conversion factor} 23 | } 24 | \value{ 25 | \link{WhiteboxRaster} object 26 | } 27 | \description{ 28 | This tool performs a hillshade operation (also called shaded relief) on an 29 | input digital elevation model (DEM). 30 | } 31 | \details{ 32 | The hillshade value (HS) of a DEM grid cell is calculate as: 33 | \deqn{HS = \frac{\tan(s)}{\sqrt{1 - \tan(s)^2}} \times 34 | [\frac{\sin(Alt)}{\tan(s)} - \cos(Alt) \times \sin(Az - a)]} 35 | where \eqn{s} and \eqn{a} are the local slope gradient and aspect 36 | (orientation) respectively and \eqn{Alt} and \eqn{Az} are the illumination 37 | source altitude and azimuth respectively. Slope and aspect are calculated 38 | using Horn's (1981) 3rd-order finate difference method. 39 | 40 | If the DEM is in the geographic coordinate system (latitude and longitude), 41 | the following equation is used: 42 | \deqn{zfactor = \frac{1.0}{111320.0 \times \cos(midlat)}} 43 | 44 | where \eqn{midlat} is the latitude of the centre of the raster, 45 | in radians. 46 | } 47 | \examples{ 48 | f <- system.file("extdata/dem.tif", package = "wbw") 49 | wbw_read_raster(f) |> 50 | wbw_hillshade() 51 | } 52 | \references{ 53 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#hillshade} 54 | 55 | Gallant, J. C., and J. P. Wilson, 2000, Primary topographic attributes, 56 | in Terrain Analysis: Principles and Applications, edited by J. P. Wilson 57 | and J. C. Gallant pp. 51-86, John Wiley, Hoboken, N.J. 58 | } 59 | \seealso{ 60 | \code{\link[=wbw_multidirectional_hillshade]{wbw_multidirectional_hillshade()}} 61 | } 62 | \keyword{geomorphometry} 63 | -------------------------------------------------------------------------------- /man/wbw_median_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_median_filter} 4 | \alias{wbw_median_filter} 5 | \title{Median Filter} 6 | \usage{ 7 | wbw_median_filter(x, filter_size_x = 11L, filter_size_y = 11L, sig_digits = 2L) 8 | } 9 | \arguments{ 10 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 13 | 14 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 15 | 16 | \item{sig_digits}{\code{integer}, default 2. Required for rounding of 17 | floating points inputs.} 18 | } 19 | \value{ 20 | \link{WhiteboxRaster} object containing filtered values 21 | } 22 | \description{ 23 | This tool performs a median filter on a raster image. Median filters, a 24 | type of low-pass filter, can be used to emphasize the longer-range 25 | variability in an image, effectively acting to smooth the image. This 26 | can be useful for reducing the noise in an image. 27 | } 28 | \details{ 29 | The algorithm operates by calculating the median value (middle value in 30 | a sorted list) in a moving window centred on each grid cell. Specifically, 31 | this tool uses the efficient running-median filtering algorithm of 32 | Huang et al. (1979). The median value is not influenced by 33 | anomolously high or low values in the distribution to the extent 34 | that the average is. As such, the median filter is far less sensitive 35 | to shot noise in an image than the mean filter. 36 | 37 | Neighbourhood size, or filter size, is specified in the x and y dimensions 38 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 39 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 40 | } 41 | \examples{ 42 | f <- system.file("extdata/dem.tif", package = "wbw") 43 | wbw_read_raster(f) |> 44 | wbw_median_filter(filter_size_x = 3L, filter_size_y = 3L) 45 | } 46 | \references{ 47 | Huang, T., Yang, G.J.T.G.Y. and Tang, G., 1979. A fast two-dimensional 48 | median filtering algorithm. IEEE Transactions on Acoustics, Speech, and 49 | Signal Processing, 27(1), pp.13-18. 50 | 51 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#median_filter} 52 | } 53 | \seealso{ 54 | \code{\link[=wbw_mean_filter]{wbw_mean_filter()}}, \code{\link[=wbw_high_pass_filter]{wbw_high_pass_filter()}}, 55 | \code{\link[=wbw_high_pass_median_filter]{wbw_high_pass_median_filter()}} 56 | } 57 | \keyword{image_processing} 58 | -------------------------------------------------------------------------------- /vignettes/articles/FAQ.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Frequently Asked Questions" 3 | output: 4 | rmarkdown::html_vignette: 5 | toc: false 6 | vignette: > 7 | %\VignetteIndexEntry{Frequently Asked Questions} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | %\VignetteDepends{terra} 11 | %\VignetteDepends{waldo} 12 | --- 13 | 14 | ```{r knitr_setup, include = FALSE} 15 | knitr::opts_chunk$set( 16 | collapse = TRUE, 17 | comment = "#>", 18 | warning = FALSE, 19 | message = FALSE, 20 | dpi = 150, 21 | retina = 2 22 | ) 23 | 24 | requireNamespace("terra", quietly = TRUE) 25 | requireNamespace("waldo", quietly = TRUE) 26 | ``` 27 | 28 | # How can I plot `WhiteboxRaster` objects? 29 | 30 | While there exists a built-in `plot` method for all `WhiteboxRaster` and `WhiteboxVector` objects, it is highly advisable to use [`{terra}`](https://rspatial.github.io/terra/index.html)'s plotting functionality by converting the `Whitebox...` objects to `SpatRaster` or `SpatVector` accordingly as follows: 31 | 32 | ```{r terra_plot, message=FALSE, warning=FALSE} 33 | library(wbw) 34 | library(terra) 35 | 36 | f <- system.file("extdata/dem.tif", package = "wbw") 37 | 38 | wbw_read_raster(f) |> 39 | wbw_multidirectional_hillshade() |> 40 | as_rast() |> 41 | plot(legend = FALSE) 42 | ``` 43 | 44 | # I've noticed that raster extent differs between WhiteboxRaster and terra/GDAL 45 | 46 | Yes, that's true! Since the original Python library Whitebox Workflows for Python is [closed-source](https://www.whiteboxgeo.com/manual/wbw-user-manual/book/introduction.html#how-does-wbw-compare-with-related-whitebox-products), the reason for this behavior is unknown. One possible explanation (i.e., my guess) is that GDAL typically defines the extent based on the outer edges of the corner pixels, while WbW may define the extent based on the centers of the corner pixels. 47 | 48 | ```{r extent_difference, warning = FALSE, message = FALSE} 49 | library(wbw) 50 | library(terra) 51 | 52 | f <- system.file("extdata/dem.tif", package = "wbw") 53 | 54 | wbw_raster <- wbw_read_raster(f) 55 | terra_raster <- rast(f) 56 | 57 | # Compare extents 58 | # Mind the difference in xmax (east) and ymin (south) 59 | waldo::compare( 60 | as_vector(wbw_ext(wbw_raster)), 61 | as.vector(ext(terra_raster)) 62 | ) 63 | ``` 64 | 65 | However, this issue is fixed when transitioning from `WhiteboxRaster` to `SpatRaster` via the `as_rast()` command: 66 | 67 | ```{r extent_difference2, warning = FALSE, message = FALSE} 68 | converted_raster <- as_rast(wbw_raster) 69 | waldo::compare( 70 | as.vector(ext(terra_raster)), 71 | as.vector(ext(converted_raster)) 72 | ) 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /man/wbw_plan_curvature.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/curvature.R 3 | \name{wbw_plan_curvature} 4 | \alias{wbw_plan_curvature} 5 | \title{Plan Curvature} 6 | \usage{ 7 | wbw_plan_curvature(dem, log_transform = FALSE, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{log_transform}{\code{logical}, default \code{FALSE}. Wheter 13 | log-transform the output raster or not. See details.} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object. 22 | } 23 | \description{ 24 | This tool calculates the plan curvature (i.e. contour curvature), or 25 | the rate of change in aspect along a contour line, from a digital 26 | elevation model (\eqn{dem}). Curvature is the second derivative of the 27 | topographic surface defined by a DEM. Plan curvature characterizes the 28 | degree of flow convergence or divergence within the 29 | landscape (Gallant and Wilson, 2000). 30 | } 31 | \details{ 32 | WhiteboxTools reports curvature in degrees multiplied by 100 for easier 33 | interpretation. The Z conversion factor (\eqn{z_factor}) is only important 34 | when the vertical and horizontal units are not the same in the DEM. 35 | When this is the case, the algorithm will multiply each elevation in the 36 | DEM by the Z Conversion Factor. 37 | 38 | If the DEM is in the geographic coordinate system (latitude and longitude), 39 | the following equation is used: 40 | 41 | \deqn{zfactor = \frac{1.0}{111320.0 \times \cos(midlat)}} 42 | 43 | The algorithm uses the same formula for the calculation of plan curvature 44 | as Gallant and Wilson (2000). Plan curvature is negative for diverging 45 | flow along ridges and positive for convergent areas, 46 | e.g. along valley bottoms. 47 | } 48 | \examples{ 49 | f <- system.file("extdata/dem.tif", package = "wbw") 50 | wbw_read_raster(f) |> 51 | wbw_plan_curvature() 52 | } 53 | \references{ 54 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#plan_curvature} 55 | 56 | Gallant, J. C., and J. P. Wilson, 2000, Primary topographic attributes, 57 | in Terrain Analysis: Principles and Applications, edited by J. P. 58 | Wilson and J. C. Gallant pp. 51-86, John Wiley, Hoboken, N.J. \if{html}{\out{
}} 59 | } 60 | \seealso{ 61 | \code{\link[=wbw_profile_curvature]{wbw_profile_curvature()}} 62 | } 63 | \keyword{geomorphometry} 64 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(max,"wbw::WhiteboxRaster") 4 | S3method(max,wbw::WhiteboxRaster) 5 | S3method(mean,"wbw::WhiteboxRaster") 6 | S3method(median,"wbw::WhiteboxRaster") 7 | S3method(median,wbw::WhiteboxRaster) 8 | S3method(min,"wbw::WhiteboxRaster") 9 | S3method(min,wbw::WhiteboxRaster) 10 | S3method(plot,"wbw::WhiteboxRaster") 11 | S3method(print,"wbw::WhiteboxRaster") 12 | S3method(summary,"wbw::WhiteboxRaster") 13 | S3method(xy.coords,wbw::WhiteboxRaster) 14 | export("xy.coords.wbw::WhiteboxRaster") 15 | export(WhiteboxExtent) 16 | export(WhiteboxRaster) 17 | export(as_matrix) 18 | export(as_rast) 19 | export(as_vector) 20 | export(as_wbw_raster) 21 | export(num_cells) 22 | export(print_geotiff_tags) 23 | export(stdev) 24 | export(variance) 25 | export(wbw_adaptive_filter) 26 | export(wbw_aspect) 27 | export(wbw_bilateral_filter) 28 | export(wbw_cols) 29 | export(wbw_conservative_smoothing_filter) 30 | export(wbw_data_type) 31 | export(wbw_download_sample_data) 32 | export(wbw_ext) 33 | export(wbw_fill_missing_data) 34 | export(wbw_gaussian_curvature) 35 | export(wbw_gaussian_filter) 36 | export(wbw_high_pass_filter) 37 | export(wbw_high_pass_median_filter) 38 | export(wbw_hillshade) 39 | export(wbw_install) 40 | export(wbw_is_float) 41 | export(wbw_is_int) 42 | export(wbw_is_rgb) 43 | export(wbw_majority_filter) 44 | export(wbw_max_procs) 45 | export(wbw_maximal_curvature) 46 | export(wbw_maximum_filter) 47 | export(wbw_mean_curvature) 48 | export(wbw_mean_filter) 49 | export(wbw_median_filter) 50 | export(wbw_minimal_curvature) 51 | export(wbw_minimum_filter) 52 | export(wbw_multidirectional_hillshade) 53 | export(wbw_olympic_filter) 54 | export(wbw_percentile_filter) 55 | export(wbw_plan_curvature) 56 | export(wbw_profile_curvature) 57 | export(wbw_random_sample) 58 | export(wbw_range_filter) 59 | export(wbw_read_raster) 60 | export(wbw_read_vector) 61 | export(wbw_res) 62 | export(wbw_rows) 63 | export(wbw_ruggedness_index) 64 | export(wbw_slope) 65 | export(wbw_standard_deviation_filter) 66 | export(wbw_to_degrees) 67 | export(wbw_to_radians) 68 | export(wbw_total_filter) 69 | export(wbw_version) 70 | export(wbw_write_raster) 71 | export(wbw_xres) 72 | export(wbw_yres) 73 | importFrom(cli,cli_alert_info) 74 | importFrom(cli,cli_alert_success) 75 | importFrom(cli,cli_alert_warning) 76 | importFrom(grDevices,xy.coords) 77 | importFrom(reticulate,configure_environment) 78 | importFrom(reticulate,import) 79 | importFrom(reticulate,py_discover_config) 80 | importFrom(reticulate,py_eval) 81 | importFrom(reticulate,py_run_string) 82 | importFrom(stats,median) 83 | importFrom(utils,download.file) 84 | importFrom(utils,menu) 85 | importFrom(utils,packageVersion) 86 | importFrom(utils,unzip) 87 | -------------------------------------------------------------------------------- /man/wbw_multidirectional_hillshade.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hillshade.R 3 | \name{wbw_multidirectional_hillshade} 4 | \alias{wbw_multidirectional_hillshade} 5 | \title{Multidirectional Hillshade} 6 | \usage{ 7 | wbw_multidirectional_hillshade( 8 | dem, 9 | altitude = 30, 10 | z_factor = 1, 11 | full_360_mode = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 16 | 17 | \item{altitude}{\code{double}, the altitude of the illumination sources. 18 | i.e. the elevation of the sun above the horizon, measured as an angle from 19 | 0 to 90 degrees} 20 | 21 | \item{z_factor}{\code{double}, Z conversion factor is only important 22 | when the vertical and horizontal units are not the same in the DEM. 23 | When this is the case, the algorithm will multiply each elevation in the 24 | DEM by the Z conversion factor} 25 | 26 | \item{full_360_mode}{\code{boolean}, default \code{FALSE}. I.e. whether or 27 | not to use full 360-degrees of illumination sources. When \code{FALSE} 28 | (default) the tool will perform a weighted summation of the hillshade 29 | images from four illumination azimuth positions at 225, 270, 315, and 30 | 360 (0) degrees, given weights of 0.1, 0.4, 0.4, and 0.1 respectively. When 31 | run in the full 360-degree mode, eight illumination source azimuths are 32 | used to calculate the output at 0, 45, 90, 135, 180, 225, 270, and 315 33 | degrees, with weights of 0.15, 0.125, 0.1, 0.05, 0.1, 0.125, 0.15, 34 | and 0.2 respectively.} 35 | } 36 | \value{ 37 | \link{WhiteboxRaster} object 38 | } 39 | \description{ 40 | This tool performs a hillshade operation (also called shaded relief) on 41 | an input digital elevation model (DEM) with multiple sources of 42 | illumination. 43 | } 44 | \details{ 45 | The hillshade value (HS) of a DEM grid cell is calculate as: 46 | \deqn{HS = \frac{\tan(s)}{\sqrt{1 - \tan(s)^2}} \times 47 | [\frac{\sin(Alt)}{\tan(s)} - \cos(Alt) \times \sin(Az - a)]} 48 | where \eqn{s} and \eqn{a} are the local slope gradient and aspect 49 | (orientation) respectively and \eqn{Alt} and \eqn{Az} are the illumination 50 | source altitude and azimuth respectively. Slope and aspect are calculated 51 | using Horn's (1981) 3rd-order finate difference method. 52 | } 53 | \examples{ 54 | f <- system.file("extdata/dem.tif", package = "wbw") 55 | wbw_read_raster(f) |> 56 | wbw_multidirectional_hillshade() 57 | } 58 | \references{ 59 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#multidirectional_hillshade} 60 | 61 | Horn B.K.P., 1981, Hill shading and the reflectance map, Proceedings of 62 | the I.E.E.E. 69, 14 63 | } 64 | \seealso{ 65 | \code{\link[=wbw_hillshade]{wbw_hillshade()}} 66 | } 67 | \keyword{geomorphometry} 68 | -------------------------------------------------------------------------------- /man/wbw_percentile_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filters.R 3 | \name{wbw_percentile_filter} 4 | \alias{wbw_percentile_filter} 5 | \title{Percentile Filter} 6 | \usage{ 7 | wbw_percentile_filter( 8 | x, 9 | filter_size_x = 11L, 10 | filter_size_y = 11L, 11 | sig_digits = 2L 12 | ) 13 | } 14 | \arguments{ 15 | \item{x}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 16 | 17 | \item{filter_size_x}{\code{integer}, X dimension of the neighbourhood size} 18 | 19 | \item{filter_size_y}{\code{integer}, Y dimension of the neighbourhood size} 20 | 21 | \item{sig_digits}{\code{integer}, default 2. Required for rounding of 22 | floating points inputs.} 23 | } 24 | \value{ 25 | \link{WhiteboxRaster} object containing filtered values 26 | } 27 | \description{ 28 | This tool calculates the percentile of the center cell in a moving filter 29 | window applied to an input image (\code{x}). This indicates the value 30 | below which a given percentage of the neighbouring values in within the 31 | filter fall. For example, the 35th percentile is the value below which 35\% 32 | of the neighbouring values in the filter window may be found. As such, 33 | the percentile of a pixel value is indicative of the relative location 34 | of the site within the statistical distribution of values contained 35 | within a filter window. 36 | 37 | When applied to input digital elevation models, percentile is a measure of 38 | local topographic position, or elevation residual. 39 | } 40 | \details{ 41 | Neighbourhood size, or filter size, is specified in the x and y dimensions 42 | using \code{filter_size_x} and \code{filter_size_y} These dimensions should 43 | be odd, positive integer values (e.g. 3L, 5L, 7L, 9L, etc.). 44 | 45 | This tool takes advantage of the redundancy between overlapping, 46 | neighbouring filters to enhance computationally efficiency, using a 47 | method similar to Huang et al. (1979). This efficient method of 48 | calculating percentiles requires rounding of floating-point inputs, 49 | and therefore the user must specify the number of significant 50 | digits (\code{sig_digits}) to be used during the processing. 51 | } 52 | \examples{ 53 | f <- system.file("extdata/dem.tif", package = "wbw") 54 | wbw_read_raster(f) |> 55 | wbw_percentile_filter(filter_size_x = 3L, filter_size_y = 3L) 56 | } 57 | \references{ 58 | Huang, T., Yang, G.J.T.G.Y. and Tang, G., 1979. A fast two-dimensional 59 | median filtering algorithm. IEEE Transactions on Acoustics, Speech, and 60 | Signal Processing, 27(1), pp.13-18. 61 | 62 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#percentile_filter} 63 | } 64 | \seealso{ 65 | \code{\link[=wbw_median_filter]{wbw_median_filter()}} 66 | } 67 | \keyword{image_processing} 68 | -------------------------------------------------------------------------------- /R/matrix.R: -------------------------------------------------------------------------------- 1 | #' Convert WhiteboxRaster to Matrix 2 | #' @rdname matrix 3 | #' @keywords transform 4 | #' 5 | #' @description 6 | #' Converts a WhiteboxRaster object to a matrix. The output maintains the same 7 | #' dimensions and cell values as the input raster. 8 | #' 9 | #' @param raw logical. Should the raw data be returned (`raw = TRUE`) or should 10 | #' NoData values be transformed to `NA` (`raw = FALSE`)? 11 | #' @eval rd_input_raster("x") 12 | #' 13 | #' @return matrix containing raster values 14 | #' 15 | #' @eval rd_example("as_matrix", args = c("raw = TRUE")) 16 | #' 17 | #' @export 18 | as_matrix <- S7::new_generic( 19 | name = "as_matrix", 20 | dispatch_args = "x", 21 | fun = function(x, raw = FALSE) { 22 | S7::S7_dispatch() 23 | } 24 | ) 25 | 26 | S7::method(as_matrix, WhiteboxRaster) <- function(x, raw = FALSE) { 27 | checkmate::assert_environment(wbw_env) 28 | m <- wbw_env$wbw_to_matrix(x@source, raw) 29 | as.matrix(m) 30 | } 31 | 32 | #' Convert objects to vectors 33 | #' @name as_vector 34 | #' @rdname vector 35 | #' @keywords transform 36 | #' 37 | #' @description 38 | #' Converts various Whitebox objects to vectors: 39 | #' * For [WhiteboxRaster]: converts raster values to a vector in row-major 40 | #' order 41 | #' * For [WhiteboxExtent]: converts extent boundaries to a named vector 42 | #' 43 | #' @param x Object to convert to vector. Can be: 44 | #' * A [WhiteboxRaster] object 45 | #' * A [WhiteboxExtent] object 46 | #' @param raw logical. For [WhiteboxRaster] only: Should the raw data be 47 | #' returned (`raw = TRUE`) or should NoData values be transformed 48 | #' to `NA` (`raw = FALSE`)? 49 | #' 50 | #' @return A vector, with type depending on the input: 51 | #' * For [WhiteboxRaster]: vector containing raster values 52 | #' * For [WhiteboxExtent]: named vector containing extent values 53 | #' 54 | #' @usage 55 | #' \S4method{as_vector}{WhiteboxRaster}(x, raw = FALSE) 56 | #' \S4method{as_vector}{WhiteboxExtent}(x) 57 | #' 58 | #' @aliases as_vector,WhiteboxRaster-method as_vector,WhiteboxExtent-method 59 | #' 60 | #' @examples 61 | #' f <- system.file("extdata/dem.tif", package = "wbw") 62 | #' x <- wbw_read_raster(f) 63 | #' 64 | #' # Return WhiteboxRaster's data: 65 | #' head(as_vector(x)) 66 | #' 67 | #' # Return WhiteboxExtent's data: 68 | #' as_vector(wbw_ext(x)) 69 | #' 70 | #' @export 71 | as_vector <- S7::new_generic( 72 | name = "as_vector", 73 | dispatch_args = "x" 74 | ) 75 | 76 | S7::method(as_vector, WhiteboxRaster) <- function(x, raw = FALSE) { 77 | checkmate::assert_environment(wbw_env) 78 | v <- wbw_env$wbw_to_vector(x@source, raw) 79 | as.vector(v) 80 | } 81 | 82 | S7::method(as_vector, WhiteboxExtent) <- function(x) { 83 | c( 84 | "west" = x@west, 85 | "east" = x@east, 86 | "south" = x@south, 87 | "north" = x@north 88 | ) 89 | } 90 | -------------------------------------------------------------------------------- /tests/tinytest/test_curvature.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test successful filter returns 4 | expect_inherits( 5 | wbw_gaussian_curvature(x), 6 | c("wbw::WhiteboxRaster", "S7_object") 7 | ) 8 | expect_inherits( 9 | wbw_maximal_curvature(x), 10 | c("wbw::WhiteboxRaster", "S7_object") 11 | ) 12 | expect_inherits( 13 | wbw_minimal_curvature(x), 14 | c("wbw::WhiteboxRaster", "S7_object") 15 | ) 16 | expect_inherits( 17 | wbw_mean_curvature(x), 18 | c("wbw::WhiteboxRaster", "S7_object") 19 | ) 20 | expect_inherits( 21 | wbw_profile_curvature(x), 22 | c("wbw::WhiteboxRaster", "S7_object") 23 | ) 24 | expect_inherits( 25 | wbw_plan_curvature(x), 26 | c("wbw::WhiteboxRaster", "S7_object") 27 | ) 28 | 29 | # Test curvature alterations 30 | # Here is near-equality check is happening. If two values are close to 31 | # be equal, i.e. 2.222222226 and 2.222222225, then all.equal() returns TRUE 32 | # In other cases the function will return the mean relative difference as 33 | # a character vector 34 | true_median <- median(x) 35 | 36 | expect_true( 37 | wbw_gaussian_curvature(x) |> 38 | median() |> 39 | all.equal(true_median) |> 40 | is.character() 41 | ) 42 | expect_true( 43 | wbw_gaussian_curvature(x, log_transform = TRUE) |> 44 | median() |> 45 | all.equal(true_median) |> 46 | is.character() 47 | ) 48 | expect_true( 49 | wbw_maximal_curvature(x) |> 50 | median() |> 51 | all.equal(true_median) |> 52 | is.character() 53 | ) 54 | expect_true( 55 | wbw_maximal_curvature(x, log_transform = TRUE) |> 56 | median() |> 57 | all.equal(true_median) |> 58 | is.character() 59 | ) 60 | expect_true( 61 | wbw_minimal_curvature(x) |> 62 | median() |> 63 | all.equal(true_median) |> 64 | is.character() 65 | ) 66 | expect_true( 67 | wbw_minimal_curvature(x, log_transform = TRUE) |> 68 | median() |> 69 | all.equal(true_median) |> 70 | is.character() 71 | ) 72 | expect_true( 73 | wbw_mean_curvature(x) |> 74 | median() |> 75 | all.equal(true_median) |> 76 | is.character() 77 | ) 78 | expect_true( 79 | wbw_mean_curvature(x, log_transform = TRUE) |> 80 | median() |> 81 | all.equal(true_median) |> 82 | is.character() 83 | ) 84 | expect_true( 85 | wbw_profile_curvature(x) |> 86 | median() |> 87 | all.equal(true_median) |> 88 | is.character() 89 | ) 90 | expect_true( 91 | wbw_profile_curvature(x, log_transform = TRUE) |> 92 | median() |> 93 | all.equal(true_median) |> 94 | is.character() 95 | ) 96 | expect_true( 97 | wbw_plan_curvature(x) |> 98 | median() |> 99 | all.equal(true_median) |> 100 | is.character() 101 | ) 102 | expect_true( 103 | wbw_plan_curvature(x, log_transform = TRUE) |> 104 | median() |> 105 | all.equal(true_median) |> 106 | is.character() 107 | ) 108 | -------------------------------------------------------------------------------- /.github/workflows/progress-tracker.yml: -------------------------------------------------------------------------------- 1 | name: Progress Tracker 2 | 3 | on: 4 | issues: 5 | types: [opened, edited] 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: read 10 | 11 | jobs: 12 | calculate-progress: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Get issue content 17 | id: issue 18 | uses: actions/github-script@v6 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | script: | 22 | const issue = await github.rest.issues.get({ 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | issue_number: 1 26 | }); 27 | core.setOutput('content', issue.data.body); 28 | 29 | - name: Calculate progress 30 | id: progress 31 | run: | 32 | python3 -c " 33 | import os 34 | content = '''${{ steps.issue.outputs.content }}''' 35 | completed = content.count('- [x]') 36 | total = completed + content.count('- [ ]') 37 | percentage = (completed / total * 100) if total > 0 else 0 38 | 39 | with open(os.environ['GITHUB_OUTPUT'], 'a') as f: 40 | print(f'completed={completed}', file=f) 41 | print(f'total={total}', file=f) 42 | print(f'percentage={percentage:.1f}', file=f) 43 | " 44 | 45 | - name: Initialize/Update Gist 46 | uses: actions/github-script@v6 47 | with: 48 | github-token: ${{ secrets.GIST_SECRET }} 49 | script: | 50 | const gist_id = '0c46250def94614c4a3ef8b4de7460e6'; 51 | const filename = 'wbw-progress.json'; 52 | const content = { 53 | schemaVersion: 1, 54 | label: "WbW Tools ported", 55 | message: "${{ steps.progress.outputs.percentage }}% (${{ steps.progress.outputs.completed }}/${{ steps.progress.outputs.total }})", 56 | color: "d5ad18" 57 | }; 58 | 59 | try { 60 | await github.rest.gists.update({ 61 | gist_id: gist_id, 62 | files: { 63 | [filename]: { 64 | content: JSON.stringify(content) 65 | } 66 | } 67 | }); 68 | } catch (error) { 69 | console.log('Error updating gist:', error); 70 | } 71 | 72 | - name: Update progress badge 73 | uses: schneegans/dynamic-badges-action@v1.6.0 74 | with: 75 | auth: ${{ secrets.GIST_SECRET }} 76 | gistID: 0c46250def94614c4a3ef8b4de7460e6 77 | filename: wbw-progress.json 78 | label: WbW Tools ported 79 | message: "${{ steps.progress.outputs.percentage }}% (${{ steps.progress.outputs.completed }}/${{ steps.progress.outputs.total }})" 80 | color: d5ad18 -------------------------------------------------------------------------------- /man/wbw_mean_curvature.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/curvature.R 3 | \name{wbw_mean_curvature} 4 | \alias{wbw_mean_curvature} 5 | \title{Mean Curvature} 6 | \usage{ 7 | wbw_mean_curvature(dem, log_transform = FALSE, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{log_transform}{\code{logical}, default \code{FALSE}. Wheter 13 | log-transform the output raster or not. See details.} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object. 22 | } 23 | \description{ 24 | This tool calculates the mean curvature from a digital elevation 25 | model (\eqn{dem}). 26 | 27 | WhiteboxTools reports curvature in radians multiplied by 100 for easier 28 | interpretation because curvature values are typically very small. 29 | } 30 | \details{ 31 | Curvature values are often very small and as such the user may opt to 32 | log-transform the output raster (\eqn{log_transform}). Transforming 33 | the values applies the equation by Shary et al. (2002): 34 | 35 | \deqn{Θ' = sign(Θ) ln(1 + 10^n|Θ|)} 36 | 37 | where \eqn{Θ} is the parameter value and \eqn{n} is dependent on the 38 | grid cell size. 39 | 40 | For DEMs in projected coordinate systems, the tool uses the 41 | 3rd-order bivariate Taylor polynomial method described by 42 | Florinsky (2016). Based on a polynomial fit of the elevations 43 | within the 5x5 neighbourhood surrounding each cell, this method 44 | is considered more robust against outlier elevations (noise) 45 | than other methods. 46 | 47 | For DEMs in geographic coordinate systems (i.e. angular units), the 48 | tool uses the 3x3 polynomial fitting method for equal angle grids also 49 | described by Florinsky (2016). 50 | } 51 | \examples{ 52 | f <- system.file("extdata/dem.tif", package = "wbw") 53 | wbw_read_raster(f) |> 54 | wbw_mean_curvature() 55 | } 56 | \references{ 57 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#mean_curvature} 58 | 59 | Florinsky, I. (2016). Digital terrain analysis in soil science and 60 | geology. Academic Press. \if{html}{\out{
}} 61 | Florinsky, I. V. (2017). An illustrated introduction to general 62 | geomorphometry. Progress in Physical Geography, 41(6), 723-752. \if{html}{\out{
}} 63 | Shary P. A., Sharaya L. S. and Mitusov A. V. (2002) Fundamental 64 | quantitative methods of land surface analysis. Geoderma 107: 1–32. \if{html}{\out{
}} 65 | } 66 | \seealso{ 67 | \code{\link[=wbw_gaussian_curvature]{wbw_gaussian_curvature()}}, \code{\link[=wbw_maximal_curvature]{wbw_maximal_curvature()}} 68 | } 69 | \keyword{geomorphometry} 70 | -------------------------------------------------------------------------------- /R/print.R: -------------------------------------------------------------------------------- 1 | #' Print Method for WhiteboxRaster 2 | #' @keywords methods 3 | #' 4 | #' @param x [WhiteboxRaster] object to print 5 | #' @param ... additional arguments passed to print method 6 | #' @export 7 | `print.wbw::WhiteboxRaster` <- function(x, ...) { 8 | conf <- x@source$configs 9 | name <- if (nchar(conf$title) == 0) x@name else conf$title 10 | epsg <- if (conf$epsg_code != 0) conf$epsg_code else "" 11 | 12 | # Create content 13 | lines <- c( 14 | sub("^.*::", "", class(x)[1]), 15 | name, 16 | sprintf("bands : %d", conf$bands), 17 | sprintf("dimensions : %d, %d (nrow, ncol)", conf$rows, conf$columns), 18 | sprintf( 19 | "resolution : %f, %f (x, y)", 20 | conf$resolution_x, 21 | conf$resolution_y 22 | ), 23 | sprintf("EPSG : %s (%s)", epsg, conf$xy_units), 24 | sprintf( 25 | "extent : %1.0f %1.0f %1.0f %1.0f", 26 | conf$west, 27 | conf$east, 28 | conf$south, 29 | conf$north 30 | ), 31 | sprintf("min value : %f", x@min), 32 | sprintf("max value : %f", x@max) 33 | ) 34 | 35 | # Find the longest line 36 | width <- max(nchar(lines)) 37 | 38 | # Create box elements with ASCII 39 | horizontal_line <- paste0("+", paste(rep("-", width + 2), collapse = ""), "+") 40 | dotted_line <- paste0("|", paste(rep(".", width + 2), collapse = ""), "|") 41 | 42 | # Print boxed content 43 | cat(horizontal_line, "\n") 44 | 45 | # Print class name and file name 46 | padded_class <- format(lines[1], width = width) 47 | padded_name <- format(lines[2], width = width) 48 | cat("| ", padded_class, " |\n", sep = "") 49 | cat("| ", padded_name, " |\n", sep = "") 50 | 51 | # Print separator 52 | cat(dotted_line, "\n") 53 | 54 | # Print rest of the content (skip first two lines as they're already printed) 55 | for (line in lines[-(1:2)]) { 56 | padded_line <- format(line, width = width) 57 | cat("| ", padded_line, " |\n", sep = "") 58 | } 59 | 60 | cat(horizontal_line, "\n") 61 | 62 | invisible(x) 63 | } 64 | 65 | #' Print GeoTIFF Tags 66 | #' @keywords utils 67 | #' 68 | #' @description 69 | #' Displays the tags contained within a GeoTIFF file. This is useful when 70 | #' importing GeoTIFF files into different software environments. The tool prints 71 | #' tag information to the console. Tags containing more than 100 values are 72 | #' truncated in the output. GeoKeys are interpreted according to the GeoTIFF 73 | #' specification. 74 | #' 75 | #' @param file_name \code{character}, path to raster file 76 | #' 77 | #' @eval rd_wbw_link("print_geotiff_tags") 78 | #' 79 | #' @examples 80 | #' \dontrun{ 81 | #' raster_path <- system.file("extdata/dem.tif", package = "wbw") 82 | #' print_geotiff_tags(raster_path) 83 | #' } 84 | #' 85 | #' @export 86 | print_geotiff_tags <- function(file_name) { 87 | check_env(wbe) 88 | check_input_file(file_name, "r") 89 | wbe$print_geotiff_tags(file_name) 90 | } 91 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | # landing page is build from pkgdown/index.md, NOT README.md 2 | # This is done to support builtin Github Formatting of callouts 3 | 4 | url: https://wbw.anatolii.nz 5 | 6 | repo: 7 | url: 8 | home: https://github.com/atsyplenkov/wbw 9 | source: https://github.com/atsyplenkov/wbw/tree/master 10 | issues: https://github.com/atsyplenkov/wbw/issues 11 | 12 | authors: 13 | Anatolii Tsyplenkov: 14 | href: https://anatolii.nz 15 | 16 | template: 17 | bootstrap: 5 18 | light-switch: true 19 | math-rendering: mathjax 20 | bslib: 21 | base_font: {google: "Inter"} 22 | heading_font: {google: "Recursive"} 23 | code_font: {google: "Fira Code"} 24 | 25 | navbar: 26 | components: 27 | faq: 28 | text: FAQ 29 | href: articles/FAQ.html 30 | tutorials: 31 | text: Tutorials 32 | href: articles/tutorials.html 33 | menu: 34 | - text: Tutorial 1 35 | - text: Tutorial 2 36 | href: articles/Tutorial-2.html 37 | - text: Tutorial 3 38 | articles: 39 | text: Articles 40 | menu: 41 | - text: Benchmarks 42 | href: articles/benchmarks.html 43 | - text: wbw and terra 44 | structure: 45 | left: [faq, reference, tutorials, articles, news] 46 | right: [search, github, lightswitch] 47 | 48 | footer: 49 | structure: 50 | left: [attribution] 51 | components: 52 | attribution: "R bindings to the ['Whitebox Workflows for Python'](https://www.whiteboxgeo.com/whitebox-workflows-for-python/)" 53 | 54 | reference: 55 | - title: "Input/Output" 56 | desc: "Functions for reading and writing data" 57 | contents: 58 | - has_keyword("io") 59 | - wbw_read_raster 60 | - wbw_write_raster 61 | - print_geotiff_tags 62 | 63 | - title: "{terra}" 64 | desc: "Functions for conversion between wbw and terra" 65 | contents: 66 | - has_keyword("terra") 67 | 68 | - title: "Raster Operations" 69 | desc: "Functions for raster manipulation" 70 | contents: 71 | - has_keyword("conversions") 72 | - has_keyword("raster") 73 | - has_keyword("crs") 74 | - has_keyword("utils") 75 | - has_keyword("math") 76 | - has_keyword("transform") 77 | 78 | - title: "Stats" 79 | desc: "Summary and zonal statistics for raster and vector data" 80 | contents: 81 | - has_keyword("stats") 82 | 83 | - title: "System" 84 | desc: "System utilities" 85 | contents: 86 | - has_keyword("system") 87 | - wbw_version 88 | 89 | - title: "Geomorphometry" 90 | desc: "Tools for geomorphometric analysis of digital elevation models" 91 | contents: 92 | - has_keyword("geomorphometry") 93 | 94 | - title: "Image Processing" 95 | contents: 96 | - has_keyword("image_processing") 97 | 98 | - title: "Classes" 99 | desc: "Introduced S7 classes" 100 | contents: 101 | - has_keyword("class") 102 | 103 | - title: "Methods" 104 | desc: "S3 methods for WhiteboxRaster and WhiteboxVector objects" 105 | contents: 106 | - has_keyword("methods") 107 | -------------------------------------------------------------------------------- /man/wbw_gaussian_curvature.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/curvature.R 3 | \name{wbw_gaussian_curvature} 4 | \alias{wbw_gaussian_curvature} 5 | \title{Gaussian Curvature} 6 | \usage{ 7 | wbw_gaussian_curvature(dem, log_transform = FALSE, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{log_transform}{\code{logical}, default \code{FALSE}. Wheter 13 | log-transform the output raster or not. See details.} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object in units of \eqn{m^{-2}}. 22 | } 23 | \description{ 24 | This tool calculates the Gaussian curvature from a digital elevation 25 | model (\eqn{dem}). Gaussian curvature is the product of maximal and 26 | minimal curvatures, and retains values in each point of the topographic 27 | surface after its bending without breaking, stretching, and 28 | compressing (Florinsky, 2017). 29 | 30 | Gaussian curvature is measured in units of \eqn{m^{-2}}. 31 | } 32 | \details{ 33 | Curvature values are often very small and as such the user may opt to 34 | log-transform the output raster (\eqn{log_transform}). Transforming 35 | the values applies the equation by Shary et al. (2002): 36 | 37 | \deqn{Θ' = sign(Θ) ln(1 + 10^n|Θ|)} 38 | 39 | where \eqn{Θ} is the parameter value and \eqn{n} is dependent on the 40 | grid cell size. 41 | 42 | For DEMs in projected coordinate systems, the tool uses the 43 | 3rd-order bivariate Taylor polynomial method described by 44 | Florinsky (2016). Based on a polynomial fit of the elevations 45 | within the 5x5 neighbourhood surrounding each cell, this method 46 | is considered more robust against outlier elevations (noise) 47 | than other methods. 48 | 49 | For DEMs in geographic coordinate systems (i.e. angular units), the 50 | tool uses the 3x3 polynomial fitting method for equal angle grids also 51 | described by Florinsky (2016). 52 | } 53 | \examples{ 54 | f <- system.file("extdata/dem.tif", package = "wbw") 55 | wbw_read_raster(f) |> 56 | wbw_gaussian_curvature() 57 | } 58 | \references{ 59 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#gaussian_curvature} 60 | 61 | Florinsky, I. (2016). Digital terrain analysis in soil science and 62 | geology. Academic Press. \if{html}{\out{
}} 63 | Florinsky, I. V. (2017). An illustrated introduction to general 64 | geomorphometry. Progress in Physical Geography, 41(6), 723-752. \if{html}{\out{
}} 65 | Shary P. A., Sharaya L. S. and Mitusov A. V. (2002) Fundamental 66 | quantitative methods of land surface analysis. Geoderma 107: 1–32. \if{html}{\out{
}} 67 | } 68 | \seealso{ 69 | \code{\link[=wbw_maximal_curvature]{wbw_maximal_curvature()}}, \code{\link[=wbw_minimal_curvature]{wbw_minimal_curvature()}} 70 | } 71 | \keyword{geomorphometry} 72 | -------------------------------------------------------------------------------- /man/wbw_maximal_curvature.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/curvature.R 3 | \name{wbw_maximal_curvature} 4 | \alias{wbw_maximal_curvature} 5 | \title{Maximal Curvature} 6 | \usage{ 7 | wbw_maximal_curvature(dem, log_transform = FALSE, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{log_transform}{\code{logical}, default \code{FALSE}. Wheter 13 | log-transform the output raster or not. See details.} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object in units of \eqn{m^{-1}}. 22 | } 23 | \description{ 24 | This tool calculates the maximal curvature from a digital elevation model 25 | (\eqn{dem}). Maximal curvature is the curvature of a principal section 26 | with the highest value of curvature at a given point of the topographic 27 | surface (Florinsky, 2017). The values of this curvature are unbounded, 28 | and positive values correspond to ridge positions while negative values 29 | are indicative of closed depressions (Florinsky, 2016). 30 | 31 | Maximal curvature is measured in units of \eqn{m^{-1}}. 32 | } 33 | \details{ 34 | Curvature values are often very small and as such the user may opt to 35 | log-transform the output raster (\eqn{log_transform}). Transforming 36 | the values applies the equation by Shary et al. (2002): 37 | 38 | \deqn{Θ' = sign(Θ) ln(1 + 10^n|Θ|)} 39 | 40 | where \eqn{Θ} is the parameter value and \eqn{n} is dependent on the 41 | grid cell size. 42 | 43 | For DEMs in projected coordinate systems, the tool uses the 44 | 3rd-order bivariate Taylor polynomial method described by 45 | Florinsky (2016). Based on a polynomial fit of the elevations 46 | within the 5x5 neighbourhood surrounding each cell, this method 47 | is considered more robust against outlier elevations (noise) 48 | than other methods. 49 | 50 | For DEMs in geographic coordinate systems (i.e. angular units), the 51 | tool uses the 3x3 polynomial fitting method for equal angle grids also 52 | described by Florinsky (2016). 53 | } 54 | \examples{ 55 | f <- system.file("extdata/dem.tif", package = "wbw") 56 | wbw_read_raster(f) |> 57 | wbw_maximal_curvature() 58 | } 59 | \references{ 60 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#maximal_curvature} 61 | 62 | Florinsky, I. (2016). Digital terrain analysis in soil science and 63 | geology. Academic Press. \if{html}{\out{
}} 64 | Florinsky, I. V. (2017). An illustrated introduction to general 65 | geomorphometry. Progress in Physical Geography, 41(6), 723-752. \if{html}{\out{
}} 66 | Shary P. A., Sharaya L. S. and Mitusov A. V. (2002) Fundamental 67 | quantitative methods of land surface analysis. Geoderma 107: 1–32. \if{html}{\out{
}} 68 | } 69 | \seealso{ 70 | \code{\link[=wbw_gaussian_curvature]{wbw_gaussian_curvature()}}, \code{\link[=wbw_minimal_curvature]{wbw_minimal_curvature()}} 71 | } 72 | \keyword{geomorphometry} 73 | -------------------------------------------------------------------------------- /man/wbw_minimal_curvature.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/curvature.R 3 | \name{wbw_minimal_curvature} 4 | \alias{wbw_minimal_curvature} 5 | \title{Minimal Curvature} 6 | \usage{ 7 | wbw_minimal_curvature(dem, log_transform = FALSE, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{log_transform}{\code{logical}, default \code{FALSE}. Wheter 13 | log-transform the output raster or not. See details.} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object in units of \eqn{m^{-1}}. 22 | } 23 | \description{ 24 | This tool calculates the minimal curvature from a digital elevation model 25 | (\eqn{dem}). Minimal curvature is the curvature of a principal section 26 | with the highest value of curvature at a given point of the topographic 27 | surface (Florinsky, 2017). The values of this curvature are unbounded, 28 | and positive values correspond to ridge positions while negative values 29 | are indicative of closed depressions (Florinsky, 2016). 30 | 31 | Minimal curvature is measured in units of \eqn{m^{-1}}. 32 | } 33 | \details{ 34 | Curvature values are often very small and as such the user may opt to 35 | log-transform the output raster (\eqn{log_transform}). Transforming 36 | the values applies the equation by Shary et al. (2002): 37 | 38 | \deqn{Θ' = sign(Θ) ln(1 + 10^n|Θ|)} 39 | 40 | where \eqn{Θ} is the parameter value and \eqn{n} is dependent on the 41 | grid cell size. 42 | 43 | For DEMs in projected coordinate systems, the tool uses the 44 | 3rd-order bivariate Taylor polynomial method described by 45 | Florinsky (2016). Based on a polynomial fit of the elevations 46 | within the 5x5 neighbourhood surrounding each cell, this method 47 | is considered more robust against outlier elevations (noise) 48 | than other methods. 49 | 50 | For DEMs in geographic coordinate systems (i.e. angular units), the 51 | tool uses the 3x3 polynomial fitting method for equal angle grids also 52 | described by Florinsky (2016). 53 | } 54 | \examples{ 55 | f <- system.file("extdata/dem.tif", package = "wbw") 56 | wbw_read_raster(f) |> 57 | wbw_minimal_curvature() 58 | } 59 | \references{ 60 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#minimal_curvature} 61 | 62 | Florinsky, I. (2016). Digital terrain analysis in soil science and 63 | geology. Academic Press. \if{html}{\out{
}} 64 | Florinsky, I. V. (2017). An illustrated introduction to general 65 | geomorphometry. Progress in Physical Geography, 41(6), 723-752. \if{html}{\out{
}} 66 | Shary P. A., Sharaya L. S. and Mitusov A. V. (2002) Fundamental 67 | quantitative methods of land surface analysis. Geoderma 107: 1–32. \if{html}{\out{
}} 68 | } 69 | \seealso{ 70 | \code{\link[=wbw_gaussian_curvature]{wbw_gaussian_curvature()}}, \code{\link[=wbw_maximal_curvature]{wbw_maximal_curvature()}} 71 | } 72 | \keyword{geomorphometry} 73 | -------------------------------------------------------------------------------- /man/wbw_profile_curvature.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/curvature.R 3 | \name{wbw_profile_curvature} 4 | \alias{wbw_profile_curvature} 5 | \title{Profile Curvature} 6 | \usage{ 7 | wbw_profile_curvature(dem, log_transform = FALSE, z_factor = 1) 8 | } 9 | \arguments{ 10 | \item{dem}{Raster object of class \link{WhiteboxRaster}. See \code{\link[=wbw_read_raster]{wbw_read_raster()}} for more details.} 11 | 12 | \item{log_transform}{\code{logical}, default \code{FALSE}. Wheter 13 | log-transform the output raster or not. See details.} 14 | 15 | \item{z_factor}{\code{double}, Z conversion factor is only important 16 | when the vertical and horizontal units are not the same in the DEM. 17 | When this is the case, the algorithm will multiply each elevation in the 18 | DEM by the Z conversion factor} 19 | } 20 | \value{ 21 | \link{WhiteboxRaster} object. 22 | } 23 | \description{ 24 | This tool calculates the profile curvature from a digital elevation 25 | model (\eqn{dem}), or the rate of change in slope along a flow line. 26 | 27 | Curvature is the second derivative of the topographic surface defined 28 | by a DEM. Profile curvature characterizes the degree of downslope 29 | acceleration or deceleration within the 30 | landscape (Gallant and Wilson, 2000). 31 | 32 | WhiteboxTools reports curvature in radians multiplied by 100 for easier 33 | interpretation because curvature values are typically very small. 34 | } 35 | \details{ 36 | Curvature values are often very small and as such the user may opt to 37 | log-transform the output raster (\eqn{log_transform}). Transforming 38 | the values applies the equation by Shary et al. (2002): 39 | 40 | \deqn{Θ' = sign(Θ) ln(1 + 10^n|Θ|)} 41 | 42 | where \eqn{Θ} is the parameter value and \eqn{n} is dependent on the 43 | grid cell size. 44 | 45 | For DEMs in projected coordinate systems, the tool uses the 46 | 3rd-order bivariate Taylor polynomial method described by 47 | Florinsky (2016). Based on a polynomial fit of the elevations 48 | within the 5x5 neighbourhood surrounding each cell, this method 49 | is considered more robust against outlier elevations (noise) 50 | than other methods. 51 | 52 | For DEMs in geographic coordinate systems (i.e. angular units), the 53 | tool uses the 3x3 polynomial fitting method for equal angle grids also 54 | described by Florinsky (2016). 55 | } 56 | \examples{ 57 | f <- system.file("extdata/dem.tif", package = "wbw") 58 | wbw_read_raster(f) |> 59 | wbw_profile_curvature() 60 | } 61 | \references{ 62 | For more information, see \url{https://www.whiteboxgeo.com/manual/wbw-user-manual/book/tool_help.html#profile_curvature} 63 | 64 | Gallant, J. C., and J. P. Wilson, 2000, Primary topographic attributes, 65 | in Terrain Analysis: Principles and Applications, edited by J. P. 66 | Wilson and J. C. Gallant pp. 51-86, John Wiley, Hoboken, N.J. \if{html}{\out{
}} 67 | Florinsky, I. (2016). Digital terrain analysis in soil science and 68 | geology. Academic Press. \if{html}{\out{
}} 69 | Florinsky, I. V. (2017). An illustrated introduction to general 70 | geomorphometry. Progress in Physical Geography, 41(6), 723-752. \if{html}{\out{
}} 71 | Shary P. A., Sharaya L. S. and Mitusov A. V. (2002) Fundamental 72 | quantitative methods of land surface analysis. Geoderma 107: 1–32. \if{html}{\out{
}} 73 | } 74 | \seealso{ 75 | \code{\link[=wbw_gaussian_curvature]{wbw_gaussian_curvature()}}, \code{\link[=wbw_mean_curvature]{wbw_mean_curvature()}} 76 | } 77 | \keyword{geomorphometry} 78 | -------------------------------------------------------------------------------- /vignettes/articles/Tutorial-2.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tutorial 2: Geomorphometric Analysis" 3 | output: 4 | rmarkdown::html_vignette: 5 | toc: true 6 | vignette: > 7 | %\VignetteIndexEntry{Tutorial 2: Geomorphometric Analysis} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | %\VignetteDepends{terra, waldo} 11 | --- 12 | 13 | ```{r knitr_setup, include = FALSE} 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>", 17 | warning = FALSE, 18 | message = FALSE, 19 | dpi = 150, 20 | retina = 2 21 | ) 22 | 23 | requireNamespace("terra", quietly = TRUE) 24 | requireNamespace("waldo", quietly = TRUE) 25 | ``` 26 | 27 | > NB! This tutorial is based on the [Whitebox Workflows for Python (WbW) Tutorial 2: Geomorphometric Analysis](https://github.com/jblindsay/jblindsay.github.io/blob/master/WhiteboxTutorials/WbW_tutorials/WbW_tutorial2.ipynb) 28 | 29 | # 1. Download sample data 30 | 31 | ```{r load_layers} 32 | library(wbw) 33 | 34 | # Download sample dataset 35 | sample <- 36 | wbw_download_sample_data( 37 | data_set = "Grand_Junction", 38 | path = tempdir() 39 | ) 40 | 41 | # Load as WhiteboxRaster 42 | dem <- wbw_read_raster(file.path(sample, "DEM.tif")) 43 | dem 44 | ``` 45 | 46 | # 2. Fill missing data 47 | 48 | However, there are some missing values presented in the DEM. 49 | ```{r plot_one} 50 | library(terra) 51 | 52 | dem |> 53 | as_rast() |> 54 | plot() 55 | 56 | ``` 57 | 58 | To fill them, one can use `wbw_fill_missing_data` function: 59 | ```{r fill_nodata} 60 | dem_filled <- 61 | wbw_fill_missing_data(dem, filter_size = 35, exclude_edge_nodata = TRUE) 62 | 63 | dem_filled |> 64 | as_rast() |> 65 | plot() 66 | ``` 67 | 68 | # 3. Vizualization 69 | 70 | To properly vizualize the DEM, let's create a hillshade map of the relief: 71 | 72 | ```{r hillshade} 73 | dem_hillshade <- wbw_multidirectional_hillshade(dem_filled) 74 | 75 | dem_hillshade |> 76 | as_rast() |> 77 | plot(col = gray.colors(10), legend = FALSE) 78 | ``` 79 | 80 | # 4. Raster summary stats 81 | 82 | One can get `WhiteboxRaster` summary statistics by running familiar functions from base R (and `{terra}`): 83 | 84 | ```{r summary_stats} 85 | # Raw DEM 86 | wbw_cols(dem) # Number of columns 87 | wbw_rows(dem) # Number of rows 88 | num_cells(dem) # Number of cells 89 | wbw_res(dem) # Raster resolution 90 | 91 | # NA-Filled DEM 92 | wbw_cols(dem_filled) # Number of columns 93 | wbw_rows(dem_filled) # Number of rows 94 | num_cells(dem_filled) # Number of cells 95 | wbw_res(dem_filled) # Raster resolution 96 | 97 | # Number of NoData values: 98 | waldo::compare( 99 | sum(is.na(as_vector(dem))), 100 | sum(is.na(as_vector(dem_filled))) 101 | ) 102 | ``` 103 | 104 | # 5. Extracting land-surface parameters (LSPs) 105 | Now let's extract some common land-surface parameters (LSPs), the basic building blocks of a geomorphometric analysis. 106 | 107 | ## Slope and Aspect 108 | ```{r slope_and_aspect} 109 | # Slope 110 | slope <- wbw_slope(dem_filled) 111 | aspect <- wbw_aspect(dem_filled) 112 | 113 | # Set up a 1x2 plotting layout 114 | par(mfrow = c(1, 2)) 115 | 116 | # Plot the first raster (Slope) 117 | plot(as_rast(slope), 118 | main = "Slope", 119 | legend = FALSE, 120 | col = terrain.colors(100), 121 | mar=c(1,1,1,1)) 122 | 123 | # Plot the second raster (Aspect) 124 | plot(as_rast(aspect), 125 | main = "Aspect", 126 | legend = FALSE, 127 | mar=c(1,1,1,1)) 128 | 129 | ``` -------------------------------------------------------------------------------- /inst/wbw_helpers.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def wbw_to_vector(wbw_raster, raw=False): 5 | """ 6 | Converts a Whitebox Raster object to a 1d numpy array, column-wise. 7 | 8 | Args: 9 | wbw_raster: A Whitebox Raster object 10 | raw: If False (default), replaces NoData values with None/np.nan. If True, keeps original values. 11 | 12 | Returns: 13 | A 1d numpy array with values arranged column by column. 14 | """ 15 | # First get the data as a matrix 16 | matrix = wbw_to_matrix(wbw_raster, raw=raw) 17 | 18 | # Convert to 1D array column-wise (Fortran-style ordering) 19 | return matrix.flatten(order="C") 20 | 21 | 22 | def wbw_to_matrix(wbw_raster, raw=False): 23 | """ 24 | Converts a Whitebox Raster object to a 2d numpy array. 25 | 26 | Args: 27 | wbw_raster: A Whitebox Raster object 28 | raw: If False (default), replaces NoData values with None/np.nan. If True, keeps original values. 29 | 30 | Returns: 31 | A 2d numpy array. 32 | """ 33 | nrows = wbw_raster.configs.rows 34 | ncols = wbw_raster.configs.columns 35 | dtype = raster_dtype(wbw_raster) 36 | 37 | # For integer types, convert to float64 if we need to represent NoData 38 | if not raw and np.issubdtype(dtype, np.integer): 39 | dtype = np.float64 40 | 41 | matrix = np.zeros((nrows, ncols), dtype=dtype) 42 | 43 | for i in range(nrows): 44 | matrix[i] = wbw_raster.get_row_data(i) 45 | 46 | if not raw: 47 | nodata = wbw_raster.configs.nodata 48 | matrix[matrix == nodata] = np.nan 49 | 50 | return matrix 51 | 52 | 53 | def matrix_to_wbw(matrix, wbw_raster): 54 | """ 55 | Writes a 2d numpy array to a Whitebox Raster object. 56 | 57 | Args: 58 | matrix: A 2d numpy array with values to write 59 | wbw_raster: A Whitebox Raster object to write the data to 60 | 61 | Returns: 62 | None. The wbw_raster is modified in place. 63 | """ 64 | if matrix.shape != (wbw_raster.configs.rows, wbw_raster.configs.columns): 65 | raise ValueError( 66 | f"Matrix shape {matrix.shape} does not match raster dimensions " 67 | f"({wbw_raster.configs.rows}, {wbw_raster.configs.columns})" 68 | ) 69 | 70 | # Ensure matrix data type matches raster 71 | target_dtype = raster_dtype(wbw_raster) 72 | if matrix.dtype != target_dtype: 73 | matrix = matrix.astype(target_dtype) 74 | 75 | # Write data row by row 76 | for i in range(wbw_raster.configs.rows): 77 | wbw_raster.set_row_data(i, matrix[i]) 78 | 79 | 80 | def raster_dtype(wbw_raster): 81 | dtype_mapping = { 82 | "RasterDataType.F64": np.float64, 83 | "RasterDataType.F32": np.float32, 84 | "RasterDataType.I64": np.int64, 85 | "RasterDataType.U64": np.uint64, 86 | "RasterDataType.RGB48": np.uint16, # Typically represented as unsigned 16-bit 87 | "RasterDataType.I32": np.int32, 88 | "RasterDataType.U32": np.uint32, 89 | "RasterDataType.RGB24": np.uint8, # Typically represented as unsigned 8-bit 90 | "RasterDataType.RGBA32": np.uint8, # Assuming RGBA32 as unsigned 8-bit per channel 91 | "RasterDataType.I16": np.int16, 92 | "RasterDataType.U16": np.uint16, 93 | "RasterDataType.I8": np.int8, 94 | "RasterDataType.U8": np.uint8, 95 | } 96 | 97 | dt = str(wbw_raster.configs.data_type) 98 | 99 | return dtype_mapping.get(dt, None) 100 | -------------------------------------------------------------------------------- /R/installation.R: -------------------------------------------------------------------------------- 1 | #' Install Required Python Modules 2 | #' @keywords system 3 | #' 4 | #' This function installs the latest \code{numpy}, \code{whitebox-workflows}. 5 | #' The default uses \code{pip} for package installation. 6 | #' 7 | #' @param system \code{boolean}, Use a \code{system()} call to 8 | #' \code{python -m pip install --user ...} 9 | #' instead of \code{reticulate::py_install()}. Default: \code{FALSE}. 10 | #' @param force \code{boolean}, Force update (uninstall/reinstall) and ignore 11 | #' existing installed packages? Default: \code{FALSE}. 12 | #' Applies to \code{system=TRUE}. 13 | #' @param ... Additional arguments passed to `reticulate::py_install()` 14 | #' 15 | #' @details This function provides a basic wrapper around 16 | #' `reticulate::py_install()`, except it defaults to using the Python package 17 | #' manager \code{pip} and virtual environment. It creates the \code{r-wbw} 18 | #' virtual environment in the default location 19 | #' (run `reticulate::virtualenv_root()` to find it) and installs the 20 | #' required python packages. 21 | #' 22 | #' @return `NULL`, or `try-error` (invisibly) on R code execution error. 23 | #' 24 | #' @export 25 | wbw_install <- function(system = FALSE, force = FALSE, ...) { 26 | args <- list(...) 27 | 28 | venv_name <- "r-wbw" 29 | venv_exists <- try( 30 | reticulate::virtualenv_exists(venv_name), 31 | silent = TRUE 32 | ) 33 | 34 | # Check if venv exists 35 | if (venv_exists && !system) { 36 | # Check if `whitebox-workflows` is installed 37 | reticulate::use_virtualenv(venv_name) 38 | wbw_version <- wbw_version() 39 | } 40 | 41 | # Install `whitebox-workflows` 42 | if (venv_exists && is.null(wbw_version) && !system) { 43 | # venv exists but whitebox is not installed 44 | # install it from https://pypi.org/project/whitebox-workflows/ 45 | reticulate::use_virtualenv(virtualenv = venv_name) 46 | reticulate::virtualenv_install( 47 | packages = c("numpy", "whitebox-workflows==1.3.3"), 48 | envname = venv_name 49 | ) 50 | .success_message(wbw_version()) 51 | } else if (!venv_exists && !system) { 52 | # nothing is installed, create venv and install deps 53 | # from https://pypi.org/project/whitebox-workflows/ 54 | reticulate::virtualenv_create( 55 | envname = venv_name, 56 | version = ">= 3.8" 57 | ) 58 | reticulate::use_virtualenv( 59 | virtualenv = venv_name 60 | ) 61 | reticulate::virtualenv_install( 62 | packages = c("numpy", "whitebox-workflows==1.3.3"), 63 | envname = venv_name 64 | ) 65 | .success_message(wbw_version()) 66 | cli::cli_alert_info( 67 | c( 68 | "Please, restart you R session" 69 | ) 70 | ) 71 | } else if (venv_exists && !is.null(wbw_version) && !system) { 72 | .success_message(wbw_version()) 73 | } 74 | 75 | if (system) { 76 | fp <- .find_python() 77 | if (nchar(fp) > 0) { 78 | return( 79 | invisible( 80 | system( 81 | paste( 82 | shQuote(fp), 83 | "-m pip install --user", 84 | ifelse(force, "-U --force", ""), 85 | "whitebox-workflows==1.3.3 numpy" 86 | ) 87 | ) 88 | ) 89 | ) 90 | } 91 | } 92 | } 93 | 94 | .success_message <- function(wbw_version) { 95 | cli::cli_alert_success( 96 | c( 97 | "You're all set! The Python library {.code whitebox-workflows} ", 98 | "v{wbw_version} has been found!" 99 | ) 100 | ) 101 | } 102 | -------------------------------------------------------------------------------- /R/summary.R: -------------------------------------------------------------------------------- 1 | #' @importFrom stats median 2 | NULL 3 | 4 | #' @exportS3Method max wbw::WhiteboxRaster 5 | #' @exportS3Method min wbw::WhiteboxRaster 6 | #' @exportS3Method median wbw::WhiteboxRaster 7 | NULL 8 | 9 | #' Summary Statistics for WhiteboxRaster 10 | #' @rdname summarize 11 | #' @docType methods 12 | #' @keywords stats 13 | #' 14 | #' @description 15 | #' Computes summary statistics for cells in a [WhiteboxRaster] object. 16 | #' 17 | #' @param object [WhiteboxRaster] object 18 | #' @param ... additional arguments passed to summary 19 | #' 20 | #' @eval rd_example("summary") 21 | #' @eval rd_wbw_link("raster_summary_stats") 22 | #' 23 | #' @export 24 | `summary.wbw::WhiteboxRaster` <- function(object, ...) { 25 | cat( 26 | wbe$raster_summary_stats(object@source) 27 | ) 28 | } 29 | 30 | #' @rdname summarize 31 | #' @docType methods 32 | #' 33 | #' @param object WhiteboxRaster object 34 | #' @param ... additional arguments (not used) 35 | #' @return numeric value 36 | #' 37 | #' @eval rd_example("max") 38 | #' 39 | #' @export 40 | `max.wbw::WhiteboxRaster` <- function(object, ...) { 41 | check_env(wbe) 42 | stats <- wbe$raster_summary_stats(object@source) 43 | extract_stat(stats, "maximum") 44 | } 45 | 46 | #' @rdname summarize 47 | #' @docType methods 48 | #' 49 | #' @param x WhiteboxRaster object 50 | #' @param ... additional arguments (not used) 51 | #' 52 | #' @eval rd_example("mean") 53 | #' 54 | #' @export 55 | `mean.wbw::WhiteboxRaster` <- function(x, ...) { 56 | x@source$calculate_mean() 57 | } 58 | 59 | #' @rdname summarize 60 | #' @docType methods 61 | #' 62 | #' @param x WhiteboxRaster object 63 | #' @param na.rm logical indicating whether NA values should 64 | #' be stripped (not used) 65 | #' @param ... additional arguments (not used) 66 | #' 67 | #' @eval rd_example("median") 68 | #' 69 | #' @export 70 | `median.wbw::WhiteboxRaster` <- function(x, na.rm = FALSE, ...) { 71 | x@source$calculate_clip_values(percent = 50)[[1]] 72 | } 73 | 74 | #' @rdname summarize 75 | #' @docType methods 76 | #' 77 | #' @param object WhiteboxRaster object 78 | #' @param ... additional arguments (not used) 79 | #' 80 | #' @eval rd_example("min") 81 | #' 82 | #' @export 83 | `min.wbw::WhiteboxRaster` <- function(object, ...) { 84 | check_env(wbe) 85 | stats <- wbe$raster_summary_stats(object@source) 86 | extract_stat(stats, "minimum") 87 | } 88 | 89 | #' @rdname summarize 90 | #' @docType methods 91 | #' 92 | #' @param x WhiteboxRaster object 93 | #' 94 | #' @eval rd_example("stdev") 95 | #' 96 | #' @export 97 | stdev <- S7::new_generic( 98 | name = "stdev", 99 | dispatch_args = "x", 100 | fun = function(x) { 101 | S7::S7_dispatch() 102 | } 103 | ) 104 | 105 | S7::method(stdev, WhiteboxRaster) <- function(x) { 106 | check_env(wbe) 107 | stats <- wbe$raster_summary_stats(x@source) 108 | extract_stat(stats, "standard deviation") 109 | } 110 | 111 | #' @rdname summarize 112 | #' @docType methods 113 | #' 114 | #' @param x WhiteboxRaster object 115 | #' 116 | #' @eval rd_example("variance") 117 | #' 118 | #' @export 119 | variance <- S7::new_generic( 120 | name = "variance", 121 | dispatch_args = "x", 122 | fun = function(x) { 123 | S7::S7_dispatch() 124 | } 125 | ) 126 | 127 | S7::method(variance, WhiteboxRaster) <- function(x) { 128 | check_env(wbe) 129 | stats <- wbe$raster_summary_stats(x@source) 130 | extract_stat(stats, "variance") 131 | } 132 | 133 | # Helper function to extract numeric value from a specific stat line 134 | extract_stat <- function(stats_text, pattern) { 135 | line <- grep(pattern, strsplit(stats_text, "\n")[[1]], value = TRUE) 136 | as.numeric(sub(".*: ", "", line)) 137 | } 138 | -------------------------------------------------------------------------------- /R/WhiteboxClasses.R: -------------------------------------------------------------------------------- 1 | #' WhiteboxExtent Class 2 | #' @keywords class 3 | #' 4 | #' @description 5 | #' Defines the spatial extent of a raster dataset using coordinates for 6 | #' the west, east, south, and north boundaries. 7 | #' 8 | #' @param west \code{double} Western boundary coordinate 9 | #' @param east \code{double} Eastern boundary coordinate 10 | #' @param south \code{double} Southern boundary coordinate 11 | #' @param north \code{double} Northern boundary coordinate 12 | #' 13 | #' @export 14 | WhiteboxExtent <- S7::new_class( 15 | name = "WhiteboxExtent", 16 | package = "wbw", 17 | properties = list( 18 | west = S7::new_property( 19 | class = S7::class_double, 20 | validator = function(value) { 21 | if (length(value) != 1) { 22 | return("'west' must be a single numeric value") 23 | } 24 | } 25 | ), 26 | east = S7::new_property( 27 | class = S7::class_double, 28 | validator = function(value) { 29 | if (length(value) != 1) { 30 | return("'east' must be a single numeric value") 31 | } 32 | } 33 | ), 34 | south = S7::new_property( 35 | class = S7::class_double, 36 | validator = function(value) { 37 | if (length(value) != 1) { 38 | return("'south' must be a single numeric value") 39 | } 40 | } 41 | ), 42 | north = S7::new_property( 43 | class = S7::class_double, 44 | validator = function(value) { 45 | if (length(value) != 1) { 46 | return("'north' must be a single numeric value") 47 | } 48 | } 49 | ) 50 | ) 51 | ) 52 | 53 | #' WhiteboxRaster Class 54 | #' @keywords class 55 | #' 56 | #' @description 57 | #' Represents a raster dataset in Whitebox Workflows for Python (WbW) format. 58 | #' Provides access to raster properties including name, Python pointer, summary 59 | #' statistics, and min/max values. 60 | #' 61 | #' @param name \code{character} Name of the raster 62 | #' @param source \code{any} Source data for the raster 63 | #' 64 | #' @section Properties: 65 | #' \describe{ 66 | #' \item{stats}{\code{character} Summary statistics for the raster} 67 | #' \item{min}{\code{numeric} Minimum value for the raster} 68 | #' \item{max}{\code{numeric} Maximum value for the raster} 69 | #' \item{extent}{\code{WhiteboxExtent} Spatial extent of the raster} 70 | #' } 71 | #' @export 72 | WhiteboxRaster <- S7::new_class( 73 | name = "WhiteboxRaster", 74 | package = "wbw", 75 | properties = list( 76 | name = S7::class_character, 77 | source = S7::class_any, 78 | stats = S7::new_property( 79 | class = S7::class_character, 80 | getter = function(self) { 81 | check_env(wbe) 82 | wbe$raster_summary_stats(self@source) 83 | } 84 | ), 85 | min = S7::new_property( 86 | class = S7::class_double, 87 | getter = function(self) { 88 | extract_stat(self@stats, "minimum") 89 | }, 90 | validator = function(value) { 91 | if (!is.numeric(value) || length(value) != 1) { 92 | return("@min must be a single numeric value") 93 | } 94 | } 95 | ), 96 | max = S7::new_property( 97 | class = S7::class_double, 98 | getter = function(self) { 99 | extract_stat(self@stats, "maximum") 100 | }, 101 | validator = function(value) { 102 | if (!is.numeric(value) || length(value) != 1) { 103 | return("@max must be a single numeric value") 104 | } 105 | } 106 | ) 107 | # extent = S7::new_property( 108 | # class = WhiteboxExtent, 109 | # getter = function(self) { 110 | # conf <- self@source$configs 111 | # WhiteboxExtent( 112 | # west = conf$west, 113 | # east = conf$east, 114 | # south = conf$south, 115 | # north = conf$north 116 | # ) 117 | # } 118 | # ) 119 | ) 120 | ) 121 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to wbw 2 | 3 | This outlines how to propose a change to wbw. 4 | For a detailed discussion on contributing to this and other tidyverse packages, please see the [development contributing guide](https://rstd.io/tidy-contrib) and our [code review principles](https://code-review.tidyverse.org/). 5 | 6 | ## Fixing typos 7 | 8 | You can fix typos, spelling mistakes, or grammatical errors in the documentation directly using the GitHub web interface, as long as the changes are made in the _source_ file. 9 | This generally means you'll need to edit [roxygen2 comments](https://roxygen2.r-lib.org/articles/roxygen2.html) in an `.R`, not a `.Rd` file. 10 | You can find the `.R` file that generates the `.Rd` by reading the comment in the first line. 11 | 12 | ## Bigger changes 13 | 14 | If you want to make a bigger change, it's a good idea to first file an issue and make sure someone from the team agrees that it’s needed. 15 | If you’ve found a bug, please file an issue that illustrates the bug with a minimal 16 | [reprex](https://www.tidyverse.org/help/#reprex) (this will also help you write a unit test, if needed). 17 | See our guide on [how to create a great issue](https://code-review.tidyverse.org/issues/) for more advice. 18 | 19 | ### Pull request process 20 | 21 | * Fork the package and clone onto your computer. If you haven't done this before, we recommend using `usethis::create_from_github("atsyplenkov/wbw", fork = TRUE)`. 22 | 23 | * Install all development dependencies with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 24 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 25 | * Create a Git branch for your pull request (PR). We recommend using `usethis::pr_init("brief-description-of-change")`. 26 | 27 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 28 | The title of your PR should briefly describe the change. 29 | The body of your PR should contain `Fixes #issue-number`. 30 | 31 | * For user-facing changes, add a bullet to the top of `NEWS.md` (i.e. just below the first header). Follow the style described in . 32 | 33 | ### Code style 34 | 35 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 36 | You can use the [air](https://github.com/posit-dev/air) to apply these styles, but please don't restyle code that has nothing to do with your PR. 37 | 38 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/rd-formatting.html), for documentation. 39 | 40 | * We use [tinytest](https://cran.r-project.org/package=tinytest) for unit tests. 41 | Contributions with test cases included are easier to accept. 42 | 43 | ### Writing S7 documentation 44 | 45 | The `{wbw}` package uses several [S7 46 | classes](https://rconsortium.github.io/S7/articles/classes-objects.html) which 47 | should be documented properly. There's a function documentation [template](https://github.com/atsyplenkov/wbw/blob/main/.github/template.R) 48 | available in the `.github` folder, which we advise you to use when creating or 49 | editing new functions. 50 | 51 | ### Git message format 52 | 53 | This repo adheres to the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) convention. 54 | 55 | #### Used prefixes 56 | 57 | - **chore**: Changes that affect the build system or external dependencies 58 | - **ci**: Changes to our CI configuration files and scripts 59 | - **docs**: Documentation only changes 60 | - **feat**: A new feature 61 | - **fix**: A bug fix 62 | - **perf**: A code change that improves performance 63 | - **refactor**: A code change that neither fixes a bug nor adds a feature 64 | - **lint**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 65 | - **test**: Adding missing tests or correcting existing tests 66 | - **revert**: When reverting a commit 67 | - **release**: All related to changeset (pre exit...) 68 | 69 | For example: 70 | 71 | ``` 72 | docs: Added documentation for wbw_slope() function 73 | ``` 74 | 75 | ## Code of Conduct 76 | 77 | Please note that the wbw project is released with a 78 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 79 | project you agree to abide by its terms. 80 | -------------------------------------------------------------------------------- /R/terra.R: -------------------------------------------------------------------------------- 1 | #' Convert WhiteboxRaster to SpatRaster 2 | #' @keywords terra 3 | #' 4 | #' Converts a [WhiteboxRaster] to a SpatRaster object 5 | #' 6 | #' @eval rd_input_raster("x") 7 | #' 8 | #' @return SpatRaster object 9 | #' 10 | #' @seealso [WhiteboxRaster()] 11 | #' 12 | #' @examples 13 | #' \dontrun{ 14 | #' f <- system.file("extdata/dem.tif", package = "wbw") 15 | #' wbw_read_raster(f) |> 16 | #' wbw_slope(units = "r") |> 17 | #' as_rast() 18 | #' } 19 | #' @export 20 | as_rast <- S7::new_generic( 21 | name = "as_rast", 22 | dispatch_args = "x", 23 | fun = function(x) { 24 | S7::S7_dispatch() 25 | } 26 | ) 27 | 28 | S7::method(as_rast, WhiteboxRaster) <- function(x) { 29 | # Checks 30 | check_env(wbe) 31 | 32 | # Prepare data 33 | v <- as_vector(x) 34 | wbw_nodata <- x@source$configs$nodata 35 | # TODO: 36 | # Depending on the x@source$configs$data_type, 37 | # Use either NA_integer_ or NA_real_ 38 | v[v == wbw_nodata] <- NA 39 | 40 | # Prepare CRS and Extent 41 | ext <- c( 42 | # Note the differences in east and south between reading by 43 | # GDAL (i.e. terra) and WhiteboxTools 44 | x@source$configs$west, 45 | x@source$configs$east + wbw_xres(x), 46 | x@source$configs$south - wbw_yres(x), 47 | x@source$configs$north 48 | ) 49 | crs <- if (x@source$configs$epsg_code == 0) { 50 | x@source$configs$coordinate_ref_system_wkt 51 | } else { 52 | terra::crs(paste0("epsg:", x@source$configs$epsg_code)) 53 | } 54 | 55 | # Convert 56 | new_rast <- terra::rast( 57 | vals = v, 58 | nlyrs = 1L, 59 | crs = crs, 60 | extent = ext, 61 | resolution = wbw_res(x), 62 | names = x@name 63 | ) 64 | 65 | # Convert to integer if necessary 66 | if (wbw_is_int(x)) { 67 | new_rast <- terra::as.int(new_rast) 68 | } 69 | 70 | # Return 71 | new_rast 72 | } 73 | 74 | #' Convert SpatRaster to WhiteboxRaster 75 | #' @keywords terra 76 | #' 77 | #' Converts a SpatRaster to a [WhiteboxRaster] object 78 | #' 79 | #' @param x SpatRaster object 80 | #' 81 | #' @return [WhiteboxRaster] object 82 | #' 83 | #' @seealso [WhiteboxRaster()] 84 | #' 85 | #' @examples 86 | #' \dontrun{ 87 | #' library(terra) 88 | #' f <- system.file("extdata/dem.tif", package = "wbw") 89 | #' rast(f) |> 90 | #' as_wbw_raster() 91 | #' } 92 | #' @export 93 | as_wbw_raster <- function(x) { 94 | # Checks 95 | checkmate::assert_class(x, "SpatRaster") 96 | checkmate::assert_class( 97 | wbw, 98 | classes = c( 99 | "python.builtin.module", 100 | "python.builtin.object" 101 | ) 102 | ) 103 | checkmate::assert_true( 104 | terra::nlyr(x) == 1 105 | ) 106 | 107 | # SpatRaster information 108 | na_terra <- terra::NAflag(x) 109 | nodata_value <- if (is.nan(na_terra)) { 110 | -9999 111 | } else { 112 | na_terra 113 | } 114 | res_terra <- terra::res(x) 115 | name_terra <- names(x) 116 | type_terra <- any( 117 | c( 118 | terra::is.int(x), 119 | terra::is.bool(x), 120 | terra::is.factor(x) 121 | ) 122 | ) 123 | ext_terra <- terra::ext(x) 124 | data_terra <- as.matrix(x, wide = TRUE) 125 | data_terra[is.na(data_terra)] <- nodata_value 126 | 127 | # Create new RasterConfigs 128 | new_config <- wbw$RasterConfigs() 129 | new_config$title <- name_terra 130 | 131 | # Dimensions 132 | new_config$bands <- as.integer(terra::nlyr(x)) 133 | new_config$columns <- as.integer(terra::ncol(x)) 134 | new_config$rows <- as.integer(terra::nrow(x)) 135 | new_config$west <- as.double(ext_terra[1]) 136 | ## Note the differences in east and south between reading by 137 | ## GDAL (i.e. terra) and WhiteboxTools 138 | new_config$east <- as.double(ext_terra[2]) - res_terra[1] 139 | new_config$south <- as.double(ext_terra[3]) + res_terra[2] 140 | new_config$north <- as.double(ext_terra[4]) 141 | 142 | # CRS 143 | new_config$coordinate_ref_system_wkt <- terra::crs(x) 144 | new_config$resolution_x <- res_terra[1] 145 | new_config$resolution_y <- res_terra[2] 146 | 147 | # Data type 148 | new_config$nodata <- nodata_value 149 | new_config$data_type <- if (type_terra) { 150 | wbw$RasterDataType$I16 151 | } else { 152 | wbw$RasterDataType$F32 153 | } 154 | 155 | # Create WhiteboxRaster 156 | new_raster <- wbe$new_raster(new_config) 157 | wbw_env$matrix_to_wbw(data_terra, new_raster) 158 | 159 | WhiteboxRaster( 160 | name = name_terra, 161 | source = new_raster 162 | ) 163 | } 164 | -------------------------------------------------------------------------------- /tests/tinytest/test_filter.R: -------------------------------------------------------------------------------- 1 | source("setup.R") 2 | 3 | # Test successful filter returns 4 | expect_inherits( 5 | wbw_adaptive_filter(x), 6 | c("wbw::WhiteboxRaster", "S7_object") 7 | ) 8 | expect_inherits( 9 | wbw_bilateral_filter(x), 10 | c("wbw::WhiteboxRaster", "S7_object") 11 | ) 12 | expect_inherits( 13 | wbw_conservative_smoothing_filter(x), 14 | c("wbw::WhiteboxRaster", "S7_object") 15 | ) 16 | expect_inherits( 17 | wbw_gaussian_filter(x), 18 | c("wbw::WhiteboxRaster", "S7_object") 19 | ) 20 | expect_inherits( 21 | wbw_high_pass_filter(x), 22 | c("wbw::WhiteboxRaster", "S7_object") 23 | ) 24 | expect_inherits( 25 | wbw_high_pass_median_filter(x), 26 | c("wbw::WhiteboxRaster", "S7_object") 27 | ) 28 | expect_inherits( 29 | wbw_majority_filter(x), 30 | c("wbw::WhiteboxRaster", "S7_object") 31 | ) 32 | expect_inherits( 33 | wbw_maximum_filter(x), 34 | c("wbw::WhiteboxRaster", "S7_object") 35 | ) 36 | expect_inherits( 37 | wbw_mean_filter(x), 38 | c("wbw::WhiteboxRaster", "S7_object") 39 | ) 40 | expect_inherits( 41 | wbw_median_filter(x), 42 | c("wbw::WhiteboxRaster", "S7_object") 43 | ) 44 | expect_inherits( 45 | wbw_minimum_filter(x), 46 | c("wbw::WhiteboxRaster", "S7_object") 47 | ) 48 | expect_inherits( 49 | wbw_olympic_filter(x), 50 | c("wbw::WhiteboxRaster", "S7_object") 51 | ) 52 | expect_inherits( 53 | wbw_percentile_filter(x), 54 | c("wbw::WhiteboxRaster", "S7_object") 55 | ) 56 | expect_inherits( 57 | wbw_range_filter(x), 58 | c("wbw::WhiteboxRaster", "S7_object") 59 | ) 60 | expect_inherits( 61 | wbw_total_filter(x), 62 | c("wbw::WhiteboxRaster", "S7_object") 63 | ) 64 | expect_inherits( 65 | wbw_standard_deviation_filter(x), 66 | c("wbw::WhiteboxRaster", "S7_object") 67 | ) 68 | 69 | # Test filter alterations 70 | # Here is near-equality check is happening. If two values are close to 71 | # be equal, i.e. 2.222222226 and 2.222222225, then all.equal() returns TRUE 72 | # In other cases the function will return the mean relative difference as 73 | # a character vector 74 | true_median <- median(x) 75 | 76 | expect_true( 77 | wbw_adaptive_filter( 78 | x, 79 | filter_size_x = 51, 80 | filter_size_y = 51 81 | ) |> 82 | median() |> 83 | all.equal(true_median) |> 84 | is.character() 85 | ) 86 | expect_true( 87 | wbw_bilateral_filter( 88 | x, 89 | sigma_dist = 3 90 | ) |> 91 | median() |> 92 | all.equal(true_median) |> 93 | is.character() 94 | ) 95 | expect_true( 96 | wbw_conservative_smoothing_filter(x) |> 97 | median() |> 98 | all.equal(true_median) |> 99 | is.character() 100 | ) 101 | expect_true( 102 | wbw_gaussian_filter(x, sigma = 1.5) |> 103 | median() |> 104 | all.equal(true_median) |> 105 | is.character() 106 | ) 107 | expect_true( 108 | wbw_high_pass_filter(x) |> 109 | median() |> 110 | all.equal(true_median) |> 111 | is.character() 112 | ) 113 | expect_true( 114 | wbw_high_pass_median_filter(x) |> 115 | median() |> 116 | all.equal(true_median) |> 117 | is.character() 118 | ) 119 | expect_true( 120 | wbw_majority_filter(x) |> 121 | median() |> 122 | all.equal(true_median) |> 123 | is.character() 124 | ) 125 | expect_true( 126 | wbw_maximum_filter(x) |> 127 | median() |> 128 | all.equal(true_median) |> 129 | is.character() 130 | ) 131 | expect_true( 132 | wbw_mean_filter(x) |> 133 | median() |> 134 | all.equal(true_median) |> 135 | is.character() 136 | ) 137 | expect_true( 138 | wbw_median_filter(x) |> 139 | median() |> 140 | all.equal(true_median) |> 141 | is.character() 142 | ) 143 | expect_true( 144 | wbw_minimum_filter(x) |> 145 | median() |> 146 | all.equal(true_median) |> 147 | is.character() 148 | ) 149 | expect_true( 150 | wbw_olympic_filter(x) |> 151 | median() |> 152 | all.equal(true_median) |> 153 | is.character() 154 | ) 155 | expect_true( 156 | wbw_percentile_filter(x) |> 157 | median() |> 158 | all.equal(true_median) |> 159 | is.character() 160 | ) 161 | expect_true( 162 | wbw_range_filter(x) |> 163 | median() |> 164 | all.equal(true_median) |> 165 | is.character() 166 | ) 167 | expect_true( 168 | wbw_total_filter(x) |> 169 | median() |> 170 | all.equal(true_median) |> 171 | is.character() 172 | ) 173 | expect_true( 174 | wbw_standard_deviation_filter(x) |> 175 | median() |> 176 | all.equal(true_median) |> 177 | is.character() 178 | ) 179 | -------------------------------------------------------------------------------- /R/system.R: -------------------------------------------------------------------------------- 1 | #' Set Maximum Parallel Processors 2 | #' @keywords system 3 | #' 4 | #' @description 5 | #' Determines the number of processors used by functions that are parallelized. 6 | #' If set to -1 (`max_procs=-1`), the default, all available processors will be 7 | #' used. To limit processing, set `max_procs` to a positive whole number less 8 | #' than the number of system processors. 9 | #' 10 | #' @param max_procs \code{integer} Number of processors to use. Use -1 for all 11 | #' available processors, or a positive integer to limit processor usage. 12 | #' 13 | #' @eval rd_wbw_link("max_procs") 14 | #' 15 | #' @examples 16 | #' \dontrun{ 17 | #' raster_path <- system.file("extdata/dem.tif", package = "wbw") 18 | #' x <- wbw_read_raster(raster_path) 19 | #' 20 | #' # Use 1 processor 21 | #' wbw_max_procs(1) 22 | #' system.time(wbw_slope(x)) 23 | #' 24 | #' # Use all available processors 25 | #' wbw_max_procs(-1) 26 | #' system.time(wbw_slope(x)) 27 | #' } 28 | #' @export 29 | wbw_max_procs <- function(max_procs = -1) { 30 | check_env(wbe) 31 | max_procs <- checkmate::asInteger( 32 | max_procs, 33 | len = 1 34 | ) 35 | checkmate::assert_true( 36 | max_procs >= -1 && max_procs != 0 37 | ) 38 | wbe$max_procs <- max_procs 39 | } 40 | 41 | #' Download Sample Data 42 | #' @keywords system 43 | #' 44 | #' @description 45 | #' There are a number of available sample datasets that can be readily used 46 | #' to test Whitebox Workflows for Python. 47 | #' 48 | #' @param data_set \code{character}, dataset name. See Details 49 | #' @param path \code{character}, path to where download sample datasets. If 50 | #' \code{NULL}, the currect working directory is used 51 | #' 52 | #' @details 53 | #' Available datasets: 54 | #' 55 | #' | **Dataset Name** | **Description** | **Size** | 56 | #' |-----------------|-----------------|-----------| 57 | #' | Guelph_landsat | Landsat 5 sub-area (7 bands) | 10.9 MB | 58 | #' | Grand_Junction | Small DEM in high-relief terrain | 5.8 MB | 59 | #' | GTA_lidar | Airborne lidar point cloud (LAZ) | 54.3 MB | 60 | #' | jay_brook | Airborne lidar point cloud (LAZ) | 76.3 MB | 61 | #' | Jay_State_Forest | Lidar-derived DEM | 27.7 MB | 62 | #' | Kitchener_lidar | Airborne lidar point cloud (LAZ) | 41.6 MB | 63 | #' | London_air_photo | High-resolution RGB air photo | 87.3 MB | 64 | #' | mill_brook | Airborne lidar point cloud (LAZ) | 49.9 MB | 65 | #' | peterborough_drumlins | Lidar-derived DEM | 22.0 MB | 66 | #' | Southern_Ontario_roads | Vector roads layer | 7.1 MB | 67 | #' | StElisAk | Airborne lidar point cloud (LAZ) | 54.5 MB | 68 | #' 69 | #' @examples 70 | #' \dontrun{ 71 | #' # Download sample data 72 | #' wbw_download_sample_data(data_set = "Guelph_landsat", path = tempdir()) 73 | #' } 74 | #' @export 75 | #' @importFrom utils download.file unzip 76 | wbw_download_sample_data <- function(data_set = NULL, path = NULL) { 77 | # Set Download Path if NULL 78 | if (is.null(path)) { 79 | path <- getwd() 80 | } 81 | checkmate::assert_directory( 82 | path, 83 | access = "w" 84 | ) 85 | 86 | # Select Dataset 87 | data_set <- checkmate::matchArg( 88 | data_set, 89 | choices = c( 90 | "Guelph_landsat", 91 | "Grand_Junction", 92 | "GTA_lidar", 93 | "jay_brook", 94 | "Jay_State_Forest", 95 | "Kitchener_lidar", 96 | "London_air_photo", 97 | "mill_brook", 98 | "peterborough_drumlins", 99 | "Southern_Ontario_roads", 100 | "StElisAk" 101 | ) 102 | ) 103 | 104 | # Create sub-directory if it doesn't exist 105 | download_path <- file.path(path, data_set) 106 | if (!dir.exists(download_path)) { 107 | dir.create(download_path) 108 | } 109 | 110 | # Download Data 111 | base_url <- "https://www.whiteboxgeo.com/sample_data/" 112 | data_url <- paste0(base_url, data_set, ".zip") 113 | download.file( 114 | data_url, 115 | destfile = file.path(download_path, paste0(data_set, ".zip")) 116 | ) 117 | 118 | # Unzip Data 119 | temp_dir <- file.path(download_path, "temp_extract") 120 | dir.create(temp_dir) 121 | 122 | unzip( 123 | file.path(download_path, paste0(data_set, ".zip")), 124 | exdir = temp_dir 125 | ) 126 | 127 | # Check if data_set folder exists inside the extracted contents 128 | dataset_folder <- file.path(temp_dir, data_set) 129 | if (dir.exists(dataset_folder)) { 130 | # If dataset folder exists, move its contents to download_path 131 | files_to_move <- list.files(dataset_folder, full.names = TRUE) 132 | file.copy(files_to_move, download_path, recursive = TRUE) 133 | } else { 134 | # If no specific dataset folder, move all contents except __MACOSX 135 | all_contents <- list.files(temp_dir, full.names = TRUE) 136 | files_to_move <- all_contents[!grepl("__MACOSX", all_contents)] 137 | file.copy(files_to_move, download_path, recursive = TRUE) 138 | } 139 | 140 | # Clean up 141 | unlink(temp_dir, recursive = TRUE) 142 | unlink(file.path(download_path, paste0(data_set, ".zip"))) 143 | 144 | # Return download path 145 | cli::cli_alert_success( 146 | c( 147 | "Sample dataset '{.val {data_set}}' ", 148 | "downloaded to: {.file {download_path}}" 149 | ) 150 | ) 151 | return(download_path) 152 | } 153 | -------------------------------------------------------------------------------- /R/dimensions.R: -------------------------------------------------------------------------------- 1 | #' Get dimensions of a WhiteboxRaster or WhiteboxVector object 2 | #' @rdname dimensions 3 | #' @keywords utils 4 | #' 5 | #' @eval rd_input_raster("x") 6 | #' 7 | #' @return \code{integer} Number of cells in the raster 8 | #' 9 | #' @export 10 | num_cells <- S7::new_generic( 11 | name = "num_cells", 12 | dispatch_args = "x", 13 | fun = function(x) { 14 | S7::S7_dispatch() 15 | } 16 | ) 17 | 18 | S7::method(num_cells, WhiteboxRaster) <- function(x) { 19 | # Checks 20 | check_env(wbe) 21 | x@source$num_cells() 22 | } 23 | 24 | #' @rdname dimensions 25 | #' @keywords utils 26 | #' 27 | #' @export 28 | wbw_rows <- S7::new_generic( 29 | name = "wbw_rows", 30 | dispatch_args = "x", 31 | fun = function(x) { 32 | S7::S7_dispatch() 33 | } 34 | ) 35 | 36 | S7::method(wbw_rows, WhiteboxRaster) <- function(x) { 37 | # Checks 38 | check_env(wbe) 39 | x@source$configs$rows 40 | } 41 | 42 | #' @rdname dimensions 43 | #' @keywords utils 44 | #' 45 | #' @export 46 | wbw_cols <- S7::new_generic( 47 | name = "wbw_cols", 48 | dispatch_args = "x", 49 | fun = function(x) { 50 | S7::S7_dispatch() 51 | } 52 | ) 53 | 54 | S7::method(wbw_cols, WhiteboxRaster) <- function(x) { 55 | # Checks 56 | check_env(wbe) 57 | x@source$configs$columns 58 | } 59 | 60 | #' Get WhiteboxRaster resolution (x and y) 61 | #' @rdname resolution 62 | #' @keywords utils 63 | #' 64 | #' @eval rd_input_raster("x") 65 | #' 66 | #' @return \code{double} Vector containing x and y resolution 67 | #' 68 | #' @export 69 | wbw_res <- S7::new_generic( 70 | name = "wbw_res", 71 | dispatch_args = "x", 72 | fun = function(x) { 73 | S7::S7_dispatch() 74 | } 75 | ) 76 | 77 | S7::method(wbw_res, WhiteboxRaster) <- function(x) { 78 | # Checks 79 | check_env(wbe) 80 | c( 81 | x@source$configs$resolution_x, 82 | x@source$configs$resolution_y 83 | ) 84 | } 85 | 86 | #' @rdname resolution 87 | #' @keywords utils 88 | #' 89 | #' @export 90 | wbw_xres <- S7::new_generic( 91 | name = "wbw_xres", 92 | dispatch_args = "x", 93 | fun = function(x) { 94 | S7::S7_dispatch() 95 | } 96 | ) 97 | 98 | S7::method(wbw_xres, WhiteboxRaster) <- function(x) { 99 | # Checks 100 | check_env(wbe) 101 | x@source$configs$resolution_x 102 | } 103 | 104 | #' @rdname resolution 105 | #' @keywords utils 106 | #' 107 | #' @export 108 | wbw_yres <- S7::new_generic( 109 | name = "wbw_yres", 110 | dispatch_args = "x", 111 | fun = function(x) { 112 | S7::S7_dispatch() 113 | } 114 | ) 115 | 116 | S7::method(wbw_yres, WhiteboxRaster) <- function(x) { 117 | # Checks 118 | check_env(wbe) 119 | x@source$configs$resolution_y 120 | } 121 | 122 | #' Get WhiteboxRaster data type 123 | #' @rdname datatype 124 | #' @keywords utils 125 | #' 126 | #' @eval rd_input_raster("x") 127 | #' @eval rd_example("wbw_data_type") 128 | #' 129 | #' @return \code{character} String representing the raster data type 130 | #' 131 | #' @export 132 | wbw_data_type <- S7::new_generic( 133 | name = "wbw_data_type", 134 | dispatch_args = "x", 135 | fun = function(x) { 136 | S7::S7_dispatch() 137 | } 138 | ) 139 | 140 | S7::method(wbw_data_type, WhiteboxRaster) <- function(x) { 141 | as.character(x@source$configs$data_type) 142 | } 143 | 144 | #' @rdname datatype 145 | #' @keywords utils 146 | #' 147 | #' @eval rd_input_raster("x") 148 | #' @eval rd_example("wbw_is_int") 149 | #' 150 | #' @export 151 | wbw_is_int <- S7::new_generic( 152 | name = "wbw_is_int", 153 | dispatch_args = "x", 154 | fun = function(x) { 155 | S7::S7_dispatch() 156 | } 157 | ) 158 | 159 | S7::method(wbw_is_int, WhiteboxRaster) <- function(x) { 160 | wbw_type <- as.character(x@source$configs$data_type) 161 | 162 | any( 163 | wbw_type == "RasterDataType.I32", 164 | wbw_type == "RasterDataType.U32", 165 | wbw_type == "RasterDataType.I64", 166 | wbw_type == "RasterDataType.U64", 167 | wbw_type == "RasterDataType.I16", 168 | wbw_type == "RasterDataType.I8", 169 | wbw_type == "RasterDataType.U16", 170 | wbw_type == "RasterDataType.U8" 171 | ) 172 | } 173 | 174 | #' @rdname datatype 175 | #' @keywords utils 176 | #' 177 | #' @eval rd_input_raster("x") 178 | #' @eval rd_example("wbw_is_float") 179 | #' 180 | #' @export 181 | wbw_is_float <- S7::new_generic( 182 | name = "wbw_is_float", 183 | dispatch_args = "x", 184 | fun = function(x) { 185 | S7::S7_dispatch() 186 | } 187 | ) 188 | 189 | S7::method(wbw_is_float, WhiteboxRaster) <- function(x) { 190 | wbw_type <- as.character(x@source$configs$data_type) 191 | 192 | any( 193 | wbw_type == "RasterDataType.F32", 194 | wbw_type == "RasterDataType.F64" 195 | ) 196 | } 197 | 198 | #' @rdname datatype 199 | #' @keywords utils 200 | #' 201 | #' @eval rd_input_raster("x") 202 | #' @eval rd_example("wbw_is_rgb") 203 | #' 204 | #' @export 205 | wbw_is_rgb <- S7::new_generic( 206 | name = "wbw_is_rgb", 207 | dispatch_args = "x", 208 | fun = function(x) { 209 | S7::S7_dispatch() 210 | } 211 | ) 212 | 213 | S7::method(wbw_is_rgb, WhiteboxRaster) <- function(x) { 214 | wbw_type <- as.character(x@source$configs$data_type) 215 | 216 | any( 217 | wbw_type == "RasterDataType.RGB48", 218 | wbw_type == "RasterDataType.RGB24", 219 | wbw_type == "RasterDataType.RGBA32" 220 | ) 221 | } 222 | -------------------------------------------------------------------------------- /R/hillshade.R: -------------------------------------------------------------------------------- 1 | #' Multidirectional Hillshade 2 | #' @keywords geomorphometry 3 | #' 4 | #' @description 5 | #' This tool performs a hillshade operation (also called shaded relief) on 6 | #' an input digital elevation model (DEM) with multiple sources of 7 | #' illumination. 8 | #' 9 | #' @details 10 | #' The hillshade value (HS) of a DEM grid cell is calculate as: 11 | #' \deqn{HS = \frac{\tan(s)}{\sqrt{1 - \tan(s)^2}} \times 12 | #' [\frac{\sin(Alt)}{\tan(s)} - \cos(Alt) \times \sin(Az - a)]} 13 | #' where \eqn{s} and \eqn{a} are the local slope gradient and aspect 14 | #' (orientation) respectively and \eqn{Alt} and \eqn{Az} are the illumination 15 | #' source altitude and azimuth respectively. Slope and aspect are calculated 16 | #' using Horn's (1981) 3rd-order finate difference method. 17 | #' 18 | #' @eval rd_input_raster("dem") 19 | #' @param altitude \code{double}, the altitude of the illumination sources. 20 | #' i.e. the elevation of the sun above the horizon, measured as an angle from 21 | #' 0 to 90 degrees 22 | #' @param z_factor \code{double}, Z conversion factor is only important 23 | #' when the vertical and horizontal units are not the same in the DEM. 24 | #' When this is the case, the algorithm will multiply each elevation in the 25 | #' DEM by the Z conversion factor 26 | #' @param full_360_mode \code{boolean}, default \code{FALSE}. I.e. whether or 27 | #' not to use full 360-degrees of illumination sources. When \code{FALSE} 28 | #' (default) the tool will perform a weighted summation of the hillshade 29 | #' images from four illumination azimuth positions at 225, 270, 315, and 30 | #' 360 (0) degrees, given weights of 0.1, 0.4, 0.4, and 0.1 respectively. When 31 | #' run in the full 360-degree mode, eight illumination source azimuths are 32 | #' used to calculate the output at 0, 45, 90, 135, 180, 225, 270, and 315 33 | #' degrees, with weights of 0.15, 0.125, 0.1, 0.05, 0.1, 0.125, 0.15, 34 | #' and 0.2 respectively. 35 | #' 36 | #' @return [WhiteboxRaster] object 37 | #' 38 | #' @eval rd_wbw_link("multidirectional_hillshade") 39 | #' @references 40 | #' Horn B.K.P., 1981, Hill shading and the reflectance map, Proceedings of 41 | #' the I.E.E.E. 69, 14 42 | #' 43 | #' @seealso [wbw_hillshade()] 44 | #' 45 | #' @eval rd_example("wbw_multidirectional_hillshade") 46 | #' 47 | #' @export 48 | wbw_multidirectional_hillshade <- S7::new_generic( 49 | name = "wbw_multidirectional_hillshade", 50 | dispatch_args = "dem", 51 | fun = function(dem, altitude = 30, z_factor = 1, full_360_mode = FALSE) { 52 | S7::S7_dispatch() 53 | } 54 | ) 55 | 56 | S7::method(wbw_multidirectional_hillshade, WhiteboxRaster) <- function( 57 | dem, 58 | altitude = 30, 59 | z_factor = 1, 60 | full_360_mode = FALSE 61 | ) { 62 | # Checks 63 | check_env(wbe) 64 | checkmate::assert_double(altitude, len = 1, lower = 0, upper = 90) 65 | checkmate::assert_double(z_factor, len = 1) 66 | checkmate::assert_logical(full_360_mode, len = 1) 67 | 68 | # WBT 69 | out <- wbe$multidirectional_hillshade( 70 | dem = dem@source, 71 | altitude = altitude, 72 | z_factor = z_factor, 73 | full_360_mode = full_360_mode 74 | ) 75 | 76 | # Return 77 | WhiteboxRaster( 78 | name = paste0(dem@name, "(Hillshade)"), 79 | source = out 80 | ) 81 | } 82 | 83 | #' Hillshade 84 | #' @keywords geomorphometry 85 | #' 86 | #' @description 87 | #' This tool performs a hillshade operation (also called shaded relief) on an 88 | #' input digital elevation model (DEM). 89 | #' 90 | #' @details 91 | #' The hillshade value (HS) of a DEM grid cell is calculate as: 92 | #' \deqn{HS = \frac{\tan(s)}{\sqrt{1 - \tan(s)^2}} \times 93 | #' [\frac{\sin(Alt)}{\tan(s)} - \cos(Alt) \times \sin(Az - a)]} 94 | #' where \eqn{s} and \eqn{a} are the local slope gradient and aspect 95 | #' (orientation) respectively and \eqn{Alt} and \eqn{Az} are the illumination 96 | #' source altitude and azimuth respectively. Slope and aspect are calculated 97 | #' using Horn's (1981) 3rd-order finate difference method. 98 | #' 99 | #' If the DEM is in the geographic coordinate system (latitude and longitude), 100 | #' the following equation is used: 101 | #' \deqn{zfactor = \frac{1.0}{111320.0 \times \cos(midlat)}} 102 | #' 103 | #' where \eqn{midlat} is the latitude of the centre of the raster, 104 | #' in radians. 105 | #' 106 | #' @eval rd_input_raster("dem") 107 | #' @param azimuth \code{double}, illumination source azimuth or 108 | #' sun direction (0 to 360 degrees) 109 | #' @param altitude \code{double}, the altitude of the illumination sources. 110 | #' i.e. the elevation of the sun above the horizon, measured as an angle from 111 | #' 0 to 90 degrees 112 | #' @param z_factor \code{double}, Z conversion factor is only important 113 | #' when the vertical and horizontal units are not the same in the DEM. 114 | #' When this is the case, the algorithm will multiply each elevation in the 115 | #' DEM by the Z conversion factor 116 | #' 117 | #' @return [WhiteboxRaster] object 118 | #' 119 | #' @eval rd_wbw_link("hillshade") 120 | #' @references 121 | #' Gallant, J. C., and J. P. Wilson, 2000, Primary topographic attributes, 122 | #' in Terrain Analysis: Principles and Applications, edited by J. P. Wilson 123 | #' and J. C. Gallant pp. 51-86, John Wiley, Hoboken, N.J. 124 | #' 125 | #' @seealso [wbw_multidirectional_hillshade()] 126 | #' 127 | #' @eval rd_example("wbw_hillshade") 128 | #' 129 | #' @export 130 | wbw_hillshade <- S7::new_generic( 131 | name = "wbw_hillshade", 132 | dispatch_args = "dem", 133 | fun = function(dem, azimuth = 315, altitude = 30, z_factor = 1) { 134 | S7::S7_dispatch() 135 | } 136 | ) 137 | 138 | S7::method(wbw_hillshade, WhiteboxRaster) <- function( 139 | dem, 140 | azimuth = 315, 141 | altitude = 30, 142 | z_factor = 1 143 | ) { 144 | # Checks 145 | check_env(wbe) 146 | checkmate::assert_double(azimuth, len = 1, lower = 0, upper = 360) 147 | checkmate::assert_double(altitude, len = 1, lower = 0, upper = 90) 148 | checkmate::assert_double(z_factor, len = 1) 149 | 150 | # WBT 151 | out <- wbe$hillshade( 152 | dem = dem@source, 153 | azimuth = azimuth, 154 | altitude = altitude, 155 | z_factor = z_factor 156 | ) 157 | 158 | # Return 159 | WhiteboxRaster( 160 | name = paste0(dem@name, "(Hillshade)"), 161 | source = out 162 | ) 163 | } 164 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # Used https://github.com/brownag/rgeedim/blob/main/R/AAAA.R and 2 | # https://github.com/JosiahParry/pyfns as an examples. 3 | # Thanks guys! 4 | 5 | wbw <- NULL 6 | wbe <- NULL 7 | 8 | #' `wbw_version()`: Gets the `Whitebox Workflows` version 9 | #' @return character. Version Number. 10 | #' @export 11 | #' @importFrom reticulate py_eval 12 | wbw_version <- function() { 13 | try( 14 | reticulate::py_run_string("from importlib.metadata import version"), 15 | silent = TRUE 16 | ) 17 | version <- try( 18 | reticulate::py_eval("version('whitebox_workflows')"), 19 | silent = TRUE 20 | ) 21 | if (!inherits(version, "try-error")) { 22 | version 23 | } 24 | } 25 | 26 | #' @importFrom reticulate import 27 | #' @importFrom reticulate py_run_string 28 | .loadModules <- function() { 29 | if (is.null(wbw)) { 30 | try( 31 | { 32 | reticulate::use_virtualenv(virtualenv = "r-wbw") 33 | wbw <<- reticulate::import( 34 | "whitebox_workflows", 35 | delay_load = TRUE 36 | ) 37 | }, 38 | silent = TRUE 39 | ) 40 | } 41 | 42 | if (is.null(wbe)) { 43 | try(wbe <<- wbw$WbEnvironment(), silent = TRUE) 44 | } 45 | 46 | # note: requires Python >= 3.8; 47 | # but is not essential for functioning of package 48 | try( 49 | reticulate::source_python( 50 | system.file("wbw_helpers.py", package = "wbw"), 51 | envir = wbw_env 52 | ), 53 | silent = TRUE 54 | ) 55 | 56 | !is.null(wbe) 57 | } 58 | 59 | #' @importFrom reticulate py_discover_config 60 | .has_python3 <- function() { 61 | # get reticulate python information 62 | # NB: reticulate::py_config() calls configure_environment() etc. 63 | # .:. use py_discover_config() 64 | x <- try(reticulate::py_discover_config(), silent = TRUE) 65 | 66 | # need python 3 for reticulate 67 | # need python 3.8+ for whitebox_workflows 68 | if (length(x) > 0 && !inherits(x, "try-error")) { 69 | if (numeric_version(x$version) >= "3.8") { 70 | return(TRUE) 71 | } else if (numeric_version(x$version) >= "3.0") { 72 | return(FALSE) 73 | } 74 | } 75 | FALSE 76 | } 77 | 78 | .has_wbw <- function() { 79 | if (reticulate::virtualenv_exists("r-wbw")) { 80 | reticulate::use_virtualenv("r-wbw") 81 | have_wbw <- reticulate::py_module_available("whitebox_workflows") 82 | have_numpy <- reticulate::py_module_available("numpy") 83 | } else { 84 | have_wbw <- reticulate::py_module_available("whitebox_workflows") 85 | have_numpy <- reticulate::py_module_available("numpy") 86 | } 87 | 88 | all(have_wbw, have_numpy) 89 | } 90 | 91 | #' @importFrom reticulate configure_environment 92 | #' @importFrom cli cli_alert_info cli_alert_success cli_alert_warning 93 | #' @importFrom utils menu 94 | .onLoad <- function(libname, pkgname) { 95 | S7::methods_register() 96 | 97 | if (.has_python3() && !.has_wbw()) { 98 | if (interactive()) { 99 | cli::cli_alert_warning( 100 | "Library {.code whitebox-workflows} is required but not found." 101 | ) 102 | choice <- utils::menu( 103 | c( 104 | "Install dependencies in a virtual environment (recommended)", 105 | "Install dependencies system-wide", 106 | "Do nothing" 107 | ) 108 | ) 109 | 110 | if (choice == 1) { 111 | cli::cli_alert_info( 112 | "Installing dependencies in virtual environment..." 113 | ) 114 | i <- try(wbw_install(), silent = TRUE) 115 | if (!inherits(i, "try-error")) { 116 | cli::cli_alert_success( 117 | c( 118 | "All done! Please, restart you R session" 119 | ) 120 | ) 121 | } else { 122 | cli::cli_alert_danger( 123 | c( 124 | "Oups, something went wrong :-(\n", 125 | "Please try to install dependencies manually by running ", 126 | "{.run wbw::wbw_install()}" 127 | ) 128 | ) 129 | } 130 | } else if (choice == 2) { 131 | cli::cli_alert_info( 132 | "Installing dependencies system-wide..." 133 | ) 134 | i <- try(wbw_install(system = TRUE), silent = TRUE) 135 | if (!inherits(i, "try-error")) { 136 | cli::cli_alert_success( 137 | c( 138 | "All done!" 139 | ) 140 | ) 141 | } else { 142 | cli::cli_alert_danger( 143 | c( 144 | "Oups, something went wrong :-(\n", 145 | "Please try to install dependencies manually by running ", 146 | "{.run wbw::wbw_install()}" 147 | ) 148 | ) 149 | } 150 | } 151 | } else { 152 | # Non-interactive: install system-wide 153 | wbw_install(system = TRUE) 154 | } 155 | } 156 | 157 | # Try loading modules after potential installation 158 | if (.has_python3() && .has_wbw()) { 159 | if (!.loadModules()) { 160 | x <- try(reticulate::configure_environment(pkgname), silent = TRUE) 161 | if (!inherits(x, "try-error")) { 162 | .loadModules() 163 | } 164 | } 165 | } 166 | } 167 | 168 | #' @importFrom utils packageVersion 169 | .onAttach <- function(libname, pkgname) { 170 | wbwv <- wbw_version() 171 | suppress <- !grepl( 172 | "suppressed", 173 | Sys.getenv("wbw.message"), 174 | ignore.case = TRUE 175 | ) 176 | 177 | if (is.null(wbwv) && suppress && interactive()) { 178 | cli::cli_alert_warning( 179 | c( 180 | "Python package `whitebox-workflows` cannot be found.", 181 | "Run {.code wbw::wbw_install()} and reload R session." 182 | ) 183 | ) 184 | } else if (!is.null(wbwv) && suppress) { 185 | cli::cli_alert_success( 186 | c( 187 | "wbw v{utils::packageVersion('wbw')} -- using whitebox-workflows v{wbwv}" 188 | ) 189 | ) 190 | } 191 | } 192 | 193 | .find_python <- function() { 194 | # find python 195 | py_path <- Sys.which("python") 196 | if (nchar(py_path) == 0) { 197 | py_path <- Sys.which("python3") 198 | } 199 | py_path 200 | } 201 | -------------------------------------------------------------------------------- /pkgdown/index.md: -------------------------------------------------------------------------------- 1 | # Whitebox Workflows for R `{wbw}` 2 | 3 | 4 | [![Project Status: WIP – Initial development is in progress, but there 5 | has not yet been a stable, usable release suitable for the 6 | public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip) 7 | [![WBW 8 | Functions](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/atsyplenkov/0c46250def94614c4a3ef8b4de7460e6/raw/wbw-progress.json)](https://github.com/atsyplenkov/wbw/issues/1) 9 | [![R-CMD-check](https://github.com/atsyplenkov/wbw/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/atsyplenkov/wbw/actions/workflows/R-CMD-check.yaml) 10 | [![Codecov test 11 | coverage](https://codecov.io/gh/atsyplenkov/wbw/graph/badge.svg)](https://app.codecov.io/gh/atsyplenkov/wbw) 12 | ![GitHub last 13 | commit](https://img.shields.io/github/last-commit/atsyplenkov/wbw) 14 | 15 | 16 | The `{wbw}` package provides R bindings for the [Whitebox Workflows for 17 | Python](https://www.whiteboxgeo.com/manual/wbw-user-manual/book/preface.html) 18 | — a powerful and fast library for advanced geoprocessing, with focus on 19 | hydrological, geomorphometric and remote sensing analysis of raster, 20 | vector and LiDAR data. 21 | 22 | ## Basic workflow 23 | 24 | The `{wbw}` R package introduces several new S7 classes, including 25 | `WhiteboxRaster` and `WhiteboxVector` which serves as a bridge between 26 | Python and R. 27 | 28 | ``` r 29 | library(wbw) 30 | 31 | raster_path <- system.file("extdata/dem.tif", package = "wbw") 32 | dem <- wbw_read_raster(raster_path) 33 | dem 34 | #> +------------------------------------------+ 35 | #> | WhiteboxRaster | 36 | #> | dem.tif | 37 | #> |..........................................| 38 | #> | bands : 1 | 39 | #> | dimensions : 726, 800 (nrow, ncol) | 40 | #> | resolution : 5.002392, 5.000243 (x, y) | 41 | #> | EPSG : 2193 (Linear_Meter) | 42 | #> | min value : 63.698193 | 43 | #> | max value : 361.020721 | 44 | #> +------------------------------------------+ 45 | ``` 46 | 47 | The true power of `{wbw}` unleashes when there’s a need to run several 48 | operations sequentially, i.e., in a pipeline. Unlike the original 49 | Whitebox Tools, WbW [stores files in 50 | memory](https://www.whiteboxgeo.com/manual/wbw-user-manual/book/introduction.html#how-does-wbw-compare-with-related-whitebox-products), 51 | reducing the amount of intermediate I/O operations. 52 | 53 | For example, a DEM can be smoothed (or filtered), and then the slope can 54 | be estimated as follows: 55 | 56 | ``` r 57 | dem |> 58 | wbw_mean_filter() |> 59 | wbw_slope(units = "d") 60 | #> +------------------------------------------+ 61 | #> | WhiteboxRaster | 62 | #> | Slope (degrees) | 63 | #> |..........................................| 64 | #> | bands : 1 | 65 | #> | dimensions : 726, 800 (nrow, ncol) | 66 | #> | resolution : 5.002392, 5.000243 (x, y) | 67 | #> | EPSG : 2193 (Linear_Meter) | 68 | #> | min value : 0.005972 | 69 | #> | max value : 50.069439 | 70 | #> +------------------------------------------+ 71 | ``` 72 | 73 | ## Yet Another RSpatial Package? Why? 74 | 75 | The above example may remind you of the `{terra}` package, and it is not 76 | a coincidence. The `{wbw}` package is designed to be fully compatible 77 | with `{terra}`, and the conversion between `WhiteboxRaster` and 78 | `SpatRaster` objects happens in milliseconds (well, depending on the 79 | raster size, of course). 80 | 81 | ``` r 82 | library(terra) 83 | 84 | wbw_read_raster(raster_path) |> 85 | wbw_gaussian_filter(sigma = 1.5) |> 86 | wbw_aspect() |> 87 | as_rast() |> # Conversion to SpatRaster 88 | plot(main = "Aspect") 89 | ``` 90 | 91 | 92 | 93 | Even though `{wbw}` can be faster than `{terra}` in some cases, it is by 94 | no means intended to replace it. 95 | 96 | ``` r 97 | requireNamespace("bench", quietly = TRUE) 98 | 99 | bench::mark( 100 | terra = { 101 | s <- 102 | raster_path |> 103 | rast() |> 104 | terrain("slope", unit = "radians") |> 105 | focal(w = 15, "mean") |> 106 | global(\(x) median(x, na.rm = TRUE)) 107 | 108 | round(s$global, 2) 109 | 110 | }, 111 | wbw = { 112 | raster_path |> 113 | wbw_read_raster() |> 114 | wbw_slope("radians") |> 115 | wbw_mean_filter(15, 15) |> 116 | median() |> 117 | round(2) 118 | }, 119 | check = TRUE, 120 | iterations = 11L 121 | ) 122 | #> # A tibble: 2 × 6 123 | #> expression min median `itr/sec` mem_alloc `gc/sec` 124 | #> 125 | #> 1 terra 290.7ms 291.5ms 3.43 28.58MB 17.2 126 | #> 2 wbw 37.5ms 39.1ms 25.5 3.72KB 0 127 | ``` 128 | 129 | ## Installation 130 | 131 | You can install the development version of `{wbw}` from 132 | [GitHub](https://github.com/) with: 133 | 134 | ``` r 135 | # install.packages("pak") 136 | pak::pak("atsyplenkov/wbw") 137 | ``` 138 | 139 | > [!TIP] 140 | > The `{wbw}` package requires the `whitebox-workflows` Python library 141 | > v1.3.3+. However, you should not worry about it, as the package 142 | > is designed to install all dependencies automatically on the first run. 143 | 144 | Your machine should have **Python 3.8+** installed with `pip` and `venv` configured. Usually, these requirements are met on all modern computers. However, clean Debian installs may require the installation of system dependencies: 145 | 146 | 147 | ```bash 148 | apt update 149 | apt install python3 python3-pip python3-venv -y 150 | ``` 151 | 152 | ## Contributing 153 | 154 | Contributions are welcome! Please see our [contributing 155 | guidelines](CONTRIBUTING.md) for details. There is an open issue for the 156 | `{wbw}` package [here](https://github.com/atsyplenkov/wbw/issues/1) that 157 | contains a list of functions yet to be implemented. This is a good place 158 | to start. 159 | 160 | ## See also 161 | 162 | Geomorphometric and hydrological analysis in R can be also done with: 163 | 164 | - [`{whitebox}`](https://github.com/opengeos/whiteboxR) — An R frontend for the [WhiteboxTools](https://www.whiteboxgeo.com) standalone runner.
165 | - [`{traudem}`](https://github.com/lucarraro/traudem/) — R bindings to [TauDEM](https://hydrology.usu.edu/taudem/taudem5/) (Terrain Analysis Using Digital Elevation Models) command-line interface.
166 | - [`{RSagacmd}`](https://github.com/stevenpawley/Rsagacmd/) and [`{RSAGA}`](https://github.com/r-spatial/RSAGA) — Links R with [SAGA GIS](https://sourceforge.net/projects/saga-gis/).
167 | - [`{rivnet}`](https://github.com/lucarraro/rivnet) — river network extraction from DEM using TauDEM. --------------------------------------------------------------------------------