From 3c0b00b3c53fc03328883abe489baf420ec8fccc Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 30 Mar 2024 18:08:52 +0000 Subject: [PATCH 01/12] collect unique sf types using a bitmask --- src/wkb.cpp | 22 ++++++++++++++++------ src/wkb.h | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/wkb.cpp b/src/wkb.cpp index c91277ff2..24e372a5a 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -400,11 +400,16 @@ int native_endian(void) { return (int) *cp; } + + // [[Rcpp::export]] Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) { Rcpp::List output(wkb_list.size()); - int type = 0, last_type = 0, n_types = 0, n_empty = 0; + int type = 0, n_empty = 0; + // An integer such that (all_types & (1 << sf_type)) != 0 if sf_type was + // encountered while reading the wkb items. + int64_t all_types = 0; int endian = native_endian(); uint32_t srid = 0; @@ -421,11 +426,16 @@ Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite n_empty++; } // Rcpp::Rcout << "type is " << type << "\n"; - if (n_types <= 1 && type != last_type) { - last_type = type; - n_types++; // check if there's more than 1 type: + all_types |= static_cast(1) << type; + } + + int n_types = 0; + for (int i = 0; i < SF_Type_Max; i++) { + if (all_types & (static_cast(1) << i)) { + n_types++; } } + output.attr("single_type") = n_types <= 1; // if 0, we have only empty geometrycollections output.attr("n_empty") = (int) n_empty; if ((EWKB || spatialite) && srid != 0) @@ -679,8 +689,8 @@ Rcpp::List CPL_write_wkb(Rcpp::List sfc, bool EWKB = false) { } int srid = 0; - if (EWKB) { - // get SRID from crs[["input"]], either of the form "4326" + if (EWKB) { + // get SRID from crs[["input"]], either of the form "4326" // or "XXXX:4326" with arbitrary XXXX string, // or else from the wkt field of the crs using srid_from_crs() Rcpp::List crs = sfc.attr("crs"); diff --git a/src/wkb.h b/src/wkb.h index ff718b6dc..445f4ba6e 100644 --- a/src/wkb.h +++ b/src/wkb.h @@ -17,6 +17,7 @@ #define SF_PolyhedralSurface 15 #define SF_TIN 16 #define SF_Triangle 17 +#define SF_Type_Max 17 Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite); Rcpp::List CPL_write_wkb(Rcpp::List sfc, bool EWKB); From d994803b6224cd195b246c878de8487348c6489b Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 30 Mar 2024 18:24:15 +0000 Subject: [PATCH 02/12] structure of promotion code --- src/wkb.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/wkb.cpp b/src/wkb.cpp index 24e372a5a..2454f727e 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -400,7 +400,27 @@ int native_endian(void) { return (int) *cp; } +static int64_t sf_type_bitmask(int sf_type) { + return static_cast(1) << sf_type; +} + +static void read_wkb_promote_multi_if_possible(Rcpp::List output, int64_t* all_types) { + int64_t can_promote_multipoint = sf_type_bitmask(SF_Point) | + sf_type_bitmask(SF_MultiPoint); + int64_t can_promote_multilinestring = sf_type_bitmask(SF_LineString) | + sf_type_bitmask(SF_MultiLineString); + int64_t can_promote_multipolygon = sf_type_bitmask(SF_Polygon) | + sf_type_bitmask(SF_MultiPolygon); + + if (*all_types != can_promote_multipoint && + *all_types != can_promote_multilinestring && + *all_types != can_promote_multipolygon) { + // No type promotion possible + return; + } + Rcpp::stop("Not implemented"); +} // [[Rcpp::export]] Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) { @@ -426,12 +446,18 @@ Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite n_empty++; } // Rcpp::Rcout << "type is " << type << "\n"; - all_types |= static_cast(1) << type; + all_types |= sf_type_bitmask(type); + } + + bool promote_multi = false; + + if (promote_multi) { + read_wkb_promote_multi_if_possible(output, &all_types); } int n_types = 0; for (int i = 0; i < SF_Type_Max; i++) { - if (all_types & (static_cast(1) << i)) { + if (all_types & sf_type_bitmask(i)) { n_types++; } } From 50d3fd6a13e736529b08e90681ccdc03b8a0c275 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 00:09:18 +0000 Subject: [PATCH 03/12] maybe with promote --- src/wkb.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/src/wkb.cpp b/src/wkb.cpp index 2454f727e..c0e7b49b3 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -404,6 +404,49 @@ static int64_t sf_type_bitmask(int sf_type) { return static_cast(1) << sf_type; } +static Rcpp::NumericMatrix read_wkb_promote_sfg_multipoint(Rcpp::NumericVector item) { + SEXP cls_old = Rf_getAttrib(item, R_ClassSymbol); + Rcpp::CharacterVector cls_new = Rf_duplicate(cls_old); + cls_new[0] = "MULTIPOINT"; + + bool is_empty = true; + for (const auto ordinate : item) { + if (!ISNAN(ordinate)) { + is_empty = false; + break; + } + } + + Rcpp::NumericMatrix multi; + if (is_empty) { + multi = Rcpp::NumericMatrix(0, item.size()); + } else { + multi = Rcpp::NumericMatrix(1, item.size()); + memcpy(REAL(multi), REAL(item), item.size() * sizeof(double)); + } + + multi.attr("class") = cls_new; + return multi; +} + +static Rcpp::List read_wkb_promote_sfg_multipolygon_or_linestring(SEXP item, const char* cls_multi) { + // Technically we could mutate this class because we allocated it above; however, + // safer to not make this assumption. + SEXP cls_old = Rf_getAttrib(item, R_ClassSymbol); + Rcpp::CharacterVector cls_new = Rf_duplicate(cls_old); + cls_new[0] = cls_multi; + + Rcpp::List multi; + if (Rf_length(item) == 0) { + multi = Rcpp::List::create(); + } else { + multi = Rcpp::List::create(item); + } + + multi.attr("class") = cls_new; + return multi; +} + static void read_wkb_promote_multi_if_possible(Rcpp::List output, int64_t* all_types) { int64_t can_promote_multipoint = sf_type_bitmask(SF_Point) | sf_type_bitmask(SF_MultiPoint); @@ -412,14 +455,44 @@ static void read_wkb_promote_multi_if_possible(Rcpp::List output, int64_t* all_t int64_t can_promote_multipolygon = sf_type_bitmask(SF_Polygon) | sf_type_bitmask(SF_MultiPolygon); - if (*all_types != can_promote_multipoint && - *all_types != can_promote_multilinestring && - *all_types != can_promote_multipolygon) { - // No type promotion possible + const char* cls_simple; + int sf_type_multi; + if (*all_types == can_promote_multipoint) { + cls_simple = "POINT"; + sf_type_multi = SF_MultiPoint; + } else if (*all_types == can_promote_multilinestring) { + cls_simple = "LINESTRING"; + sf_type_multi = SF_MultiLineString; + } else if (*all_types == can_promote_multipolygon) { + cls_simple = "POLYGON"; + sf_type_multi = SF_MultiPolygon; + } else { + // Promotion is not possible or is not necessary return; } - Rcpp::stop("Not implemented"); + for (int i = 0; i < output.size(); i++) { + SEXP item = output[i]; + if (!Rf_inherits(item, cls_simple)) { + continue; + } + + switch (sf_type_multi) { + case SF_MultiPoint: + output[i] = read_wkb_promote_sfg_multipoint(item); + break; + case SF_MultiLineString: + output[i] = read_wkb_promote_sfg_multipolygon_or_linestring(item, "MULTILINESTRING"); + break; + case SF_MultiPolygon: + output[i] = read_wkb_promote_sfg_multipolygon_or_linestring(item, "MULTIPOLYGON"); + break; + default: + Rcpp::stop("promote to multi not implemented"); + } + } + + *all_types = sf_type_multi; } // [[Rcpp::export]] From 1fd150d7e400ee315ccdae8ededcb14aa0b5d4c9 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 00:42:11 +0000 Subject: [PATCH 04/12] plumb option --- R/RcppExports.R | 4 ++-- inst/include/sf_RcppExports.h | 8 ++++---- src/RcppExports.cpp | 15 ++++++++------- src/gdal.cpp | 4 ++-- src/geos.cpp | 10 +++++----- src/wkb.cpp | 4 +--- src/wkb.h | 2 +- 7 files changed, 23 insertions(+), 24 deletions(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index aa9e80abf..b08d09c1a 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -393,8 +393,8 @@ CPL_create <- function(file, nxy, value, wkt, xlim, ylim) { invisible(.Call(`_sf_CPL_create`, file, nxy, value, wkt, xlim, ylim)) } -CPL_read_wkb <- function(wkb_list, EWKB = FALSE, spatialite = FALSE) { - .Call(`_sf_CPL_read_wkb`, wkb_list, EWKB, spatialite) +CPL_read_wkb <- function(wkb_list, EWKB = FALSE, spatialite = FALSE, promote_multi = FALSE) { + .Call(`_sf_CPL_read_wkb`, wkb_list, EWKB, spatialite, promote_multi) } CPL_write_wkb <- function(sfc, EWKB = FALSE) { diff --git a/inst/include/sf_RcppExports.h b/inst/include/sf_RcppExports.h index 21aa39db0..3b2820ec4 100644 --- a/inst/include/sf_RcppExports.h +++ b/inst/include/sf_RcppExports.h @@ -24,17 +24,17 @@ namespace sf { } } - inline Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) { - typedef SEXP(*Ptr_CPL_read_wkb)(SEXP,SEXP,SEXP); + inline Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false, bool promote_multi = false) { + typedef SEXP(*Ptr_CPL_read_wkb)(SEXP,SEXP,SEXP,SEXP); static Ptr_CPL_read_wkb p_CPL_read_wkb = NULL; if (p_CPL_read_wkb == NULL) { - validateSignature("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool)"); + validateSignature("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool,bool)"); p_CPL_read_wkb = (Ptr_CPL_read_wkb)R_GetCCallable("sf", "_sf_CPL_read_wkb"); } RObject rcpp_result_gen; { RNGScope RCPP_rngScope_gen; - rcpp_result_gen = p_CPL_read_wkb(Shield(Rcpp::wrap(wkb_list)), Shield(Rcpp::wrap(EWKB)), Shield(Rcpp::wrap(spatialite))); + rcpp_result_gen = p_CPL_read_wkb(Shield(Rcpp::wrap(wkb_list)), Shield(Rcpp::wrap(EWKB)), Shield(Rcpp::wrap(spatialite)), Shield(Rcpp::wrap(promote_multi))); } if (rcpp_result_gen.inherits("interrupted-error")) throw Rcpp::internal::InterruptedException(); diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 1b7adf161..18331f4b2 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1343,22 +1343,23 @@ BEGIN_RCPP END_RCPP } // CPL_read_wkb -Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite); -static SEXP _sf_CPL_read_wkb_try(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP) { +Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite, bool promote_multi); +static SEXP _sf_CPL_read_wkb_try(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP, SEXP promote_multiSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::traits::input_parameter< Rcpp::List >::type wkb_list(wkb_listSEXP); Rcpp::traits::input_parameter< bool >::type EWKB(EWKBSEXP); Rcpp::traits::input_parameter< bool >::type spatialite(spatialiteSEXP); - rcpp_result_gen = Rcpp::wrap(CPL_read_wkb(wkb_list, EWKB, spatialite)); + Rcpp::traits::input_parameter< bool >::type promote_multi(promote_multiSEXP); + rcpp_result_gen = Rcpp::wrap(CPL_read_wkb(wkb_list, EWKB, spatialite, promote_multi)); return rcpp_result_gen; END_RCPP_RETURN_ERROR } -RcppExport SEXP _sf_CPL_read_wkb(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP) { +RcppExport SEXP _sf_CPL_read_wkb(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP, SEXP promote_multiSEXP) { SEXP rcpp_result_gen; { Rcpp::RNGScope rcpp_rngScope_gen; - rcpp_result_gen = PROTECT(_sf_CPL_read_wkb_try(wkb_listSEXP, EWKBSEXP, spatialiteSEXP)); + rcpp_result_gen = PROTECT(_sf_CPL_read_wkb_try(wkb_listSEXP, EWKBSEXP, spatialiteSEXP, promote_multiSEXP)); } Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, "interrupted-error"); if (rcpp_isInterrupt_gen) { @@ -1442,7 +1443,7 @@ END_RCPP static int _sf_RcppExport_validate(const char* sig) { static std::set signatures; if (signatures.empty()) { - signatures.insert("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool)"); + signatures.insert("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool,bool)"); signatures.insert("Rcpp::List(*CPL_write_wkb)(Rcpp::List,bool)"); } return signatures.find(sig) != signatures.end(); @@ -1555,7 +1556,7 @@ static const R_CallMethodDef CallEntries[] = { {"_sf_CPL_write_gdal", (DL_FUNC) &_sf_CPL_write_gdal, 13}, {"_sf_CPL_extract", (DL_FUNC) &_sf_CPL_extract, 3}, {"_sf_CPL_create", (DL_FUNC) &_sf_CPL_create, 6}, - {"_sf_CPL_read_wkb", (DL_FUNC) &_sf_CPL_read_wkb, 3}, + {"_sf_CPL_read_wkb", (DL_FUNC) &_sf_CPL_read_wkb, 4}, {"_sf_CPL_write_wkb", (DL_FUNC) &_sf_CPL_write_wkb, 2}, {"_sf_CPL_get_z_range", (DL_FUNC) &_sf_CPL_get_z_range, 2}, {"_sf_CPL_get_m_range", (DL_FUNC) &_sf_CPL_get_m_range, 2}, diff --git a/src/gdal.cpp b/src/gdal.cpp index 802d5cc4e..f1afaabd0 100644 --- a/src/gdal.cpp +++ b/src/gdal.cpp @@ -330,7 +330,7 @@ Rcpp::List CPL_crs_parameters(Rcpp::List crs) { Rcpp::IntegerVector orientation(ac); for (int i = 0; i < ac; i++) { OGRAxisOrientation peOrientation; - const char *ret = srs->GetAxis(srs->IsGeographic() ? "GEOGCS" : "PROJCS", + const char *ret = srs->GetAxis(srs->IsGeographic() ? "GEOGCS" : "PROJCS", i, &peOrientation); if (ret != NULL) { nms[i] = ret; @@ -497,7 +497,7 @@ Rcpp::List sfc_from_ogr(std::vector g, bool destroy = false) { if (destroy) OGRGeometryFactory::destroyGeometry(g[i]); } - Rcpp::List ret = CPL_read_wkb(lst, false, false); + Rcpp::List ret = CPL_read_wkb(lst, false, false, false); ret.attr("crs") = crs; ret.attr("class") = "sfc"; return ret; diff --git a/src/geos.cpp b/src/geos.cpp index 703cf1d6a..f20b21a17 100644 --- a/src/geos.cpp +++ b/src/geos.cpp @@ -252,7 +252,7 @@ Rcpp::List sfc_from_geometry(GEOSContextHandle_t hGEOSCtxt, std::vector } } GEOSWKBWriter_destroy_r(hGEOSCtxt, wkb_writer); - return CPL_read_wkb(out, true, false); + return CPL_read_wkb(out, true, false, false); } Rcpp::NumericVector get_dim(double dim0, double dim1) { @@ -871,7 +871,7 @@ Rcpp::List CPL_geos_op(std::string op, Rcpp::List sfc, #ifdef HAVE310 if (op == "triangulate_constrained") { for (size_t i = 0; i < g.size(); i++) - out[i] = geos_ptr(chkNULL(GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, g[i].get())), + out[i] = geos_ptr(chkNULL(GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, g[i].get())), hGEOSCtxt); } else #endif @@ -1307,14 +1307,14 @@ Rcpp::List CPL_nary_intersection(Rcpp::List sfc) { errors++; else if (!chk_(GEOSisEmpty_r(hGEOSCtxt, inters.get()))) { // i and k intersection // cut out inters from geom: - geom = geos_ptr(GEOSDifference_r(hGEOSCtxt, geom.get(), inters.get()), hGEOSCtxt); + geom = geos_ptr(GEOSDifference_r(hGEOSCtxt, geom.get(), inters.get()), hGEOSCtxt); if (geom == nullptr) Rcpp::stop("GEOS exception"); // #nocov // cut out inters from out[k]: #ifndef HAVE_390 - GeomPtr g = geos_ptr(GEOSDifference_r(hGEOSCtxt, out[k].get(), inters.get()), hGEOSCtxt); + GeomPtr g = geos_ptr(GEOSDifference_r(hGEOSCtxt, out[k].get(), inters.get()), hGEOSCtxt); #else - GeomPtr g = geos_ptr(GEOSDifferencePrec_r(hGEOSCtxt, out[k].get(), inters.get(), grid_size), hGEOSCtxt); + GeomPtr g = geos_ptr(GEOSDifferencePrec_r(hGEOSCtxt, out[k].get(), inters.get(), grid_size), hGEOSCtxt); #endif if (g == nullptr) Rcpp::warning("GEOS difference returns NULL"); // #nocov diff --git a/src/wkb.cpp b/src/wkb.cpp index c0e7b49b3..ce460da32 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -496,7 +496,7 @@ static void read_wkb_promote_multi_if_possible(Rcpp::List output, int64_t* all_t } // [[Rcpp::export]] -Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) { +Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false, bool promote_multi = false) { Rcpp::List output(wkb_list.size()); int type = 0, n_empty = 0; @@ -522,8 +522,6 @@ Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite all_types |= sf_type_bitmask(type); } - bool promote_multi = false; - if (promote_multi) { read_wkb_promote_multi_if_possible(output, &all_types); } diff --git a/src/wkb.h b/src/wkb.h index 445f4ba6e..c1e682779 100644 --- a/src/wkb.h +++ b/src/wkb.h @@ -19,7 +19,7 @@ #define SF_Triangle 17 #define SF_Type_Max 17 -Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite); +Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite, bool promote_multi); Rcpp::List CPL_write_wkb(Rcpp::List sfc, bool EWKB); unsigned int make_type(const char *cls, const char *dim, bool EWKB, int *tp, int srid); Rcpp::List get_dim_sfc(Rcpp::List sfc); From 34f2827104602b9a5c232bc83d49bb011516f42c Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 00:59:22 +0000 Subject: [PATCH 05/12] plumb into R --- R/wkb.R | 5 +++-- src/wkb.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/R/wkb.R b/R/wkb.R index 16582ab0d..49ed32125 100644 --- a/R/wkb.R +++ b/R/wkb.R @@ -32,7 +32,8 @@ skip0x = function(x) { #' wkb = structure(list("0x01010000204071000000000000801A064100000000AC5C1441"), class = "WKB") #' st_as_sfc(wkb, EWKB = TRUE) #' @export -st_as_sfc.WKB = function(x, ..., EWKB = FALSE, spatialite = FALSE, pureR = FALSE, crs = NA_crs_) { +st_as_sfc.WKB = function(x, ..., EWKB = FALSE, spatialite = FALSE, pureR = FALSE, crs = NA_crs_, + promote_multi = FALSE) { if (EWKB && spatialite) stop("arguments EWKB and spatialite cannot both be TRUE") if (spatialite && pureR) @@ -49,7 +50,7 @@ st_as_sfc.WKB = function(x, ..., EWKB = FALSE, spatialite = FALSE, pureR = FALSE ret = if (pureR) R_read_wkb(x, readWKB, EWKB = EWKB) else - CPL_read_wkb(x, EWKB, spatialite) + CPL_read_wkb(x, EWKB, spatialite, promote_multi) if (is.na(crs) && (EWKB || spatialite) && !is.null(attr(ret, "srid")) && attr(ret, "srid") != 0) crs = attr(ret, "srid") if (! is.na(st_crs(crs))) { diff --git a/src/wkb.cpp b/src/wkb.cpp index ce460da32..97476d5c4 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -407,7 +407,7 @@ static int64_t sf_type_bitmask(int sf_type) { static Rcpp::NumericMatrix read_wkb_promote_sfg_multipoint(Rcpp::NumericVector item) { SEXP cls_old = Rf_getAttrib(item, R_ClassSymbol); Rcpp::CharacterVector cls_new = Rf_duplicate(cls_old); - cls_new[0] = "MULTIPOINT"; + cls_new[1] = "MULTIPOINT"; bool is_empty = true; for (const auto ordinate : item) { @@ -434,7 +434,7 @@ static Rcpp::List read_wkb_promote_sfg_multipolygon_or_linestring(SEXP item, con // safer to not make this assumption. SEXP cls_old = Rf_getAttrib(item, R_ClassSymbol); Rcpp::CharacterVector cls_new = Rf_duplicate(cls_old); - cls_new[0] = cls_multi; + cls_new[1] = cls_multi; Rcpp::List multi; if (Rf_length(item) == 0) { From a8c4091ef3256108d5a71d7e07870f21c2d90d05 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 01:45:47 +0000 Subject: [PATCH 06/12] maybe fix --- R/read.R | 28 ++++++++++++---------------- src/wkb.cpp | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/R/read.R b/R/read.R index 93a73fdb7..57cadf6f4 100644 --- a/R/read.R +++ b/R/read.R @@ -76,7 +76,7 @@ set_utf8 = function(x) { #' #' In case of problems reading shapefiles from USB drives on OSX, please see #' \url{https://github.com/r-spatial/sf/issues/252}. Reading shapefiles (or other -#' data sources) directly from zip files can be done by prepending the path +#' data sources) directly from zip files can be done by prepending the path #' with \code{/vsizip/}. This is part of the GDAL Virtual File Systems interface #' that also supports .gz, curl, and other operations, including chaining; see #' \url{https://gdal.org/user/virtual_file_systems.html} for a complete @@ -225,42 +225,38 @@ process_cpl_read_ogr_stream = function(x, geom_column_info, num_features, fid_co function(s) identical(s$metadata[["ARROW:extension:name"]], "ogc.wkb"), logical(1) ) - + geom_column_info$index = which(is_geometry_column) - + if (num_features == -1) { num_features = NULL } - + # Suppress warnings about extension type conversion (since we want the # default behaviour of converting the storage type) df = suppressWarnings(nanoarrow::convert_array_stream(x, size = num_features)) - + for (i in seq_len(nrow(geom_column_info))) { crs = if (is.null(crs)) st_crs(geom_column_info$crs[[i]]) else st_crs(crs) name = geom_column_info$name[[i]] index = geom_column_info$index[[i]] - + column_wkb = df[[index]] - attributes(column_wkb) = NULL - column_sfc = wk::wk_handle( - wk::new_wk_wkb(column_wkb), - wk::sfc_writer(promote_multi = promote_to_multi) - ) - - df[[index]] = st_set_crs(column_sfc, crs) + class(column_wkb) <- "WKB" + column_sfc = sf::st_as_sfc(column_wkb, crs = crs, promote_multi = promote_to_multi) + df[[index]] = column_sfc names(df)[index] = name } - + # Rename OGC_FID to fid_column_name and move to end if (length(fid_column_name) == 1 && "OGC_FID" %in% names(df)) { df = df[c(setdiff(names(df), "OGC_FID"), "OGC_FID")] names(df)[names(df) == "OGC_FID"] = fid_column_name } - + # All geometry columns to the end df = df[c(setdiff(seq_along(df), geom_column_info$index), geom_column_info$index)] - + process_cpl_read_ogr(df, ...) } diff --git a/src/wkb.cpp b/src/wkb.cpp index 97476d5c4..f2e863641 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -492,7 +492,7 @@ static void read_wkb_promote_multi_if_possible(Rcpp::List output, int64_t* all_t } } - *all_types = sf_type_multi; + *all_types = sf_type_bitmask(sf_type_multi); } // [[Rcpp::export]] From 433124089d02ebf75585ce2db6dae371ba220ce6 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 16:29:08 +0000 Subject: [PATCH 07/12] maybe revert wkb to existing usage --- R/RcppExports.R | 8 ++++-- R/read.R | 2 +- R/wkb.R | 7 +++-- inst/include/sf_RcppExports.h | 29 ++++++++++++++++--- src/RcppExports.cpp | 53 +++++++++++++++++++++++++++++------ src/gdal.cpp | 2 +- src/geos.cpp | 2 +- src/wkb.cpp | 34 ++++++++++++++++++++-- src/wkb.h | 2 +- 9 files changed, 117 insertions(+), 22 deletions(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index b08d09c1a..1ee3c878f 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -393,8 +393,12 @@ CPL_create <- function(file, nxy, value, wkt, xlim, ylim) { invisible(.Call(`_sf_CPL_create`, file, nxy, value, wkt, xlim, ylim)) } -CPL_read_wkb <- function(wkb_list, EWKB = FALSE, spatialite = FALSE, promote_multi = FALSE) { - .Call(`_sf_CPL_read_wkb`, wkb_list, EWKB, spatialite, promote_multi) +CPL_read_wkb <- function(wkb_list, EWKB = FALSE, spatialite = FALSE) { + .Call(`_sf_CPL_read_wkb`, wkb_list, EWKB, spatialite) +} + +CPL_read_wkb2 <- function(wkb_list, options) { + .Call(`_sf_CPL_read_wkb2`, wkb_list, options) } CPL_write_wkb <- function(sfc, EWKB = FALSE) { diff --git a/R/read.R b/R/read.R index 57cadf6f4..f4635b5a7 100644 --- a/R/read.R +++ b/R/read.R @@ -243,7 +243,7 @@ process_cpl_read_ogr_stream = function(x, geom_column_info, num_features, fid_co column_wkb = df[[index]] class(column_wkb) <- "WKB" - column_sfc = sf::st_as_sfc(column_wkb, crs = crs, promote_multi = promote_to_multi) + column_sfc = sf::st_as_sfc(column_wkb, crs = crs, promote_to_multi = promote_to_multi) df[[index]] = column_sfc names(df)[index] = name } diff --git a/R/wkb.R b/R/wkb.R index 49ed32125..9116c8a8d 100644 --- a/R/wkb.R +++ b/R/wkb.R @@ -33,7 +33,7 @@ skip0x = function(x) { #' st_as_sfc(wkb, EWKB = TRUE) #' @export st_as_sfc.WKB = function(x, ..., EWKB = FALSE, spatialite = FALSE, pureR = FALSE, crs = NA_crs_, - promote_multi = FALSE) { + promote_to_multi = FALSE) { if (EWKB && spatialite) stop("arguments EWKB and spatialite cannot both be TRUE") if (spatialite && pureR) @@ -50,7 +50,10 @@ st_as_sfc.WKB = function(x, ..., EWKB = FALSE, spatialite = FALSE, pureR = FALSE ret = if (pureR) R_read_wkb(x, readWKB, EWKB = EWKB) else - CPL_read_wkb(x, EWKB, spatialite, promote_multi) + CPL_read_wkb2(x, + list(EKWB = EWKB, + spatialite = spatialite, + promote_to_multi = promote_to_multi)) if (is.na(crs) && (EWKB || spatialite) && !is.null(attr(ret, "srid")) && attr(ret, "srid") != 0) crs = attr(ret, "srid") if (! is.na(st_crs(crs))) { diff --git a/inst/include/sf_RcppExports.h b/inst/include/sf_RcppExports.h index 3b2820ec4..aa63b7899 100644 --- a/inst/include/sf_RcppExports.h +++ b/inst/include/sf_RcppExports.h @@ -24,17 +24,38 @@ namespace sf { } } - inline Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false, bool promote_multi = false) { - typedef SEXP(*Ptr_CPL_read_wkb)(SEXP,SEXP,SEXP,SEXP); + inline Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) { + typedef SEXP(*Ptr_CPL_read_wkb)(SEXP,SEXP,SEXP); static Ptr_CPL_read_wkb p_CPL_read_wkb = NULL; if (p_CPL_read_wkb == NULL) { - validateSignature("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool,bool)"); + validateSignature("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool)"); p_CPL_read_wkb = (Ptr_CPL_read_wkb)R_GetCCallable("sf", "_sf_CPL_read_wkb"); } RObject rcpp_result_gen; { RNGScope RCPP_rngScope_gen; - rcpp_result_gen = p_CPL_read_wkb(Shield(Rcpp::wrap(wkb_list)), Shield(Rcpp::wrap(EWKB)), Shield(Rcpp::wrap(spatialite)), Shield(Rcpp::wrap(promote_multi))); + rcpp_result_gen = p_CPL_read_wkb(Shield(Rcpp::wrap(wkb_list)), Shield(Rcpp::wrap(EWKB)), Shield(Rcpp::wrap(spatialite))); + } + if (rcpp_result_gen.inherits("interrupted-error")) + throw Rcpp::internal::InterruptedException(); + if (Rcpp::internal::isLongjumpSentinel(rcpp_result_gen)) + throw Rcpp::LongjumpException(rcpp_result_gen); + if (rcpp_result_gen.inherits("try-error")) + throw Rcpp::exception(Rcpp::as(rcpp_result_gen).c_str()); + return Rcpp::as(rcpp_result_gen); + } + + inline Rcpp::List CPL_read_wkb2(Rcpp::List wkb_list, Rcpp::List options) { + typedef SEXP(*Ptr_CPL_read_wkb2)(SEXP,SEXP); + static Ptr_CPL_read_wkb2 p_CPL_read_wkb2 = NULL; + if (p_CPL_read_wkb2 == NULL) { + validateSignature("Rcpp::List(*CPL_read_wkb2)(Rcpp::List,Rcpp::List)"); + p_CPL_read_wkb2 = (Ptr_CPL_read_wkb2)R_GetCCallable("sf", "_sf_CPL_read_wkb2"); + } + RObject rcpp_result_gen; + { + RNGScope RCPP_rngScope_gen; + rcpp_result_gen = p_CPL_read_wkb2(Shield(Rcpp::wrap(wkb_list)), Shield(Rcpp::wrap(options))); } if (rcpp_result_gen.inherits("interrupted-error")) throw Rcpp::internal::InterruptedException(); diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 18331f4b2..abf19d937 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1343,23 +1343,57 @@ BEGIN_RCPP END_RCPP } // CPL_read_wkb -Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite, bool promote_multi); -static SEXP _sf_CPL_read_wkb_try(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP, SEXP promote_multiSEXP) { +Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite); +static SEXP _sf_CPL_read_wkb_try(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::traits::input_parameter< Rcpp::List >::type wkb_list(wkb_listSEXP); Rcpp::traits::input_parameter< bool >::type EWKB(EWKBSEXP); Rcpp::traits::input_parameter< bool >::type spatialite(spatialiteSEXP); - Rcpp::traits::input_parameter< bool >::type promote_multi(promote_multiSEXP); - rcpp_result_gen = Rcpp::wrap(CPL_read_wkb(wkb_list, EWKB, spatialite, promote_multi)); + rcpp_result_gen = Rcpp::wrap(CPL_read_wkb(wkb_list, EWKB, spatialite)); return rcpp_result_gen; END_RCPP_RETURN_ERROR } -RcppExport SEXP _sf_CPL_read_wkb(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP, SEXP promote_multiSEXP) { +RcppExport SEXP _sf_CPL_read_wkb(SEXP wkb_listSEXP, SEXP EWKBSEXP, SEXP spatialiteSEXP) { SEXP rcpp_result_gen; { Rcpp::RNGScope rcpp_rngScope_gen; - rcpp_result_gen = PROTECT(_sf_CPL_read_wkb_try(wkb_listSEXP, EWKBSEXP, spatialiteSEXP, promote_multiSEXP)); + rcpp_result_gen = PROTECT(_sf_CPL_read_wkb_try(wkb_listSEXP, EWKBSEXP, spatialiteSEXP)); + } + Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, "interrupted-error"); + if (rcpp_isInterrupt_gen) { + UNPROTECT(1); + Rf_onintr(); + } + bool rcpp_isLongjump_gen = Rcpp::internal::isLongjumpSentinel(rcpp_result_gen); + if (rcpp_isLongjump_gen) { + Rcpp::internal::resumeJump(rcpp_result_gen); + } + Rboolean rcpp_isError_gen = Rf_inherits(rcpp_result_gen, "try-error"); + if (rcpp_isError_gen) { + SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen); + UNPROTECT(1); + Rf_error("%s", CHAR(rcpp_msgSEXP_gen)); + } + UNPROTECT(1); + return rcpp_result_gen; +} +// CPL_read_wkb2 +Rcpp::List CPL_read_wkb2(Rcpp::List wkb_list, Rcpp::List options); +static SEXP _sf_CPL_read_wkb2_try(SEXP wkb_listSEXP, SEXP optionsSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::traits::input_parameter< Rcpp::List >::type wkb_list(wkb_listSEXP); + Rcpp::traits::input_parameter< Rcpp::List >::type options(optionsSEXP); + rcpp_result_gen = Rcpp::wrap(CPL_read_wkb2(wkb_list, options)); + return rcpp_result_gen; +END_RCPP_RETURN_ERROR +} +RcppExport SEXP _sf_CPL_read_wkb2(SEXP wkb_listSEXP, SEXP optionsSEXP) { + SEXP rcpp_result_gen; + { + Rcpp::RNGScope rcpp_rngScope_gen; + rcpp_result_gen = PROTECT(_sf_CPL_read_wkb2_try(wkb_listSEXP, optionsSEXP)); } Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, "interrupted-error"); if (rcpp_isInterrupt_gen) { @@ -1443,7 +1477,8 @@ END_RCPP static int _sf_RcppExport_validate(const char* sig) { static std::set signatures; if (signatures.empty()) { - signatures.insert("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool,bool)"); + signatures.insert("Rcpp::List(*CPL_read_wkb)(Rcpp::List,bool,bool)"); + signatures.insert("Rcpp::List(*CPL_read_wkb2)(Rcpp::List,Rcpp::List)"); signatures.insert("Rcpp::List(*CPL_write_wkb)(Rcpp::List,bool)"); } return signatures.find(sig) != signatures.end(); @@ -1452,6 +1487,7 @@ static int _sf_RcppExport_validate(const char* sig) { // registerCCallable (register entry points for exported C++ functions) RcppExport SEXP _sf_RcppExport_registerCCallable() { R_RegisterCCallable("sf", "_sf_CPL_read_wkb", (DL_FUNC)_sf_CPL_read_wkb_try); + R_RegisterCCallable("sf", "_sf_CPL_read_wkb2", (DL_FUNC)_sf_CPL_read_wkb2_try); R_RegisterCCallable("sf", "_sf_CPL_write_wkb", (DL_FUNC)_sf_CPL_write_wkb_try); R_RegisterCCallable("sf", "_sf_RcppExport_validate", (DL_FUNC)_sf_RcppExport_validate); return R_NilValue; @@ -1556,7 +1592,8 @@ static const R_CallMethodDef CallEntries[] = { {"_sf_CPL_write_gdal", (DL_FUNC) &_sf_CPL_write_gdal, 13}, {"_sf_CPL_extract", (DL_FUNC) &_sf_CPL_extract, 3}, {"_sf_CPL_create", (DL_FUNC) &_sf_CPL_create, 6}, - {"_sf_CPL_read_wkb", (DL_FUNC) &_sf_CPL_read_wkb, 4}, + {"_sf_CPL_read_wkb", (DL_FUNC) &_sf_CPL_read_wkb, 3}, + {"_sf_CPL_read_wkb2", (DL_FUNC) &_sf_CPL_read_wkb2, 2}, {"_sf_CPL_write_wkb", (DL_FUNC) &_sf_CPL_write_wkb, 2}, {"_sf_CPL_get_z_range", (DL_FUNC) &_sf_CPL_get_z_range, 2}, {"_sf_CPL_get_m_range", (DL_FUNC) &_sf_CPL_get_m_range, 2}, diff --git a/src/gdal.cpp b/src/gdal.cpp index f1afaabd0..d89026f56 100644 --- a/src/gdal.cpp +++ b/src/gdal.cpp @@ -497,7 +497,7 @@ Rcpp::List sfc_from_ogr(std::vector g, bool destroy = false) { if (destroy) OGRGeometryFactory::destroyGeometry(g[i]); } - Rcpp::List ret = CPL_read_wkb(lst, false, false, false); + Rcpp::List ret = CPL_read_wkb(lst, false, false); ret.attr("crs") = crs; ret.attr("class") = "sfc"; return ret; diff --git a/src/geos.cpp b/src/geos.cpp index f20b21a17..e9ff03a02 100644 --- a/src/geos.cpp +++ b/src/geos.cpp @@ -252,7 +252,7 @@ Rcpp::List sfc_from_geometry(GEOSContextHandle_t hGEOSCtxt, std::vector } } GEOSWKBWriter_destroy_r(hGEOSCtxt, wkb_writer); - return CPL_read_wkb(out, true, false, false); + return CPL_read_wkb(out, true, false); } Rcpp::NumericVector get_dim(double dim0, double dim1) { diff --git a/src/wkb.cpp b/src/wkb.cpp index f2e863641..d65fe8c8e 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -495,8 +495,7 @@ static void read_wkb_promote_multi_if_possible(Rcpp::List output, int64_t* all_t *all_types = sf_type_bitmask(sf_type_multi); } -// [[Rcpp::export]] -Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false, bool promote_multi = false) { +static Rcpp::List CPL_read_wkb_internal(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false, bool promote_multi = false) { Rcpp::List output(wkb_list.size()); int type = 0, n_empty = 0; @@ -540,6 +539,37 @@ Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite return output; } + +// This function is used by other packages that link to sf, so its signature cannot be changed +// [[Rcpp::export]] +Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) { + return CPL_read_wkb_internal(wkb_list, EWKB, spatialite); +} + + +// This version of the function is designed to allow evolution of options without +// breaking compatability with existing usage +// [[Rcpp::export]] +Rcpp::List CPL_read_wkb2(Rcpp::List wkb_list, Rcpp::List options) { + bool EWKB = false; + bool spatialite = false; + bool promote_to_multi = false; + + if (options.containsElementNamed("EWKB")) { + EWKB = options("EWKB"); + } + + if (options.containsElementNamed("spatialite")) { + spatialite = options("spatialite"); + } + + if (options.containsElementNamed("promote_to_multi")) { + promote_to_multi = options("promote_to_multi"); + } + + return CPL_read_wkb_internal(wkb_list, EWKB, spatialite, promote_to_multi); +} + // // write wkb: // diff --git a/src/wkb.h b/src/wkb.h index c1e682779..445f4ba6e 100644 --- a/src/wkb.h +++ b/src/wkb.h @@ -19,7 +19,7 @@ #define SF_Triangle 17 #define SF_Type_Max 17 -Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite, bool promote_multi); +Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB, bool spatialite); Rcpp::List CPL_write_wkb(Rcpp::List sfc, bool EWKB); unsigned int make_type(const char *cls, const char *dim, bool EWKB, int *tp, int srid); Rcpp::List get_dim_sfc(Rcpp::List sfc); From 5515f3c6c92d31d83af6e8a45d1a6a534fdbd3c1 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 16:30:28 +0000 Subject: [PATCH 08/12] whoops --- R/wkb.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/wkb.R b/R/wkb.R index 9116c8a8d..2d46b5b12 100644 --- a/R/wkb.R +++ b/R/wkb.R @@ -51,7 +51,7 @@ st_as_sfc.WKB = function(x, ..., EWKB = FALSE, spatialite = FALSE, pureR = FALSE R_read_wkb(x, readWKB, EWKB = EWKB) else CPL_read_wkb2(x, - list(EKWB = EWKB, + list(EWKB = EWKB, spatialite = spatialite, promote_to_multi = promote_to_multi)) if (is.na(crs) && (EWKB || spatialite) && !is.null(attr(ret, "srid")) && attr(ret, "srid") != 0) From bcb241d04fd8c9040dac2074394a26de46e792c1 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 16:55:31 +0000 Subject: [PATCH 09/12] add tests --- tests/testthat/test-wkb.R | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/testthat/test-wkb.R b/tests/testthat/test-wkb.R index a5b10ca65..cd9cdf52d 100644 --- a/tests/testthat/test-wkb.R +++ b/tests/testthat/test-wkb.R @@ -60,3 +60,69 @@ test_that("st_as_sfc() honors crs argument", { expect_identical(st_as_sfc(list, crs = 2056), st_as_sfc(wkb, crs = 2056)) expect_identical(st_as_sfc(blob, crs = 2056), st_as_sfc(wkb, crs = 2056)) }) + +test_that("st_as_sfc() for WKB can promote_to_multi for multipoint + point", { + sfc_mixed = st_sfc( + st_point(), + st_point(c(2, 3)), + st_multipoint() + ) + + wkb_mixed = st_as_binary(sfc_mixed) + + expect_identical( + st_as_sfc(wkb_mixed, promote_to_multi = FALSE), + sfc_mixed + ) + + expect_identical( + st_as_sfc(wkb_mixed, promote_to_multi = TRUE), + st_cast(sfc_mixed, "MULTIPOINT") + ) +}) + +test_that("st_as_sfc() for WKB can promote_to_multi for multilinestring + linestring", { + sfc_mixed = st_sfc( + st_linestring(), + st_linestring(rbind(c(2, 3), c(4, 5))), + st_multilinestring() + ) + + wkb_mixed = st_as_binary(sfc_mixed) + + expect_identical( + st_as_sfc(wkb_mixed, promote_to_multi = FALSE), + sfc_mixed + ) + + # st_as_sfc() assigns different attribute order than st_cast, but we ony care + # about whether the geometries are correct + expect_equal( + st_as_sfc(wkb_mixed, promote_to_multi = TRUE), + st_cast(sfc_mixed, "MULTILINESTRING"), + ignore_attr = TRUE + ) +}) + +test_that("st_as_sfc() for WKB can promote_to_multi for multipolygon + polygon", { + sfc_mixed = st_sfc( + st_polygon(), + st_polygon(list(rbind(c(0, 0), c(1, 0), c(0, 1), c(0, 0)))), + st_multipolygon() + ) + + wkb_mixed = st_as_binary(sfc_mixed) + + expect_identical( + st_as_sfc(wkb_mixed, promote_to_multi = FALSE), + sfc_mixed + ) + + # st_as_sfc() assigns different attribute order than st_cast, but we ony care + # about whether the geometries are correct + expect_equal( + st_as_sfc(wkb_mixed, promote_to_multi = TRUE), + st_cast(sfc_mixed, "MULTILINESTRING"), + ignore_attr = TRUE + ) +}) From 600564bb58e5813149ebfd3bad647a93d58ea5d0 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 17:34:54 +0000 Subject: [PATCH 10/12] maybe fix check --- tests/testthat/test-wkb.R | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-wkb.R b/tests/testthat/test-wkb.R index cd9cdf52d..ef9b02127 100644 --- a/tests/testthat/test-wkb.R +++ b/tests/testthat/test-wkb.R @@ -97,10 +97,9 @@ test_that("st_as_sfc() for WKB can promote_to_multi for multilinestring + linest # st_as_sfc() assigns different attribute order than st_cast, but we ony care # about whether the geometries are correct - expect_equal( + expect_equivalent( st_as_sfc(wkb_mixed, promote_to_multi = TRUE), - st_cast(sfc_mixed, "MULTILINESTRING"), - ignore_attr = TRUE + st_cast(sfc_mixed, "MULTILINESTRING") ) }) @@ -120,9 +119,8 @@ test_that("st_as_sfc() for WKB can promote_to_multi for multipolygon + polygon", # st_as_sfc() assigns different attribute order than st_cast, but we ony care # about whether the geometries are correct - expect_equal( + expect_equivalent( st_as_sfc(wkb_mixed, promote_to_multi = TRUE), - st_cast(sfc_mixed, "MULTILINESTRING"), - ignore_attr = TRUE + st_cast(sfc_mixed, "MULTILINESTRING") ) }) From 39cb85c8b8c0d92db8f9bfa96a726c0c7ebfbee9 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 17:52:25 +0000 Subject: [PATCH 11/12] whoops --- tests/testthat/test-wkb.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-wkb.R b/tests/testthat/test-wkb.R index ef9b02127..87dfc9d04 100644 --- a/tests/testthat/test-wkb.R +++ b/tests/testthat/test-wkb.R @@ -121,6 +121,6 @@ test_that("st_as_sfc() for WKB can promote_to_multi for multipolygon + polygon", # about whether the geometries are correct expect_equivalent( st_as_sfc(wkb_mixed, promote_to_multi = TRUE), - st_cast(sfc_mixed, "MULTILINESTRING") + st_cast(sfc_mixed, "MULTIPOLYGON") ) }) From 99ca07221b81ba2b6239ac69970ede6f38a4e434 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 31 Mar 2024 18:19:39 +0000 Subject: [PATCH 12/12] add doc --- R/wkb.R | 2 ++ man/st_as_sfc.Rd | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/R/wkb.R b/R/wkb.R index 2d46b5b12..bfa3dcab6 100644 --- a/R/wkb.R +++ b/R/wkb.R @@ -25,6 +25,8 @@ skip0x = function(x) { #' @param EWKB logical; if `TRUE`, parse as EWKB (extended WKB; PostGIS: ST_AsEWKB), otherwise as ISO WKB (PostGIS: ST_AsBinary) #' @param spatialite logical; if \code{TRUE}, WKB is assumed to be in the spatialite dialect, see \url{https://www.gaia-gis.it/gaia-sins/BLOB-Geometry.html}; this is only supported in native endian-ness (i.e., files written on system with the same endian-ness as that on which it is being read). #' @param pureR logical; if `TRUE`, use only R code, if `FALSE`, use compiled (C++) code; use `TRUE` when the endian-ness of the binary differs from the host machine (\code{.Platform$endian}). +#' @param promote_to_multi logical; if `TRUE`, attempt to promote combinations of simple/multi +#' such that all output geometries are multi geometries of the same type. #' @details When converting from WKB, the object \code{x} is either a character vector such as typically obtained from PostGIS (either with leading "0x" or without), or a list with raw vectors representing the features in binary (raw) form. #' @examples #' wkb = structure(list("01010000204071000000000000801A064100000000AC5C1441"), class = "WKB") diff --git a/man/st_as_sfc.Rd b/man/st_as_sfc.Rd index 6b71a53b3..71a923925 100644 --- a/man/st_as_sfc.Rd +++ b/man/st_as_sfc.Rd @@ -41,7 +41,8 @@ EWKB = FALSE, spatialite = FALSE, pureR = FALSE, - crs = NA_crs_ + crs = NA_crs_, + promote_to_multi = FALSE ) \method{st_as_sfc}{raw}(x, ...) @@ -84,6 +85,9 @@ st_as_sfc(x, ...) \item{crs}{coordinate reference system to be assigned; object of class \code{crs}} +\item{promote_to_multi}{logical; if \code{TRUE}, attempt to promote combinations of simple/multi +such that all output geometries are multi geometries of the same type.} + \item{GeoJSON}{logical; if \code{TRUE}, try to read geometries from GeoJSON text strings geometry, see \code{\link[=st_crs]{st_crs()}}}