diff --git a/DESCRIPTION b/DESCRIPTION index 86271c9fe..9f088abb0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: terra Type: Package Title: Spatial Data Analysis -Version: 1.8-61 -Date: 2025-07-18 +Version: 1.8-71 +Date: 2025-09-27 Depends: R (>= 3.5.0) Suggests: parallel, tinytest, ncdf4, sf (>= 0.9-8), deldir, XML, leaflet (>= 2.2.1), htmlwidgets LinkingTo: Rcpp diff --git a/NEWS.md b/NEWS.md index ff6f3d23c..619fe05c7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,17 +1,39 @@ -# version 1.8-61 +# version 1.8-71 ## bug fixes -- `project(mask=TRUE)` could fail with high-resolution global rasters because of data-line flipping [SO 79708536](https://stackoverflow.com/q/79708536/635245) by Patrick -- `plot(pax=list(mgp=c(1,1,2))` now sets mgp seperately for horizontal and vertical axes [#1873](https://github.com/rspatial/terra/issues/1873) by Hu shiyu +## enhancements + +## new -## enhancements +# version 1.8-70 + +Released 2025-09-27 +## bug fixes + +- `project(mask=TRUE)` could fail with high-resolution global rasters because of date-line flipping [SO 79708536](https://stackoverflow.com/q/79708536/635245) by Patrick +- `plot(pax=list(mgp=c(1,1,2))` now sets mgp seperately for horizontal and vertical axes [#1873](https://github.com/rspatial/terra/issues/1873) by Hu shiyu +- `coltab(x, ..., layer=1)<-` argument layer did not work for layer names [#1879](https://github.com/rspatial/terra/issues/1879) by Alex Ilich +- `sds` could create a SpatRasterDataset with SpatRasters with different spatial resolutions [#1884](https://github.com/rspatial/terra/issues/1884) by Stefan Fallert +- `identical` did not consider NA values [#1890](https://github.com/rspatial/terra/issues/1890) by Facundo Muñoz +- `add_grid` did not respect the clipping region if a second raster was added with `add=TRUE` [#1889](https://github.com/rspatial/terra/issues/1889) by Lucas Salinas Morales +- `rast(x, type = "xyz")` did not inherit CRS from a SpatVector [#1886](https://github.com/rspatial/terra/issues/1886) by Danielle Ferraro +- `distance(values = TRUE)` returned unexpected results [#1891](https://github.com/rspatial/terra/issues/1891) by Jason Flower +- `plot` with arguments for a continuous legend failed if no such legend was drawn [#1897](https://github.com/rspatial/terra/issues/1897) by François Rousseu +## enhancements + +- when computing aggregated time steps such as days from POSIXct (seconds) time, terra now uses the date in the specified time zone, unlike base `as.Date` that seems to return the date in the UTC time zone [#1896](https://github.com/rspatial/terra/issues/1896) by Kodi Arfer +- better support for creating a SpatVector with an EMPTY wkt geometry [#1903](https://github.com/rspatial/terra/issues/1903) by Anatolii Tsyplenkov +- `makeTiles` gains argument "value" to set the returned value to be the filenames (default), a SpatRaster or a SpatRasterCollection [#1894](https://github.com/rspatial/terra/issues/1894) by Márcia Barbosa ## new +- it is now possible to create a SpatVector from Well Known Binary (WKB) data [#1895](https://github.com/rspatial/terra/pull/1895) by Jan Hartman +- `resample` now also accepts, instead of a template SpatRaster, one or two numbers to set the resolution of the output SpatRaster [#1874](https://github.com/rspatial/terra/pull/1874) by Agustin Lobo + # version 1.8-60 @@ -260,7 +282,7 @@ Released 2024-12-12 - `plet` now works for RGB rasters and rasters with a color table [#1596](https://github.com/rspatial/terra/issues/1596) by Agustin Lobo - `vect` did not work properly [#1376](https://github.com/rspatial/terra/issues/1376) by silasprincipe - `compareGeom` did not work [#1654](https://github.com/rspatial/terra/issues/1654) by Jason Flower -- `buffer` is now more accurate buffers for lonlat polygons [#1616](https://github.com/rspatial/terra/issues/1616) by Roberto Amaral-Santos +- `buffer` is now more accurate for lonlat polygons [#1616](https://github.com/rspatial/terra/issues/1616) by Roberto Amaral-Santos - `terra:interpNear` used square windows, not circles, beyond 100 points [#1509](https://github.com/rspatial/terra/issues/1509) by Jean-Luc Dupouey - `vect` read INT64 fields as integers, sometimes leading to overflows. [#1666](https://github.com/rspatial/terra/issues/1666) by bengannon-fc - `plot` showed a legend title even if none was requested if title parameters were specified . [#1664](https://github.com/rspatial/terra/issues/1664) by Márcia Barbosa diff --git a/R/SpatVectorCollection.R b/R/SpatVectorCollection.R index 0b28d6d64..a4755fb43 100644 --- a/R/SpatVectorCollection.R +++ b/R/SpatVectorCollection.R @@ -99,7 +99,9 @@ setReplaceMethod("[", c("SpatVectorCollection", "numeric", "missing"), setMethod("[", c("SpatVectorCollection", "numeric", "missing"), function(x, i, j, drop=TRUE) { - if (i < 0) {i <- (1:length(x))[i]} + if (isTRUE(any(i < 0))) { + i <- (1:length(x))[i] + } if (drop && (length(i) == 1)) { tptr <- x@pntr$get(i-1) x <- methods::new("SpatVector") diff --git a/R/aggregate.R b/R/aggregate.R index d83ba2c6c..b6c6b9d02 100644 --- a/R/aggregate.R +++ b/R/aggregate.R @@ -37,6 +37,8 @@ setMethod("aggregate", signature(x="SpatRaster"), function(x, fact=2, fun="mean", ..., cores=1, filename="", overwrite=FALSE, wopt=list()) { + # check if fact is an integer? round fact? + if (hasValues(x)) { fun <- .makeTextFun(fun) toc <- FALSE @@ -55,6 +57,7 @@ function(x, fact=2, fun="mean", ..., cores=1, filename="", overwrite=FALSE, wopt toc = TRUE fun = "mean" } + if (toc) { # fun="mean", expand=TRUE, na.rm=TRUE, filename="" narm <- isTRUE(list(...)$na.rm) diff --git a/R/generics.R b/R/generics.R index 2ad224af1..0a98558d9 100644 --- a/R/generics.R +++ b/R/generics.R @@ -822,18 +822,53 @@ setMethod("rectify", signature(x="SpatRaster"), } ) -setMethod("resample", signature(x="SpatRaster", y="SpatRaster"), - function(x, y, method, threads=FALSE, by_util=FALSE, filename="", ...) { - if (missing(method)) { - if (is.factor(x)[1] || isTRUE(x@pntr$rgb)) { - method <- "near" - } else { - method <- "bilinear" - } +resample_method <- function(x, method) { + if (missing(method)) { + if (is.factor(x)[1] || isTRUE(x@pntr$rgb)) { + method <- "near" } else { - method <- match.arg(tolower(method[1]), c("near", "bilinear", "cubic", "cubicspline", "lanczos", "average", "sum", "mode", "min", "q1", "median", "q3", "max", "rms")) + method <- "bilinear" } + } else { + mts <- c("near", "bilinear", "cubic", "cubicspline", "lanczos", "mean", "sum", "modal", "min", "q1", "median", "q3", "max", "rms") + if (!inherits(method, "character")) { + method <- .makeTextFun(method) + if (!inherits(method, "character")) { + error("resample", paste("Not a valid method. Use one of:", paste(mts, collapse=", "))) + } + } + method <- match.arg(tolower(method[1]), c(mts, "average", "mode")) + method[method == "mean"] <- "average" + method[method == "modal"] <- "mode" + } + method +} + + +setMethod("resample", signature(x="SpatRaster", y="numeric"), + function(x, y, method, threads=FALSE, by_util=FALSE, filename="", ...) { + + stopifnot(all(y > 0)) + stopifnot(length(y) <= 2) + fact <- rep_len(y, 2) + r <- rast(x) + res(r) <- res(r) * fact + r <- crop(extend(r, c(0,1,0,1)), ext(x), snap="out") + + if (!hasValues(x)) { + return(r) + } + method <- resample_method(x, method) + resample(x, r, method, threads=threads, by_util=by_util, filename=filename, ...) + } +) + + +setMethod("resample", signature(x="SpatRaster", y="SpatRaster"), + function(x, y, method, threads=FALSE, by_util=FALSE, filename="", ...) { + + method <- resample_method(x, method) xcrs = crs(x) ycrs = crs(y) if ((xcrs == "") && (ycrs != "")) { diff --git a/R/plot_add.R b/R/plot_add.R index 08c470151..63b3f3322 100644 --- a/R/plot_add.R +++ b/R/plot_add.R @@ -80,6 +80,7 @@ add_box <- function(...) { add_grid <- function(nx=NULL, ny=nx, col="lightgray", lty="dotted", lwd=1) { p <- get.clip() + reset.clip() ## adapted from graphics::grid g.grid.at <- function (side, n, axp, usr2) { diff --git a/R/plot_axes.R b/R/plot_axes.R index 113089ed0..ed6035c7f 100644 --- a/R/plot_axes.R +++ b/R/plot_axes.R @@ -105,7 +105,7 @@ retro_labels <- function(x, lat=TRUE) { y <- x$axs retro <- isTRUE(y$retro) if (retro && (!x$lonlat)) { - warn("plot", "'retro' labels can only be used with lonlat data") + warn("plot", "'retro' labels can only be used with lon/lat data") retro <- FALSE } y$retro <- y$lab <- y$tick <- NULL diff --git a/R/plot_legend_classes.R b/R/plot_legend_classes.R index 4210d4f8d..32e517b3a 100644 --- a/R/plot_legend_classes.R +++ b/R/plot_legend_classes.R @@ -28,10 +28,11 @@ get_legxy <- function(r, e, pos, yshift, nudge=NULL) { bty="n", border="black", seg.len=1, plotlim, yshift=NULL, order=FALSE, sort=FALSE, reverse=FALSE, text.col=graphics::par("col"), nudge=c(0,0), title=NULL, leg_i=1, title.x=NULL, title.y=NULL, title.adj=0.5, title.pos=NULL, - title.cex=cex[1], title.col=text.col[1], title.font=NULL, ..., + title.cex=cex[1], title.col=text.col[1], title.font=NULL, # catch and kill - merge, trace, size) { - + merge, trace, digits, size, at, format, title.srt, + tick, tick.length, tick.col, tick.box.col, tick.lwd, + tic, tic.length, tic.col, tic.box.col, tic.lwd, ...) { cex <- cex * 0.8 if (x %in% c("top", "default")) { diff --git a/R/plot_vector.R b/R/plot_vector.R index d7382a434..616fefe44 100644 --- a/R/plot_vector.R +++ b/R/plot_vector.R @@ -787,7 +787,7 @@ setMethod("plot", signature(x="SpatVectorProxy", y="missing"), setMethod("plot", signature(x="SpatVectorCollection", y="missing"), - function(x, y, main, mar=NULL, nc, nr, maxnl=16, ...) { + function(x, y, main, mar=NULL, nc, nr, maxnl=16, col=NULL, ...) { nl <- max(1, min(length(x), maxnl)) if (nl==1) { @@ -809,8 +809,15 @@ setMethod("plot", signature(x="SpatVectorCollection", y="missing"), } else { main <- rep_len(main, nl) } - for (i in 1:nl) { - plot(x[i], main=main[i], mar=mar, ...) + if (is.list(col)) { + col <- rep_len(col, nl) + for (i in 1:nl) { + plot(x[i], main=main[i], mar=mar, col=col[[i]], ...) + } + } else { + for (i in 1:nl) { + plot(x[i], main=main[i], mar=mar, ...) + } } } ) diff --git a/R/rast.R b/R/rast.R index db60aa307..198fb063a 100644 --- a/R/rast.R +++ b/R/rast.R @@ -168,22 +168,28 @@ setMethod("rast", signature(x="SpatExtent"), setMethod("rast", signature(x="SpatVector"), function(x, type="", ...) { - + + dots <- list(...) + noCRS <- all(is.na(pmatch(names(dots), "crs"))) + if (type == "xyz") { if (geomtype(x) != "points") { error("rast", "xyz can only be used with points") } - x <- data.frame(crds(x), data.frame(x)) - return(rast(x, type="xyz", ...)) + v <- data.frame(crds(x), data.frame(x)) + if (noCRS) { + return(rast(v, type="xyz", crs=crs(x), ...)) + } else { + return(rast(v, type="xyz", ...)) + } } - dots <- list(...) e <- ext(x) dots$xmin=e[1] dots$xmax=e[2] dots$ymin=e[3] dots$ymax=e[4] - if (all(is.na(pmatch(names(dots), "crs")))) { + if (noCRS) { dots$crs <- crs(x) } do.call(new_rast, dots) diff --git a/R/tapp.R b/R/tapp.R index 168e47ba7..8b4ab907e 100644 --- a/R/tapp.R +++ b/R/tapp.R @@ -18,7 +18,7 @@ function(x, index, fun, ..., cores=1, filename="", overwrite=FALSE, wopt=list()) error("tapp", paste("invalid time step. Use one of:", paste(choices, collapse=", "))) } if (!x@pntr$hasTime) { - error("tapp", "x has no time data") + error("tapp", "x has no time stamps") } choice <- choices[i] if (choice == "doy") { @@ -52,7 +52,7 @@ function(x, index, fun, ..., cores=1, filename="", overwrite=FALSE, wopt=list()) index <- as.integer(format(time(x, "days"), "%j")) index <- as.character((index-1) %/% 15 + 1) prename <- "d15_" - } else { + } else { index <- time(x, choice) out_time <- time_as_seconds(x)[!duplicated(index)] out_tstep <- choice @@ -84,9 +84,10 @@ function(x, index, fun, ..., cores=1, filename="", overwrite=FALSE, wopt=list()) nl <- nlyr(x) if (length(index) > nl) { error("tapp", "length(index) > nlyr(x)") - } else if (length(unique(index)) == 1) { - warn("tapp", "it is not sensible to a single value as index (use app instead)") - } + } + # else if (length(unique(index)) == 1) { + # warn("tapp", "it is not sensible to a single value as index (use app instead)") + #} index <- rep_len(index, nl) if (!is.factor(index)) { index <- factor(index, levels=unique(index)) diff --git a/R/tiles.R b/R/tiles.R index 17ed45405..0fb455efb 100644 --- a/R/tiles.R +++ b/R/tiles.R @@ -1,6 +1,8 @@ setMethod("makeTiles", signature(x="SpatRaster"), - function(x, y, filename="tile_.tif", extend=FALSE, na.rm=FALSE, buffer=0, overwrite=FALSE, ...) { + function(x, y, filename="tile_.tif", extend=FALSE, na.rm=FALSE, buffer=0, value="files", overwrite=FALSE, ...) { + + value <- match.arg(value, c("files", "raster", "collection")) filename <- trimws(filename[1]) filename <- filename[!is.na(filename)] if (filename == "") error("makeTiles", "filename cannot be empty") @@ -20,7 +22,14 @@ setMethod("makeTiles", signature(x="SpatRaster"), error("makeTiles", "y must be numeric or a SpatRaster or SpatVector") } messages(x, "makeTiles") - ff + if (value == "files") { + ff + } else if (value == "raster") { + filename <- gsub("_\\.", "s.", filename) + vrt(ff, filename=filename) + } else { + sprc(ff) + } } ) diff --git a/R/time.R b/R/time.R index b3a092806..0ba2416fd 100644 --- a/R/time.R +++ b/R/time.R @@ -126,9 +126,10 @@ setMethod("time", signature(x="SpatRaster"), } else if ((format == "days") && (!(tstep %in% c("seconds", "days")))) { error("time", "cannot extract days from this type of time data") } - tstep <- format } else if (tstep == "raw") { return(d) + } else { + format = tstep } @@ -138,17 +139,22 @@ setMethod("time", signature(x="SpatRaster"), if (!(tz %in% c("", "UTC"))) { attr(d, "tzone") = tz } - d - } else if (tstep == "days") { - as.Date(d) - } else if (tstep == "yearmonths") { + if (format == "seconds") { + return(d) + } + } + + if (format == "days") { + # first use format to avoid time zone trouble #1896 + as.Date(format(d, "%Y-%m-%d")) + } else if (format == "yearmonths") { y <- as.integer(format(d, "%Y")) y + (as.integer(format(d, "%m"))-1)/12 - } else if (tstep == "months") { + } else if (format == "months") { as.integer(format(d, "%m")) - } else if (tstep == "years") { + } else if (format == "years") { as.integer(format(d, "%Y")) -# } else if (tstep == "yearweeks") { +# } else if (format == "yearweeks") { # yearweek(as.Date(d)) } else { # ??? d @@ -210,7 +216,7 @@ setMethod("time<-", signature(x="SpatRaster"), if (tstep == "") stept <- "days" } else if (inherits(value, "POSIXt")) { if (tstep == "") stept <- "seconds" - tzone <- attr(value, "tzone")[1] + tzone <- attr(value[1], "tzone")[1] if (is.null(tzone)) tzone = "UTC" } else if (inherits(value, "yearmon")) { value <- as.numeric(value) diff --git a/R/values.R b/R/values.R index 017f47b3f..b2e6e9466 100644 --- a/R/values.R +++ b/R/values.R @@ -483,8 +483,6 @@ setMethod("compareGeom", signature(x="SpatVector", y="missing"), out } ) - - setMethod("all.equal", signature(target="SpatRaster", current="SpatRaster"), function(target, current, maxcell=100000, ...) { a <- base::all.equal(rast(target), rast(current)) @@ -514,6 +512,37 @@ setMethod("all.equal", signature(target="SpatRaster", current="SpatRaster"), } ) +setMethod("all.equal", signature(target="SpatExtent", current="SpatExtent"), + function(target, current, ...) { + target == current + } +) + +setMethod("all.equal", signature(target="SpatVector", current="SpatVector"), + function(target, current, ...) { + if (any(dim(target) != dim(current))) { + message("different dimensions") + return(FALSE) + } + if (crs(target) != crs(current)) { + message("different crs") + return(FALSE) + } + if (geomtype(target) != geomtype(current)) { + message("different geometry type") + return(FALSE) + } + if (!isTRUE(all.equal(values(target), values(current), ...))) { + message("different values") + return(FALSE) + } + if (!isTRUE(all.equal(geom(target), geom(current), ...))) { + message("different geometry") + return(FALSE) + } + TRUE + } +) @@ -523,12 +552,28 @@ setMethod("identical", signature(x="SpatRaster", y="SpatRaster"), if (a && hasValues(x)) { v <- unique(unlist(unique(x - y) )) a <- identical(v, 0) + if (a) { + v <- unique(unlist(unique(is.na(x) - is.na(y)) )) + a <- all(v == 0) + } } a } ) +setMethod("identical", signature(x="SpatExtent", y="SpatExtent"), + function(x, y) { + identical(as.vector(x), as.vector(y)) + } +) + +setMethod("identical", signature(x="SpatVector", y="SpatVector"), + function(x, y) { + all.equal(x, y) + } +) + setMethod("values", signature("SpatVector"), function(x, ...) { as.data.frame(x, ...) diff --git a/R/vect.R b/R/vect.R index 2efbb83f1..f5a82aaa4 100644 --- a/R/vect.R +++ b/R/vect.R @@ -27,7 +27,6 @@ setMethod("vect", signature(x="missing"), p <- methods::new("SpatVector") p@pntr <- SpatVector$new() messages(p, "vect") - return(p) } ) @@ -426,13 +425,19 @@ setMethod("vect", signature(x="data.frame"), setMethod("vect", signature(x="list"), function(x, type="points", crs="") { - x <- lapply(x, function(i) { - if (inherits(i, "SpatVector")) return(i) - vect(i, type=type) - }) - x <- svc(x) + v <- methods::new("SpatVector") - v@pntr <- x@pntr$append() + if (inherits(x[[1]], "raw")) { + v@pntr <- SpatVector$new() + v@pntr$addWKB(x) + } else { + x <- lapply(x, function(i) { + if (inherits(i, "SpatVector")) return(i) + vect(i, type=type) + }) + x <- svc(x) + v@pntr <- x@pntr$append() + } if (crs != "") { crs(v) <- crs } diff --git a/inst/tinytest/test_vect-geom.R b/inst/tinytest/test_vect-geom.R index 0c0ca4b70..3592e1638 100644 --- a/inst/tinytest/test_vect-geom.R +++ b/inst/tinytest/test_vect-geom.R @@ -4,8 +4,10 @@ lux <- vect(f) listofraw <- geom(lux[1:2, ], wkb = TRUE) wkb <- listofraw[[1]] -hex <- geom(lux[1, ], hex = TRUE) +expect_equal(vect(listofraw, type = NULL, crs = crs(lux)), lux[1:2, 0]) + +hex <- geom(lux[1, ], hex = TRUE) expect_equal(typeof(listofraw), "list") expect_equal(typeof(wkb), "raw") diff --git a/man/all.equal.Rd b/man/all.equal.Rd index 88b33c0c5..41402f2fa 100644 --- a/man/all.equal.Rd +++ b/man/all.equal.Rd @@ -4,26 +4,33 @@ \alias{all.equal} \alias{all.equal,SpatRaster,SpatRaster-method} +\alias{all.equal,SpatVector,SpatVector-method} +\alias{all.equal,SpatExtent,SpatExtent-method} -\title{Compare two SpatRasters for equality} +\title{Compare two SpatRaster, SpatVector, or SpatExtent objects for equality} \description{ -Compare two SpatRasters for (near) equality. -First the attributes of the objects are compared. If these are the same, a (perhaps small) sample of the raster cells is compared as well. +Compare two objects for (near) equality + +In the case of SpatRasters, first the attributes of the objects are compared. If these are the same, a (perhaps small) sample of the raster cells is compared as well. The sample size used can be increased with the \code{maxcell} argument. You can set it to \code{Inf}, but for large rasters your computer may not have sufficient memory. See the examples for a safe way to compare all values. } \usage{ \S4method{all.equal}{SpatRaster,SpatRaster}(target, current, maxcell=100000, ...) + +\S4method{all.equal}{SpatVector,SpatVector}(target, current, ...) + +\S4method{all.equal}{SpatExtent,SpatExtent}(target, current, ...) } \arguments{ - \item{target}{SpatRaster} - \item{current}{SpatRaster} + \item{target}{SpatRaster, SpatVector, or SpatExtent} + \item{current}{object of the same class as \code{target}} \item{maxcell}{positive integer. The size of the regular sample used to compare cell values} - \item{...}{additional arguments passed to \code{\link{all.equal.numeric}} to compare cell values} + \item{...}{additional arguments passed to \code{\link{all.equal.numeric}} to compare cell values for SpatRaster and geometry and attribute values for SpatVectors} } \seealso{\code{\link{identical}}, \code{\link{compareGeom}}} diff --git a/man/app.Rd b/man/app.Rd index c2682fab4..16c11d895 100644 --- a/man/app.Rd +++ b/man/app.Rd @@ -42,7 +42,7 @@ SpatRaster To speed things up, parallelization is supported, but this is often not helpful, and it may actually be slower. There is only a speed gain if you have many cores (> 8) and/or a very complex (slow) function \code{fun}. If you write \code{fun} yourself, consider supplying a \code{cppFunction} made with the Rcpp package instead (or go have a cup of tea while the computer works for you). } -\seealso{ \code{\link{lapp}}, \code{\link{tapp}}, \code{\link[terra]{Math-methods}}, \code{\link{roll}} } +\seealso{ \code{\link{lapp}}, \code{\link{tapp}}, \code{\link[terra]{Math-methods}}, \code{\link{roll}}; \code{\link{global}} to summarize the values of a single SpatRaster } \examples{ diff --git a/man/ar_info.Rd b/man/ar_info.Rd index b25240256..27856478e 100644 --- a/man/ar_info.Rd +++ b/man/ar_info.Rd @@ -24,6 +24,6 @@ ar_info(x, what="describe", simplify=TRUE, filter=TRUE, array="") character or data.frame } -\seealso{\code{\link{ar_info}}} +\seealso{\code{\link{describe}}} \keyword{spatial} diff --git a/man/distance.Rd b/man/distance.Rd index f9d2afe25..5d5e385ee 100644 --- a/man/distance.Rd +++ b/man/distance.Rd @@ -66,7 +66,7 @@ If \code{y} is missing, the distance between each points in \code{x} with all ot \item{exclude}{numeric. The value of the cells that should not be considered for computing distances} \item{unit}{character. Can be either "m" or "km"} \item{method}{character. One of "geo", "cosine" or "haversine". With "geo" the most precise but slower method of Karney (2003) is used. The other two methods are faster but less precise} - \item{maxdist}{numeric. Distance above this values are not set to \code{NA}} + \item{maxdist}{numeric. Distances above this value are set to \code{NA}} \item{values}{logical. If \code{TRUE}, the value of the nearest non-target cell is returned instead of the distance to that cell} \item{rasterize}{logical. If \code{TRUE} distance is computed from the cells covered by the geometries after rasterization. This can be much faster in some cases} \item{filename}{character. Output filename} @@ -76,7 +76,7 @@ If \code{y} is missing, the distance between each points in \code{x} with all ot \item{lonlat}{logical. If \code{TRUE} the coordinates are interpreted as angular (longitude/latitude). If \code{FALSE} they are interpreted as planar} \item{pairs}{logical. If \code{TRUE} a "from", "to", "distance" matrix is returned} \item{symmetrical}{logical. If \code{TRUE} and \code{pairs=TRUE}, the distance between a pair is only included once. The distance between geometry 1 and 3 is included, but the (same) distance between 3 and 1 is not} - \item{use_nodes}{logical. If \code{TRUE} and the crs is longitude/latitude, the nodes (vertices) of lines or polygons are used to compute distances, instead of the lines that conntect them. This is faster, but can be less precise of the nodes are far apart} + \item{use_nodes}{logical. If \code{TRUE} and the crs is longitude/latitude, the nodes (vertices) of lines or polygons are used to compute distances, instead of the lines that conntect them. This is faster, but can be less precise if the nodes are far apart} } \value{ diff --git a/man/divide.Rd b/man/divide.Rd index cec039439..379f35b2d 100644 --- a/man/divide.Rd +++ b/man/divide.Rd @@ -37,7 +37,7 @@ Divides a \code{SpatVector} of polygons into \code{n} compact and approximately \item{...}{additional arguments such as \code{iter.max} passed on to \code{\link{kmeans}}} } -\seealso{\code{\link{thresh}}} +\seealso{\code{\link{thresh}}; \code{\link{makeTiles}}} \value{SpatVector or SpatRaster, or a list with both} diff --git a/man/identical.Rd b/man/identical.Rd index e622af8d4..d79789ccf 100644 --- a/man/identical.Rd +++ b/man/identical.Rd @@ -4,22 +4,26 @@ \alias{identical} \alias{identical,SpatRaster,SpatRaster-method} +\alias{identical,SpatVector,SpatVector-method} +\alias{identical,SpatExtent,SpatExtent-method} -\title{Compare two SpatRasters for equality} +\title{Compare two SpatRaster, SpatVector or SpatExtent objects for equality} \description{ -Compare two SpatRasters for equality. - -First the attributes of the objects are compared. If these are the same, a the raster cells are compared as well. This can be time consuming, and you may prefer to use a sample instead with \code{\link{all.equal}} +When, comparing two SpatRasters for equality, first the attributes of the objects are compared. If these are the same, a the raster cells are compared as well. This can be time consuming, and you may prefer to use a sample instead with \code{\link{all.equal}} } \usage{ \S4method{identical}{SpatRaster,SpatRaster}(x, y) + +\S4method{identical}{SpatVector,SpatVector}(x, y) + +\S4method{identical}{SpatExtent,SpatExtent}(x, y) } \arguments{ - \item{x}{SpatRaster} - \item{y}{SpatRaster} + \item{x}{SpatRaster, SpatVector, or SpatExtent} + \item{y}{object of the same class as \code{x}} } \seealso{\code{\link{all.equal}}, \code{\link{compareGeom}}} diff --git a/man/makeTiles.Rd b/man/makeTiles.Rd index 6e60ca4f5..605796a5b 100644 --- a/man/makeTiles.Rd +++ b/man/makeTiles.Rd @@ -20,7 +20,8 @@ Divide a SpatRaster into "tiles". The cells of another SpatRaster (normally with \usage{ \S4method{makeTiles}{SpatRaster}(x, y, filename="tile_.tif", extend=FALSE, - na.rm=FALSE, buffer=0, overwrite=FALSE, ...) + na.rm=FALSE, buffer=0, value="files", overwrite=FALSE, ...) + \S4method{getTileExtents}{SpatRaster}(x, y, extend=FALSE, buffer=0) } @@ -32,17 +33,18 @@ Divide a SpatRaster into "tiles". The cells of another SpatRaster (normally with \item{extend}{logical. If \code{TRUE}, the extent of \code{y} is expanded to assure that it covers all of \code{x}} \item{na.rm}{logical. If \code{TRUE}, tiles with only missing values are ignored} \item{buffer}{integer. The number of additional rows and columns added to each tile. Can be a single number, or two numbers to specify a separate number of rows and columns. This allows for creating overlapping tiles that can be used for computing spatial context dependent values with e.g. \code{\link{focal}}. The expansion is only inside \code{x}, no rows or columns outside of \code{x} are added} + \item{value}{character. The type of return value desired. Either "files" (for the filenames), "raster" (for a SpatRaster), or "collection" (for a SpatRasterCollection)} \item{overwrite}{logical. If \code{TRUE}, existing tiles are overwritten; otherwise they are skipped (without error or warning)} \item{...}{additional arguments for writing files as in \code{\link{writeRaster}}} } \value{ -character (filenames) or matrix (extents) +\code{makeTiles} returns a character (filenames), SpatRaster or SpatRasterCollection value. \code{getTileExtents} returns a matrix with extents } \seealso{ -\code{\link{vrt}} to create a virtual raster from tiles and \code{\link{crop}} for sub-setting arbitrary parts of a SpatRaster. +\code{\link{vrt}} to create a SpatRaster from tiles; \code{\link{crop}} for sub-setting arbitrary parts of a SpatRaster; \code{\link{divide}} to divide a SpatRaster into parts. } diff --git a/man/north.Rd b/man/north.Rd index 0b89a8158..fa0f4a4b8 100644 --- a/man/north.Rd +++ b/man/north.Rd @@ -15,7 +15,7 @@ north(xy=NULL, type=1, label="N", angle=0, d, head=0.1, xpd=TRUE, ...) \arguments{ \item{xy}{numeric. x and y coordinate to place the arrow. It can also be one of following character values: "bottomleft", "bottom", "bottomright", topleft", "top", "topright", "left", "right", or NULL} -\item{type}{integer between 1 and 12, or a character (unicode) representation of a right pointing arrow such as \code{"\u27A9"}} +\item{type}{integer between 1 and 12, or a character (unicode) representation of a right pointing arrow such as \code{"\u27A9"}. You may need to install the fonts for this. See the discussion on \href{https://stackoverflow.com/a/79747858/635245}{stackoverflow}} \item{label}{character, to be printed near the arrow} \item{angle}{numeric. The angle of the arrow in degrees} \item{d}{numeric. Distance covered by the arrow in plot coordinates. Only applies to \code{type=1}} diff --git a/man/plet.Rd b/man/plet.Rd index 469d3e5fc..cda5ca0ea 100644 --- a/man/plet.Rd +++ b/man/plet.Rd @@ -51,7 +51,7 @@ Plot the SpatRaster or SpatVector(s) to make an interactive leaflet map that is \arguments{ \item{x}{SpatRaster, SpatVector, or leaflet object} \item{y}{missing, or positive integer, or character (variable or layer name) indicating the layer(s) to be plotted. If \code{x} is a SpatRaster, you can select multiple layers} - \item{col}{character. Vector of colors or color generating function} + \item{col}{character. Vector of colors or a color generating function. If \code{x} is a SpatVectorCollection, you can provide a list with colors and/or functions, with one list element for each SpatVector} \item{alpha}{Number between 0 and 1 to set the transparency for lines (0 is transparent, 1 is opaque)} \item{fill}{Number between 0 and 1 to set the transparency for polygon areas (0 is transparent, 1 is opaque)} \item{tiles}{character or NULL. Names of background tile providers} diff --git a/man/plot.Rd b/man/plot.Rd index 667ce84b5..8fae3f712 100644 --- a/man/plot.Rd +++ b/man/plot.Rd @@ -46,7 +46,7 @@ There is a separate help file for plotting a \code{\link[=plot,SpatGraticule,mis \S4method{plot}{SpatVector,missing}(x, y, values=NULL, ...) -\S4method{plot}{SpatVectorCollection,missing}(x, y, main, mar=NULL, nc, nr, maxnl=16, ...) +\S4method{plot}{SpatVectorCollection,missing}(x, y, main, mar=NULL, nc, nr, maxnl=16, col=NULL, ...) \S4method{plot}{SpatVectorCollection,numeric}(x, y, main, mar=NULL, ext=NULL, ...) } @@ -54,7 +54,7 @@ There is a separate help file for plotting a \code{\link[=plot,SpatGraticule,mis \arguments{ \item{x}{SpatRaster or SpatVector} \item{y}{missing or positive integer or name indicating the layer(s) to be plotted} - \item{col}{character vector to specify the colors to use. The default is \code{map.pal("viridis", 100)}. The default can be changed with the \code{terra.pal} option. For example: \code{options(terra.pal=terrain.colors(10))}. If \code{x} is a \code{SpatRaster}, it can also be a \code{data.frame} with two columns (value, color) to get a "classes" type legend or with three columns (from, to, color) to get an "interval" type legend} + \item{col}{character vector to specify the colors to use. The default is \code{map.pal("viridis", 100)}. The default can be changed with the \code{terra.pal} option. For example: \code{options(terra.pal=terrain.colors(10))}. If \code{x} is a \code{SpatRaster}, it can also be a \code{data.frame} with two columns (value, color) to get a "classes" type legend or with three columns (from, to, color) to get an "interval" type legend. If \code{x} us a SpatVectorCollection, a list can be provided with colors for each SpatVector} \item{type}{character. Type of map/legend. One of "continuous", "classes", or "interval". If not specified, the type is chosen based on the data} \item{mar}{numeric vector of length 4 to set the margins of the plot (to make space for the legend). The default is (3.1, 3.1, 2.1, 7.1) for a single plot with a legend and (3.1, 3.1, 2.1, 2.1) otherwise. The default for a RGB raster is 0. Use \code{mar=NA} to not set the margins} \item{legend}{logical or character. If not \code{FALSE} a legend is drawn. The character value can be used to indicate where the legend is to be drawn. For example "topright" or "bottomleft". Use \code{plg} for more refined placement. Not supported for continuous legends (the default for raster data)} diff --git a/man/resample.Rd b/man/resample.Rd index 0a702b5e4..1adb9ca55 100644 --- a/man/resample.Rd +++ b/man/resample.Rd @@ -2,6 +2,7 @@ \alias{resample} \alias{resample,SpatRaster,SpatRaster-method} +\alias{resample,SpatRaster,numeric-method} \title{Transfer values of a SpatRaster to another one with a different geometry} @@ -17,16 +18,16 @@ If the origin and extent of the input and output are the same, you should consid \arguments{ \item{x}{SpatRaster to be resampled} - \item{y}{SpatRaster with the geometry that \code{x} should be resampled to} + \item{y}{SpatRaster with the geometry that \code{x} should be resampled to. You can also provide one or two positive numbers to set the resolution of the output raster} \item{method}{character. Method used for estimating the new cell values. One of: \code{bilinear}: bilinear interpolation (3x3 cell window). This is used by default if the first layer of \code{x} is not categorical - \code{average}: This can be a good choice with continuous variables if the output cells overlap with multiple input cells. + \code{mean}: This can be a good choice with continuous variables if the output cells overlap with multiple input cells. \code{near}: nearest neighbor. This is used by default if the first layer of \code{x} is categorical. This method is not a good choice for continuous values. - \code{mode}: The modal value. This can be a good choice for categrical rasters, if the output cells overlap with multiple input cells. + \code{modal}: The modal value. This can be a good choice for categorical rasters, if the output cells overlap with multiple input cells. \code{cubic}: cubic interpolation (5x5 cell window). diff --git a/man/terra-package.Rd b/man/terra-package.Rd index 615f21737..865e46117 100644 --- a/man/terra-package.Rd +++ b/man/terra-package.Rd @@ -680,7 +680,7 @@ Also note that even if function names are the same in \code{terra} and \code{ras } \section{Contributors}{ -Except where indicated otherwise, the methods and functions in this package were written by Robert Hijmans. The configuration scripts were written by Roger Bivand. Some of code using the GEOS library was adapted from code by Edzer Pebesma for \code{sf}. Emanuele Cordano contributed functionality for catchment related computations. Andrew Gene Brown, Márcia Barbosa, Michael Chirico, Krzysztof Dyba, Barry Rowlingson, and Michael D. Sumner also provided major contributions +Except where indicated otherwise, the methods and functions in this package were written by Robert Hijmans. The configuration scripts were written by Roger Bivand. Some of code using the GEOS library was adapted from code by Edzer Pebesma for \code{sf}. Emanuele Cordano contributed functionality for catchment related computations. Andrew Gene Brown, Márcia Barbosa, Michael Chirico, Krzysztof Dyba, Barry Rowlingson, and Michael D. Sumner also made important contributions This package is an attempt to climb on the shoulders of giants (GDAL, PROJ, GEOS, NCDF, GeographicLib, Rcpp, R). Many people have contributed by asking questions or \href{https://github.com/rspatial/terra}{raising issues}. Feedback and suggestions by Kendon Bell, Jean-Luc Dupouey, Sarah Endicott, Derek Friend, Alex Ilich, Agustin Lobo, Gerald Nelson, Jakub Nowosad, and Monika Tomaszewska have been especially helpful. diff --git a/man/vect.Rd b/man/vect.Rd index 51ca2494c..5b679f53c 100644 --- a/man/vect.Rd +++ b/man/vect.Rd @@ -51,13 +51,13 @@ SpatVectors can also be created from "Well Known Text", and from spatial vector } \arguments{ -\item{x}{character. A filename; or a "Well Known Text" string; SpatExtent, data.frame (to make a SpatVector of points); a "geom" matrix to make a SpatVector of any supported geometry (see examples and \code{\link{geom}}); a spatial vector data object defined in the \code{sf} or \code{sp} packages; or a list with matrices with coordinates} +\item{x}{character. A filename; or a "Well Known Text" string; SpatExtent, data.frame (to make a SpatVector of points); a "geom" matrix to make a SpatVector of any supported geometry (see examples and \code{\link{geom}}); a spatial vector data object defined in the \code{sf} or \code{sp} packages; or a list with either matrices with coordinates, or raw "Well Known Binary" (WKB) blobs} \item{layer}{character. layer name to select a layer from a file (database) with multiple layers} \item{query}{character. A query to subset the dataset} \item{dialect}{character. The SQL dialect to use (if any). For example: "SQLite". "" refers to the default \href{https://gdal.org/en/latest/user/ogr_sql_dialect.html}{OGR-SQL dialect}} \item{extent}{Spat* object. The extent of the object is used as a spatial filter to select the geometries to read. Ignored if \code{filter} is not \code{NULL}} \item{filter}{SpatVector. Used as a spatial filter to select geometries to read (the convex hull is used for lines or points). It is guaranteed that all features that overlap with the extent of filter will be returned. It can happen that additional geometries are returned} -\item{type}{character. Geometry type. Must be "points", "lines", or "polygons"} +\item{type}{character. Geometry type. Must be "points", "lines", or "polygons". Ignored if \code{x} is a WKB list} \item{atts}{data.frame with the attributes. The number of rows must match the number of geometrical elements} \item{crs}{character. The coordinate reference system in one of the following formats: WKT/WKT2, :, or PROJ-string notation (see \code{\link{crs}})} \item{proxy}{logical. If \code{TRUE} a SpatVectorProxy is returned} diff --git a/src/RcppModule.cpp b/src/RcppModule.cpp index 730ef737d..2878e1838 100644 --- a/src/RcppModule.cpp +++ b/src/RcppModule.cpp @@ -10,6 +10,23 @@ //} +bool addWKB(SpatVector* x, Rcpp::ListOf wkb) { + size_t n = wkb.size(); + std::vector raw; + std::vector sizes; + raw.reserve(n); + sizes.reserve(n); + for (size_t i=0; iaddRawGeoms(raw, sizes); +} + + + + /* Rcpp::List getBlockSizeR(SpatRaster* r, unsigned n, double frac) { SpatOptions opt; @@ -435,6 +452,7 @@ RCPP_MODULE(spat){ .constructor>() + .method("addWKB", &addWKB) // .method("pointInPolygon", &SpatVector::pointInPolygon) .method("deepcopy", &SpatVector::deepCopy) diff --git a/src/distValueRaster.cpp b/src/distValueRaster.cpp index 4395f3ec2..9b98d8026 100644 --- a/src/distValueRaster.cpp +++ b/src/distValueRaster.cpp @@ -223,7 +223,7 @@ void dist_only_vals(std::vector &d, std::vector &dv, const std:: } -SpatRaster SpatRaster::distance_crds_vals(std::vector& x, std::vector& y, const std::vector& v, const std::string& method, bool skip, bool setNA, std::string unit, double maxdist, SpatOptions &opt) { +SpatRaster SpatRaster::distance_crds_vals(std::vector& x, std::vector& y, std::vector& v, const std::string& method, bool skip, bool setNA, std::string unit, double maxdist, SpatOptions &opt) { SpatRaster out = geometry(); if (x.empty()) { @@ -234,6 +234,7 @@ SpatRaster SpatRaster::distance_crds_vals(std::vector& x, std::vector pm = sort_order_d(y); permute(x, pm); permute(y, pm); + permute(v, pm); bool lonlat = is_lonlat(); double m=1; diff --git a/src/distVector.cpp b/src/distVector.cpp index 8d01ce680..887de5b72 100644 --- a/src/distVector.cpp +++ b/src/distVector.cpp @@ -25,8 +25,6 @@ //#include "sort.h" #include "math_utils.h" -#include "Rcpp.h" - #if defined(USE_TBB) #include #include diff --git a/src/gdal_algs.cpp b/src/gdal_algs.cpp index 60de5c243..d7b063fe5 100644 --- a/src/gdal_algs.cpp +++ b/src/gdal_algs.cpp @@ -1433,7 +1433,13 @@ SpatVector SpatRaster::polygonize(bool round, bool values, bool narm, bool aggre } GDALDataset *poDS = NULL; + +#if (GDAL_VERSION_MAJOR == 3 && GDAL_VERSION_MINOR >= 11) || (GDAL_VERSION_MAJOR >= 4) + GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName( "MEM" ); +#else GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName( "Memory" ); +#endif + if( poDriver == NULL ) { out.setError( "cannot create output driver"); return out; diff --git a/src/gdal_multidimensional.cpp b/src/gdal_multidimensional.cpp index cd99f9143..369250bcd 100644 --- a/src/gdal_multidimensional.cpp +++ b/src/gdal_multidimensional.cpp @@ -358,10 +358,36 @@ bool SpatRaster::constructFromFileMulti(std::string fname, std::vector subd s.m_missing_value = poVar->GetNoDataValueAsDouble(&s.m_hasNA); } +/* + int ix, iy, it, iz; + it = 0; + iz = -1; + +// ignore dims for now + dims.resize(0); +*/ if (dims.size() < 2) { dims = {2,1,0}; - } +/* ix = ndim - 1; + iy = ix - 1; + if (ix == 3) { + iz = 1; + dims = {ix, iy, iz, it}; + } else if (ndim == 2) { + dims = {ix, iy}; + } + } else { + ix = dims[dims.size() - 1]; + iy = dims[dims.size() - 2]; + if (dims.size() == 4) { + iz = dims[1]; + it = dims[0]; + } else if (dims.size() == 4) { + it = dims[0]; + } +*/ + } int ix = ndim - 1; int iy = ix - 1; int it = 0; @@ -371,8 +397,8 @@ bool SpatRaster::constructFromFileMulti(std::string fname, std::vector subd dims = {ix, iy, iz, it}; } else if (ndim == 2) { dims = {ix, iy}; - } - + } + //Rcpp::Rcout << ix << ", " << iy << ", " << iz << ", " << it << std::endl; SpatExtent e; diff --git a/src/geos_methods.cpp b/src/geos_methods.cpp index df1ee3c91..6415f3141 100644 --- a/src/geos_methods.cpp +++ b/src/geos_methods.cpp @@ -1121,6 +1121,7 @@ SpatVector SpatVector::hull(std::string htype, std::string by, double param, boo + SpatVector SpatVector::voronoi(SpatVector bnd, double tolerance, int onlyEdges) { SpatVector out; diff --git a/src/geos_spat.h b/src/geos_spat.h index a9af184f6..0ccb38b29 100644 --- a/src/geos_spat.h +++ b/src/geos_spat.h @@ -314,6 +314,13 @@ inline std::vector geos_geoms(SpatVector *v, GEOSContextHandle_t hGEOSC geoms.push_back(pt); } } +// GEOSGeometry* gcol; +// if (np == 0) { +// gcol = GEOSGeom_createEmptyPoint_r(hGEOSCtxt); +// } else { +// gcol = (np = 1) ? geoms[0] : +// GEOSGeom_createCollection_r(hGEOSCtxt, GEOS_MULTIPOINT, &geoms[0], np); +// } GEOSGeometry* gcol = (np == 1) ? geoms[0] : GEOSGeom_createCollection_r(hGEOSCtxt, GEOS_MULTIPOINT, &geoms[0], np); g.push_back( geos_ptr(gcol, hGEOSCtxt) ); diff --git a/src/geosphere.cpp b/src/geosphere.cpp index ce21e67e6..e45276355 100644 --- a/src/geosphere.cpp +++ b/src/geosphere.cpp @@ -41,8 +41,6 @@ #endif -//#include "Rcpp.h" - inline void normLon(double &lon) { lon = fmod(lon + 180, 360.) - 180; } diff --git a/src/ncdf.cpp b/src/ncdf.cpp index b5d1ad001..0b76a8a93 100644 --- a/src/ncdf.cpp +++ b/src/ncdf.cpp @@ -1,6 +1,5 @@ /* #include -#include "Rcpp.h" */ /* #if defined(__APPLE__) diff --git a/src/read_ogr.cpp b/src/read_ogr.cpp index 2ad5f9493..55625c320 100644 --- a/src/read_ogr.cpp +++ b/src/read_ogr.cpp @@ -43,7 +43,6 @@ std::string geomType(OGRLayer *poLayer) { } -#include "Rcpp.h" SpatDataFrame readAttributes(OGRLayer *poLayer, bool as_proxy) { SpatDataFrame df; @@ -247,11 +246,14 @@ SpatGeom getPointGeom(OGRGeometry *poGeometry) { } SpatGeom getMultiPointGeom(OGRGeometry *poGeometry) { + SpatGeom g(points); + if (poGeometry->IsEmpty()) { + return g; + } OGRMultiPoint *poMultipoint = ( OGRMultiPoint * )poGeometry; unsigned ng = poMultipoint->getNumGeometries(); std::vector X(ng); std::vector Y(ng); - SpatGeom g(points); for (size_t i=0; igetGeometryRef(i); #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,3,0) @@ -269,8 +271,12 @@ SpatGeom getMultiPointGeom(OGRGeometry *poGeometry) { SpatGeom getLinesGeom(OGRGeometry *poGeometry) { - + SpatGeom g(lines); + if (poGeometry->IsEmpty()) { + return g; + } OGRLineString *poGeom = (OGRLineString *) poGeometry; + unsigned np = poGeom->getNumPoints(); std::vector X(np); std::vector Y(np); @@ -281,13 +287,15 @@ SpatGeom getLinesGeom(OGRGeometry *poGeometry) { Y[i] = ogrPt.getY(); } SpatPart p(X, Y); - SpatGeom g(lines); g.addPart(p); return g; } SpatGeom getMultiLinesGeom(OGRGeometry *poGeometry) { SpatGeom g(lines); + if (poGeometry->IsEmpty()) { + return g; + } OGRMultiLineString *poGeom = ( OGRMultiLineString * )poGeometry; unsigned ng = poGeom->getNumGeometries(); OGRPoint ogrPt; @@ -308,10 +316,12 @@ SpatGeom getMultiLinesGeom(OGRGeometry *poGeometry) { return g; } -//#include "Rcpp.h" SpatGeom getPolygonsGeom(OGRGeometry *poGeometry) { SpatGeom g(polygons); + if (poGeometry->IsEmpty()) { + return g; + } OGRPoint ogrPt; // OGRwkbGeometryType geomtype = poGeometry->getGeometryType(); // if ( geomtype == wkbPolygon ) { @@ -346,10 +356,13 @@ SpatGeom getPolygonsGeom(OGRGeometry *poGeometry) { SpatGeom getMultiPolygonsGeom(OGRGeometry *poGeometry) { + SpatGeom g(polygons); + if (poGeometry->IsEmpty()) { + return g; + } OGRMultiPolygon *poGeom = ( OGRMultiPolygon * )poGeometry; OGRPoint ogrPt; unsigned ng = poGeom->getNumGeometries(); - SpatGeom g(polygons); for (size_t i=0; igetGeometryRef(i); OGRPolygon *poPolygon = ( OGRPolygon * )poPolygonGeometry; @@ -381,6 +394,65 @@ SpatGeom getMultiPolygonsGeom(OGRGeometry *poGeometry) { return g; } + +void addOGRgeometry(SpatVector &x, OGRGeometry *poGeometry) { + SpatGeom g; + + OGRwkbGeometryType gtype = wkbFlatten(poGeometry->getGeometryType()); + if (gtype == wkbPoint) { + g = getPointGeom(poGeometry); + } else if (gtype == wkbMultiPoint) { + g = getMultiPointGeom(poGeometry); + } else if (gtype == wkbLineString) { + g = getLinesGeom(poGeometry); + } else if (gtype == wkbMultiLineString) { + g = getMultiLinesGeom(poGeometry); + } else if (gtype == wkbPolygon) { + g = getPolygonsGeom(poGeometry); + } else if (gtype == wkbMultiPolygon) { + g = getMultiPolygonsGeom(poGeometry); + } else { + const char *geomtypechar = OGRGeometryTypeToName(gtype); + std::string strgeomtype = geomtypechar; + x.setError("cannot read geometry type: " + strgeomtype); + return; + } + if ((x.size() > 1) && (x.geoms[0].gtype != g.gtype)) { + if (x.geoms[0].gtype != null) { + x.setError("a SpatVector can only have a single geometry type"); + return; + } + } + x.addGeom(g); + OGRGeometryFactory::destroyGeometry(poGeometry); +} + + +bool SpatVector::addRawGeoms(std::vector wkbs, std::vector sizes) { + + if (wkbs.size() == 0) { + SpatGeom g = emptyGeom(); + addGeom(g); + return true; + } + + for (size_t i=0; i SpatVector::layer_names(std::string filename) { std::vector out; @@ -498,6 +570,9 @@ bool layerQueryFilter(GDALDataset *&poDS, OGRLayer *&poLayer, std::string &layer } + + + bool SpatVector::read_ogr(GDALDataset *&poDS, std::string layer, std::string query, std::vector ext, SpatVector filter, bool as_proxy, std::string what, std::string dialect) { if (poDS == NULL) { @@ -521,7 +596,7 @@ bool SpatVector::read_ogr(GDALDataset *&poDS, std::string layer, std::string que for (size_t i=0; i < wrnmsg.size(); i++) addWarning(wrnmsg[i]); } - OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); + const OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); if (poSRS) { char *psz = NULL; #if GDAL_VERSION_MAJOR >= 3 @@ -859,7 +934,7 @@ bool SpatVectorCollection::read_ogr(GDALDataset *&poDS, std::string layer, std:: for (size_t i=0; i < wrnmsg.size(); i++) addWarning(wrnmsg[i]); } std::string crs = ""; - OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); + const OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); if (poSRS) { char *psz = NULL; #if GDAL_VERSION_MAJOR >= 3 diff --git a/src/spatRaster.h b/src/spatRaster.h index 220898ff1..29eebb48c 100644 --- a/src/spatRaster.h +++ b/src/spatRaster.h @@ -691,7 +691,7 @@ class SpatRaster { SpatRaster distance_crds(std::vector& x, std::vector& y, const std::string& method, bool skip, bool setNA, std::string unit,double threshold, SpatOptions &opt); - SpatRaster distance_crds_vals(std::vector& x, std::vector& y, const std::vector& v, const std::string& method, bool skip, bool setNA, std::string unit, double threshold, SpatOptions &opt); + SpatRaster distance_crds_vals(std::vector& x, std::vector& y, std::vector& v, const std::string& method, bool skip, bool setNA, std::string unit, double threshold, SpatOptions &opt); SpatRaster dn_crds(std::vector& x, std::vector& y, const std::string& method, bool skip, bool setNA, std::string unit, SpatOptions &opt); diff --git a/src/spatRasterMultiple.cpp b/src/spatRasterMultiple.cpp index e92141ff6..a83389521 100644 --- a/src/spatRasterMultiple.cpp +++ b/src/spatRasterMultiple.cpp @@ -469,7 +469,7 @@ std::string SpatRasterStack::getSRS(std::string s) { bool SpatRasterStack::push_back(SpatRaster r, std::string name, std::string longname, std::string unit, bool warn) { if (!ds.empty()) { - if (!r.compare_geom(ds[0], false, false, true, true, true, false)) { + if (!r.compare_geom(ds[0], false, false, true, true, true, false, true)) { // if (!ds[0].compare_geom(r, false, false, true, true, false, false)) { if (warn) { addWarning(r.msg.getError() +" (" + name + ")"); diff --git a/src/spatVector.cpp b/src/spatVector.cpp index ef7a05875..3fbc54a87 100644 --- a/src/spatVector.cpp +++ b/src/spatVector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2025 Robert J. Hijmans +// Copyright (c) 2018-2025 :: Robert J. Hijmans // // This file is part of the "spat" library. // @@ -66,10 +66,12 @@ SpatPart::SpatPart(double X, double Y) { SpatPart::SpatPart(std::vector X, std::vector Y) { x = X; y = Y; - extent.xmin = *std::min_element(X.begin(), X.end()); - extent.xmax = *std::max_element(X.begin(), X.end()); - extent.ymin = *std::min_element(Y.begin(), Y.end()); - extent.ymax = *std::max_element(Y.begin(), Y.end()); + if ((x.size() > 0) && (y.size() > 0)) { + extent.xmin = *std::min_element(X.begin(), X.end()); + extent.xmax = *std::max_element(X.begin(), X.end()); + extent.ymin = *std::min_element(Y.begin(), Y.end()); + extent.ymax = *std::max_element(Y.begin(), Y.end()); + } } @@ -630,6 +632,7 @@ std::vector> SpatVector::getGeometry() { out[2].push_back(NAN); out[3].push_back(NAN); out[4].push_back(0); + continue; } for (size_t j=0; j < g.size(); j++) { diff --git a/src/spatVector.h b/src/spatVector.h index b9b4145a7..51091148a 100644 --- a/src/spatVector.h +++ b/src/spatVector.h @@ -171,6 +171,8 @@ class SpatVector { std::vector getGeometryWKT(); void computeExtent(); + bool addRawGeoms(std::vector wkbs, std::vector sizes); + size_t nparts(bool holes); size_t ncoords(); diff --git a/src/vector_methods.cpp b/src/vector_methods.cpp index 6c6cc4300..42d344718 100644 --- a/src/vector_methods.cpp +++ b/src/vector_methods.cpp @@ -249,8 +249,6 @@ SpatVector SpatVector::elongate(double length, bool flat) { return out; } -//#include "Rcpp.h" - SpatVectorCollection SpatVector::split(std::string field) { SpatVectorCollection out; diff --git a/src/write_ogr.cpp b/src/write_ogr.cpp index 877bc34c6..e579655b0 100644 --- a/src/write_ogr.cpp +++ b/src/write_ogr.cpp @@ -37,10 +37,15 @@ bool driverSupports(std::string driver, std::string option) { } -#include "Rcpp.h" - GDALDataset* SpatVector::write_ogr(std::string filename, std::string lyrname, std::string driver, bool append, bool overwrite, std::vector options) { + #if (GDAL_VERSION_MAJOR == 3 && GDAL_VERSION_MINOR >= 11) || (GDAL_VERSION_MAJOR >= 4) + if (driver == "Memory") { + driver = "MEM"; + } + #endif + + GDALDataset *poDS = NULL; if (nrow() == 0) { setError("SpatVector has no records to write");