From 028fc6a7e63640c80d7ec4feb405e52be59d9072 Mon Sep 17 00:00:00 2001 From: Henry Scharf Date: Tue, 27 Jun 2023 10:08:48 -0700 Subject: [PATCH 01/31] Fix issue with key in plot.R When plotting multiple character/factor-valued columns of an `sf` object, an error occurs when requesting a key because a portion of the code intended for numeric values gets executed (`classInt::classIntervals()`). I added lines to convert character values to a factor, and to check for numeric values before using `classIntervals()`. --- R/plot.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/plot.R b/R/plot.R index 08e4ca24d..1f39f5f41 100644 --- a/R/plot.R +++ b/R/plot.R @@ -113,9 +113,11 @@ plot.sf <- function(x, y, ..., main, pal = NULL, nbreaks = 10, breaks = "pretty" if (!is.null(key.pos)) { values = do.call(c, as.data.frame(x)[cols]) + if (is.character(values)) + values = as.factor(values) if (logz) values = log10(values) - if (is.character(breaks)) { # compute breaks from values: + if (is.character(breaks) && is.numeric(values))) { # compute breaks from values: v0 = values[!is.na(values)] n.unq = length(unique(v0)) breaks = if (! all(is.na(values)) && n.unq > 1) From f4d5b3777b014e7eca2cb3356a35c9d999558d53 Mon Sep 17 00:00:00 2001 From: Henry Scharf Date: Tue, 27 Jun 2023 13:39:23 -0700 Subject: [PATCH 02/31] Update plot.R Sorry about that. --- R/plot.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/plot.R b/R/plot.R index 1f39f5f41..e84c24a61 100644 --- a/R/plot.R +++ b/R/plot.R @@ -117,7 +117,7 @@ plot.sf <- function(x, y, ..., main, pal = NULL, nbreaks = 10, breaks = "pretty" values = as.factor(values) if (logz) values = log10(values) - if (is.character(breaks) && is.numeric(values))) { # compute breaks from values: + if (is.character(breaks) && is.numeric(values)) { # compute breaks from values: v0 = values[!is.na(values)] n.unq = length(unique(v0)) breaks = if (! all(is.na(values)) && n.unq > 1) From 950c5ae5e6ece417a7acd93830b21e45c474f9c6 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Sat, 1 Jul 2023 13:06:50 +0200 Subject: [PATCH 03/31] news, #2196 --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 1c05c036f..82a881039 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # version 1.0-14 +* fix `plot.sf()` when using a key for multiple factor variables; #2196, #2195 + * fix use of `as.numeric_version` in a test, for upcoming change in r-devel * code tidy-ing: fix many lintr suggestions, thanks to Michael Chirico (#2181 - #2191) From d20be5df43fdb2c9f42195ede6c477bcf75fe011 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Wed, 5 Jul 2023 21:42:16 +0200 Subject: [PATCH 04/31] add ref --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8f1e1832a..bf1eb5932 100644 --- a/README.md +++ b/README.md @@ -160,11 +160,13 @@ Functions and methods that require `liblwgeom`, including `st_make_valid` and al ## How to cite -Package `sf` can be cited as: Edzer Pebesma, 2018. -Simple Features for R: Standardized Support -for Spatial Vector Data. The R Journal [10:1, -439-446.](https://journal.r-project.org/archive/2018/RJ-2018-009/index.html) +Package `sf` can be cited as: +* Edzer Pebesma, 2018. Simple Features for R: Standardized Support +for Spatial Vector Data. The R Journal [10:1, 439-446.](https://journal.r-project.org/archive/2018/RJ-2018-009/index.html) + +* Pebesma, E.; Bivand, R. (2023). [Spatial Data Science: With Applications in R](https://r-spatial.org/book) +(1st ed.). 314 pages. [Chapman and Hall/CRC](https://doi.org/10.1201/9780429459016). ## Acknowledgment From 32fc74bcc48cfcc60eb43a4bc87df61e896d3209 Mon Sep 17 00:00:00 2001 From: bart1 <1662852+bart1@users.noreply.github.com> Date: Thu, 6 Jul 2023 00:13:18 +0200 Subject: [PATCH 05/31] Clarify documentation #2199 --- R/sf.R | 2 +- man/sf.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/sf.R b/R/sf.R index b5170d8f8..6daf2d07a 100644 --- a/R/sf.R +++ b/R/sf.R @@ -298,7 +298,7 @@ st_sf = function(..., agr = NA_agr_, row.names, #' @name sf #' @param x object of class \code{sf} -#' @param i record selection, see \link{[.data.frame} +#' @param i record selection, see \link{[.data.frame}, or a \code{sf} object to work with the \code{op} argument #' @param j variable selection, see \link{[.data.frame} #' @param drop logical, default \code{FALSE}; if \code{TRUE} drop the geometry column and return a \code{data.frame}, else make the geometry sticky and return a \code{sf} object. #' @param op function; geometrical binary predicate function to apply when \code{i} is a simple feature object diff --git a/man/sf.Rd b/man/sf.Rd index 287b4e452..00746af19 100644 --- a/man/sf.Rd +++ b/man/sf.Rd @@ -45,7 +45,7 @@ there is more than one and \code{sf_column_name} is \code{NULL}, the first one i \item{x}{object of class \code{sf}} -\item{i}{record selection, see \link{[.data.frame}} +\item{i}{record selection, see \link{[.data.frame}, or a \code{sf} object to work with the \code{op} argument} \item{j}{variable selection, see \link{[.data.frame}} From e6e5696c7b9e7777299fc45d10c8354621f81803 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Mon, 10 Jul 2023 15:29:48 +0200 Subject: [PATCH 06/31] CRAN submisison 1.0-14 --- R/spatstat.R | 4 ++-- tests/aggregate.R | 2 +- tests/aggregate.Rout.save | 6 +++--- tests/crs.R | 2 +- tests/crs.Rout.save | 9 ++++++--- tests/dist.R | 2 +- tests/dist.Rout.save | 8 ++++---- tests/grid.R | 2 +- tests/grid.Rout.save | 8 ++++---- tests/plot.R | 4 +++- tests/plot.Rout.save | 10 ++++++---- tests/roundtrip.R | 2 +- tests/roundtrip.Rout.save | 11 +++++++---- tests/sfc.R | 2 +- tests/sfc.Rout.save | 6 +++--- 15 files changed, 44 insertions(+), 34 deletions(-) diff --git a/R/spatstat.R b/R/spatstat.R index ed7b711e8..202b6b1a7 100644 --- a/R/spatstat.R +++ b/R/spatstat.R @@ -19,8 +19,8 @@ check_spatstat <- function(pkg, X = NULL) { if (!requireNamespace(pkg, quietly = TRUE)) stop("package ", pkg, " required, please install it (or the full spatstat package) first", call. = FALSE) spst_ver <- try(packageVersion("spatstat"), silent = TRUE) - if (!inherits(spst_ver, "try-error") && spst_ver < 2.0-0) - stop(wrp(paste("You have an old version of spatstat installed which is incompatible with ", pkg, + if (!inherits(spst_ver, "try-error") && spst_ver < "2.0-0") + stop(wrp(paste0("You have an old version of spatstat installed that is incompatible with ", pkg, ". Please update spatstat (or uninstall it).")), call. = FALSE) if (!is.null(X)) check_spatstat_ll(X) diff --git a/tests/aggregate.R b/tests/aggregate.R index aba42f832..723f8f238 100644 --- a/tests/aggregate.R +++ b/tests/aggregate.R @@ -6,7 +6,7 @@ s = st_sf(a = 1:2, geom = st_sfc(pl1, pl2)) (a = aggregate(s, list(c(1,1)), mean, do_union = FALSE)) (a = aggregate(s, list(c(1,1)), mean, do_union = TRUE)) # expect_warning(st_cast(a, "POINT")) -if (require(sp, quietly = TRUE)) { +if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { demo(meuse_sf, echo = FALSE, ask = FALSE) a = aggregate(meuse_sf, list(meuse_sf$soil), mean) print(attributes(a)$agr) diff --git a/tests/aggregate.Rout.save b/tests/aggregate.Rout.save index 312eb2651..b78a7db81 100644 --- a/tests/aggregate.Rout.save +++ b/tests/aggregate.Rout.save @@ -1,5 +1,5 @@ -R version 4.3.0 (2023-04-21) -- "Already Tomorrow" +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) @@ -40,7 +40,7 @@ CRS: NA Group.1 a geometry 1 1 1.5 POLYGON ((1 0, 0 0, 0 1, 1 ... > # expect_warning(st_cast(a, "POINT")) -> if (require(sp, quietly = TRUE)) { +> if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { + demo(meuse_sf, echo = FALSE, ask = FALSE) + a = aggregate(meuse_sf, list(meuse_sf$soil), mean) + print(attributes(a)$agr) @@ -109,4 +109,4 @@ CRS: NA > > proc.time() user system elapsed - 1.149 0.773 1.033 + 1.330 0.635 1.259 diff --git a/tests/crs.R b/tests/crs.R index ca5cc9701..461caadf6 100644 --- a/tests/crs.R +++ b/tests/crs.R @@ -41,7 +41,7 @@ if (sf_extSoftVersion()["USE_PROJ_H"] == "true" || sf_proj_info("have_datum_file "datum files not installed" } -if (require(sp, quietly = TRUE)) { +if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { x0 = sp::CRS("+init=epsg:4326") cat(sp::wkt(x0), "\n") x = st_crs(x0) diff --git a/tests/crs.Rout.save b/tests/crs.Rout.save index 2d1213ccf..d117ae7e4 100644 --- a/tests/crs.Rout.save +++ b/tests/crs.Rout.save @@ -1,5 +1,5 @@ -R version 4.2.3 (2023-03-15) -- "Shortstop Beagle" +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) @@ -82,7 +82,7 @@ POINT (111319.5 111325.1) + } [1] "datum files installed" > -> if (require(sp, quietly = TRUE)) { +> if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { + x0 = sp::CRS("+init=epsg:4326") + cat(sp::wkt(x0), "\n") + x = st_crs(x0) @@ -196,6 +196,9 @@ GEOGCRS["WGS 84", AREA["World."], BBOX[-90,-180,90,180]], ID["EPSG",4326]] +Warning message: +In CPL_crs_from_input(x) : + GDAL Message 1: +init=epsg:XXXX syntax is deprecated. It might return a CRS with a non-EPSG compliant axis order. > > # https://github.com/r-spatial/sf/issues/1170 > g = st_as_sfc("POLYGON ((-61.66957 10.69214, -61.565 10.75728, -61.37453 10.77654, -61.40721 10.60681, -61.66957 10.69214))") @@ -213,4 +216,4 @@ GEOGCRS["WGS 84", > > proc.time() user system elapsed - 1.345 0.712 1.171 + 1.344 0.669 1.160 diff --git a/tests/dist.R b/tests/dist.R index 91053279b..453ebbcc3 100644 --- a/tests/dist.R +++ b/tests/dist.R @@ -18,7 +18,7 @@ st_point(c(4,0)), crs = 4326 ) -if (require(sp, quietly = TRUE)) { +if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { d.sf = st_distance(x, y) d.sp = spDists(as(x, "Spatial"), as(y, "Spatial")) units(d.sp) = as_units("km") diff --git a/tests/dist.Rout.save b/tests/dist.Rout.save index ebced7abc..66e9717d1 100644 --- a/tests/dist.Rout.save +++ b/tests/dist.Rout.save @@ -1,6 +1,6 @@ -R version 4.2.0 (2022-04-22) -- "Vigorous Calisthenics" -Copyright (C) 2022 The R Foundation for Statistical Computing +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" +Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. @@ -35,7 +35,7 @@ Type 'q()' to quit R. + crs = 4326 + ) > -> if (require(sp, quietly = TRUE)) { +> if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { + d.sf = st_distance(x, y) + d.sp = spDists(as(x, "Spatial"), as(y, "Spatial")) + units(d.sp) = as_units("km") @@ -109,4 +109,4 @@ Error in st_line_sample(ls, density = 1/1000) : > > proc.time() user system elapsed - 1.343 0.096 1.431 + 1.257 0.770 1.138 diff --git a/tests/grid.R b/tests/grid.R index 889397245..78abf52b2 100644 --- a/tests/grid.R +++ b/tests/grid.R @@ -15,7 +15,7 @@ pushViewport(st_viewport(nc)) invisible(lapply(st_geometry(nc), function(x) grid.draw(st_as_grob(x, gp = gpar(fill = 'red'))))) # POINTS: -if (require(sp, quietly = TRUE)) { +if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { data(meuse, package = "sp") meuse_sf = st_as_sf(meuse, coords = c("x", "y"), crs = 28992, agr = "constant") grid.newpage() diff --git a/tests/grid.Rout.save b/tests/grid.Rout.save index 8e737eb88..b7e71492d 100644 --- a/tests/grid.Rout.save +++ b/tests/grid.Rout.save @@ -1,6 +1,6 @@ -R version 4.2.0 (2022-04-22) -- "Vigorous Calisthenics" -Copyright (C) 2022 The R Foundation for Statistical Computing +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" +Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. @@ -32,7 +32,7 @@ Type 'q()' to quit R. > invisible(lapply(st_geometry(nc), function(x) grid.draw(st_as_grob(x, gp = gpar(fill = 'red'))))) > > # POINTS: -> if (require(sp, quietly = TRUE)) { +> if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { + data(meuse, package = "sp") + meuse_sf = st_as_sf(meuse, coords = c("x", "y"), crs = 28992, agr = "constant") + grid.newpage() @@ -80,4 +80,4 @@ Type 'q()' to quit R. > > proc.time() user system elapsed - 1.496 0.059 1.566 + 1.462 0.800 1.365 diff --git a/tests/plot.R b/tests/plot.R index d815f1e20..a4a285df6 100644 --- a/tests/plot.R +++ b/tests/plot.R @@ -10,7 +10,9 @@ l3 = st_linestring(matrix(runif(6)-0.5,,2)) s = st_sf(a=2:4, b=st_sfc(l1,l2,l3)) plot(s, col = s$a, axes = FALSE) plot(s, col = s$a) -attr(s$b, "proj4string") = sp::CRS("+proj=longlat +ellps=WGS84 +no_defs")@projargs +if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { + attr(s$b, "proj4string") = sp::CRS("+proj=longlat +ellps=WGS84 +no_defs")@projargs +} plot(s, col = s$a, axes = TRUE) plot(s, col = s$a, lty = s$a, lwd = s$a, pch = s$a, type = 'b') l4 = st_linestring(matrix(runif(6),,2)) diff --git a/tests/plot.Rout.save b/tests/plot.Rout.save index 9491bf548..70a14583a 100644 --- a/tests/plot.Rout.save +++ b/tests/plot.Rout.save @@ -1,6 +1,6 @@ -R version 4.2.0 (2022-04-22) -- "Vigorous Calisthenics" -Copyright (C) 2022 The R Foundation for Statistical Computing +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" +Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. @@ -27,7 +27,9 @@ Type 'q()' to quit R. + s = st_sf(a=2:4, b=st_sfc(l1,l2,l3)) + plot(s, col = s$a, axes = FALSE) + plot(s, col = s$a) -+ attr(s$b, "proj4string") = sp::CRS("+proj=longlat +ellps=WGS84 +no_defs")@projargs ++ if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { ++ attr(s$b, "proj4string") = sp::CRS("+proj=longlat +ellps=WGS84 +no_defs")@projargs ++ } + plot(s, col = s$a, axes = TRUE) + plot(s, col = s$a, lty = s$a, lwd = s$a, pch = s$a, type = 'b') + l4 = st_linestring(matrix(runif(6),,2)) @@ -148,4 +150,4 @@ There were 13 warnings (use warnings() to see them) > > proc.time() user system elapsed - 5.863 0.119 6.022 + 4.822 0.828 4.857 diff --git a/tests/roundtrip.R b/tests/roundtrip.R index 7aa1076e8..c3a425340 100644 --- a/tests/roundtrip.R +++ b/tests/roundtrip.R @@ -12,7 +12,7 @@ pol3 = list(outer + 24) mp = list(pol1,pol2,pol3) mp1 = st_multipolygon(mp) sf = st_sf(a=1, st_sfc(mp1)) -if (require(sp, quietly = TRUE)) { +if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { a = as(sf, "Spatial") print(class(a)) b = st_as_sf(a) diff --git a/tests/roundtrip.Rout.save b/tests/roundtrip.Rout.save index 2c01c9419..a54563d14 100644 --- a/tests/roundtrip.Rout.save +++ b/tests/roundtrip.Rout.save @@ -1,6 +1,6 @@ -R version 4.2.2 (2022-10-31) -- "Innocent and Trusting" -Copyright (C) 2022 The R Foundation for Statistical Computing +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" +Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. @@ -29,7 +29,7 @@ Type 'q()' to quit R. > mp = list(pol1,pol2,pol3) > mp1 = st_multipolygon(mp) > sf = st_sf(a=1, st_sfc(mp1)) -> if (require(sp, quietly = TRUE)) { +> if (suppressPackageStartupMessages(require(sp, quietly = TRUE))) { + a = as(sf, "Spatial") + print(class(a)) + b = st_as_sf(a) @@ -83,7 +83,10 @@ attr(,"package") + + detach("package:sp") + } +Warning message: +In CPL_crs_from_input(x) : + GDAL Message 1: +init=epsg:XXXX syntax is deprecated. It might return a CRS with a non-EPSG compliant axis order. > > proc.time() user system elapsed - 1.364 0.068 1.434 + 1.666 0.736 1.524 diff --git a/tests/sfc.R b/tests/sfc.R index b2b907e79..1e89beefa 100644 --- a/tests/sfc.R +++ b/tests/sfc.R @@ -241,7 +241,7 @@ plot(st_jitter(st_geometry(nc), factor = .01), add = TRUE, col = '#ff8888') st_jitter(st_sfc(st_point(0:1)), amount = .1) # st_bbox: -if (require(sp, quietly = TRUE) && require(raster, quietly = TRUE)) { +if (suppressPackageStartupMessages(require(sp, quietly = TRUE)) && require(raster, quietly = TRUE)) { demo(meuse, ask = FALSE, echo = FALSE) suppressWarnings(st_bbox(meuse)) crs = suppressWarnings(st_crs(meuse)) diff --git a/tests/sfc.Rout.save b/tests/sfc.Rout.save index c874e4406..61c258a7e 100644 --- a/tests/sfc.Rout.save +++ b/tests/sfc.Rout.save @@ -1,5 +1,5 @@ -R version 4.3.0 (2023-04-21) -- "Already Tomorrow" +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) @@ -821,7 +821,7 @@ CRS: NA POINT (-0.0500922 0.992953) > > # st_bbox: -> if (require(sp, quietly = TRUE) && require(raster, quietly = TRUE)) { +> if (suppressPackageStartupMessages(require(sp, quietly = TRUE)) && require(raster, quietly = TRUE)) { + demo(meuse, ask = FALSE, echo = FALSE) + suppressWarnings(st_bbox(meuse)) + crs = suppressWarnings(st_crs(meuse)) @@ -1088,4 +1088,4 @@ CRS: NA > > proc.time() user system elapsed - 6.642 0.811 6.564 + 6.713 0.877 6.702 From 0ce6a023873e33bda86c40e391db7996f2cf8a12 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Wed, 12 Jul 2023 22:31:51 +0200 Subject: [PATCH 07/31] address #2201 --- R/spatstat.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/spatstat.R b/R/spatstat.R index 202b6b1a7..2fed6ba29 100644 --- a/R/spatstat.R +++ b/R/spatstat.R @@ -138,9 +138,9 @@ as.ppp.sfc = function(X, W = NULL, ..., check = TRUE) { spatstat.geom::ppp(cc[,1], cc[,2], window = W, marks = NULL, check = check) } -as.ppp.sf = function(X) { +as.ppp.sf = function(X, ...) { check_spatstat("spatstat.geom", X) - pp = spatstat.geom::as.ppp(st_geometry(X)) + pp = spatstat.geom::as.ppp(st_geometry(X), ...) if (st_dimension(X[1,]) == 2) X = X[-1,] st_geometry(X) = NULL # remove geometry column From 78d4eeeaeb85f99db0847fc5a7dd591f4e2e0811 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Thu, 13 Jul 2023 22:51:32 +0200 Subject: [PATCH 08/31] fixes #2202 --- src/gdal_write.cpp | 48 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/gdal_write.cpp b/src/gdal_write.cpp index 814534c18..5ab844478 100644 --- a/src/gdal_write.cpp +++ b/src/gdal_write.cpp @@ -28,13 +28,16 @@ std::vector SetupFields(OGRLayer *poLayer, Rcpp::List obj, bool up Rcpp::Rcout << "Field " << nm[i] << " of type " << cls[i] << " not supported." << std::endl; Rcpp::stop("Layer creation failed.\n"); } // #nocov end - OGRFieldDefn oField(nm[i], ret[i]); - if (strcmp(cls[i], "logical") == 0) - oField.SetSubType(OFSTBoolean); - if (!update_layer && poLayer->CreateField(&oField) != OGRERR_NONE) { // #nocov start - Rcpp::Rcout << "Creating field " << nm[i] << " failed." << std::endl; - Rcpp::stop("Layer creation failed.\n"); - } // #nocov end + if (poLayer->FindFieldIndex(nm[i], TRUE) == -1) { // not already present: + OGRFieldDefn oField(nm[i], ret[i]); + if (strcmp(cls[i], "logical") == 0) + oField.SetSubType(OFSTBoolean); + if (!update_layer && poLayer->CreateField(&oField) != OGRERR_NONE) { // #nocov start + Rcpp::Rcout << "Creating field " << nm[i] << " failed." << std::endl; + Rcpp::stop("Layer creation failed.\n"); + } // #nocov end + } else { + } } return ret; } @@ -61,7 +64,8 @@ void SetNull(OGRFeature *poFeature, size_t field) { void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List obj, size_t i) { Rcpp::CharacterVector nm = obj.attr("names"); for (size_t j = 0; j < tp.size(); j++) { - if (i == 0 && poFeature->GetFieldIndex(nm[j]) == -1) { + size_t fld = poFeature->GetFieldIndex(nm[j]); + if (i == 0 && fld == -1) { Rcpp::Rcout << "Unknown field name `" << nm[j] << "': updating a layer with improper field name(s)?" << std::endl; Rcpp::stop("Write error\n"); @@ -73,9 +77,9 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o Rcpp::CharacterVector cv; cv = obj[j]; if (! Rcpp::CharacterVector::is_na(cv[i])) - poFeature->SetField(j, (const char *) cv[i]); + poFeature->SetField(fld, (const char *) cv[i]); else - SetNull(poFeature, j); + SetNull(poFeature, fld); } break; case OFTInteger: { const OGRFieldDefn *def = poFeature->GetFieldDefnRef(j); @@ -83,31 +87,31 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o Rcpp::LogicalVector lv; lv = obj[j]; if (! Rcpp::LogicalVector::is_na(lv[i])) - poFeature->SetField(j, (int) lv[i]); + poFeature->SetField(fld, (int) lv[i]); else - SetNull(poFeature, j); // #nocov + SetNull(poFeature, fld); // #nocov } else { // integer: Rcpp::IntegerVector iv; iv = obj[j]; if (! Rcpp::IntegerVector::is_na(iv[i])) - poFeature->SetField(j, (int) iv[i]); + poFeature->SetField(fld, (int) iv[i]); else - SetNull(poFeature, j); // #nocov + SetNull(poFeature, fld); // #nocov } } break; case OFTReal: { Rcpp::NumericVector nv; nv = obj[j]; if (! Rcpp::NumericVector::is_na(nv[i])) - poFeature->SetField(j, (double) nv[i]); + poFeature->SetField(fld, (double) nv[i]); else - SetNull(poFeature, j); + SetNull(poFeature, fld); } break; case OFTDate: { Rcpp::NumericVector nv; nv = obj[j]; if (Rcpp::NumericVector::is_na(nv[i])) { - SetNull(poFeature, j); + SetNull(poFeature, fld); break; } Rcpp::NumericVector nv0(1); @@ -115,13 +119,13 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o nv0.attr("class") = "Date"; Rcpp::Function as_POSIXlt_Date("as.POSIXlt.Date"); Rcpp::NumericVector ret = get_dbl6(as_POSIXlt_Date(nv0)); - poFeature->SetField(j, 1900 + (int) ret[5], (int) ret[4] + 1, (int) ret[3]); + poFeature->SetField(fld, 1900 + (int) ret[5], (int) ret[4] + 1, (int) ret[3]); } break; case OFTDateTime: { Rcpp::NumericVector nv; nv = obj[j]; if (Rcpp::NumericVector::is_na(nv[i])) { - SetNull(poFeature, j); + SetNull(poFeature, fld); break; } Rcpp::NumericVector nv0(1); @@ -129,7 +133,7 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o nv0.attr("tzone") = "UTC"; Rcpp::Function as_POSIXlt_POSIXct("as.POSIXlt.POSIXct"); Rcpp::NumericVector rd = get_dbl6(as_POSIXlt_POSIXct(nv0)); // use R - poFeature->SetField(j, 1900 + (int) rd[5], (int) rd[4] + 1, // #nocov start + poFeature->SetField(fld, 1900 + (int) rd[5], (int) rd[4] + 1, // #nocov start (int) rd[3], (int) rd[2], (int) rd[1], (float) rd[0], 100); // nTZFlag: 0=unkown, 1=local, 100=GMT; #nocov end } break; @@ -141,11 +145,11 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o Rcpp::RawVector rv; rv = lv(0); if (rv.size() == 0) - SetNull(poFeature, j); // #nocov + SetNull(poFeature, fld); // #nocov else { const void *ptr = &(rv[0]); int size = rv.size(); - poFeature->SetField(j, size, ptr); + poFeature->SetField(fld, size, ptr); } } break; #endif From 65a42e00684eb7014149ba8cd5272c976120a825 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Thu, 13 Jul 2023 23:22:56 +0200 Subject: [PATCH 09/31] better fixes #2202 --- NEWS.md | 4 ++++ src/gdal_write.cpp | 53 ++++++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/NEWS.md b/NEWS.md index 82a881039..bc4d4f0e4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# version 1.0-15 + +* `st_write()` matches fields on name first, than on position; this matters for formats that have pre-defined names, such as GPX; #2202 + # version 1.0-14 * fix `plot.sf()` when using a key for multiple factor variables; #2196, #2195 diff --git a/src/gdal_write.cpp b/src/gdal_write.cpp index 5ab844478..d75bc58bc 100644 --- a/src/gdal_write.cpp +++ b/src/gdal_write.cpp @@ -37,6 +37,8 @@ std::vector SetupFields(OGRLayer *poLayer, Rcpp::List obj, bool up Rcpp::stop("Layer creation failed.\n"); } // #nocov end } else { + // otherwise, one could check the type? + // some type conversion, e.g. int -> char seems to be done by GDAL, see #2202 } } return ret; @@ -61,25 +63,32 @@ void SetNull(OGRFeature *poFeature, size_t field) { #endif } -void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List obj, size_t i) { +std::vector GetFieldIndex(OGRLayer *poLayer, Rcpp::List obj) { + std::vector ret(obj.size()); Rcpp::CharacterVector nm = obj.attr("names"); - for (size_t j = 0; j < tp.size(); j++) { - size_t fld = poFeature->GetFieldIndex(nm[j]); - if (i == 0 && fld == -1) { - Rcpp::Rcout << "Unknown field name `" << nm[j] << + for (int i = 0; i < obj.size(); i++) { + ret[i] = poLayer->FindFieldIndex(nm[i], TRUE); + if (ret[i] == -1) { + Rcpp::Rcout << "Unknown field name `" << nm[i] << "': updating a layer with improper field name(s)?" << std::endl; Rcpp::stop("Write error\n"); } - if (j == (size_t) poFeature->GetFieldCount()) + } + return ret; +} + +void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List obj, size_t i, std::vector fld) { + for (size_t j = 0; j < tp.size(); j++) { + if (j >= (size_t) poFeature->GetFieldCount()) Rcpp::stop("Impossible: field count reached\n"); // #nocov switch (tp[j]) { case OFTString: { Rcpp::CharacterVector cv; cv = obj[j]; if (! Rcpp::CharacterVector::is_na(cv[i])) - poFeature->SetField(fld, (const char *) cv[i]); + poFeature->SetField(fld[j], (const char *) cv[i]); else - SetNull(poFeature, fld); + SetNull(poFeature, fld[j]); } break; case OFTInteger: { const OGRFieldDefn *def = poFeature->GetFieldDefnRef(j); @@ -87,31 +96,31 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o Rcpp::LogicalVector lv; lv = obj[j]; if (! Rcpp::LogicalVector::is_na(lv[i])) - poFeature->SetField(fld, (int) lv[i]); + poFeature->SetField(fld[j], (int) lv[i]); else - SetNull(poFeature, fld); // #nocov + SetNull(poFeature, fld[j]); // #nocov } else { // integer: Rcpp::IntegerVector iv; iv = obj[j]; if (! Rcpp::IntegerVector::is_na(iv[i])) - poFeature->SetField(fld, (int) iv[i]); + poFeature->SetField(fld[j], (int) iv[i]); else - SetNull(poFeature, fld); // #nocov + SetNull(poFeature, fld[j]); // #nocov } } break; case OFTReal: { Rcpp::NumericVector nv; nv = obj[j]; if (! Rcpp::NumericVector::is_na(nv[i])) - poFeature->SetField(fld, (double) nv[i]); + poFeature->SetField(fld[j], (double) nv[i]); else - SetNull(poFeature, fld); + SetNull(poFeature, fld[j]); } break; case OFTDate: { Rcpp::NumericVector nv; nv = obj[j]; if (Rcpp::NumericVector::is_na(nv[i])) { - SetNull(poFeature, fld); + SetNull(poFeature, fld[j]); break; } Rcpp::NumericVector nv0(1); @@ -119,13 +128,13 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o nv0.attr("class") = "Date"; Rcpp::Function as_POSIXlt_Date("as.POSIXlt.Date"); Rcpp::NumericVector ret = get_dbl6(as_POSIXlt_Date(nv0)); - poFeature->SetField(fld, 1900 + (int) ret[5], (int) ret[4] + 1, (int) ret[3]); + poFeature->SetField(fld[j], 1900 + (int) ret[5], (int) ret[4] + 1, (int) ret[3]); } break; case OFTDateTime: { Rcpp::NumericVector nv; nv = obj[j]; if (Rcpp::NumericVector::is_na(nv[i])) { - SetNull(poFeature, fld); + SetNull(poFeature, fld[j]); break; } Rcpp::NumericVector nv0(1); @@ -133,7 +142,7 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o nv0.attr("tzone") = "UTC"; Rcpp::Function as_POSIXlt_POSIXct("as.POSIXlt.POSIXct"); Rcpp::NumericVector rd = get_dbl6(as_POSIXlt_POSIXct(nv0)); // use R - poFeature->SetField(fld, 1900 + (int) rd[5], (int) rd[4] + 1, // #nocov start + poFeature->SetField(fld[j], 1900 + (int) rd[5], (int) rd[4] + 1, // #nocov start (int) rd[3], (int) rd[2], (int) rd[1], (float) rd[0], 100); // nTZFlag: 0=unkown, 1=local, 100=GMT; #nocov end } break; @@ -145,11 +154,11 @@ void SetFields(OGRFeature *poFeature, std::vector tp, Rcpp::List o Rcpp::RawVector rv; rv = lv(0); if (rv.size() == 0) - SetNull(poFeature, fld); // #nocov + SetNull(poFeature, fld[j]); // #nocov else { const void *ptr = &(rv[0]); int size = rv.size(); - poFeature->SetField(fld, size, ptr); + poFeature->SetField(fld[j], size, ptr); } } break; #endif @@ -332,9 +341,11 @@ int CPL_write_ogr(Rcpp::List obj, Rcpp::CharacterVector dsn, Rcpp::CharacterVect Rcpp::Rcout << "." << std::endl; } + std::vector fieldIndex = GetFieldIndex(poLayer, obj); + for (size_t i = 0; i < geomv.size(); i++) { // create all features & add to layer: OGRFeature *poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn()); - SetFields(poFeature, fieldTypes, obj, i); + SetFields(poFeature, fieldTypes, obj, i, fieldIndex); if (write_geometries) poFeature->SetGeometryDirectly(geomv[i]); if (fids.size() > (int) i) From a736a37f360466a7d9e640d4715030eb9e336bbe Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Thu, 13 Jul 2023 23:24:18 +0200 Subject: [PATCH 10/31] tidy test --- tests/stars.Rout.save | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/stars.Rout.save b/tests/stars.Rout.save index 707da5170..3eb062dae 100644 --- a/tests/stars.Rout.save +++ b/tests/stars.Rout.save @@ -1,6 +1,6 @@ -R version 4.2.2 Patched (2022-11-10 r83330) -- "Innocent and Trusting" -Copyright (C) 2022 The R Foundation for Statistical Computing +R version 4.3.1 (2023-06-16) -- "Beagle Scouts" +Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. @@ -153,9 +153,9 @@ BIR79 319.000 1606.000 3108.000 4.852046e+03 5400.000 30757.000 30904 SID79 0.000 3.000 6.000 9.584098e+00 13.000 57.000 30904 NWBIR79 3.000 360.000 1058.000 1.604642e+03 1524.000 11631.000 30904 dimension(s): - from to offset delta refsys point x/y -x 1 461 -84.3239 0.0192484 NAD27 FALSE [x] -y 1 141 36.5896 -0.0192484 NAD27 FALSE [y] + from to offset delta refsys point x/y +x 1 461 -84.32 0.01925 NAD27 FALSE [x] +y 1 141 36.59 -0.01925 NAD27 FALSE [y] Simple feature collection with 34097 features and 12 fields Geometry type: POLYGON Dimension: XY @@ -261,4 +261,4 @@ First 10 features: > > proc.time() user system elapsed - 3.042 2.167 2.592 + 3.195 2.107 2.719 From 856ea7d96b031e67ec54390e8955d6dc5d967223 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Fri, 14 Jul 2023 10:03:46 +0200 Subject: [PATCH 11/31] fixes #2203 --- R/cast_sfc.R | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/R/cast_sfc.R b/R/cast_sfc.R index 7ec67ec14..c949b3722 100644 --- a/R/cast_sfc.R +++ b/R/cast_sfc.R @@ -147,6 +147,15 @@ empty_sfg <- function(to) { ) } +has_curves = function(x) { + if (inherits(x, c("sfc_MULTICURVE", "sfc_COMPOUNDCURVE", "sfc_CURVEPOLYGON"))) # for which GEOS has no st_is_empty() + TRUE + else if(inherits(x, "sfc_GEOMETRY")) { + cls = sapply(x, class) + any(cls[2,] %in% c("MULTICURVE", "COMPOUNDCURVE", "CURVEPOLYGON")) + } else + FALSE +} #' @name st_cast #' @param ids integer vector, denoting how geometries should be grouped (default: no grouping) @@ -169,7 +178,7 @@ st_cast.sfc = function(x, to, ..., ids = seq_along(x), group_or_split = TRUE) { return(st_cast_sfc_default(x)) e = rep(FALSE, length(x)) - if (!inherits(x, c("sfc_MULTICURVE", "sfc_COMPOUNDCURVE", "sfc_CURVEPOLYGON"))) { # for which GEOS has no st_is_empty() + if (!has_curves(x)) { # for which GEOS has no st_is_empty() e = st_is_empty(x) if (all(e)) { x[e] = empty_sfg(to) From 2c9e349ba460b317710881c88562e45d8deee258 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Fri, 14 Jul 2023 12:17:20 +0200 Subject: [PATCH 12/31] fix #2183 --- R/cast_sfc.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/cast_sfc.R b/R/cast_sfc.R index c949b3722..7409f775d 100644 --- a/R/cast_sfc.R +++ b/R/cast_sfc.R @@ -257,7 +257,6 @@ st_cast.sfc = function(x, to, ..., ids = seq_along(x), group_or_split = TRUE) { #' @details the \code{st_cast} method for \code{sf} objects can only split geometries, e.g. cast \code{MULTIPOINT} into multiple \code{POINT} features. In case of splitting, attributes are repeated and a warning is issued when non-constant attributes are assigned to sub-geometries. To merge feature geometries and attribute values, use \link[sf:aggregate.sf]{aggregate} or \link[sf:tidyverse]{summarise}. st_cast.sf = function(x, to, ..., warn = TRUE, do_split = TRUE) { geom = st_cast(st_geometry(x), to, group_or_split = do_split) - crs = st_crs(x) agr = st_agr(x) all_const = all_constant(x) sf_column = attr(x, "sf_column") # keep name From a768744243289908d9a935b6d6d96b182ec772c9 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Sun, 6 Aug 2023 19:32:58 +0200 Subject: [PATCH 13/31] raise error when .by is given but no across() #2207 --- R/tidyverse.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/tidyverse.R b/R/tidyverse.R index d2fcf1fb3..88670e4fb 100644 --- a/R/tidyverse.R +++ b/R/tidyverse.R @@ -308,6 +308,8 @@ summarise.sf <- function(.data, ..., .dots, do_union = TRUE, is_coverage = FALSE geom = list() #676 #nocov do.call(st_sfc, c(geom, crs = list(crs), precision = precision)) } else { # single group: + if (nrow(ret) > 1) + stop(paste0("when using .by, also add across(", sf_column, ", st_union) as argument")) # https://github.com/r-spatial/sf/issues/2207 if (do_union) st_union(geom, is_coverage = is_coverage) else From 7ae54ed8f046307bd136f13c1c3fe00eaab34851 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Sun, 6 Aug 2023 19:39:37 +0200 Subject: [PATCH 14/31] closes #2205 --- NEWS.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index bc4d4f0e4..3c5deee9f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # version 1.0-15 +* `summarise.sf` raises an error if `.by` is given but no `across()` on the geometry; #2207 + * `st_write()` matches fields on name first, than on position; this matters for formats that have pre-defined names, such as GPX; #2202 # version 1.0-14 diff --git a/README.md b/README.md index bf1eb5932..6a044427e 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ sudo dnf install gdal-devel proj-devel geos-devel sqlite-devel udunits2-devel Get gdal, proj and geos from the main repos, and udunits from the AUR: ``` -pacman -S gdal proj geos arrow podofo +pacman -S gdal proj geos arrow podofo-0.9 yay/pacaur/yaourt/whatever -S udunits ``` From 9a7f212d1c8a89c12635c12209b701546c94474e Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Sun, 6 Aug 2023 20:02:17 +0200 Subject: [PATCH 15/31] closes #2204 --- NEWS.md | 2 ++ R/tidyverse.R | 2 ++ 2 files changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 3c5deee9f..b91bc40d0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # version 1.0-15 +* `distinct.sf` is type-safe for `sf` objects with zero rows; #2204 + * `summarise.sf` raises an error if `.by` is given but no `across()` on the geometry; #2207 * `st_write()` matches fields on name first, than on position; this matters for formats that have pre-defined names, such as GPX; #2202 diff --git a/R/tidyverse.R b/R/tidyverse.R index 88670e4fb..96419f9a7 100644 --- a/R/tidyverse.R +++ b/R/tidyverse.R @@ -333,6 +333,8 @@ distinct.sf <- function(.data, ..., .keep_all = FALSE) { sf_column = attr(.data, "sf_column") geom = st_geometry(.data) eq = sapply(st_equals(.data), head, n = 1) + if (is.list(eq) && length(eq) == 0) # empty list: geometry was empty set + eq = integer(0) empties = which(lengths(eq) == 0) eq[ empties ] = empties[1] # first empty record .data[[ sf_column ]] = unlist(eq) From 0ec3ec4faaff1e1ffe9a452b6b0aca58bff0641a Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 8 Aug 2023 16:34:31 -0400 Subject: [PATCH 16/31] Quietly load sf namespace when loading sf objects --- R/sf.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/R/sf.R b/R/sf.R index b5170d8f8..f7e1fe383 100644 --- a/R/sf.R +++ b/R/sf.R @@ -293,9 +293,14 @@ st_sf = function(..., agr = NA_agr_, row.names, st_agr(df) = agr if (! missing(crs)) st_crs(df) = crs + + attr(df, ".sf_namespace") <- .sf_namespace + df } +.sf_namespace <- function() NULL + #' @name sf #' @param x object of class \code{sf} #' @param i record selection, see \link{[.data.frame} From 4ed38bfb28f2c2068ea83a7295a2107812df303f Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Fri, 11 Aug 2023 13:38:36 +0200 Subject: [PATCH 17/31] Rcpp update --- R/RcppExports.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index 1d95c7b85..c5e8b4254 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -403,5 +403,5 @@ CPL_get_m_range <- function(sf, depth) { # Register entry points for exported C++ functions methods::setLoadAction(function(ns) { - .Call('_sf_RcppExport_registerCCallable', PACKAGE = 'sf') + .Call(`_sf_RcppExport_registerCCallable`) }) From 4dc5e8d86ff0758f9f73e717cef7e85d461e2cc2 Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Fri, 11 Aug 2023 13:42:48 +0200 Subject: [PATCH 18/31] add news entry for #2212 --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index b91bc40d0..14dfb5c54 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # version 1.0-15 +* `sf` objects get a new attribute, `.sf_namespace`, which forces loading the `sf` namespace when it has not been loaded so far, e.g. for proper printing or plotting of an `sf` object; #2212 by Mike Mahoney + * `distinct.sf` is type-safe for `sf` objects with zero rows; #2204 * `summarise.sf` raises an error if `.by` is given but no `across()` on the geometry; #2207 From e6de6c233c7a385108f3ea7876f2c854e17b792a Mon Sep 17 00:00:00 2001 From: edzer Date: Wed, 16 Aug 2023 14:24:19 +0200 Subject: [PATCH 19/31] new rcpp version --- R/RcppExports.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index c5e8b4254..1d95c7b85 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -403,5 +403,5 @@ CPL_get_m_range <- function(sf, depth) { # Register entry points for exported C++ functions methods::setLoadAction(function(ns) { - .Call(`_sf_RcppExport_registerCCallable`) + .Call('_sf_RcppExport_registerCCallable', PACKAGE = 'sf') }) From b301342ae472e93db905f9cc4546d84cab2283ac Mon Sep 17 00:00:00 2001 From: edzer Date: Wed, 16 Aug 2023 14:25:37 +0200 Subject: [PATCH 20/31] add [<-.sf that fixes agr attribute; addresses #2211 and https://github.com/tidyverse/tibble/issues/1552 --- NAMESPACE | 1 + R/agr.R | 10 +++++++++- R/sf.R | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index 1a469681e..a1615d0fc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,6 +7,7 @@ S3method("$",z_range) S3method("$<-",sf) S3method("[",sf) S3method("[",sfc) +S3method("[<-",sf) S3method("[<-",sfc) S3method("[[<-",sf) S3method("st_agr<-",sf) diff --git a/R/agr.R b/R/agr.R index 7c9f7b7c5..e75e6c23f 100644 --- a/R/agr.R +++ b/R/agr.R @@ -80,7 +80,15 @@ st_agr.default = function(x = NA_character_, ...) { #' @name st_agr #' @export st_set_agr = function(x, value) { - st_agr(x) = value + if (!missing(value)) + st_agr(x) = value + else { # needs repair? + value = st_agr(x) + if (any(is.na(names(value))) && length(value) == length(x) - 1) { + names(value) = setdiff(names(x), attr(x, "sf_column")) + st_agr(x) = value + } + } x } diff --git a/R/sf.R b/R/sf.R index f7e1fe383..cfa267cc3 100644 --- a/R/sf.R +++ b/R/sf.R @@ -373,6 +373,11 @@ st_sf = function(..., agr = NA_agr_, row.names, } } +#' @export +"[<-.sf" = function(x, i, j, value) { + st_set_agr(NextMethod()) +} + #' @export "[[<-.sf" = function(x, i, value) { agr = st_agr(x) From 2fe61e7c7685d6307c59618052c31721f0ba1fe5 Mon Sep 17 00:00:00 2001 From: edzer Date: Wed, 16 Aug 2023 14:27:48 +0200 Subject: [PATCH 21/31] tidy --- tests/aggregate.Rout.save | 4 ++-- tests/sfc.Rout.save | 5 +++-- tests/stars.Rout.save | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/aggregate.Rout.save b/tests/aggregate.Rout.save index b78a7db81..bd2b1282f 100644 --- a/tests/aggregate.Rout.save +++ b/tests/aggregate.Rout.save @@ -16,7 +16,7 @@ Type 'demo()' for some demos, 'help()' for on-line help, or Type 'q()' to quit R. > library(sf) -Linking to GEOS 3.11.1, GDAL 3.6.2, PROJ 9.1.1; sf_use_s2() is TRUE +Linking to GEOS 3.11.1, GDAL 3.6.4, PROJ 9.1.1; sf_use_s2() is TRUE > # aggregate > pl1 = st_polygon(list(rbind(c(0,0),c(1,0),c(1,1),c(0,0)))) > pl2 = st_polygon(list(rbind(c(0,0),c(1,1),c(0,1),c(0,0)))) @@ -109,4 +109,4 @@ CRS: NA > > proc.time() user system elapsed - 1.330 0.635 1.259 + 0.704 0.056 0.751 diff --git a/tests/sfc.Rout.save b/tests/sfc.Rout.save index 61c258a7e..c8d96d8fa 100644 --- a/tests/sfc.Rout.save +++ b/tests/sfc.Rout.save @@ -347,7 +347,7 @@ use `st_zm(...)` to coerce to XY dimensions > > sf_extSoftVersion()[1:3] GEOS GDAL proj.4 -"3.11.1" "3.6.2" "9.1.1" +"3.11.1" "3.6.4" "9.1.1" > > # Ops.sfc: > ls = st_sfc(st_linestring(rbind(c(0,0),c(0,1)))) @@ -437,6 +437,7 @@ Classes 'sf' and 'data.frame': 100 obs. of 15 variables: - attr(*, "sf_column")= chr "geometry" - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ... ..- attr(*, "names")= chr [1:14] "AREA" "PERIMETER" "CNTY_" "CNTY_ID" ... + - attr(*, ".sf_namespace")=function () > bb = st_as_sfc(st_bbox(nc)) > format(st_bbox(nc)) [1] "((-84.32385,33.88199),(-75.45698,36.58965))" @@ -1088,4 +1089,4 @@ CRS: NA > > proc.time() user system elapsed - 6.713 0.877 6.702 + 4.676 0.117 4.784 diff --git a/tests/stars.Rout.save b/tests/stars.Rout.save index 3eb062dae..cfa8ce0cc 100644 --- a/tests/stars.Rout.save +++ b/tests/stars.Rout.save @@ -153,9 +153,9 @@ BIR79 319.000 1606.000 3108.000 4.852046e+03 5400.000 30757.000 30904 SID79 0.000 3.000 6.000 9.584098e+00 13.000 57.000 30904 NWBIR79 3.000 360.000 1058.000 1.604642e+03 1524.000 11631.000 30904 dimension(s): - from to offset delta refsys point x/y -x 1 461 -84.32 0.01925 NAD27 FALSE [x] -y 1 141 36.59 -0.01925 NAD27 FALSE [y] + from to offset delta refsys point x/y +x 1 461 -84.3239 0.0192484 NAD27 FALSE [x] +y 1 141 36.5896 -0.0192484 NAD27 FALSE [y] Simple feature collection with 34097 features and 12 fields Geometry type: POLYGON Dimension: XY @@ -261,4 +261,4 @@ First 10 features: > > proc.time() user system elapsed - 3.195 2.107 2.719 + 1.821 0.083 1.896 From aeefffdf3f6c3889ffe2b1a3b2f33a46c0196bb1 Mon Sep 17 00:00:00 2001 From: edzer Date: Thu, 17 Aug 2023 22:51:28 +0200 Subject: [PATCH 22/31] tidy, news --- NEWS.md | 2 ++ tests/stars.Rout.save | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 14dfb5c54..9bbb5e7e9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # version 1.0-15 +* `[<-.sf` fixes the `agr` attribute if it is broken; #2211 + * `sf` objects get a new attribute, `.sf_namespace`, which forces loading the `sf` namespace when it has not been loaded so far, e.g. for proper printing or plotting of an `sf` object; #2212 by Mike Mahoney * `distinct.sf` is type-safe for `sf` objects with zero rows; #2204 diff --git a/tests/stars.Rout.save b/tests/stars.Rout.save index cfa8ce0cc..9768f4b09 100644 --- a/tests/stars.Rout.save +++ b/tests/stars.Rout.save @@ -153,9 +153,9 @@ BIR79 319.000 1606.000 3108.000 4.852046e+03 5400.000 30757.000 30904 SID79 0.000 3.000 6.000 9.584098e+00 13.000 57.000 30904 NWBIR79 3.000 360.000 1058.000 1.604642e+03 1524.000 11631.000 30904 dimension(s): - from to offset delta refsys point x/y -x 1 461 -84.3239 0.0192484 NAD27 FALSE [x] -y 1 141 36.5896 -0.0192484 NAD27 FALSE [y] + from to offset delta refsys point x/y +x 1 461 -84.32 0.01925 NAD27 FALSE [x] +y 1 141 36.59 -0.01925 NAD27 FALSE [y] Simple feature collection with 34097 features and 12 fields Geometry type: POLYGON Dimension: XY @@ -261,4 +261,4 @@ First 10 features: > > proc.time() user system elapsed - 1.821 0.083 1.896 + 1.870 0.111 1.970 From 6b2dd7f03041b9fb08ed41ebd51e5bc2d89570c8 Mon Sep 17 00:00:00 2001 From: edzer Date: Thu, 17 Aug 2023 22:51:45 +0200 Subject: [PATCH 23/31] handle cex.axis in legend keys addresses https://github.com/r-spatial/stars/issues/642 --- R/plot.R | 10 ++++++---- man/stars.Rd | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/R/plot.R b/R/plot.R index e84c24a61..451f075bf 100644 --- a/R/plot.R +++ b/R/plot.R @@ -790,8 +790,10 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer #' @param logz ignore #' @param ... ignore #' @param lab ignore +#' @param cex.axis see \link{par} .image_scale = function(z, col, breaks = NULL, key.pos, add.axis = TRUE, - at = NULL, ..., axes = FALSE, key.length, logz = FALSE, lab = "") { + at = NULL, ..., axes = FALSE, key.length, logz = FALSE, lab = "", + cex.axis = 1) { if (!is.null(breaks) && length(breaks) != (length(col) + 1)) stop("must have one more break than colour") stopifnot(is.character(lab) || is.expression(lab)) @@ -861,14 +863,14 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer TRUE if (add.axis) - axis(key.pos, at = at, labels = labels) + axis(key.pos, at = at, labels = labels, cex.axis = cex.axis) } #' @name stars #' @export #' @param key.width ignore .image_scale_factor = function(z, col, key.pos, add.axis = TRUE, - ..., axes = FALSE, key.width, key.length) { + ..., axes = FALSE, key.width, key.length, cex.axis = 1) { n = length(z) # TODO: @@ -918,7 +920,7 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer if (add.axis) { opar = par(las = 1) - axis(key.pos, at = 1:n, labels = z) + axis(key.pos, at = 1:n, labels = z, cex.axis = cex.axis) par(opar) } } diff --git a/man/stars.Rd b/man/stars.Rd index 4fa422fd4..8afa7d027 100644 --- a/man/stars.Rd +++ b/man/stars.Rd @@ -23,7 +23,8 @@ axes = FALSE, key.length, logz = FALSE, - lab = "" + lab = "", + cex.axis = 1 ) .image_scale_factor( @@ -34,7 +35,8 @@ ..., axes = FALSE, key.width, - key.length + key.length, + cex.axis = 1 ) } \arguments{ @@ -80,6 +82,8 @@ \item{lab}{ignore} +\item{cex.axis}{see \link{par}} + \item{key.width}{ignore} } \description{ From 1761ed3ffb0ceaa57af99ed2f932ff01ccd3088e Mon Sep 17 00:00:00 2001 From: edzer Date: Fri, 18 Aug 2023 00:00:37 +0200 Subject: [PATCH 24/31] better defaults for cex.axis --- R/plot.R | 4 ++-- man/stars.Rd | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/plot.R b/R/plot.R index 451f075bf..cf817fae9 100644 --- a/R/plot.R +++ b/R/plot.R @@ -793,7 +793,7 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer #' @param cex.axis see \link{par} .image_scale = function(z, col, breaks = NULL, key.pos, add.axis = TRUE, at = NULL, ..., axes = FALSE, key.length, logz = FALSE, lab = "", - cex.axis = 1) { + cex.axis = par("cex.axis")) { if (!is.null(breaks) && length(breaks) != (length(col) + 1)) stop("must have one more break than colour") stopifnot(is.character(lab) || is.expression(lab)) @@ -870,7 +870,7 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer #' @export #' @param key.width ignore .image_scale_factor = function(z, col, key.pos, add.axis = TRUE, - ..., axes = FALSE, key.width, key.length, cex.axis = 1) { + ..., axes = FALSE, key.width, key.length, cex.axis = par("cex.axis")) { n = length(z) # TODO: diff --git a/man/stars.Rd b/man/stars.Rd index 8afa7d027..2cc25ff87 100644 --- a/man/stars.Rd +++ b/man/stars.Rd @@ -24,7 +24,7 @@ key.length, logz = FALSE, lab = "", - cex.axis = 1 + cex.axis = par("cex.axis") ) .image_scale_factor( @@ -36,7 +36,7 @@ axes = FALSE, key.width, key.length, - cex.axis = 1 + cex.axis = par("cex.axis") ) } \arguments{ From bf7db6e0a492be899a8beb641dfe15689e7c2b97 Mon Sep 17 00:00:00 2001 From: edzer Date: Fri, 18 Aug 2023 12:36:58 +0200 Subject: [PATCH 25/31] key.pos can hold relative key position in available space see https://github.com/r-spatial/stars/issues/642 --- NEWS.md | 4 +++- R/plot.R | 52 +++++++++++++++++++++++++++++++++++++++------------- man/plot.Rd | 2 +- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9bbb5e7e9..7c79aa65a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # version 1.0-15 -* `[<-.sf` fixes the `agr` attribute if it is broken; #2211 +* `plot.sf()`: `key.width` is sensitive to pointsize graphics parameter, `key.pos` can hold a second value in [0, 1] determining the relative position of the key in the available space + +* `[<-.sf` fixes the `agr` attribute when it is broken; #2211 * `sf` objects get a new attribute, `.sf_namespace`, which forces loading the `sf` namespace when it has not been loaded so far, e.g. for proper printing or plotting of an `sf` object; #2212 by Mike Mahoney diff --git a/R/plot.R b/R/plot.R index cf817fae9..45b991f3e 100644 --- a/R/plot.R +++ b/R/plot.R @@ -11,7 +11,7 @@ #' @param nbreaks number of colors breaks (ignored for \code{factor} or \code{character} variables) #' @param breaks either a numeric vector with the actual breaks, or a name of a method accepted by the \code{style} argument of \link[classInt]{classIntervals} #' @param max.plot integer; lower boundary to maximum number of attributes to plot; the default value (9) can be overriden by setting the global option \code{sf_max.plot}, e.g. \code{options(sf_max.plot=2)} -#' @param key.pos integer; side to plot a color key: 1 bottom, 2 left, 3 top, 4 right; set to \code{NULL} to omit key completely, 0 to only not plot the key, or -1 to select automatically. If multiple columns are plotted in a single function call by default no key is plotted and every submap is stretched individually; if a key is requested (and \code{col} is missing) all maps are colored according to a single key. Auto select depends on plot size, map aspect, and, if set, parameter \code{asp}. +#' @param key.pos numeric; side to plot a color key: 1 bottom, 2 left, 3 top, 4 right; set to \code{NULL} to omit key completely, 0 to only not plot the key, or -1 to select automatically. If multiple columns are plotted in a single function call by default no key is plotted and every submap is stretched individually; if a key is requested (and \code{col} is missing) all maps are colored according to a single key. Auto select depends on plot size, map aspect, and, if set, parameter \code{asp}. If it has lenght 2, the second value, ranging from 0 to 1, determines where the key is placed in the available space (default: 0.5, center). #' @param key.width amount of space reserved for the key (incl. labels), thickness/width of the scale bar #' @param key.length amount of space reserved for the key along its axis, length of the scale bar #' @param pch plotting symbol @@ -72,7 +72,7 @@ #' @export plot.sf <- function(x, y, ..., main, pal = NULL, nbreaks = 10, breaks = "pretty", max.plot = if(is.null(n <- getOption("sf_max.plot"))) 9 else n, - key.pos = get_key_pos(x, ...), key.length = .618, key.width = lcm(1.8), + key.pos = get_key_pos(x, ...), key.length = .618, key.width = lcm(1.8 * par("ps")/12), reset = TRUE, logz = FALSE, extent = x, xlim = st_bbox(extent)[c(1,3)], ylim = st_bbox(extent)[c(2,4)]) { @@ -89,8 +89,9 @@ plot.sf <- function(x, y, ..., main, pal = NULL, nbreaks = 10, breaks = "pretty" opar = par() if (ncol(x) > 2 && !isTRUE(dots$add)) { # multiple maps to plot... cols = setdiff(names(x), attr(x, "sf_column")) - lt = .get_layout(st_bbox(x), min(max.plot, length(cols)), par("din"), key.pos, key.width) - key.pos = lt$key.pos + lt = .get_layout(st_bbox(x), min(max.plot, length(cols)), par("din"), key.pos[1], key.width) + if (key.pos.missing || key.pos == -1) + key.pos = lt$key.pos layout(lt$m, widths = lt$widths, heights = lt$heights, respect = FALSE) if (isTRUE(dots$axes)) @@ -140,7 +141,7 @@ plot.sf <- function(x, y, ..., main, pal = NULL, nbreaks = 10, breaks = "pretty" plot.new() # plot key? - if (!is.null(key.pos) && key.pos != 0 && col_missing) { + if (!is.null(key.pos) && key.pos[1] != 0 && col_missing) { if (is.null(pal)) pal = function(n) sf.colors(n, categorical = is.factor(values)) colors = if (is.function(pal)) @@ -229,7 +230,7 @@ plot.sf <- function(x, y, ..., main, pal = NULL, nbreaks = 10, breaks = "pretty" (is.factor(values) || length(unique(na.omit(values))) > 1 || breaks_numeric) && # 2065 length(col) > 1) { # plot key? - switch(key.pos, + switch(key.pos[1], layout(matrix(c(2,1), nrow = 2, ncol = 1), widths = 1, heights = c(1, key.width)), # 1 bottom layout(matrix(c(1,2), nrow = 1, ncol = 2), @@ -779,6 +780,24 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer axis(side, at = at, labels = labels, ...) } +# given range r = (a,b), return a value range that: +# * scales such that (b-a)/(y-x)=l +# * shifts linearly within [x,y] such that a==x if o==0, or b==y if o==1 +xy_from_r = function(r, l, o) { + stopifnot(length(r) == 2, l <= 1, l > 0, o >= 0, o <= 1) + a = r[1] + b = r[2] + if (o == 1) { + y = b + x = b - (b-a)/l + } else { + i = o / (o-1) + y = (a + (b-a)/l - i * b)/(1 - i) + x = i * (y - b) + a + } + c(x, y) +} + #' @name stars #' @export #' @param z ignore @@ -801,6 +820,11 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer zlim = range(z, na.rm = TRUE) if (is.null(breaks)) breaks = seq(zlim[1], zlim[2], length.out = length(col) + 1) + offset = 0.5 + if (length(key.pos) == 2) { + offset = key.pos[2] + key.pos = key.pos[1] + } if (is.character(key.length)) { kl = as.numeric(gsub(" cm", "", key.length)) sz = if (key.pos %in% c(1,3)) @@ -814,14 +838,13 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer at = pretty(br) at = at[at > br[1] & at < br[2]] } - kl_lim = function(r, kl) { m = mean(r); (r - m)/kl + m } if (key.pos %in% c(1,3)) { ylim = c(0, 1) - xlim = kl_lim(range(breaks), key.length) + xlim = xy_from_r(range(breaks), key.length, offset) mar = c(0, ifelse(axes, 2.1, 1), 0, 1) } if (key.pos %in% c(2,4)) { - ylim = kl_lim(range(breaks), key.length) + ylim = xy_from_r(range(breaks), key.length, offset) xlim = c(0, 1) mar = c(ifelse(axes, 2.1, 1), 0, 1.2, 0) } @@ -876,6 +899,11 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer # TODO: ksz = as.numeric(gsub(" cm", "", key.width)) * 2 breaks = (0:n) + 0.5 + offset = 0.5 + if (length(key.pos) == 2) { + offset = key.pos[2] + key.pos = key.pos[1] + } if (is.character(key.length)) { kl = as.numeric(gsub(" cm", "", key.length)) sz = if (key.pos %in% c(1,3)) @@ -884,17 +912,15 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer dev.size("cm")[2] key.length = kl/sz } - kl_lim = function(r, kl) { m = mean(r); (r - m)/kl + m } if (key.pos %in% c(1,3)) { ylim = c(0, 1) - xlim = kl_lim(range(breaks), key.length) + xlim = xy_from_r(range(breaks), key.length, offset) mar = c(0, ifelse(axes, 2.1, 1), 0, 1) mar[key.pos] = 2.1 } else { - ylim = kl_lim(range(breaks), key.length) + ylim = xy_from_r(range(breaks), key.length, offset) xlim = c(0, 1) mar = c(ifelse(axes, 2.1, 1), 0, 1.2, 0) - #mar[key.pos] = 2.1 mar[key.pos] = max(ksz - 1.3, 0.0) } par(mar = mar) diff --git a/man/plot.Rd b/man/plot.Rd index 8da999045..0277c8bde 100644 --- a/man/plot.Rd +++ b/man/plot.Rd @@ -29,7 +29,7 @@ max.plot = if (is.null(n <- getOption("sf_max.plot"))) 9 else n, key.pos = get_key_pos(x, ...), key.length = 0.618, - key.width = lcm(1.8), + key.width = lcm(1.8 * par("ps")/12), reset = TRUE, logz = FALSE, extent = x, From 14dc4331540e2965b1d37f099e199648d5111d3e Mon Sep 17 00:00:00 2001 From: edzer Date: Fri, 18 Aug 2023 15:08:32 +0200 Subject: [PATCH 26/31] docs --- man/plot.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/plot.Rd b/man/plot.Rd index 0277c8bde..d142377de 100644 --- a/man/plot.Rd +++ b/man/plot.Rd @@ -170,7 +170,7 @@ sf.colors(n = 10, cutoff.tails = c(0.35, 0.2), alpha = 1, categorical = FALSE) \item{max.plot}{integer; lower boundary to maximum number of attributes to plot; the default value (9) can be overriden by setting the global option \code{sf_max.plot}, e.g. \code{options(sf_max.plot=2)}} -\item{key.pos}{integer; side to plot a color key: 1 bottom, 2 left, 3 top, 4 right; set to \code{NULL} to omit key completely, 0 to only not plot the key, or -1 to select automatically. If multiple columns are plotted in a single function call by default no key is plotted and every submap is stretched individually; if a key is requested (and \code{col} is missing) all maps are colored according to a single key. Auto select depends on plot size, map aspect, and, if set, parameter \code{asp}.} +\item{key.pos}{numeric; side to plot a color key: 1 bottom, 2 left, 3 top, 4 right; set to \code{NULL} to omit key completely, 0 to only not plot the key, or -1 to select automatically. If multiple columns are plotted in a single function call by default no key is plotted and every submap is stretched individually; if a key is requested (and \code{col} is missing) all maps are colored according to a single key. Auto select depends on plot size, map aspect, and, if set, parameter \code{asp}. If it has lenght 2, the second value, ranging from 0 to 1, determines where the key is placed in the available space (default: 0.5, center).} \item{key.length}{amount of space reserved for the key along its axis, length of the scale bar} From ebf336c651d5e35621b838f111a7d71f3f0cb6fd Mon Sep 17 00:00:00 2001 From: edzer Date: Fri, 18 Aug 2023 15:42:45 +0200 Subject: [PATCH 27/31] tidy --- R/plot.R | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/R/plot.R b/R/plot.R index 45b991f3e..9233b8fca 100644 --- a/R/plot.R +++ b/R/plot.R @@ -780,19 +780,19 @@ bb2merc = function(x, cls = "ggmap") { # return bbox in the appropriate "web mer axis(side, at = at, labels = labels, ...) } -# given range r = (a,b), return a value range that: -# * scales such that (b-a)/(y-x)=l -# * shifts linearly within [x,y] such that a==x if o==0, or b==y if o==1 +# find out where to place the legend key: +# given range r = (a, b), key.length l, key offset o, return a value range that: +# * scales such that (b - a) / (y - x) = l +# * shifts linearly within [x, y] from a = x when o = 0 to b = y when o = 1 xy_from_r = function(r, l, o) { stopifnot(length(r) == 2, l <= 1, l > 0, o >= 0, o <= 1) - a = r[1] - b = r[2] + a = r[1]; b = r[2] if (o == 1) { y = b - x = b - (b-a)/l + x = b - (b - a)/l } else { - i = o / (o-1) - y = (a + (b-a)/l - i * b)/(1 - i) + i = o / (o - 1) + y = (a + (b - a)/l - i * b)/(1 - i) x = i * (y - b) + a } c(x, y) From 69f127d4f203f14c915490136637555ff90b3a0d Mon Sep 17 00:00:00 2001 From: edzer Date: Fri, 18 Aug 2023 21:17:02 +0200 Subject: [PATCH 28/31] user proper key widths, stop() if there's not enough space see also https://github.com/r-spatial/stars/issues/642 --- NEWS.md | 2 +- R/plot.R | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7c79aa65a..81e97dd63 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # version 1.0-15 -* `plot.sf()`: `key.width` is sensitive to pointsize graphics parameter, `key.pos` can hold a second value in [0, 1] determining the relative position of the key in the available space +* `plot.sf()`: `key.width` is sensitive to pointsize graphics parameter, `key.pos` can hold a second value in [0, 1] determining the relative position of the key in the available space; keys with factor levels suggest a proper size if they won't fit. * `[<-.sf` fixes the `agr` attribute when it is broken; #2211 diff --git a/R/plot.R b/R/plot.R index 9233b8fca..7c8bbeb46 100644 --- a/R/plot.R +++ b/R/plot.R @@ -896,8 +896,7 @@ xy_from_r = function(r, l, o) { ..., axes = FALSE, key.width, key.length, cex.axis = par("cex.axis")) { n = length(z) - # TODO: - ksz = as.numeric(gsub(" cm", "", key.width)) * 2 + ksz = max(strwidth(z, "inches")) / par("cin")[1] # in "mar" lines breaks = (0:n) + 0.5 offset = 0.5 if (length(key.pos) == 2) { @@ -921,15 +920,24 @@ xy_from_r = function(r, l, o) { ylim = xy_from_r(range(breaks), key.length, offset) xlim = c(0, 1) mar = c(ifelse(axes, 2.1, 1), 0, 1.2, 0) - mar[key.pos] = max(ksz - 1.3, 0.0) + mar[key.pos] = ksz } par(mar = mar) poly = vector(mode="list", length(col)) for (i in seq(poly)) poly[[i]] = c(breaks[i], breaks[i+1], breaks[i+1], breaks[i]) - plot(1, 1, t = "n", ylim = ylim, xlim = xlim, axes = FALSE, - xlab = "", ylab = "", xaxs = "i", yaxs = "i") + + tryCatch({ + plot(1, 1, t = "n", ylim = ylim, xlim = xlim, axes = FALSE, + xlab = "", ylab = "", xaxs = "i", yaxs = "i") + }, + error = function(x) { + sz = max(strwidth(z, "inches")) * 2.54 + par("ps")/12 # cm + stop(paste0("key.width too small, try key.width = lcm(", signif(sz, 3), ")"), call. = FALSE) + } + ) + for(i in seq_along(poly)) { if (key.pos %in% c(1,3)) polygon(poly[[i]], c(0, 0, 1, 1), col = col[i], border = NA) From 35404893a22f7ba583c9c934f3e39f29d0eaa7a6 Mon Sep 17 00:00:00 2001 From: edzer Date: Fri, 18 Aug 2023 21:37:48 +0200 Subject: [PATCH 29/31] factor key sizes --- R/plot.R | 2 +- vignettes/sf5.Rmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/plot.R b/R/plot.R index 7c8bbeb46..9490dd93b 100644 --- a/R/plot.R +++ b/R/plot.R @@ -933,7 +933,7 @@ xy_from_r = function(r, l, o) { xlab = "", ylab = "", xaxs = "i", yaxs = "i") }, error = function(x) { - sz = max(strwidth(z, "inches")) * 2.54 + par("ps")/12 # cm + sz = max(strwidth(z, "inches")) * 2.54 * 1.1 + par("ps")/12 # cm stop(paste0("key.width too small, try key.width = lcm(", signif(sz, 3), ")"), call. = FALSE) } ) diff --git a/vignettes/sf5.Rmd b/vignettes/sf5.Rmd index 3b05ff990..8b78b488d 100644 --- a/vignettes/sf5.Rmd +++ b/vignettes/sf5.Rmd @@ -81,7 +81,7 @@ Keys for factor variables are a bit different, as we typically don't want to rot ```{r} nc$f = cut(nc$AREA, 10) -plot(nc["f"], axes = TRUE, key.pos = 4, pal = sf.colors(10), key.width = lcm(4.5)) +plot(nc["f"], axes = TRUE, key.pos = 4, pal = sf.colors(10), key.width = lcm(5)) ``` # Class intervals From d86920e7fc0782373ca151a6ea16a18ede5be37f Mon Sep 17 00:00:00 2001 From: Uchida Mizuki Date: Sun, 20 Aug 2023 18:29:37 +0900 Subject: [PATCH 30/31] Fix `rename_with()` and add tests (#2215) --- R/tidyverse.R | 9 +++++++++ tests/testthat/test_tidy.R | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/R/tidyverse.R b/R/tidyverse.R index 96419f9a7..454959fb6 100644 --- a/R/tidyverse.R +++ b/R/tidyverse.R @@ -239,10 +239,19 @@ rename_with.sf = function(.data, .fn, .cols, ...) { if (!requireNamespace("rlang", quietly = TRUE)) stop("rlang required: install that first") # nocov .fn = rlang::as_function(.fn) + + sf_column = attr(.data, "sf_column") + sf_column_loc = match(sf_column, names(.data)) + + if (length(sf_column_loc) != 1 || is.na(sf_column_loc)) + stop("internal error: can't find sf column") # nocov + agr = st_agr(.data) + ret = NextMethod() names(agr) = .fn(names(agr)) st_agr(ret) = agr + st_geometry(ret) = names(ret)[sf_column_loc] ret } diff --git a/tests/testthat/test_tidy.R b/tests/testthat/test_tidy.R index 492e5a0ec..151ecb410 100644 --- a/tests/testthat/test_tidy.R +++ b/tests/testthat/test_tidy.R @@ -255,6 +255,17 @@ test_that("can rename geometry column with `rename()` (#1431)", { ) }) +test_that("`rename_with()` correctly changes the sf_column attribute (#2215)", { + skip_if_not_installed("dplyr") + + sf_column = attr(nc, "sf_column") + fn = function(x) paste0(x, "_renamed") + + expect_equal(nc %>% rename_with(fn) %>% attr("sf_column"), fn(sf_column)) + expect_equal(nc %>% rename_with(fn, "NAME") %>% attr("sf_column"), sf_column) + expect_equal(nc %>% rename_with(fn, "geometry") %>% attr("sf_column"), fn(sf_column)) +}) + test_that("`select()` and `transmute()` observe back-stickiness of geometry column (#1425)", { skip_if_not_installed("dplyr") sf = read_sf(system.file("shape/nc.shp", package = "sf")) From 732525f7ce1027473bfd4700192ec969e9af768e Mon Sep 17 00:00:00 2001 From: edzer Date: Mon, 21 Aug 2023 15:39:27 +0200 Subject: [PATCH 31/31] tidy --- R/defunct.R | 3 +-- man/sf-defunct.Rd | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/R/defunct.R b/R/defunct.R index db60843c7..cadfef846 100644 --- a/R/defunct.R +++ b/R/defunct.R @@ -13,9 +13,8 @@ #' it will try to cast all the character columns, which can be long for very wide #' tables. #' @inheritParams st_read -#' @docType package #' @export st_read_db st_write_db -#' @aliases st_read_db, st_write_db +#' @aliases sf-package st_read_db, st_write_db #' @section Details: #' \tabular{rl}{ #' \code{st_read_db} \tab now a synonym for \code{\link{st_read}}\cr diff --git a/man/sf-defunct.Rd b/man/sf-defunct.Rd index 8c7672ab3..d4ec56d10 100644 --- a/man/sf-defunct.Rd +++ b/man/sf-defunct.Rd @@ -1,9 +1,9 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/defunct.R -\docType{package} \name{sf-defunct} \alias{sf-defunct} \alias{st_read_db} +\alias{sf-package} \alias{st_read_db,} \alias{st_write_db} \title{Deprecated functions in \code{sf}}