diff --git a/.Rbuildignore b/.Rbuildignore index 4df043f..9cc17a1 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -34,4 +34,4 @@ methods-in-ecology-and-evolution.csl ^CRAN-RELEASE$ ^CRAN-SUBMISSION$ ^CITATION\.cff$ -R/make_unity.R + diff --git a/.github/workflows/run-examples.yaml b/.github/workflows/run-examples.yaml index ceb7b8d..7450fe0 100644 --- a/.github/workflows/run-examples.yaml +++ b/.github/workflows/run-examples.yaml @@ -25,6 +25,7 @@ jobs: env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true RSPM: ${{ matrix.config.rspm }} + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v2 @@ -43,25 +44,25 @@ jobs: shell: Rscript {0} - name: Cache R packages - if: runner.os != 'Windows' uses: actions/cache@v2 with: path: ${{ env.R_LIBS_USER }} key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- - - name: Install system dependencies + - name: Install Linux dependencies if: runner.os == 'Linux' run: | while read -r cmd do eval sudo $cmd done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') + - name: Install dependencies run: | remotes::install_cran("devtools") + remotes::install_github("mikemahoney218/unifir") remotes::install_deps(dependencies = TRUE) - remotes::install_cran("rcmdcheck") shell: Rscript {0} - name: Check diff --git a/DESCRIPTION b/DESCRIPTION index 158cfff..1d6e49e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,53 +1,52 @@ -Package: terrainr Type: Package +Package: terrainr Title: Landscape Visualizations in R and 'Unity' -Version: 0.6.1 +Version: 0.7.0 Authors@R: c( - person(given = "Michael", - family = "Mahoney", - role = c("aut", "cre"), - email = "mike.mahoney.218@gmail.com", + person("Michael", "Mahoney", , "mike.mahoney.218@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-2402-304X")), - person(given = "Mike", - family = "Johnson", - role = c("rev"), - comment = c("Mike reviewed the package (v. 0.2.1) for rOpenSci, see ")), - person(given = "Sydney", - family = "Foks", - role = c("rev"), - comment = c("Sydney reviewed the package (v. 0.2.1) for rOpenSci, see "))) -Description: Functions for the retrieval, manipulation, and visualization of - 'geospatial' data, with an aim towards producing '3D' landscape - visualizations in the 'Unity' '3D' rendering engine. Functions are also - provided for retrieving elevation data and base map tiles from the 'USGS' - National Map . -URL: https://docs.ropensci.org/terrainr/, https://github.com/ropensci/terrainr -BugReports: https://github.com/ropensci/terrainr/issues + person("Mike", "Johnson", role = "rev", + comment = "Mike reviewed the package (v. 0.2.1) for rOpenSci, see "), + person("Sydney", "Foks", role = "rev", + comment = "Sydney reviewed the package (v. 0.2.1) for rOpenSci, see ") + ) +Description: Functions for the retrieval, manipulation, and visualization + of 'geospatial' data, with an aim towards producing '3D' landscape + visualizations in the 'Unity' '3D' rendering engine. Functions are + also provided for retrieving elevation data and base map tiles from + the 'USGS' National Map . License: MIT + file LICENSE -Encoding: UTF-8 +URL: https://docs.ropensci.org/terrainr/, + https://github.com/ropensci/terrainr +BugReports: https://github.com/ropensci/terrainr/issues Imports: base64enc, + ggplot2, + grDevices, httr, - raster, - rgdal, magick (>= 2.5.0), methods, png, sf (>= 1.0-5), - units, - grDevices, - ggplot2 -RoxygenNote: 7.1.2 + terra, + unifir, + units Suggests: - testthat, + brio, covr, - progressr, + jpeg, knitr, - rmarkdown, progress, - jpeg, - tiff, - brio -Config/testthat/parallel: true + progressr, + raster, + rgdal, + rmarkdown, + testthat, + tiff +VignetteBuilder: + knitr Config/testthat/edition: 3 -VignetteBuilder: knitr +Config/testthat/parallel: true +Encoding: UTF-8 +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.1.2 diff --git a/NAMESPACE b/NAMESPACE index 9736792..72cfef1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,12 +1,15 @@ # Generated by roxygen2: do not edit by hand S3method(add_bbox_buffer,Raster) +S3method(add_bbox_buffer,SpatRaster) S3method(add_bbox_buffer,sf) S3method(get_tiles,Raster) +S3method(get_tiles,SpatRaster) S3method(get_tiles,list) S3method(get_tiles,sf) S3method(get_tiles,sfc) S3method(set_bbox_side_length,Raster) +S3method(set_bbox_side_length,SpatRaster) S3method(set_bbox_side_length,sf) export(StatSpatialRGB) export(add_bbox_buffer) @@ -16,6 +19,7 @@ export(georeference_overlay) export(get_tiles) export(hit_national_map_api) export(make_manifest) +export(make_unity) export(merge_rasters) export(raster_to_raw_tiles) export(set_bbox_side_length) @@ -24,3 +28,4 @@ export(transform_elevation) export(transform_overlay) export(vector_to_overlay) importFrom(grDevices,rgb) +importFrom(unifir,find_unity) diff --git a/NEWS.md b/NEWS.md index a581565..cc6dd84 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,38 @@ +# terrainr 0.7.0 +* New features: + * `make_unity` is a new function which uses the new `unifir` package to + automatically create Unity scenes, no clicking necessary. + * Internally, all calls to functions from {raster} have been replaced with + calls to {terra}. This future-proofs the package against any coming + deprecations and takes advantage of newer, faster terra code. + This is not intended to be a breaking change; any methods that previously + took {raster} objects should still work (and silently convert to {terra} + under the hood). If you have any workflows impacted by this change, + please file a bug report! +* Improvements and bug fixes: + * `make_manifest`, `transform_elevation`, and `transform_overlay` no longer + error when providing non-standard side lengths; they now warn as intended. + * `make_manifest`, `transform_elevation`, and `transform_overlay` + should no longer give warnings about nodata values in most cases. + being clamped to 0. + * Fixed some documentation, unused objects, restyled and removed lints. + * `transform_overlay` (and by extension, every Unity overlay importer) now + tries to automatically guess the scale of the input raster. Values under + 1 are scaled from 0-1, integers under 255 are scaled 0-255, and integers + under 65535 are scaled 0-65536. The main effect of this is more realistic + coloring when importing terrain. Floats above 1 won't be affected. +* Dependency changes: + * `terra` is now included as an Import (had been recursively imported + through `raster` previously). + * `raster` and `rgdal` are now in Suggests (used temporarily in + `Raster*` methods for `get_tiles` and `add_bbox_buffer`, until the new + version of `raster` hits CRAN). +* Internal changes: + * `raster_to_raw_tiles` is now a thin wrapper around the functions + `transform_overlay` and `transform_elevation`. It is no longer tested; + it will be removed entirely in the next release (see deprecation notice + in terrainr 0.5.0). + # terrainr 0.6.1 * Improvements and bug fixes: * The README of version 0.6.0 has a disclaimer at the top stating that the diff --git a/R/add_bbox_buffer.R b/R/add_bbox_buffer.R index b0008dd..1c72e79 100644 --- a/R/add_bbox_buffer.R +++ b/R/add_bbox_buffer.R @@ -126,20 +126,39 @@ add_bbox_buffer.Raster <- function(data, distance, distance_unit = "meters", error_crs = NULL) { - bbox <- raster::extent(data) + tmp <- tempfile(fileext = ".tiff") + raster::writeRaster(data, tmp) + data <- terra::rast(tmp) + add_bbox_buffer( + data = data, + distance = distance, + distance_unit = distance_unit, + error_crs = error_crs + ) +} + +#' @rdname addbuff +#' @export +add_bbox_buffer.SpatRaster <- function(data, + distance, + distance_unit = "meters", + error_crs = NULL) { + bbox <- as.vector(terra::ext(data)) data_sf <- data.frame( - lat = c(bbox@ymin, bbox@ymax), - lng = c(bbox@xmin, bbox@xmax) + lat = c(bbox[[3]], bbox[[4]]), + lng = c(bbox[[1]], bbox[[2]]) ) data_sf <- sf::st_as_sf(data_sf, coords = c("lng", "lat")) data_sf <- sf::st_set_crs(data_sf, sf::st_crs(data)) add_bbox_buffer(data_sf, - distance = distance, - distance_unit = distance_unit, - error_crs = error_crs + distance = distance, + distance_unit = distance_unit, + error_crs = error_crs ) } + + #' @rdname addbuff #' @examples #' @@ -193,10 +212,26 @@ set_bbox_side_length.Raster <- function(data, distance, distance_unit = "meters", error_crs = NULL) { - bbox <- raster::extent(data) + data <- terra::rast(data@file@name) + set_bbox_side_length( + data = data, + distance = distance, + distance_unit = distance_unit, + error_crs = error_crs + ) +} + + +#' @rdname addbuff +#' @export +set_bbox_side_length.SpatRaster <- function(data, + distance, + distance_unit = "meters", + error_crs = NULL) { + bbox <- terra::ext(data)@ptr$vector data_sf <- data.frame( - lat = c(bbox@ymin, bbox@ymax), - lng = c(bbox@xmin, bbox@xmax) + lat = c(bbox[[3]], bbox[[4]]), + lng = c(bbox[[1]], bbox[[2]]) ) data_sf <- sf::st_as_sf(data_sf, coords = c("lng", "lat")) data_sf <- sf::st_set_crs(data_sf, sf::st_crs(data)) diff --git a/R/geom_spatial_rgb.R b/R/geom_spatial_rgb.R index 5664dd6..5b3c793 100644 --- a/R/geom_spatial_rgb.R +++ b/R/geom_spatial_rgb.R @@ -11,12 +11,12 @@ #' options described in [ggplot2::geom_raster], there are two additional #' methods: #' -#' If a `RasterStack` object (see [raster::stack]), this function will coerce -#' the stack to a data frame and assume the raster bands are in RGB order +#' If a `SpatRaster` object (see [terra::rast]), this function will coerce +#' the raster to a data frame and assume the raster bands are in RGB order #' (while allowing for, but ignoring, a fourth alpha band). #' #' If a length-1 character vector, this function will attempt to load the object -#' via [raster::stack]. +#' via [terra::rast]. #' #' @inheritParams ggplot2::geom_raster #' @param scale Integer. Maximum (possible) value in the three channels. @@ -45,7 +45,7 @@ #' merged_ortho <- tempfile(fileext = ".tif") #' merge_rasters(output_tiles[["ortho"]], merged_ortho) #' -#' merged_stack <- raster::stack(merged_ortho) +#' merged_stack <- terra::rast(merged_ortho) #' #' library(ggplot2) #' @@ -142,8 +142,8 @@ geom_spatial_rgb_internal.character <- function(data = NULL, inherit.aes = TRUE, scale = NULL) { stopifnot(length(data) == 1) - data <- raster::stack(data) - geom_spatial_rgb_internal.RasterStack( + data <- terra::rast(data) + geom_spatial_rgb_internal.SpatRaster( data = data, mapping = mapping, stat = stat, @@ -168,13 +168,41 @@ geom_spatial_rgb_internal.RasterStack <- function(data = NULL, show.legend = NA, inherit.aes = TRUE, scale = NULL) { - data <- raster::as.data.frame(data, xy = TRUE) - if (ncol(data) == 5) { + data <- terra::rast(data) + geom_spatial_rgb_internal( + data = data, + mapping = mapping, + stat = stat, + position = position, + na.rm = na.rm, + show.legend = show.legend, + inherit.aes = inherit.aes, + scale = scale, + ... + ) +} + +geom_spatial_rgb_internal.SpatRaster <- function(data = NULL, + mapping = NULL, + stat = "spatialRGB", + position = "identity", + ..., + hjust = 0.5, + vjust = 0.5, + interpolate = FALSE, + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE, + scale = NULL) { + data <- terra::as.data.frame(data, xy = TRUE) + if (terra::ncol(data) == 5) { data <- stats::setNames(data, c("x", "y", "red", "green", "blue")) - } else if (ncol(data) == 6) { + } else if (terra::ncol(data) == 6) { data <- stats::setNames(data, c("x", "y", "red", "green", "blue", "alpha")) } else { - stop("Can't assume band values from ", ncol(data) - 2, " band raster.") + stop("Can't assume band values from ", + terra::ncol(data) - 2, + " band raster.") } geom_spatial_rgb_internal( @@ -302,8 +330,8 @@ stat_spatial_rgb_internal.character <- function(data = NULL, scale = NULL, ...) { stopifnot(length(data) == 1) - data <- raster::stack(data) - stat_spatial_rgb_internal.RasterStack( + data <- terra::rast(data) + stat_spatial_rgb_internal.SpatRaster( data = data, mapping = mapping, geom = geom, @@ -325,13 +353,33 @@ stat_spatial_rgb_internal.RasterStack <- function(data = NULL, inherit.aes = TRUE, scale = NULL, ...) { - data <- raster::as.data.frame(data, xy = TRUE) - if (ncol(data) == 5) { + data <- terra::rast(data) + stat_spatial_rgb_internal( + data = data, mapping = mapping, geom = geom, + position = position, na.rm = na.rm, + show.legend = show.legend, inherit.aes = inherit.aes, + scale = scale, ... + ) +} + +stat_spatial_rgb_internal.SpatRaster <- function(data = NULL, + mapping = NULL, + geom = "raster", + position = "identity", + na.rm = FALSE, + show.legend = FALSE, + inherit.aes = TRUE, + scale = NULL, + ...) { + data <- terra::as.data.frame(data, xy = TRUE) + if (terra::ncol(data) == 5) { data <- stats::setNames(data, c("x", "y", "red", "green", "blue")) - } else if (ncol(data) == 6) { + } else if (terra::ncol(data) == 6) { data <- stats::setNames(data, c("x", "y", "red", "green", "blue", "alpha")) } else { - stop("Can't assume band values from ", ncol(data) - 2, " band raster.") + stop("Can't assume band values from ", + terra::ncol(data) - 2, + " band raster.") } stat_spatial_rgb_internal( diff --git a/R/georeference_overlay.R b/R/georeference_overlay.R index fcd469d..a0bc74a 100644 --- a/R/georeference_overlay.R +++ b/R/georeference_overlay.R @@ -9,9 +9,8 @@ #' detected automatically from file extension; options include `jpeg/jpg`, #' `png`, and `tif/tiff`. #' @param reference_raster The raster file to base georeferencing on. The output -#' image will have the same extent and CRS as the reference raster. Accepts both -#' Raster* objects from the `raster` package or a file readable by -#' [raster::raster]. +#' image will have the same extent and CRS as the reference raster. Accepts +#' anything that can be read by [terra::rast] #' @param output_file The path to write the georeferenced image file to. Must #' be a TIFF. #' @@ -50,6 +49,8 @@ georeference_overlay <- function(overlay_file, stopifnot(grepl("tiff?$", output_file)) file_type <- regmatches(overlay_file, regexpr("\\w*$", overlay_file)) + reference_raster <- terra::rast(reference_raster) + official_names <- c( "jpeg" = "jpg", "tiff" = "tif" @@ -72,18 +73,12 @@ georeference_overlay <- function(overlay_file, "jpeg" = jpeg::readJPEG ) - if (is.character(reference_raster) && length(reference_raster) == 1) { - reference_raster <- raster::raster(reference_raster) - } else { - stopifnot(any(grepl("^Raster", class(reference_raster)))) - } - - # Need image_read in order for brick to correctly detect scale + # Need image_read in order for brick to correctly detect scale # otherwise assumes 8bit - overlay_file <- raster::brick(image_read(overlay_file)) - raster::crs(overlay_file) <- reference_raster@crs - raster::extent(overlay_file) <- reference_raster@extent - raster::writeRaster(overlay_file, output_file) + overlay_file <- terra::rast(image_read(overlay_file)) + terra::crs(overlay_file) <- terra::crs(reference_raster) + terra::ext(overlay_file) <- terra::ext(reference_raster) + terra::writeRaster(overlay_file, output_file) return(invisible(output_file)) } diff --git a/R/get_tiles.R b/R/get_tiles.R index cac3426..49b5260 100644 --- a/R/get_tiles.R +++ b/R/get_tiles.R @@ -4,8 +4,8 @@ #' tiles, and retrieves data from the USGS National map for each tile. As of #' version 0.5.0, the method for lists has been deprecated. #' -#' @param data An `sf` or `Raster` object; tiles will be downloaded for the full -#' extent of the provided object. +#' @param data An `sf` or `SpatRast` object; tiles will be downloaded for the +#' full extent of the provided object. #' @param output_prefix The file prefix to use when saving tiles. #' @param side_length The length, in meters, of each side of tiles to download. #' If \code{NULL}, defaults to the maximum side length permitted by the least @@ -186,6 +186,32 @@ get_tiles.Raster <- function(data, georeference = TRUE, projected = NULL, ...) { + tmp <- tempfile(fileext = ".tiff") + raster::writeRaster(data, tmp) + data <- terra::rast(tmp) + get_tiles.SpatRaster(data, + output_prefix = output_prefix, + side_length = side_length, + resolution = resolution, + services = services, + verbose = verbose, + georeference = georeference, + projected = projected, + ...) +} + + +#' @rdname get_tiles +#' @export +get_tiles.SpatRaster <- function(data, + output_prefix = tempfile(), + side_length = NULL, + resolution = 1, + services = "elevation", + verbose = FALSE, + georeference = TRUE, + projected = NULL, + ...) { dots <- list(...) if (is.null(projected)) { projected <- !sf::st_is_longlat(data) @@ -206,10 +232,10 @@ get_tiles.Raster <- function(data, imageSR <- dots[["imageSR"]] } # nolint - data <- raster::extent(data) + data <- as.vector(terra::ext(data)) data <- data.frame( - lng = c(data@xmin, data@xmax), - lat = c(data@ymin, data@ymax) + lng = c(data[[1]], data[[2]]), + lat = c(data[[3]], data[[4]]) ) data <- sf::st_as_sf(data, coords = c("lng", "lat")) data <- sf::st_bbox(data) @@ -229,6 +255,7 @@ get_tiles.Raster <- function(data, ) } + #' @rdname get_tiles #' @export get_tiles.list <- function(data, @@ -335,7 +362,8 @@ get_tiles_internal <- function(data, for (i in seq_len(x_tiles)) { for (j in seq_len(y_tiles)) { - current_box <- tile_boxes[tile_boxes$x_tiles == i & tile_boxes$y_tiles == j, ] + current_box <- tile_boxes[tile_boxes$x_tiles == i & + tile_boxes$y_tiles == j, ] current_bbox <- data.frame( lat = c(current_box$min_y, current_box$max_y), lng = c(current_box$min_x, current_box$max_x) @@ -402,19 +430,19 @@ get_tiles_internal <- function(data, } if (georeference && services[[k]] != "3DEPElevation") { - cur_raster <- raster::brick(png::readPNG(cur_path)) - cur_raster@crs <- raster::crs(paste0( + cur_raster <- terra::rast(png::readPNG(cur_path)) + terra::crs(cur_raster) <- paste0( "+init=EPSG:", img_bin$extent$spatialReference$wkid - )) - cur_raster@extent <- raster::extent( + ) + terra::ext(cur_raster) <- c( img_bin$extent$xmin, img_bin$extent$xmax, img_bin$extent$ymin, img_bin$extent$ymax ) - raster::writeRaster(cur_raster, + terra::writeRaster(cur_raster, final_path, overwrite = TRUE ) diff --git a/R/hit_api.R b/R/hit_api.R index 86c4af7..84c99e7 100644 --- a/R/hit_api.R +++ b/R/hit_api.R @@ -199,9 +199,12 @@ hit_national_map_api <- function(bbox, } } else if (counter < 15) { get_href(counter = counter + 1) - } else { - stop("Map server returned error code ", httr::status_code(img_res)) # nocov - } + } else { # nocov start + stop( + "Map server returned error code ", + httr::status_code(img_res) + ) + } # nocov end } body <- get_href() diff --git a/R/make_manifest.R b/R/make_manifest.R index a2d9df2..9195280 100644 --- a/R/make_manifest.R +++ b/R/make_manifest.R @@ -92,7 +92,11 @@ make_manifest <- function(heightmap, transform_elevation <- function(heightmap, side_length = 4097, output_prefix = "import") { - manifest <- prep_table(heightmap, side_length, output_prefix, type = "elevation") + manifest <- prep_table(heightmap, + side_length, + output_prefix, + type = "elevation" + ) temptiffs <- NULL while (length(temptiffs) != nrow(manifest)) { @@ -175,18 +179,30 @@ prep_table <- function(input_raster, side_length, output_prefix, type) { - if (!all.equal(log((side_length - 1), 2), round(log((side_length - 1), 2)))) { + if (!identical(log((side_length - 1), 2), round(log((side_length - 1), 2)))) { warning( "Side lengths must be equal to 2^x + 1 (for x <= 12) for import into Unity.\n", # nolint "Tiles will still be produced but may not be usable." ) } - input_raster <- raster::raster(input_raster) - max_raster <- raster::cellStats(input_raster, "max") + input_raster <- terra::rast(input_raster) + max_raster <- max(terra::global(input_raster, "max", na.rm = TRUE)) + if (type == "overlay") { + if (max_raster < 1) { + max_raster <- 1 + } else if ( + isTRUE(all.equal(as.integer(max_raster), max_raster)) & + max_raster < 255) { + max_raster <- 255 + } else if ( + isTRUE(all.equal(as.integer(max_raster), max_raster)) & + max_raster < 65535) { + max_raster <- 65535 + } + } - x_tiles <- ceiling(input_raster@ncols / side_length) - y_tiles <- ceiling(input_raster@nrows / side_length) - n_tiles <- x_tiles * y_tiles + x_tiles <- ceiling(terra::ncol(input_raster) / side_length) + y_tiles <- ceiling(terra::nrow(input_raster) / side_length) file_combos <- expand.grid( x = 1:x_tiles, @@ -235,9 +251,9 @@ crop_tif <- function(img, manifest, temptiffs, field = "filename") { options = c( "-srcwin", -manifest$x_pos[[i]], - manifest$z_pos[[i]], - manifest$x_length[[i]], - manifest$z_length[[i]] + manifest$z_pos[[i]], + manifest$x_length[[i]], + manifest$z_length[[i]] ) ) names(temptiffs)[[i]] <- manifest[[field]][[i]] @@ -258,7 +274,8 @@ convert_to_png <- function(temptiffs, options = c( "-ot", "UInt16", "-of", "png", - "-scale", "0", max_val, "0", "65535" + "-scale", "0", max_val, "0", "65535", + "-a_nodata", "0" ) ) }, diff --git a/R/make_unity.R b/R/make_unity.R new file mode 100644 index 0000000..ff99d1a --- /dev/null +++ b/R/make_unity.R @@ -0,0 +1,122 @@ +#' Initialize terrain inside of a Unity project. +#' +#' @param project The directory path of the Unity project to create terrain +#' inside. +#' @param heightmap The file path for the raster to transform into terrain. +#' @param overlay Optionally, a file path for an image overlay to layer on top +#' of the terrain surface. Leave as NULL for no overlay. +#' @param side_length The side length, in map units, for the terrain tiles. +#' Must be equal to 2^x + 1, for any x between 5 and 12. +#' @param scene_name The name of the Unity scene to create the terrain in. +#' @param action Boolean: Execute the unifir "script" and create the Unity +#' project? If FALSE, returns a non-executed script. +#' @param unity The location of the Unity executable to create projects with. +#' By default, will be auto-detected by [unifir::find_unity] +#' +#' @return An object of class "unifir_script", containing either an executed +#' unifir script (if action = TRUE) or a non-executed script object +#' (if action = FALSE). +#' +#' @importFrom unifir find_unity +#' +#' @examples +#' \dontrun{ +#' if (!isTRUE(as.logical(Sys.getenv("CI")))) { +#' simulated_data <- data.frame( +#' id = seq(1, 100, 1), +#' lat = runif(100, 44.04905, 44.17609), +#' lng = runif(100, -74.01188, -73.83493) +#' ) +#' simulated_data <- sf::st_as_sf(simulated_data, coords = c("lng", "lat")) +#' output_files <- get_tiles(simulated_data) +#' temptiff <- tempfile(fileext = ".tif") +#' merge_rasters(output_files["elevation"][[1]], temptiff) +#' make_unity(file.path(tempdir(), "unity"), temptiff) +#' } +#' } +#' +#' @export +make_unity <- function(project, + heightmap, + overlay = NULL, + side_length = 4097, + scene_name = "terrainr_scene", + action = TRUE, + unity = find_unity()) { + if (!requireNamespace("unifir", quietly = TRUE)) { + stop( + "make_unity requires the unifir package to work correctly. ", + "Please install unifir to continue." + ) + } + + if (!(side_length %in% 2^(5:12) + 1)) { + stop( + "side_length must be equal to a value of 2^x + 1, for any x ", + "between 5 and 12." + ) + } + + elevation_prefix <- tempfile() + manifest <- prep_table(heightmap, + side_length = side_length, + output_prefix = elevation_prefix, + type = "elevation" + ) + transform_elevation( + heightmap = heightmap, + side_length = side_length, + output_prefix = elevation_prefix + ) + if (!dir.exists(project)) dir.create(project) + lapply( + manifest$filename, + function(x) file.rename(x, file.path(project, basename(x))) + ) + manifest$filename <- basename(manifest$filename) + + if (!is.null(overlay)) { + overlay_prefix <- tempfile() + overlay_manifest <- prep_table(heightmap, + side_length = side_length, + output_prefix = overlay_prefix, + type = "overlay" + ) + manifest$texture <- overlay_manifest$texture + transform_overlay( + overlay = overlay, + side_length = side_length, + output_prefix = overlay_prefix + ) + lapply( + manifest$texture, + function(x) file.rename(x, file.path(project, basename(x))) + ) + manifest$texture <- basename(manifest$texture) + } + + script <- unifir::make_script(project, + scene_name = scene_name, + unity = unity) + script <- unifir::new_scene(script, "DefaultGameObjects", "Single") + + for (i in seq_len(nrow(manifest))) { + script <- unifir::create_terrain( + script, + heightmap_path = manifest$filename[i], + x_pos = manifest$x_pos[i], + z_pos = manifest$z_pos[i], + width = manifest$x_length[i], + height = manifest$height[i], + length = manifest$z_length[i], + heightmap_resolution = manifest$resolution[i], + texture_path = ifelse(is.null(overlay), "", manifest$texture[i]) + ) + } + + script <- unifir::save_scene(script) + if (action) { + script <- unifir::action(script) + } + script +} diff --git a/R/merge_rasters.R b/R/merge_rasters.R index 2d3fb99..6c33729 100644 --- a/R/merge_rasters.R +++ b/R/merge_rasters.R @@ -2,7 +2,7 @@ #' #' Some functions like [get_tiles] return multiple separate files #' when it can be useful to have a single larger raster instead. This function -#' is a thin wrapper over [sf::gdal_utils(util = "warp")], making it easy to +#' is a thin wrapper over [sf::gdal_utils], making it easy to #' collapse those multiple raster files into a single TIFF. #' #' @param input_rasters A character vector containing the file paths to the @@ -53,8 +53,6 @@ merge_rasters <- function(input_rasters, options <- c(options, "-overwrite") } - initial_file <- output_raster - if (!force_fallback) { tryCatch( { @@ -86,9 +84,10 @@ merge_rasters <- function(input_rasters, merge_rasters_deprecated <- function(input_rasters, output_raster = tempfile(fileext = ".tif"), options = character(0)) { - if (length(options) > 0 || - !(length(options == 1) && options == "-overwrite")) { - warning("Options are not respected when trying to merge rasters with differing numbers of bands") # nolint + if (length(options) > 0) { + if(!(length(options == 1) && options == "-overwrite")) { + warning("Options are not respected when trying to merge rasters with differing numbers of bands") # nolint + } } temp_output <- tempfile(fileext = ".vrt") diff --git a/R/raster_to_raw_tiles.R b/R/raster_to_raw_tiles.R index 125c51b..86da13e 100644 --- a/R/raster_to_raw_tiles.R +++ b/R/raster_to_raw_tiles.R @@ -44,142 +44,22 @@ raster_to_raw_tiles <- function(input_file, side_length = 4097, raw = TRUE) { .Deprecated( - "make_manifest", + "transform_elevation", "terrainr", msg = paste("'raster_to_raw_tiles' is deprecated as of terrainr 0.5.0.", - "Use 'make_manifest' instead.", + "Use 'transform_elevation' instead.", sep = "\n" ) ) - input_raster <- raster::raster(input_file) - max_raster <- raster::cellStats(input_raster, "max") - - x_tiles <- ceiling(input_raster@ncols / side_length) - y_tiles <- ceiling(input_raster@nrows / side_length) - if (requireNamespace("progressr", quietly = TRUE)) { # nocov start - p <- progressr::progressor(steps = x_tiles * y_tiles * 3) - } # nocov end - - temptiffs <- NULL - while (length(temptiffs) != x_tiles * y_tiles) { - temptiffs <- unique(vapply( - 1:(x_tiles * y_tiles), - function(x) tempfile(fileext = ".tiff"), - character(1) - )) - } - - x_tiles <- 0:(x_tiles - 1) - x_tiles <- (x_tiles * side_length) - - y_tiles <- 0:(y_tiles - 1) - y_tiles <- (y_tiles * side_length) - - - counter <- 1 - - for (i in seq_along(x_tiles)) { - for (j in seq_along(y_tiles)) { - if (requireNamespace("progressr", quietly = TRUE)) { # nocov start - p(message = sprintf( - "Cropping tile (%d,%d)", - x_tiles[[i]], - y_tiles[[j]] - )) - } # nocov end - sf::gdal_utils( - "translate", - input_file, - temptiffs[[counter]], - options = c( - "-srcwin", - x_tiles[[i]], - y_tiles[[j]], - side_length, - side_length - ) - ) - names(temptiffs)[[counter]] <- paste0( - output_prefix, - "_", - i, - "_", - j, - ifelse(raw, ".raw", ".png") - ) - counter <- counter + 1 - } - } - - temppngs <- NULL if (raw) { - while (length(temppngs) != length(temptiffs)) { - temppngs <- unique(vapply( - seq_along(temptiffs), - function(x) tempfile(fileext = ".png"), - character(1) - )) - } + transform_elevation(heightmap = input_file, + side_length = side_length, + output_prefix = output_prefix) } else { - temppngs <- names(temptiffs) + transform_overlay(overlay = input_file, + side_length = side_length, + output_prefix = output_prefix) } - names(temppngs) <- names(temptiffs) - - mapply( - function(x, y) { - if (requireNamespace("progressr", quietly = TRUE)) { # nocov start - p(message = sprintf("Converting tile %s to PNG", x)) - } # nocov end - sf::gdal_utils( - "translate", - source = x, - destination = y, - options = c( - "-ot", "UInt16", - "-of", "png", - "-scale", "0", max_raster, "0", "65535" - ) - ) - }, - temptiffs, - temppngs - ) - - unlink(temptiffs) - - mapply( - function(x, y) { - processing_image <- magick::image_read(x) - - if (requireNamespace("progressr", quietly = TRUE)) { # nocov start - if (raw) { - p(message = sprintf("Converting tile %s to RAW", x)) - } else { - p(message = sprintf("Flipping tile %s for Unity", x)) - } - } # nocov end - - if (raw) { - processing_image <- magick::image_flop(processing_image) - processing_image <- magick::image_convert(processing_image, - format = "RGB", - depth = 16, - interlace = "Plane" - ) - } else { - processing_image <- magick::image_flip(processing_image) - processing_image <- magick::image_flop(processing_image) - } - - magick::image_write(processing_image, y) - }, - temppngs, - names(temppngs) - ) - - if (raw) unlink(temppngs) - - return(invisible(names(temppngs))) } diff --git a/R/utils.R b/R/utils.R index a1ca1f4..461deb7 100644 --- a/R/utils.R +++ b/R/utils.R @@ -59,7 +59,7 @@ get_centroid <- function(lat, lng) { #' the original point to apply #' @param azimuth A azimuth (in units specified in \code{azimuth_unit}) #' representing the direction to apply the distance from the original point in -#' @param distance_unit A string passed to [convert_distance] +#' @param distance_unit A string passed to convert_distance #' indicating the units of the provided distance. #' @param azimuth_unit A string (either \code{degrees} or \code{radians}) #' indicating the units of the \code{azimuth} argument diff --git a/R/vector_to_overlay.R b/R/vector_to_overlay.R index 403488b..31207f1 100644 --- a/R/vector_to_overlay.R +++ b/R/vector_to_overlay.R @@ -9,7 +9,7 @@ #' @param reference_raster The raster file to produce an overlay for. The output #' overlay will have the same extent and resolution as the input raster. Users #' may provide either a Raster* object or a length 1 character -#' vector containing a path to a file readable by [raster::raster]. +#' vector containing a path to a file readable by [terra::rast]. #' @param output_file The path to save the image overlay to. If `NULL`, saves to #' a tempfile. #' @param transparent The hex code for a color to be made transparent in the @@ -71,11 +71,7 @@ vector_to_overlay <- function(vector_data, stopifnot(any(grepl("^sf", class(vector_data)))) } - if (is.character(reference_raster) && length(reference_raster) == 1) { - reference_raster <- raster::raster(reference_raster) - } else { - stopifnot(any(grepl("^Raster", class(reference_raster)))) - } + reference_raster <- terra::rast(reference_raster) if (is.na(sf::st_crs(vector_data))) { if (is.null(error_crs)) { @@ -115,6 +111,8 @@ vector_to_overlay <- function(vector_data, # quiet R CMD check not appreciating ggplot's NSE... X <- Y <- NULL # nolint + extent <- as.vector(terra::ext(reference_raster)) + output_ggplot <- ggplot2::ggplot( vector_data, ggplot2::aes(x = X, y = Y, group = grouping) @@ -123,15 +121,15 @@ vector_to_overlay <- function(vector_data, ggplot2::scale_x_continuous( expand = c(0, 0), limits = c( - reference_raster@extent@xmin, - reference_raster@extent@xmax + extent[[1]], + extent[[2]] ) ) + ggplot2::scale_y_continuous( expand = c(0, 0), limits = c( - reference_raster@extent@ymin, - reference_raster@extent@ymax + extent[[3]], + extent[[4]] ) ) + ggplot2::theme_void() + @@ -150,8 +148,8 @@ vector_to_overlay <- function(vector_data, ggplot2::ggsave( filename = output_file, plot = output_ggplot, - width = reference_raster@ncols / 72, - height = reference_raster@nrows / 72, + width = terra::ncol(reference_raster) / 72, + height = terra::nrow(reference_raster) / 72, units = "in", dpi = "screen", limitsize = FALSE diff --git a/README.Rmd b/README.Rmd index 56cbce4..e0ee1ad 100644 --- a/README.Rmd +++ b/README.Rmd @@ -137,7 +137,7 @@ https://www.usgs.gov/faqs/how-should-i-cite-datasets-and-services-national-map . To cite terrainr in publications please use: -> Mahoney et al., (2022). terrainr: An R package for creating immersive virtual environments. Journal of Open Source Software, 7(69), 4060, https://doi.org/10.21105/joss.04060 +> Mahoney, M. J., Beier, C. M., and Ackerman, A. C., (2022). terrainr: An R package for creating immersive virtual environments. Journal of Open Source Software, 7(69), 4060, https://doi.org/10.21105/joss.04060 A BibTeX entry for LaTeX users is: diff --git a/README.md b/README.md index 069ffc9..7bc0221 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,9 @@ data products (as downloaded from `get_tiles`) at To cite terrainr in publications please use: -> Mahoney et al., (2022). terrainr: An R package for creating immersive -> virtual environments. Journal of Open Source Software, 7(69), 4060, +> Mahoney, M. J., Beier, C. M., and Ackerman, A. C., (2022). terrainr: +> An R package for creating immersive virtual environments. Journal of +> Open Source Software, 7(69), 4060, > A BibTeX entry for LaTeX users is: diff --git a/codemeta.json b/codemeta.json index e206f36..5d2a730 100644 --- a/codemeta.json +++ b/codemeta.json @@ -4,17 +4,17 @@ "identifier": "terrainr", "description": "Functions for the retrieval, manipulation, and visualization of 'geospatial' data, with an aim towards producing '3D' landscape visualizations in the 'Unity' '3D' rendering engine. Functions are also provided for retrieving elevation data and base map tiles from the 'USGS' National Map .", "name": "terrainr: Landscape Visualizations in R and 'Unity'", - "relatedLink": "https://docs.ropensci.org/terrainr/", + "relatedLink": ["https://docs.ropensci.org/terrainr/", "https://CRAN.R-project.org/package=terrainr"], "codeRepository": "https://github.com/ropensci/terrainr", "issueTracker": "https://github.com/ropensci/terrainr/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.6.0", + "version": "0.7.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", "url": "https://r-project.org" }, - "runtimePlatform": "R version 4.1.2 (2021-11-01)", + "runtimePlatform": "R version 4.2.0 (2022-04-22)", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", @@ -42,15 +42,15 @@ "softwareSuggestions": [ { "@type": "SoftwareApplication", - "identifier": "testthat", - "name": "testthat", + "identifier": "brio", + "name": "brio", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=testthat" + "sameAs": "https://CRAN.R-project.org/package=brio" }, { "@type": "SoftwareApplication", @@ -66,15 +66,15 @@ }, { "@type": "SoftwareApplication", - "identifier": "progressr", - "name": "progressr", + "identifier": "jpeg", + "name": "jpeg", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=progressr" + "sameAs": "https://CRAN.R-project.org/package=jpeg" }, { "@type": "SoftwareApplication", @@ -90,63 +90,92 @@ }, { "@type": "SoftwareApplication", - "identifier": "rmarkdown", - "name": "rmarkdown", + "identifier": "progress", + "name": "progress", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=rmarkdown" + "sameAs": "https://CRAN.R-project.org/package=progress" }, { "@type": "SoftwareApplication", - "identifier": "progress", - "name": "progress", + "identifier": "progressr", + "name": "progressr", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=progress" + "sameAs": "https://CRAN.R-project.org/package=progressr" }, { "@type": "SoftwareApplication", - "identifier": "jpeg", - "name": "jpeg", + "identifier": "raster", + "name": "raster", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=jpeg" + "sameAs": "https://CRAN.R-project.org/package=raster" }, { "@type": "SoftwareApplication", - "identifier": "tiff", - "name": "tiff", + "identifier": "rgdal", + "name": "rgdal", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=tiff" + "sameAs": "https://CRAN.R-project.org/package=rgdal" }, { "@type": "SoftwareApplication", - "identifier": "brio", - "name": "brio", + "identifier": "rmarkdown", + "name": "rmarkdown", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=brio" + "sameAs": "https://CRAN.R-project.org/package=rmarkdown" + }, + { + "@type": "SoftwareApplication", + "identifier": "testthat", + "name": "testthat", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=testthat" + }, + { + "@type": "SoftwareApplication", + "identifier": "tiff", + "name": "tiff", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tiff" + }, + { + "@type": "SoftwareApplication", + "identifier": "unifir", + "name": "unifir" } ], "softwareRequirements": { @@ -164,39 +193,32 @@ }, "2": { "@type": "SoftwareApplication", - "identifier": "httr", - "name": "httr", + "identifier": "ggplot2", + "name": "ggplot2", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=httr" + "sameAs": "https://CRAN.R-project.org/package=ggplot2" }, "3": { "@type": "SoftwareApplication", - "identifier": "raster", - "name": "raster", - "provider": { - "@id": "https://cran.r-project.org", - "@type": "Organization", - "name": "Comprehensive R Archive Network (CRAN)", - "url": "https://cran.r-project.org" - }, - "sameAs": "https://CRAN.R-project.org/package=raster" + "identifier": "grDevices", + "name": "grDevices" }, "4": { "@type": "SoftwareApplication", - "identifier": "rgdal", - "name": "rgdal", + "identifier": "httr", + "name": "httr", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=rgdal" + "sameAs": "https://CRAN.R-project.org/package=httr" }, "5": { "@type": "SoftwareApplication", @@ -243,36 +265,31 @@ }, "9": { "@type": "SoftwareApplication", - "identifier": "units", - "name": "units", + "identifier": "terra", + "name": "terra", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=units" + "sameAs": "https://CRAN.R-project.org/package=terra" }, "10": { "@type": "SoftwareApplication", - "identifier": "grDevices", - "name": "grDevices" - }, - "11": { - "@type": "SoftwareApplication", - "identifier": "ggplot2", - "name": "ggplot2", + "identifier": "units", + "name": "units", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Comprehensive R Archive Network (CRAN)", "url": "https://cran.r-project.org" }, - "sameAs": "https://CRAN.R-project.org/package=ggplot2" + "sameAs": "https://CRAN.R-project.org/package=units" }, "SystemRequirements": null }, - "fileSize": "4051.402KB", + "fileSize": "4059.882KB", "citation": [ { "@type": "ScholarlyArticle", @@ -311,5 +328,15 @@ } } } - ] + ], + "releaseNotes": "https://github.com/ropensci/terrainr/blob/master/NEWS.md", + "readme": "https://github.com/ropensci/terrainr/blob/main/README.md", + "contIntegration": ["https://app.codecov.io/gh/ropensci/terrainr", "https://github.com/ropensci/terrainr/actions"], + "developmentStatus": ["https://lifecycle.r-lib.org/articles/stages.html#maturing", "https://www.repostatus.org/#active"], + "review": { + "@type": "Review", + "url": "https://github.com/ropensci/software-review/issues/416", + "provider": "https://ropensci.org" + }, + "keywords": ["r", "r-package", "cran", "terrainr", "rstats", "usgs", "map", "mapping", "national-map", "unity-rendering-engine", "unity", "map-tiles", "progressr", "peer-reviewed", "retrieve-data", "orthoimagery", "dems", "datasets", "nhd"] } diff --git a/cran-comments.md b/cran-comments.md index fbb012a..c363838 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,16 +1,9 @@ -I apologize for the rapid re-submission! -The README of version 0.6.0 has a disclaimer at the top stating that the -package is an experimental build relying on as-of-yet unreleased packages. -This is not true. -This release removes that language. -Otherwise, this release is identical to version 0.6.0. - ## Test environments -* local R installation, R 4.1.2 -* ubuntu 20.04 (on GitHub Actions), R devel, 4.1.2, 4.0.5 -* MacOS X 11.6.3 (on GitHub Actions), R 4.1.2 -* Windows Server 2019 (on GitHub Actions), R 4.1.2 -* win-builder (devel, 4.1.2, 4.0.5) +* local R installation, R release +* ubuntu 20.04 (on GitHub Actions), R devel, release, oldrelease +* MacOS X 11.6.3 (on GitHub Actions), R release +* Windows Server 2019 (on GitHub Actions), R release +* win-builder (devel, release, oldrelease) ## R CMD check results diff --git a/inst/CITATION b/inst/CITATION index a43ffbc..1ae3224 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -15,4 +15,4 @@ bibentry(bibtype = "Article", journal = "Journal of Open Source Software", doi = "10.21105/joss.04060", url = "https://doi.org/10.21105/joss.04060", - textVersion = "Mahoney et al., (2022). terrainr: An R package for creating immersive virtual environments. Journal of Open Source Software, 7(69), 4060, https://doi.org/10.21105/joss.04060") + textVersion = "Mahoney M. J., Beier C. M., and Ackerman, A. C. (2022). terrainr: An R package for creating immersive virtual environments. Journal of Open Source Software, 7(69), 4060, https://doi.org/10.21105/joss.04060") diff --git a/man/addbuff.Rd b/man/addbuff.Rd index fddcdbb..a5956af 100644 --- a/man/addbuff.Rd +++ b/man/addbuff.Rd @@ -5,9 +5,11 @@ \alias{add_bbox_buffer} \alias{add_bbox_buffer.sf} \alias{add_bbox_buffer.Raster} +\alias{add_bbox_buffer.SpatRaster} \alias{set_bbox_side_length} \alias{set_bbox_side_length.sf} \alias{set_bbox_side_length.Raster} +\alias{set_bbox_side_length.SpatRaster} \title{Add a uniform buffer around a bounding box for geographic coordinates} \usage{ add_bbox_buffer(data, distance, distance_unit = "meters", error_crs = NULL) @@ -16,6 +18,8 @@ add_bbox_buffer(data, distance, distance_unit = "meters", error_crs = NULL) \method{add_bbox_buffer}{Raster}(data, distance, distance_unit = "meters", error_crs = NULL) +\method{add_bbox_buffer}{SpatRaster}(data, distance, distance_unit = "meters", error_crs = NULL) + set_bbox_side_length( data, distance, @@ -36,36 +40,43 @@ set_bbox_side_length( distance_unit = "meters", error_crs = NULL ) + +\method{set_bbox_side_length}{SpatRaster}( + data, + distance, + distance_unit = "meters", + error_crs = NULL +) } \arguments{ -\item{data}{The original data to add a buffer around. Must be either an `sf` -or `Raster` object.} +\item{data}{The original data to add a buffer around. Must be either an \code{sf} +or \code{Raster} object.} \item{distance}{The distance to add or to set side lengths equal to.} \item{distance_unit}{The units of the distance to add to the buffer, passed -to [units::as_units].} +to \link[units:units]{units::as_units}.} -\item{error_crs}{Logical: Should this function error if `data` has no CRS? -If `TRUE`, function errors; if `FALSE`, function quietly assumes EPSG:4326. -If `NULL`, the default, function assumes EPSG:4326 with a warning.} +\item{error_crs}{Logical: Should this function error if \code{data} has no CRS? +If \code{TRUE}, function errors; if \code{FALSE}, function quietly assumes EPSG:4326. +If \code{NULL}, the default, function assumes EPSG:4326 with a warning.} } \value{ -An `sfc` object (from [sf::st_as_sfc]). +An \code{sfc} object (from \link[sf:st_as_sfc]{sf::st_as_sfc}). } \description{ -[add_bbox_buffer] calculates the great circle distance both corners of +\link{add_bbox_buffer} calculates the great circle distance both corners of your bounding box are from the centroid and extends those by a set distance. Due to using Haversine/great circle distance, latitude/longitude calculations will not be exact. -[set_bbox_side_length] is a thin wrapper around [add_bbox_buffer] which sets +\link{set_bbox_side_length} is a thin wrapper around \link{add_bbox_buffer} which sets all sides of the bounding box to (approximately) a specified length. Both of these functions are intended to be used with geographic coordinate systems (data using longitude and latitude for position). For projected -coordinate systems, a more sane approach is to use [sf::st_buffer] to add a -buffer, or combine [sf::st_centroid] with the buffer to set a specific side +coordinate systems, a more sane approach is to use \link[sf:geos_unary]{sf::st_buffer} to add a +buffer, or combine \link[sf:geos_unary]{sf::st_centroid} with the buffer to set a specific side length. } \examples{ diff --git a/man/geom_spatial_rgb.Rd b/man/geom_spatial_rgb.Rd index 29707aa..c734d2f 100644 --- a/man/geom_spatial_rgb.Rd +++ b/man/geom_spatial_rgb.Rd @@ -41,15 +41,15 @@ default), it is combined with the default mapping at the top level of the plot. You must supply \code{mapping} if there is no plot mapping.} \item{data}{The data to be displayed in this layer. In addition to the three -options described in [ggplot2::geom_raster], there are two additional +options described in \link[ggplot2:geom_tile]{ggplot2::geom_raster}, there are two additional methods: -If a `RasterStack` object (see [raster::stack]), this function will coerce -the stack to a data frame and assume the raster bands are in RGB order +If a \code{SpatRaster} object (see \link[terra:rast]{terra::rast}), this function will coerce +the raster to a data frame and assume the raster bands are in RGB order (while allowing for, but ignoring, a fourth alpha band). If a length-1 character vector, this function will attempt to load the object -via [raster::stack].} +via \link[terra:rast]{terra::rast}.} \item{stat}{The statistical transformation to use on the data for this layer, as a string.} @@ -88,17 +88,17 @@ that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} \item{scale}{Integer. Maximum (possible) value in the three channels. -If `NULL`, attempts to infer proper values from data -- if all RGB values +If \code{NULL}, attempts to infer proper values from data -- if all RGB values are <= 1 then 1, <= 255 then 255, and otherwise 65535.} \item{geom}{The geometric object to use display the data} } \description{ -`geom_spatial_rgb` and `stat_spatial_rgb` allow users to plot three-band RGB -rasters in [ggplot2], using these layers as background base maps for other -spatial plotting. Note that unlike [ggplot2::geom_sf], this function does -_not_ force [ggplot2::coord_sf]; for accurate mapping, add -[ggplot2::coord_sf] with a `crs` value matching your input raster as a layer. +\code{geom_spatial_rgb} and \code{stat_spatial_rgb} allow users to plot three-band RGB +rasters in \link{ggplot2}, using these layers as background base maps for other +spatial plotting. Note that unlike \link[ggplot2:ggsf]{ggplot2::geom_sf}, this function does +\emph{not} force \link[ggplot2:ggsf]{ggplot2::coord_sf}; for accurate mapping, add +\link[ggplot2:ggsf]{ggplot2::coord_sf} with a \code{crs} value matching your input raster as a layer. } \examples{ \dontrun{ @@ -120,7 +120,7 @@ output_tiles <- get_tiles(simulated_data, merged_ortho <- tempfile(fileext = ".tif") merge_rasters(output_tiles[["ortho"]], merged_ortho) -merged_stack <- raster::stack(merged_ortho) +merged_stack <- terra::rast(merged_ortho) library(ggplot2) diff --git a/man/georeference_overlay.Rd b/man/georeference_overlay.Rd index b7f9279..9f9fa53 100644 --- a/man/georeference_overlay.Rd +++ b/man/georeference_overlay.Rd @@ -12,13 +12,12 @@ georeference_overlay( } \arguments{ \item{overlay_file}{The image overlay to georeference. File format will be -detected automatically from file extension; options include `jpeg/jpg`, -`png`, and `tif/tiff`.} +detected automatically from file extension; options include \code{jpeg/jpg}, +\code{png}, and \code{tif/tiff}.} \item{reference_raster}{The raster file to base georeferencing on. The output -image will have the same extent and CRS as the reference raster. Accepts both -Raster* objects from the `raster` package or a file readable by -[raster::raster].} +image will have the same extent and CRS as the reference raster. Accepts +anything that can be read by \link[terra:rast]{terra::rast}} \item{output_file}{The path to write the georeferenced image file to. Must be a TIFF.} @@ -30,7 +29,7 @@ The file path written to, invisibly. This function georeferences an image overlay based on a reference raster, setting the extent and CRS of the image to those of the raster file. To georeference multiple images and merge them into a single file, see -[merge_rasters]. +\link{merge_rasters}. } \examples{ \dontrun{ diff --git a/man/get_tiles.Rd b/man/get_tiles.Rd index 82c95ed..c3191a3 100644 --- a/man/get_tiles.Rd +++ b/man/get_tiles.Rd @@ -5,6 +5,7 @@ \alias{get_tiles.sf} \alias{get_tiles.sfc} \alias{get_tiles.Raster} +\alias{get_tiles.SpatRaster} \alias{get_tiles.list} \title{A user-friendly way to get USGS National Map data tiles for an area} \usage{ @@ -56,6 +57,18 @@ get_tiles( ... ) +\method{get_tiles}{SpatRaster}( + data, + output_prefix = tempfile(), + side_length = NULL, + resolution = 1, + services = "elevation", + verbose = FALSE, + georeference = TRUE, + projected = NULL, + ... +) + \method{get_tiles}{list}( data, output_prefix = tempfile(), @@ -69,8 +82,8 @@ get_tiles( ) } \arguments{ -\item{data}{An \code{sf} or \code{Raster} object; tiles will be downloaded for the full -extent of the provided object.} +\item{data}{An \code{sf} or \code{SpatRast} object; tiles will be downloaded for the +full extent of the provided object.} \item{output_prefix}{The file prefix to use when saving tiles.} diff --git a/man/make_unity.Rd b/man/make_unity.Rd new file mode 100644 index 0000000..9ffb9e8 --- /dev/null +++ b/man/make_unity.Rd @@ -0,0 +1,61 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/make_unity.R +\name{make_unity} +\alias{make_unity} +\title{Initialize terrain inside of a Unity project.} +\usage{ +make_unity( + project, + heightmap, + overlay = NULL, + side_length = 4097, + scene_name = "terrainr_scene", + action = TRUE, + unity = find_unity() +) +} +\arguments{ +\item{project}{The directory path of the Unity project to create terrain +inside.} + +\item{heightmap}{The file path for the raster to transform into terrain.} + +\item{overlay}{Optionally, a file path for an image overlay to layer on top +of the terrain surface. Leave as NULL for no overlay.} + +\item{side_length}{The side length, in map units, for the terrain tiles. +Must be equal to 2^x + 1, for any x between 5 and 12.} + +\item{scene_name}{The name of the Unity scene to create the terrain in.} + +\item{action}{Boolean: Execute the unifir "script" and create the Unity +project? If FALSE, returns a non-executed script.} + +\item{unity}{The location of the Unity executable to create projects with. +By default, will be auto-detected by \link[unifir:find_unity]{unifir::find_unity}} +} +\value{ +An object of class "unifir_script", containing either an executed +unifir script (if action = TRUE) or a non-executed script object +(if action = FALSE). +} +\description{ +Initialize terrain inside of a Unity project. +} +\examples{ +\dontrun{ +if (!isTRUE(as.logical(Sys.getenv("CI")))) { + simulated_data <- data.frame( + id = seq(1, 100, 1), + lat = runif(100, 44.04905, 44.17609), + lng = runif(100, -74.01188, -73.83493) + ) + simulated_data <- sf::st_as_sf(simulated_data, coords = c("lng", "lat")) + output_files <- get_tiles(simulated_data) + temptiff <- tempfile(fileext = ".tif") + merge_rasters(output_files["elevation"][[1]], temptiff) + make_unity(file.path(tempdir(), "unity"), temptiff) +} +} + +} diff --git a/man/merge_rasters.Rd b/man/merge_rasters.Rd index 12ffcab..34fd017 100644 --- a/man/merge_rasters.Rd +++ b/man/merge_rasters.Rd @@ -20,12 +20,12 @@ georeferenced rasters you want to use.} to.} \item{options}{Optionally, a character vector of options to be passed -directly to [sf::gdal_utils]. If the fallback is used and any options (other +directly to \link[sf:gdal_utils]{sf::gdal_utils}. If the fallback is used and any options (other than "-overwrite") are specified, this will issue a warning.} -\item{overwrite}{Logical: overwrite `output_raster` if it exists? If FALSE +\item{overwrite}{Logical: overwrite \code{output_raster} if it exists? If FALSE and the file exists, this function will fail with an error. The behavior if -this argument is TRUE and "-overwrite" is passed to `options` directly is +this argument is TRUE and "-overwrite" is passed to \code{options} directly is not stable.} \item{force_fallback}{Logical: if TRUE, uses the much slower fallback method @@ -33,12 +33,12 @@ by default. This is used for testing purposes and is not recommended for use by end users.} } \value{ -`output_raster`, invisibly. +\code{output_raster}, invisibly. } \description{ -Some functions like [get_tiles] return multiple separate files +Some functions like \link{get_tiles} return multiple separate files when it can be useful to have a single larger raster instead. This function -is a thin wrapper over [sf::gdal_utils(util = "warp")], making it easy to +is a thin wrapper over \link[sf:gdal_utils]{sf::gdal_utils}, making it easy to collapse those multiple raster files into a single TIFF. } \examples{ diff --git a/man/point_from_distance.Rd b/man/point_from_distance.Rd index c501895..312b8b5 100644 --- a/man/point_from_distance.Rd +++ b/man/point_from_distance.Rd @@ -21,14 +21,14 @@ the original point to apply} \item{azimuth}{A azimuth (in units specified in \code{azimuth_unit}) representing the direction to apply the distance from the original point in} -\item{distance_unit}{A string passed to [convert_distance] +\item{distance_unit}{A string passed to convert_distance indicating the units of the provided distance.} \item{azimuth_unit}{A string (either \code{degrees} or \code{radians}) indicating the units of the \code{azimuth} argument} } \value{ -An object of class [terrainr_coordinate_pair]. +An object of class \link{terrainr_coordinate_pair}. } \description{ Find latitude and longitude for a certain distance and azimuth from a point. diff --git a/man/raster_to_raw_tiles.Rd b/man/raster_to_raw_tiles.Rd index f245258..c73aca6 100644 --- a/man/raster_to_raw_tiles.Rd +++ b/man/raster_to_raw_tiles.Rd @@ -22,7 +22,7 @@ written to. } \description{ This function has been deprecated as of terrainr 0.5.0 in favor of the new -function, [make_manifest]. While it will be continued to be exported until +function, \link{make_manifest}. While it will be continued to be exported until at least 2022, improvements and bug fixes will only be made to the new function. Please open an issue if any features you relied upon is missing from the new function! diff --git a/man/terrainr_bounding_box.Rd b/man/terrainr_bounding_box.Rd index 74f8268..b10ead7 100644 --- a/man/terrainr_bounding_box.Rd +++ b/man/terrainr_bounding_box.Rd @@ -8,17 +8,17 @@ terrainr_bounding_box(bl, tr, coord_units = "degrees") } \arguments{ \item{bl, tr}{The bottom left (\code{bl}) and top right (\code{tr}) corners of -the bounding box, either as a [terrainr_coordinate_pair] object +the bounding box, either as a \link{terrainr_coordinate_pair} object or a coordinate pair. If the coordinate pair is not named, it is assumed to be in (lat, lng) format; if it is named, the function will attempt to properly identify coordinates.} -\item{coord_units}{Arguments passed to [terrainr_coordinate_pair]. -If \code{bl} and \code{tr} are already [terrainr_coordinate_pair] +\item{coord_units}{Arguments passed to \link{terrainr_coordinate_pair}. +If \code{bl} and \code{tr} are already \link{terrainr_coordinate_pair} objects, these arguments are not used.} } \value{ -An object of class [terrainr_bounding_box]. +An object of class \link{terrainr_bounding_box}. } \description{ In order to simplify code, most \code{terrainr} functions expect a set S4 diff --git a/man/unity_crops.Rd b/man/unity_crops.Rd index 334dac6..3c62866 100644 --- a/man/unity_crops.Rd +++ b/man/unity_crops.Rd @@ -22,7 +22,7 @@ transform_overlay(overlay, side_length = 4097, output_prefix = "import") \item{heightmap}{File path to the heightmap to transform.} \item{overlay}{File path to the image overlay to transform. Optional for -[make_manifest].} +\link{make_manifest}.} \item{output_prefix}{The file path to prefix output tiles with.} @@ -32,18 +32,18 @@ transform_overlay(overlay, side_length = 4097, output_prefix = "import") to not copy the importer script. Will overwrite any file at the same path.} \item{side_length}{Side length, in pixels, of each output tile. If the raster -has dimensions not evenly divisible by `side_length`, tiles will be generated +has dimensions not evenly divisible by \code{side_length}, tiles will be generated with overhanging pieces set to 0 units of elevation or RGB 0 (pure black). Side lengths not equal to 2^x + 1 (for x <= 12) will cause a warning, as tiles must be this size for import into Unity.} } \value{ -`manifest_path`, invisibly. +\code{manifest_path}, invisibly. } \description{ These functions crop input raster files into smaller square tiles and then converts them into either .png or .raw files which are ready to be imported -into the Unity game engine. [make_manifest] also writes a "manifest" file and +into the Unity game engine. \link{make_manifest} also writes a "manifest" file and importer script which may be used to automatically import the tiles into Unity. } diff --git a/man/vector_to_overlay.Rd b/man/vector_to_overlay.Rd index ad10d7c..b0845da 100644 --- a/man/vector_to_overlay.Rd +++ b/man/vector_to_overlay.Rd @@ -21,7 +21,7 @@ character vector containing a path to a file readable by \link[sf:st_read]{sf::r \item{reference_raster}{The raster file to produce an overlay for. The output overlay will have the same extent and resolution as the input raster. Users may provide either a Raster* object or a length 1 character -vector containing a path to a file readable by \link[raster:raster]{raster::raster}.} +vector containing a path to a file readable by \link[terra:rast]{terra::rast}.} \item{output_file}{The path to save the image overlay to. If \code{NULL}, saves to a tempfile.} diff --git a/tests/testthat/test-1-make_manifest.R b/tests/testthat/test-1-make_manifest.R index 7c9c4ac..ba6ecc0 100644 --- a/tests/testthat/test-1-make_manifest.R +++ b/tests/testthat/test-1-make_manifest.R @@ -24,7 +24,20 @@ test_that("make_manifest reproduces the same tiles", { ) expect_equal( - png::readPNG(outputs_table$V8)[, , 1:3], + png::readPNG(outputs_table$V8), png::readPNG("testdata/manifest_ort.png") ) }) + +test_that("prep_table warns when expected", { + + expect_warning( + prep_table("testdata/3DEP_gr.tif", + 10, + "import", + type = "elevation" + ), + "Side lengths must be equal" + ) + +}) diff --git a/tests/testthat/test-1-raster_to_raw_tiles.R b/tests/testthat/test-1-raster_to_raw_tiles.R deleted file mode 100644 index 38f10fd..0000000 --- a/tests/testthat/test-1-raster_to_raw_tiles.R +++ /dev/null @@ -1,33 +0,0 @@ -test_that("raster_to_raw warns about deprecation", { - # on GitHub, this test fails to find temp files used in the middle of - # raster_to_raw on windows and mac devices - # - # this does not occur on the windows machine I have access to, though I am - # yet to test it on a mac. As such, I believe this may be an issue with the - # GH environment rather than using magick to open a tempfile. - skip_on_os(c("windows", "mac")) - skip_on_cran() - - expect_warning( - raster_to_raw_tiles( - input_file = "testdata/merge_rasters_test.tif", - output_prefix = tempfile(), - side_length = 4097, - raw = FALSE - ) - ) - - outputs <- suppressWarnings( - raster_to_raw_tiles( - input_file = "testdata/merge_rasters_test.tif", - output_prefix = tempfile(), - side_length = 4097, - raw = FALSE - ) - ) - - expect_equal( - png::readPNG(outputs[[1]])[, , 1:3], - png::readPNG("testdata/raster_to_raw_1.png") - ) -}) diff --git a/tests/testthat/test-2-get_tiles_3dep.R b/tests/testthat/test-2-get_tiles_3dep.R index 11715c8..215b359 100644 --- a/tests/testthat/test-2-get_tiles_3dep.R +++ b/tests/testthat/test-2-get_tiles_3dep.R @@ -17,14 +17,16 @@ test_that("get_tiles gets the same elevation tiles twice", { expect_equal(length(output_tif), 1) expect_equal(length(output_tif[[1]]), 1) - stored_raster <- raster::raster("testdata/3DEP.tif") - test_raster <- raster::raster(output_tif[[1]]) + stored_raster <- terra::rast("testdata/3DEP.tif") + test_raster <- terra::rast(output_tif[[1]]) - expect_equal(stored_raster@crs, test_raster@crs) - expect_equal(stored_raster@extent, test_raster@extent) + expect_equal(as.vector(terra::crs(stored_raster)), + as.vector(terra::crs(test_raster))) + expect_equal(as.vector(terra::ext(stored_raster)), + as.vector(terra::ext(test_raster))) expect_equal( - raster::cellStats(stored_raster, "max"), - raster::cellStats(test_raster, "max"), + as.vector(terra::global(stored_raster, max))[[1]], + as.vector(terra::global(test_raster, max))[[1]], tolerance = 0.01 ) }) diff --git a/tests/testthat/test-4-merge_rasters.R b/tests/testthat/test-4-merge_rasters.R index ec6724f..667de42 100644 --- a/tests/testthat/test-4-merge_rasters.R +++ b/tests/testthat/test-4-merge_rasters.R @@ -27,15 +27,18 @@ test_that("merge_raster files are identical no matter the filename", { merge_rasters(c(tmptif[[1]], tmptif[[2]]), tmptif[[4]]) expect_equal( - raster::raster(tmptif[[3]])@extent, - raster::raster(tmptif[[4]])@extent + as.vector(terra::ext(terra::rast(tmptif[[3]]))), + as.vector(terra::ext(terra::rast(tmptif[[4]]))) ) - stored_raster <- raster::raster("testdata/merge_dem.tif") - test_raster <- raster::raster(tmptif[[4]]) + stored_raster <- terra::rast("testdata/merge_dem.tif") + test_raster <- terra::rast(tmptif[[4]]) + + expect_equal(as.vector(terra::crs(stored_raster)), + as.vector(terra::crs(test_raster))) + expect_equal(as.vector(terra::ext(stored_raster)), + as.vector(terra::ext(test_raster))) - expect_equal(stored_raster@crs, test_raster@crs) - expect_equal(stored_raster@extent, test_raster@extent) }) test_that("fallback method works", { @@ -71,15 +74,17 @@ test_that("fallback method works", { merge_rasters(c(tmptif[[1]], tmptif[[2]]), tmptif[[4]], force_fallback = TRUE) expect_equal( - raster::raster(tmptif[[3]])@extent, - raster::raster(tmptif[[4]])@extent + as.vector(terra::ext(terra::rast(tmptif[[3]]))), + as.vector(terra::ext(terra::rast(tmptif[[4]]))) ) - stored_raster <- raster::raster("testdata/merge_dem.tif") - test_raster <- raster::raster(tmptif[[4]]) + stored_raster <- terra::rast("testdata/merge_dem.tif") + test_raster <- terra::rast(tmptif[[4]]) - expect_equal(stored_raster@crs, test_raster@crs) - expect_equal(stored_raster@extent, test_raster@extent) + expect_equal(as.vector(terra::crs(stored_raster)), + as.vector(terra::crs(test_raster))) + expect_equal(as.vector(terra::ext(stored_raster)), + as.vector(terra::ext(test_raster))) }) test_that("overwrite works as expected", { @@ -116,17 +121,26 @@ test_that("overwrite works as expected", { NA ) expect_warning( - merge_rasters(c(tmptif[[1]], tmptif[[2]]), test_file, options = "-overwrite"), + merge_rasters(c(tmptif[[1]], tmptif[[2]]), + test_file, + options = "-overwrite" + ), NA ) expect_warning( - merge_rasters(c(tmptif[[1]], tmptif[[2]]), test_file, overwrite = TRUE, options = "-overwrite"), + merge_rasters(c(tmptif[[1]], tmptif[[2]]), + test_file, + overwrite = TRUE, options = "-overwrite" + ), NA ) merge_rasters(c(tmptif[[2]]), output_raster = test_file, overwrite = TRUE) expect_false( - raster::raster(test_file)@extent == raster::raster(test_copy)@extent + all( + as.vector(terra::ext(terra::rast(test_file))) == + as.vector(terra::ext(terra::rast(test_copy))) + ) ) }) diff --git a/tests/testthat/test-add_bbox_buffer.R b/tests/testthat/test-add_bbox_buffer.R index 314e01f..ba735f4 100644 --- a/tests/testthat/test-add_bbox_buffer.R +++ b/tests/testthat/test-add_bbox_buffer.R @@ -48,7 +48,7 @@ test_that("set_bbox_side_length works within 1%", { tolerance = 8000 * 0.005 ) - tmp_raster <- raster::raster("testdata/merge_rasters_test.tif") + tmp_raster <- terra::rast("testdata/merge_rasters_test.tif") rstr_bbox <- set_bbox_side_length(tmp_raster, 8000) rstr_bbox <- sf::st_bbox(rstr_bbox) diff --git a/tests/testthat/test-geom_spatial_rgb.R b/tests/testthat/test-geom_spatial_rgb.R index 502d13e..75de34a 100644 --- a/tests/testthat/test-geom_spatial_rgb.R +++ b/tests/testthat/test-geom_spatial_rgb.R @@ -17,8 +17,8 @@ test_that("all methods of geom_spatial_rgb are equivalent", { merged_ortho <- tempfile(fileext = ".tif") merge_rasters(output_tiles[["ortho"]], merged_ortho) - test <- raster::stack(merged_ortho) - test_df <- raster::as.data.frame(test, xy = TRUE) + test <- terra::rast(merged_ortho) + test_df <- terra::as.data.frame(test, xy = TRUE) test_df <- setNames(test_df, c("x", "y", "red", "green", "blue")) plots <- vapply(1:6, function(x) tempfile(fileext = ".png"), character(1)) @@ -108,28 +108,40 @@ test_that("all methods of geom_spatial_rgb are equivalent", { ggplot2::geom_sf(data = simulated_data) ggplot2::ggsave(plots[[6]]) - expect_identical( - brio::read_file_raw(plots[[1]]), - brio::read_file_raw(plots[[2]]), + agreement <- sum( + (png::readPNG(plots[[1]]) == png::readPNG(plots[[2]])) / + length(png::readPNG(plots[[1]])) ) - expect_identical( - brio::read_file_raw(plots[[3]]), - brio::read_file_raw(plots[[4]]), + expect_true( + agreement > 0.95 ) - expect_identical( - brio::read_file_raw(plots[[5]]), - brio::read_file_raw(plots[[6]]), + agreement <- sum( + (png::readPNG(plots[[3]]) == png::readPNG(plots[[4]])) / + length(png::readPNG(plots[[1]])) ) - expect_identical( - brio::read_file_raw(plots[[2]]), - brio::read_file_raw(plots[[3]]), + expect_true( + agreement > 0.95 ) - expect_identical( - brio::read_file_raw(plots[[1]]), - brio::read_file_raw(plots[[6]]), + agreement <- sum( + (png::readPNG(plots[[5]]) == png::readPNG(plots[[6]])) / + length(png::readPNG(plots[[6]])) ) + + expect_true( + agreement > 0.95 + ) + + agreement <- sum( + (png::readPNG(plots[[1]]) == png::readPNG(plots[[6]])) / + length(png::readPNG(plots[[1]])) + ) + + expect_true( + agreement > 0.95 + ) + }) diff --git a/tests/testthat/test-georeference_overlay.R b/tests/testthat/test-georeference_overlay.R index 50c98a6..9afeca9 100644 --- a/tests/testthat/test-georeference_overlay.R +++ b/tests/testthat/test-georeference_overlay.R @@ -12,20 +12,18 @@ test_that("georeference_overlay edge cases work", { }) test_that("georeference_overlay produces the same file twice", { - gr_rst <- raster::raster(georeference_overlay( + gr_rst <- terra::rast(georeference_overlay( "testdata/NAIPPlus.png", "testdata/NAIPPlus_gr.tif", tempfile(fileext = ".tif") )) - gr_rst@file@name <- "" - gr_rst@data@names <- "" - - ref_rst <- raster::raster("testdata/NAIPPlus_gr.tif") - ref_rst@file@name <- "" - ref_rst@data@names <- "" - + ref_rst <- terra::rast("testdata/NAIPPlus_gr.tif") + expect_equal( + terra::crs(gr_rst), + terra::crs(ref_rst) + ) expect_equal( - gr_rst, - ref_rst + as.vector(terra::ext(gr_rst)), + as.vector(terra::ext(ref_rst)) ) }) diff --git a/tests/testthat/test-get_tiles.R b/tests/testthat/test-get_tiles.R index b8ea44b..e94eefb 100644 --- a/tests/testthat/test-get_tiles.R +++ b/tests/testthat/test-get_tiles.R @@ -1,19 +1,26 @@ # Note: Individual data sources also have tests as test-2-get_tiles_.R -test_that("raster method is consistent", { - tmp_raster <- raster::raster("testdata/merge_rasters_test.tif") +test_that("SpatRast method is consistent", { + tmp_raster <- terra::rast("testdata/merge_rasters_test.tif") rstr_tile <- get_tiles(tmp_raster, bboxSR = 4326, imageSR = 4326) - downloaded_raster <- raster::raster(rstr_tile[["elevation"]]) - test_raster <- raster::raster("testdata/raster_tile.tif") - expect_equal(downloaded_raster@crs, test_raster@crs) - expect_equal(downloaded_raster@extent, test_raster@extent) + downloaded_raster <- terra::rast(rstr_tile[["elevation"]]) + test_raster <- terra::rast("testdata/raster_tile.tif") + expect_equal(as.vector(terra::crs(downloaded_raster)), + as.vector(terra::crs(test_raster))) + expect_equal(as.vector(terra::ext(downloaded_raster)), + as.vector(terra::ext(test_raster))) }) test_that("warnings fire appropriately", { skip_on_cran() - tmp_raster <- raster::raster("testdata/merge_rasters_test.tif") + tmp_raster <- terra::rast("testdata/merge_rasters_test.tif") + terra::crs(tmp_raster) <- NA expect_warning( - get_tiles(tmp_raster) + expect_warning( + get_tiles(tmp_raster), + "Assuming geographic CRS" + ), + "Assuming CRS of" ) }) @@ -35,14 +42,16 @@ test_that("The deprecated list method still works", { expect_equal(length(output_tif), 1) expect_equal(length(output_tif[[1]]), 1) - stored_raster <- raster::raster("testdata/3DEP.tif") - test_raster <- raster::raster(output_tif[[1]]) + stored_raster <- terra::rast("testdata/3DEP.tif") + test_raster <- terra::rast(output_tif[[1]]) - expect_equal(stored_raster@crs, test_raster@crs) - expect_equal(stored_raster@extent, test_raster@extent) + expect_equal(as.vector(terra::crs(stored_raster)), + as.vector(terra::crs(test_raster))) + expect_equal(as.vector(terra::ext(stored_raster)), + as.vector(terra::ext(test_raster))) expect_equal( - raster::cellStats(stored_raster, "max"), - raster::cellStats(test_raster, "max"), + as.vector(terra::global(stored_raster, max))[[1]], + as.vector(terra::global(test_raster, max))[[1]], tolerance = 0.01 ) }) @@ -62,13 +71,14 @@ test_that("projected returns are consistent", { dl_loc <- sf::st_transform(dl_loc, 5071) dl_save <- get_tiles(dl_loc) - stored_raster <- suppressWarnings(raster::raster("testdata/projected.tif")) - test_raster <- suppressWarnings(raster::raster(dl_save[[1]])) + stored_raster <- terra::rast("testdata/projected.tif") + test_raster <- terra::rast(dl_save[[1]]) - expect_equal(stored_raster@crs, test_raster@crs) + expect_equal(as.vector(terra::crs(stored_raster)), + as.vector(terra::crs(test_raster))) expect_equal( - raster::cellStats(stored_raster, "max"), - raster::cellStats(test_raster, "max"), + as.vector(terra::global(stored_raster, max))[[1]], + as.vector(terra::global(test_raster, max))[[1]], tolerance = 0.01 ) }) diff --git a/tests/testthat/test-make_unity.R b/tests/testthat/test-make_unity.R new file mode 100644 index 0000000..bec4d1d --- /dev/null +++ b/tests/testthat/test-make_unity.R @@ -0,0 +1,30 @@ +test_that("make_unity is stable", { + + Sys.setenv("unifir_debugmode" = TRUE) + + outputs <- make_unity( + file.path(tempdir(), "make_unity"), + "testdata/3DEP_gr.tif", + "testdata/NAIPPlus_gr.tif", + action = FALSE, + unity = unifir::waiver() + ) + + expect_equal( + length(outputs$props), + nrow(outputs$beats) + ) + + expect_equal( + length(outputs$props), + 6 + ) + + expect_match( + outputs$scene_name, + "terrainr_scene" + ) + + Sys.setenv("unifir_debugmode" = "") + +}) diff --git a/tests/testthat/testdata/manifest_ort.png b/tests/testthat/testdata/manifest_ort.png index 2723b6d..7e80557 100644 Binary files a/tests/testthat/testdata/manifest_ort.png and b/tests/testthat/testdata/manifest_ort.png differ diff --git a/tests/testthat/testdata/raster_to_raw_1.png b/tests/testthat/testdata/raster_to_raw_1.png index e8e8804..19f641f 100644 Binary files a/tests/testthat/testdata/raster_to_raw_1.png and b/tests/testthat/testdata/raster_to_raw_1.png differ diff --git a/tests/testthat/testdata/vto_poly.png b/tests/testthat/testdata/vto_poly.png index 6b9bed0..7ed047b 100644 Binary files a/tests/testthat/testdata/vto_poly.png and b/tests/testthat/testdata/vto_poly.png differ diff --git a/vignettes/unity_instructions.Rmd b/vignettes/unity_instructions.Rmd index f79217c..dbdbd9d 100644 --- a/vignettes/unity_instructions.Rmd +++ b/vignettes/unity_instructions.Rmd @@ -64,62 +64,27 @@ merged_tiles <- zion %>% lapply(merge_rasters) ``` -We've now got our data downloaded! Our next step is to turn it into a data -format we can import into Unity. - -As of terrainr 0.5.0, the way to do this is via the function `make_manifest`. -The first argument to this function is the elevation raster you want to use as -a heightmap -- in our case, `merged_tiles$elevation`. The second argument -optionally takes the image overlay you want to put on top of that heightmap. -In our case, that means everything we need to provide is in the `merged_tiles` -list: - -```{r, eval = FALSE} -make_manifest(merged_tiles$elevation, - merged_tiles$ortho) -``` - -After a moment, this function will spit out a number of files: our heightmap and -overlay tiles, all prefixed with `import_`, a C# file named `import_terrain.cs`, -and a final file named `terrainr.manifest` (note that all of these names can be -changed via arguments to `make_manifest`, but for simplicity's sake I'm using -the default names now). - -```{r, echo = FALSE} -knitr::include_graphics("generated_files.png") -``` - -Now go ahead and open Unity. From the main "Hub" menu, click "New" to create a -new project. Set the project name to whatever you want, then click "Create". - -```{r, echo = FALSE} -knitr::include_graphics("new_unity.jpg") -``` - -Go ahead and move all the files from `make_manifest` into the root directory of -your new Unity project. Then move the `import_terrain.cs` file into the `Assets` -directory inside that folder (but leave everything else in the root directory!). - -Go back to Unity now. A second after you click into the window, you should -notice a "terrainr" menu appear in the top bar. Click that menu, then the only -option in the drop-down. A menu should appear; click "Import" to import your -tiles into Unity. - -```{r, echo = FALSE} -knitr::include_graphics("manifest_import.png") -``` - -The importer menu will disappear, then Unity will take a minute or two to import -all your tiles. Depending on your data, you may see something that looks like -this: - -```{r, echo = FALSE} -knitr::include_graphics("ominous.png") +We've now got our data downloaded! All that's left is to import these tiles into +Unity. + +As of terrainr 0.7.0, the way to do this is via the function `make_unity`. +Assuming you have Unity installed on your computer, we can go ahead and +import our terrain into a brand new project, named `zion`, through the +following: + +```{r eval = FALSE} +make_unity( + project = "zion", + heightmap = merged_tiles$elevation, + overlay = merged_tiles$ortho +) ``` -That's perfectly fine! Right click on the "Scene" window in the middle, and then -press and hold "S" on your keyboard to move the camera back. After a -second, you should see your terrain surface! +This will create a new folder, named `zion`, containing our Unity project. +Open that project in Unity, and then open the scene (either using `Ctrl+O` +or `File -> Open Scene`) named `Scenes/terrainr_scene.unity`. +Double click on one of the terrain tiles and you'll zoom out to see the +entire terrain: ```{r, echo = FALSE} knitr::include_graphics("terrain_surface.jpg") @@ -127,7 +92,8 @@ knitr::include_graphics("terrain_surface.jpg") You can now move around your surface by right clicking on the image and moving around with the W-A-S-D keys on your keyboard. Note that your movement speed -starts off very slow, and then accelerates over time. +starts off very slow, and then accelerates over time (especially if you +press and hold the "shift" key). And ta-da, you have a surface in Unity! You can go ahead and customize the scene further (I'll usually then click on "Directional Light" and change "Render Mode"