From ae9635663846c9b12440af9aa48d4d1f2802f114 Mon Sep 17 00:00:00 2001 From: David Cooley Date: Tue, 24 Apr 2018 15:33:50 +1000 Subject: [PATCH 1/7] unnest gc notes --- src/geojson_to_sf.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index 5828634..e65588b 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -96,6 +96,11 @@ Rcpp::List parse_feature_object(const Value& feature, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types) { + // TODO: + // unnest GEOMETRYCOLLECTION? + // - replicate the 'properties' by length(GEOMETRYCOLLECTION) + // - dont' 'parse_geometry_collection_objet', and instead iterate through + // 'parse_geometry_object' validate_geometry(feature, sfg_objects); validate_properties(feature, sfg_objects); @@ -137,8 +142,13 @@ Rcpp::List parse_feature_object(const Value& feature, geometry_types.insert("POLYGON"); } + // TODO: + // if 'unnest GEOMETRYCOLLECTION', increment this by the number of internal geometries + // chuck these steps in a loop, from (i = 0; i < gc.size() || 1; i++) sfg_objects++; + // TODO: + // if 'unnest GEOMETRYCOLLECTION', replicate these steps by teh number of internal geometries const Value& p = feature["properties"]; get_property_keys(p, property_keys); get_property_types(p, property_types); From 8eae3b6b216c7a9f4fe0e454c788ca4ff6c3ef02 Mon Sep 17 00:00:00 2001 From: David Cooley Date: Tue, 24 Apr 2018 15:56:37 +1000 Subject: [PATCH 2/7] prototype unnest --- R/scratch.R | 10 +++++- src/geojson_to_sf.cpp | 39 +++++++++++++++++++----- tests/testthat/test-geojson_properties.R | 1 - 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/R/scratch.R b/R/scratch.R index 1c9bbf6..0bfe7a7 100644 --- a/R/scratch.R +++ b/R/scratch.R @@ -8,4 +8,12 @@ # sf <- sf::st_read(url, quiet = T) # sf2 <- geojson_sf(url) - +# js <- '{"type":"Feature","properties":{"id":1},"geometry":{ +# "type": "GeometryCollection", +# "geometries": [ +# {"type": "Point","coordinates": [100.0, 0.0]}, +# {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, +# {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]} +# ]}}' +# +# geojson_sf(js) diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index e65588b..9ee2076 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -21,6 +21,8 @@ void parse_geometry_object(Rcpp::List& sfc, std::set< std::string >& geometry_types, int& sfg_objects) { + Rcpp::Rcout << "debug: parse_geometry_object" << std::endl; + validate_type(geometry, sfg_objects); validate_coordinates(geometry, sfg_objects); validate_array(geometry["coordinates"], sfg_objects); @@ -101,24 +103,47 @@ Rcpp::List parse_feature_object(const Value& feature, // - replicate the 'properties' by length(GEOMETRYCOLLECTION) // - dont' 'parse_geometry_collection_objet', and instead iterate through // 'parse_geometry_object' - validate_geometry(feature, sfg_objects); validate_properties(feature, sfg_objects); const Value& geometry = feature["geometry"]; //validate_type(geometry, sfg_objects); //std::string type = geometry["type"].GetString(); - Rcpp::List sfc(1); + //Rcpp::List sfc(1); + + bool unnest = true; + validate_geometries(geometry, sfg_objects); + auto geometries = geometry["geometries"].GetArray(); + unsigned int geomsize = geometries.Size(); + unsigned int i; + Rcpp::List sfc(geomsize); if (geometry.Size() > 0) { validate_type(geometry, sfg_objects); std::string type = geometry["type"].GetString(); - if (type == "GeometryCollection") { + if (type == "GeometryCollection" && !unnest) { + Rcpp::Rcout << "debug: not unnesting" << std::endl; sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects); } else { - parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); + + if (type == "GeometryCollection") { + + Rcpp::Rcout << "debug: unnesting" << std::endl; + Rcpp::Rcout << "debug: geomsize: " << geomsize << std::endl; + std::string geom_type; + for (i = 0; i < geomsize; i++) { + const Value& gcval = geometries[i]; + validate_type(gcval, sfg_objects); + geom_type = gcval["type"].GetString(); + parse_geometry_object(sfc, i, gcval, bbox, geometry_types, sfg_objects); + } + + } else { + parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); + } + } } else { // TODO: @@ -145,10 +170,10 @@ Rcpp::List parse_feature_object(const Value& feature, // TODO: // if 'unnest GEOMETRYCOLLECTION', increment this by the number of internal geometries // chuck these steps in a loop, from (i = 0; i < gc.size() || 1; i++) - sfg_objects++; + sfg_objects += geomsize; + + Rcpp::Rcout << "debug: parsing properties" << std::endl; - // TODO: - // if 'unnest GEOMETRYCOLLECTION', replicate these steps by teh number of internal geometries const Value& p = feature["properties"]; get_property_keys(p, property_keys); get_property_types(p, property_types); diff --git a/tests/testthat/test-geojson_properties.R b/tests/testthat/test-geojson_properties.R index 9bb80ed..25860f3 100644 --- a/tests/testthat/test-geojson_properties.R +++ b/tests/testthat/test-geojson_properties.R @@ -139,7 +139,6 @@ test_that("null geometries are valid for features", { ## null geometries that aren't part of features should still error js <- '{"type":"Point","coordinates":null}' expect_error(geojson_sf(js), "No 'array' member at object index 0 - invalid GeoJSON") - }) From 02eb24fdb28bdc9cf26408adead01773d231fd81 Mon Sep 17 00:00:00 2001 From: symbolixau Date: Tue, 24 Apr 2018 20:19:10 +1000 Subject: [PATCH 3/7] unnest --- DESCRIPTION | 2 +- R/RcppExports.R | 16 +-- R/geojson_sf.R | 30 ++--- R/scratch.R | 10 +- inst/include/geojson_to_sf.h | 48 ++++--- man/geojson_sf.Rd | 4 +- man/geojson_sfc.Rd | 4 +- src/RcppExports.cpp | 36 +++--- src/geojson_to_sf.cpp | 156 +++++++++++++---------- src/read_geojson.cpp | 13 +- tests/testthat/test-geometrycollection.R | 61 +++++++++ 11 files changed, 230 insertions(+), 150 deletions(-) create mode 100644 tests/testthat/test-geometrycollection.R diff --git a/DESCRIPTION b/DESCRIPTION index 0acb507..5ff8dc5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: geojsonsf Type: Package Title: GeoJSON to Simple Feature Converter -Version: 0.3.0009 +Version: 0.3.0010 Authors@R: c( person("David", "Cooley", ,"dcooley@symbolix.com.au", role = c("aut", "cre")) ) diff --git a/R/RcppExports.R b/R/RcppExports.R index 2f286eb..44f859d 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -1,24 +1,24 @@ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 -rcpp_geojson_to_sfc <- function(geojson) { - .Call(`_geojsonsf_rcpp_geojson_to_sfc`, geojson) +rcpp_geojson_to_sfc <- function(geojson, unnest) { + .Call(`_geojsonsf_rcpp_geojson_to_sfc`, geojson, unnest) } -rcpp_geojson_to_sf <- function(geojson) { - .Call(`_geojsonsf_rcpp_geojson_to_sf`, geojson) +rcpp_geojson_to_sf <- function(geojson, unnest) { + .Call(`_geojsonsf_rcpp_geojson_to_sf`, geojson, unnest) } rcpp_geojson_to_wkt <- function(geojson) { .Call(`_geojsonsf_rcpp_geojson_to_wkt`, geojson) } -rcpp_read_sfc_file <- function(file) { - .Call(`_geojsonsf_rcpp_read_sfc_file`, file) +rcpp_read_sfc_file <- function(file, unnest) { + .Call(`_geojsonsf_rcpp_read_sfc_file`, file, unnest) } -rcpp_read_sf_file <- function(file) { - .Call(`_geojsonsf_rcpp_read_sf_file`, file) +rcpp_read_sf_file <- function(file, unnest) { + .Call(`_geojsonsf_rcpp_read_sf_file`, file, unnest) } rcpp_sfc_to_geojson <- function(sfc) { diff --git a/R/geojson_sf.R b/R/geojson_sf.R index 6dd136b..c63a3ff 100644 --- a/R/geojson_sf.R +++ b/R/geojson_sf.R @@ -3,7 +3,7 @@ #' Extracts geometries from GeoJSON and returns an `sfc` object #' #' @param geojson string or vector of GeoJSON, or a URL or file pointing to a geojson file -#' +#' @param unnest logical indicating whether to unnest GEOMETRYCOLLECTION rows #' @examples #' #' ## character string of GeoJSON @@ -20,28 +20,28 @@ #'} #' #' @export -geojson_sfc <- function(geojson) UseMethod("geojson_sfc") +geojson_sfc <- function(geojson, unnest = FALSE) UseMethod("geojson_sfc") #' @export -geojson_sfc.character <- function(geojson) { +geojson_sfc.character <- function(geojson, unnest = FALSE) { if(length(geojson) > 1) { - return(rcpp_geojson_to_sfc(geojson)) + return(rcpp_geojson_to_sfc(geojson, unnest)) } if (is_url(geojson)) { return(geojson_sfc(curl::curl(geojson))) } else if (file.exists(geojson) ) { - return(rcpp_read_sfc_file(normalizePath(geojson))) + return(rcpp_read_sfc_file(normalizePath(geojson), unnest)) } - return(rcpp_geojson_to_sfc(geojson)) + return(rcpp_geojson_to_sfc(geojson, unnest)) } #' @export -geojson_sfc.connection <- function(geojson) geojson_sfc(read_url(geojson)) +geojson_sfc.connection <- function(geojson, unnest = FALSE) geojson_sfc(read_url(geojson), unnest) #' @export -geojson_sfc.default <- function(geojson) rcpp_geojson_to_sfc(geojson) +geojson_sfc.default <- function(geojson, unnest = FALSE) rcpp_geojson_to_sfc(geojson, unnest) #' Geojson to sf #' @@ -65,28 +65,28 @@ geojson_sfc.default <- function(geojson) rcpp_geojson_to_sfc(geojson) #' #' @inheritParams geojson_sfc #' @export -geojson_sf <- function(geojson) UseMethod("geojson_sf") +geojson_sf <- function(geojson, unnest = FALSE) UseMethod("geojson_sf") #' @export -geojson_sf.character <- function(geojson) { +geojson_sf.character <- function(geojson, unnest = FALSE) { if(length(geojson) > 1) { - return(rcpp_geojson_to_sf(geojson)) + return(rcpp_geojson_to_sf(geojson, unnest)) } if (is_url(geojson)) { return(geojson_sf(curl::curl(geojson))) } else if (file.exists(geojson) ) { - return(rcpp_read_sf_file(normalizePath(geojson))) + return(rcpp_read_sf_file(normalizePath(geojson), unnest)) } - return(rcpp_geojson_to_sf(geojson)) + return(rcpp_geojson_to_sf(geojson, unnest)) } #' @export -geojson_sf.connection <- function(geojson) geojson_sf(read_url(geojson)) +geojson_sf.connection <- function(geojson) geojson_sf(read_url(geojson), unnest) #' @export -geojson_sf.default <- function(geojson) rcpp_geojson_to_sf(geojson) +geojson_sf.default <- function(geojson) rcpp_geojson_to_sf(geojson, unnest) ## TODO: diff --git a/R/scratch.R b/R/scratch.R index 0bfe7a7..1f98949 100644 --- a/R/scratch.R +++ b/R/scratch.R @@ -7,13 +7,5 @@ # url <- "https://data.seattle.gov/resource/pdbw-sw7q.geojson" # sf <- sf::st_read(url, quiet = T) # sf2 <- geojson_sf(url) - -# js <- '{"type":"Feature","properties":{"id":1},"geometry":{ -# "type": "GeometryCollection", -# "geometries": [ -# {"type": "Point","coordinates": [100.0, 0.0]}, -# {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, -# {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]} -# ]}}' # -# geojson_sf(js) + diff --git a/inst/include/geojson_to_sf.h b/inst/include/geojson_to_sf.h index 310fc76..17da318 100644 --- a/inst/include/geojson_to_sf.h +++ b/inst/include/geojson_to_sf.h @@ -14,9 +14,11 @@ void parse_geometry_object(Rcpp::List& sfc, int i, const Value& geometry, std::set< std::string >& geometry_types, int& sfg_objects); -Rcpp::List parse_geometry_collection_object(const Value& val, Rcpp::NumericVector& bbox, +Rcpp::List parse_geometry_collection_object(const Value& val, + Rcpp::NumericVector& bbox, std::set< std::string >& geometry_types, - int& sfg_objects); + int& sfg_objects, + bool& unnest); Rcpp::List parse_feature_object(const Value& feature, Rcpp::NumericVector& bbox, @@ -24,7 +26,8 @@ Rcpp::List parse_feature_object(const Value& feature, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types); + std::map< std::string, std::string>& property_types, + bool& unnest); Rcpp::List parse_feature_collection_object(const Value& fc, Rcpp::NumericVector& bbox, @@ -32,7 +35,20 @@ Rcpp::List parse_feature_collection_object(const Value& fc, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types); + std::map< std::string, std::string>& property_types, + bool& unnest); + +void parse_geojson(const Value& v, + Rcpp::List& sfc, + Rcpp::List& properties, + int i, + Rcpp::NumericVector& bbox, + std::set< std::string >& geometry_types, + int& sfg_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string>& property_types, + bool& unnest); void parse_geojson_array(Document& d, Rcpp::List& sfc, @@ -43,7 +59,8 @@ void parse_geojson_array(Document& d, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types); + std::map< std::string, std::string>& property_types, + bool& unnest); void parse_geojson_object(Document& d, Rcpp::List& sfc, @@ -53,25 +70,16 @@ void parse_geojson_object(Document& d, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types); - -void parse_geojson(const Value& v, - Rcpp::List& sfc, - Rcpp::List& properties, - int i, - Rcpp::NumericVector& bbox, - std::set< std::string >& geometry_types, - int& sfg_objects, - std::set< std::string >& property_keys, - Document& doc_properties, - std::map< std::string, std::string>& property_types); + std::map< std::string, std::string>& property_types, + bool& unnest); Rcpp::List geojson_to_sf(const char* geojson, Rcpp::NumericVector& bbox, std::set< std::string >& geometry_types, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types); + std::map< std::string, std::string>& property_types, + bool& unnest); void setup_property_vectors(std::map< std::string, std::string>& property_types, Rcpp::List& properties, int& sfg_objects); @@ -87,8 +95,8 @@ Rcpp::List construct_sf(Rcpp::List& lst, std::set< std::string >& property_keys, int& sfg_objects, int& row_index); -Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson); +Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& unnest); -Rcpp::List create_sfc(Rcpp::StringVector geojson); +Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& unnest); #endif diff --git a/man/geojson_sf.Rd b/man/geojson_sf.Rd index c423f54..7604ed8 100644 --- a/man/geojson_sf.Rd +++ b/man/geojson_sf.Rd @@ -4,10 +4,12 @@ \alias{geojson_sf} \title{Geojson to sf} \usage{ -geojson_sf(geojson) +geojson_sf(geojson, unnest = FALSE) } \arguments{ \item{geojson}{string or vector of GeoJSON, or a URL or file pointing to a geojson file} + +\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows} } \description{ Converts GeoJSON to an `sf` object diff --git a/man/geojson_sfc.Rd b/man/geojson_sfc.Rd index 76af922..1999b60 100644 --- a/man/geojson_sfc.Rd +++ b/man/geojson_sfc.Rd @@ -4,10 +4,12 @@ \alias{geojson_sfc} \title{Geojson to sfc} \usage{ -geojson_sfc(geojson) +geojson_sfc(geojson, unnest = FALSE) } \arguments{ \item{geojson}{string or vector of GeoJSON, or a URL or file pointing to a geojson file} + +\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows} } \description{ Extracts geometries from GeoJSON and returns an `sfc` object diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 5c259e5..9f24765 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -7,24 +7,26 @@ using namespace Rcpp; // rcpp_geojson_to_sfc -Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson); -RcppExport SEXP _geojsonsf_rcpp_geojson_to_sfc(SEXP geojsonSEXP) { +Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& unnest); +RcppExport SEXP _geojsonsf_rcpp_geojson_to_sfc(SEXP geojsonSEXP, SEXP unnestSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::StringVector >::type geojson(geojsonSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sfc(geojson)); + Rcpp::traits::input_parameter< bool& >::type unnest(unnestSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sfc(geojson, unnest)); return rcpp_result_gen; END_RCPP } // rcpp_geojson_to_sf -Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson); -RcppExport SEXP _geojsonsf_rcpp_geojson_to_sf(SEXP geojsonSEXP) { +Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson, bool unnest); +RcppExport SEXP _geojsonsf_rcpp_geojson_to_sf(SEXP geojsonSEXP, SEXP unnestSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::StringVector >::type geojson(geojsonSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sf(geojson)); + Rcpp::traits::input_parameter< bool >::type unnest(unnestSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sf(geojson, unnest)); return rcpp_result_gen; END_RCPP } @@ -40,24 +42,26 @@ BEGIN_RCPP END_RCPP } // rcpp_read_sfc_file -Rcpp::List rcpp_read_sfc_file(std::string file); -RcppExport SEXP _geojsonsf_rcpp_read_sfc_file(SEXP fileSEXP) { +Rcpp::List rcpp_read_sfc_file(std::string file, bool unnest); +RcppExport SEXP _geojsonsf_rcpp_read_sfc_file(SEXP fileSEXP, SEXP unnestSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::string >::type file(fileSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_read_sfc_file(file)); + Rcpp::traits::input_parameter< bool >::type unnest(unnestSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_read_sfc_file(file, unnest)); return rcpp_result_gen; END_RCPP } // rcpp_read_sf_file -Rcpp::List rcpp_read_sf_file(std::string file); -RcppExport SEXP _geojsonsf_rcpp_read_sf_file(SEXP fileSEXP) { +Rcpp::List rcpp_read_sf_file(std::string file, bool unnest); +RcppExport SEXP _geojsonsf_rcpp_read_sf_file(SEXP fileSEXP, SEXP unnestSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::string >::type file(fileSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_read_sf_file(file)); + Rcpp::traits::input_parameter< bool >::type unnest(unnestSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_read_sf_file(file, unnest)); return rcpp_result_gen; END_RCPP } @@ -86,11 +90,11 @@ END_RCPP } static const R_CallMethodDef CallEntries[] = { - {"_geojsonsf_rcpp_geojson_to_sfc", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sfc, 1}, - {"_geojsonsf_rcpp_geojson_to_sf", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sf, 1}, + {"_geojsonsf_rcpp_geojson_to_sfc", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sfc, 2}, + {"_geojsonsf_rcpp_geojson_to_sf", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sf, 2}, {"_geojsonsf_rcpp_geojson_to_wkt", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_wkt, 1}, - {"_geojsonsf_rcpp_read_sfc_file", (DL_FUNC) &_geojsonsf_rcpp_read_sfc_file, 1}, - {"_geojsonsf_rcpp_read_sf_file", (DL_FUNC) &_geojsonsf_rcpp_read_sf_file, 1}, + {"_geojsonsf_rcpp_read_sfc_file", (DL_FUNC) &_geojsonsf_rcpp_read_sfc_file, 2}, + {"_geojsonsf_rcpp_read_sf_file", (DL_FUNC) &_geojsonsf_rcpp_read_sf_file, 2}, {"_geojsonsf_rcpp_sfc_to_geojson", (DL_FUNC) &_geojsonsf_rcpp_sfc_to_geojson, 1}, {"_geojsonsf_rcpp_sf_to_geojson", (DL_FUNC) &_geojsonsf_rcpp_sf_to_geojson, 2}, {NULL, NULL, 0} diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index 9ee2076..ca43f2d 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -21,8 +21,6 @@ void parse_geometry_object(Rcpp::List& sfc, std::set< std::string >& geometry_types, int& sfg_objects) { - Rcpp::Rcout << "debug: parse_geometry_object" << std::endl; - validate_type(geometry, sfg_objects); validate_coordinates(geometry, sfg_objects); validate_array(geometry["coordinates"], sfg_objects); @@ -70,9 +68,9 @@ void parse_geometry_object(Rcpp::List& sfc, Rcpp::List parse_geometry_collection_object(const Value& val, Rcpp::NumericVector& bbox, std::set< std::string >& geometry_types, - int& sfg_objects) { + int& sfg_objects, + bool& unnest) { std::string geom_type; - validate_geometries(val, sfg_objects); auto geometries = val["geometries"].GetArray(); unsigned int n = geometries.Size(); @@ -86,8 +84,14 @@ Rcpp::List parse_geometry_collection_object(const Value& val, geom_type = gcval["type"].GetString(); parse_geometry_object(geom_collection, i, gcval, bbox, geometry_types, sfg_objects); } - geom_collection.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); + if (!unnest) { + //sfg_objects++; + geom_collection.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); + } else { + sfg_objects+=n; + Rcpp::Rcout << "debug: unnest gc sfg_objects " << sfg_objects << std::endl; + } return geom_collection; } @@ -97,53 +101,28 @@ Rcpp::List parse_feature_object(const Value& feature, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types) { - // TODO: - // unnest GEOMETRYCOLLECTION? - // - replicate the 'properties' by length(GEOMETRYCOLLECTION) - // - dont' 'parse_geometry_collection_objet', and instead iterate through - // 'parse_geometry_object' + std::map< std::string, std::string>& property_types, + bool& unnest) { + validate_geometry(feature, sfg_objects); validate_properties(feature, sfg_objects); const Value& geometry = feature["geometry"]; //validate_type(geometry, sfg_objects); //std::string type = geometry["type"].GetString(); - //Rcpp::List sfc(1); - - bool unnest = true; - validate_geometries(geometry, sfg_objects); - auto geometries = geometry["geometries"].GetArray(); - unsigned int geomsize = geometries.Size(); - unsigned int i; - Rcpp::List sfc(geomsize); + Rcpp::List sfc(1); + std::string type; if (geometry.Size() > 0) { validate_type(geometry, sfg_objects); - std::string type = geometry["type"].GetString(); + type = geometry["type"].GetString(); + Rcpp::Rcout << "debug: type = " << type << std::endl; - if (type == "GeometryCollection" && !unnest) { - Rcpp::Rcout << "debug: not unnesting" << std::endl; - sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects); + if (type == "GeometryCollection") { + sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects, unnest); } else { - - if (type == "GeometryCollection") { - - Rcpp::Rcout << "debug: unnesting" << std::endl; - Rcpp::Rcout << "debug: geomsize: " << geomsize << std::endl; - std::string geom_type; - for (i = 0; i < geomsize; i++) { - const Value& gcval = geometries[i]; - validate_type(gcval, sfg_objects); - geom_type = gcval["type"].GetString(); - parse_geometry_object(sfc, i, gcval, bbox, geometry_types, sfg_objects); - } - - } else { - parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); - } - + parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); } } else { // TODO: @@ -170,21 +149,47 @@ Rcpp::List parse_feature_object(const Value& feature, // TODO: // if 'unnest GEOMETRYCOLLECTION', increment this by the number of internal geometries // chuck these steps in a loop, from (i = 0; i < gc.size() || 1; i++) - sfg_objects += geomsize; - Rcpp::Rcout << "debug: parsing properties" << std::endl; + // IFF GEOMETRYCOLLECTION && UNNEST, we've already added on the objects + Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; + if (type != "GeometryCollection") { + Rcpp::Rcout << "debug: type != GC "<< std::endl; + sfg_objects++; + } else if (type == "GeometryCollection" && !unnest){ + Rcpp::Rcout << "debug: type == GC && !unnest "<< std::endl; + sfg_objects++; + } + Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; const Value& p = feature["properties"]; get_property_keys(p, property_keys); get_property_types(p, property_types); - //https://stackoverflow.com/a/33473321/5977215 - std::string s = std::to_string(sfg_objects); - Value n(s.c_str(), doc_properties.GetAllocator()); + // TODO: + // - if unnesting GEOMETRYCOLLECTION need to expand the properties by the number of geometries. - // TODO: is this method deep-cloning? - Value properties(feature["properties"], doc_properties.GetAllocator()); - doc_properties.AddMember(n, properties, doc_properties.GetAllocator()); + unsigned int geomsize = 1; + unsigned int i; + if (unnest && type == "GeometryCollection") { + validate_geometries(geometry, sfg_objects); + auto geometries = geometry["geometries"].GetArray(); + geomsize = geometries.Size(); + } + + std::string s; + for (i = 0; i < geomsize; i++) { + //https://stackoverflow.com/a/33473321/5977215 + if (unnest) { + s = std::to_string(sfg_objects-i); + } else { + s = std::to_string(sfg_objects); + } + Value n(s.c_str(), doc_properties.GetAllocator()); + + // TODO: is this method deep-cloning? + Value properties(feature["properties"], doc_properties.GetAllocator()); + doc_properties.AddMember(n, properties, doc_properties.GetAllocator()); + } return sfc; } @@ -195,7 +200,8 @@ Rcpp::List parse_feature_collection_object(const Value& fc, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types) { + std::map< std::string, std::string>& property_types, + bool& unnest) { // a FeatureCollection MUST have members (array) called features, validate_features(fc, sfg_objects); @@ -208,7 +214,10 @@ Rcpp::List parse_feature_collection_object(const Value& fc, for (i = 0; i < n; i++) { const Value& feature = features[i]; - feature_collection[i] = parse_feature_object(feature, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + feature_collection[i] = parse_feature_object( + feature, bbox, geometry_types, sfg_objects, property_keys, doc_properties, + property_types, unnest + ); } return feature_collection; } @@ -224,7 +233,8 @@ void parse_geojson(const Value& v, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types) { + std::map< std::string, std::string>& property_types, + bool& unnest) { Rcpp::List res(1); validate_type(v, sfg_objects); @@ -232,18 +242,21 @@ void parse_geojson(const Value& v, std::string geom_type = v["type"].GetString(); if (geom_type == "Feature") { - res = parse_feature_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + res = parse_feature_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); sfc[i] = res; } else if (geom_type == "FeatureCollection") { - res = parse_feature_collection_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + res = parse_feature_collection_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); sfc[i] = res; } else if (geom_type == "GeometryCollection") { - res = parse_geometry_collection_object(v, bbox, geometry_types, sfg_objects); - sfg_objects++; + res = parse_geometry_collection_object(v, bbox, geometry_types, sfg_objects, unnest); + if (!unnest) { + sfg_objects++; + } + Rcpp::Rcout << "debug: gc unnest sfg_objects2 : " << sfg_objects << std::endl; sfc[i] = res; } else { @@ -261,9 +274,10 @@ void parse_geojson_object(Document& d, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types) { + std::map< std::string, std::string>& property_types, + bool& unnest) { const Value& v = d; - parse_geojson(v, sfc, properties, 0, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + parse_geojson(v, sfc, properties, 0, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); } void parse_geojson_array(Document& d, @@ -275,9 +289,10 @@ void parse_geojson_array(Document& d, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types) { + std::map< std::string, std::string>& property_types, + bool& unnest) { const Value& v = d[i]; - parse_geojson(v, sfc, properties, i, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + parse_geojson(v, sfc, properties, i, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); } Rcpp::List geojson_to_sf(const char* geojson, @@ -286,7 +301,8 @@ Rcpp::List geojson_to_sf(const char* geojson, int& sfg_objects, std::set< std::string >& property_keys, Document& doc_properties, - std::map< std::string, std::string>& property_types) { + std::map< std::string, std::string>& property_types, + bool& unnest) { Document d; safe_parse(d, geojson); @@ -297,7 +313,7 @@ Rcpp::List geojson_to_sf(const char* geojson, if (d.IsObject()) { Rcpp::List sfg(1); - parse_geojson_object(d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + parse_geojson_object(d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); sfc[0] = sfg; } else if (d.IsArray()) { @@ -305,7 +321,7 @@ Rcpp::List geojson_to_sf(const char* geojson, Rcpp::List sfgs(d.Size()); for (doc_ele = 0; doc_ele < d.Size(); doc_ele++) { - parse_geojson_array(d, sfgs, properties, doc_ele, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + parse_geojson_array(d, sfgs, properties, doc_ele, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); } sfc[0] = sfgs; } @@ -432,7 +448,7 @@ void fill_property_vectors(Document& doc_properties, } } -Rcpp::List create_sfc(Rcpp::StringVector geojson) { +Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& unnest) { // iterate over the geojson int n = geojson.size(); int sfg_objects = 0; // keep track of number of objects @@ -449,15 +465,15 @@ Rcpp::List create_sfc(Rcpp::StringVector geojson) { Rcpp::List sfc(n); for (int geo_ele = 0; geo_ele < n; geo_ele++ ){ - sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); } return construct_sfc(sfg_objects, sfc, bbox, geometry_types); } // [[Rcpp::export]] -Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson) { - return create_sfc(geojson); +Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& unnest) { + return create_sfc(geojson, unnest); } Rcpp::List construct_sf(Rcpp::List& lst, std::set< std::string >& property_keys, @@ -483,7 +499,7 @@ Rcpp::List construct_sf(Rcpp::List& lst, std::set< std::string >& property_keys, return properties; } -Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson) { +Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& unnest) { // iterate over the geojson int n = geojson.size(); int sfg_objects = 0; // keep track of number of objects @@ -500,7 +516,7 @@ Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson) { Rcpp::List sfc(n); for (int geo_ele = 0; geo_ele < n; geo_ele++ ){ - sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); + sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); } @@ -509,6 +525,6 @@ Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson) { } // [[Rcpp::export]] -Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson) { - return generic_geojson_to_sf(geojson); +Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson, bool unnest) { + return generic_geojson_to_sf(geojson, unnest); } diff --git a/src/read_geojson.cpp b/src/read_geojson.cpp index 01df60e..d9e47eb 100644 --- a/src/read_geojson.cpp +++ b/src/read_geojson.cpp @@ -20,18 +20,13 @@ Rcpp::StringVector buffer_string(std::string file) { } // [[Rcpp::export]] -Rcpp::List rcpp_read_sfc_file(std::string file) { - return create_sfc(buffer_string(file)); +Rcpp::List rcpp_read_sfc_file(std::string file, bool unnest) { + return create_sfc(buffer_string(file), unnest); } // [[Rcpp::export]] -Rcpp::List rcpp_read_sf_file(std::string file) { - return generic_geojson_to_sf(buffer_string(file)); - - //IStreamWrapper isw(ifs); - //Document d; - //d.ParseStream(isw); - +Rcpp::List rcpp_read_sf_file(std::string file, bool unnest) { + return generic_geojson_to_sf(buffer_string(file), unnest); } diff --git a/tests/testthat/test-geometrycollection.R b/tests/testthat/test-geometrycollection.R new file mode 100644 index 0000000..7b52545 --- /dev/null +++ b/tests/testthat/test-geometrycollection.R @@ -0,0 +1,61 @@ +context("geometrycollection") + +test_that("unnesting preserves properties, ", { + + js <- '{ + "type": "GeometryCollection", + "geometries": [ + {"type": "Point","coordinates": [100.0, 0.0]}, + {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, + {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]}]}' + + expect_true(nrow(geojson_sf(js, unnest = T)) == 3) + + js <- '{"type":"Feature","properties":{"id":1},"geometry":{ + "type": "GeometryCollection", + "geometries": [ + {"type": "Point","coordinates": [100.0, 0.0]}, + {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, + {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]}]}}' + + expect_true(nrow(geojson_sf(js, unnest = T)) == 3) + expect_true(unique(geojson_sf(js, unnest = T)[['id']]) == 1) + + js <- '[{ + "type": "Feature", + "properties" : {}, + "geometry": { + "type": "Polygon","coordinates": [[[-10.0, -10.0],[10.0, -10.0],[10.0, 10.0],[-10.0, -10.0]]]} + }, + { + "type": "Feature", + "properties" : {"id":1}, + "geometry": {"type": "MultiPolygon", + "coordinates": [[[[180.0, 40.0], [180.0, 50.0], [170.0, 50.0],[170.0, 40.0], [180.0, 40.0]]], + [[[-170.0, 40.0], [-170.0, 50.0], [-180.0, 50.0],[-180.0, 40.0], [-170.0, 40.0]]]]} + }, { + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "properties": {"id" : 2, "value" : "foo"}, + "geometry": {"type": "Point","coordinates": [100.0, 0.0]} + },{ + "type": "Feature", + "properties": null, + "geometry": { + "type": "LineString", + "coordinates": [[101.0, 0.0],[102.0, 1.0]] + }}] + },{ + "type": "GeometryCollection", + "geometries": [ + {"type": "Point","coordinates": [100.0, 0.0]}, + {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, + {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]} + ]}, + {"type": "Polygon","coordinates": [[[-10.0, -10.0],[10.0, -10.0],[10.0, 10.0],[-10.0, -10.0]]]} + ]' + + geojson_sf(js, unnest = F) + +}) From 144c4b8b58846265863a15848340832da925a6fb Mon Sep 17 00:00:00 2001 From: symbolixau Date: Tue, 24 Apr 2018 20:31:05 +1000 Subject: [PATCH 4/7] tests --- R/geojson_sf.R | 8 +- src/geojson_to_sf.cpp | 14 ++-- tests/testthat/test-errors.R | 95 ++++++------------------ tests/testthat/test-geojson_validate.R | 60 ++++----------- tests/testthat/test-geometrycollection.R | 3 +- 5 files changed, 51 insertions(+), 129 deletions(-) diff --git a/R/geojson_sf.R b/R/geojson_sf.R index c63a3ff..16d35db 100644 --- a/R/geojson_sf.R +++ b/R/geojson_sf.R @@ -30,7 +30,7 @@ geojson_sfc.character <- function(geojson, unnest = FALSE) { return(rcpp_geojson_to_sfc(geojson, unnest)) } if (is_url(geojson)) { - return(geojson_sfc(curl::curl(geojson))) + return(geojson_sfc(curl::curl(geojson), unnest)) } else if (file.exists(geojson) ) { return(rcpp_read_sfc_file(normalizePath(geojson), unnest)) } @@ -75,7 +75,7 @@ geojson_sf.character <- function(geojson, unnest = FALSE) { return(rcpp_geojson_to_sf(geojson, unnest)) } if (is_url(geojson)) { - return(geojson_sf(curl::curl(geojson))) + return(geojson_sf(curl::curl(geojson), unnest)) } else if (file.exists(geojson) ) { return(rcpp_read_sf_file(normalizePath(geojson), unnest)) } @@ -83,10 +83,10 @@ geojson_sf.character <- function(geojson, unnest = FALSE) { } #' @export -geojson_sf.connection <- function(geojson) geojson_sf(read_url(geojson), unnest) +geojson_sf.connection <- function(geojson, unnest = F) geojson_sf(read_url(geojson), unnest) #' @export -geojson_sf.default <- function(geojson) rcpp_geojson_to_sf(geojson, unnest) +geojson_sf.default <- function(geojson, unnest = F) rcpp_geojson_to_sf(geojson, unnest) ## TODO: diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index ca43f2d..38e8b33 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -90,7 +90,7 @@ Rcpp::List parse_geometry_collection_object(const Value& val, geom_collection.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); } else { sfg_objects+=n; - Rcpp::Rcout << "debug: unnest gc sfg_objects " << sfg_objects << std::endl; + //Rcpp::Rcout << "debug: unnest gc sfg_objects " << sfg_objects << std::endl; } return geom_collection; } @@ -117,7 +117,7 @@ Rcpp::List parse_feature_object(const Value& feature, validate_type(geometry, sfg_objects); type = geometry["type"].GetString(); - Rcpp::Rcout << "debug: type = " << type << std::endl; + //Rcpp::Rcout << "debug: type = " << type << std::endl; if (type == "GeometryCollection") { sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects, unnest); @@ -151,15 +151,15 @@ Rcpp::List parse_feature_object(const Value& feature, // chuck these steps in a loop, from (i = 0; i < gc.size() || 1; i++) // IFF GEOMETRYCOLLECTION && UNNEST, we've already added on the objects - Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; + //Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; if (type != "GeometryCollection") { - Rcpp::Rcout << "debug: type != GC "<< std::endl; + //Rcpp::Rcout << "debug: type != GC "<< std::endl; sfg_objects++; } else if (type == "GeometryCollection" && !unnest){ - Rcpp::Rcout << "debug: type == GC && !unnest "<< std::endl; + //Rcpp::Rcout << "debug: type == GC && !unnest "<< std::endl; sfg_objects++; } - Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; + //Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; const Value& p = feature["properties"]; get_property_keys(p, property_keys); @@ -256,7 +256,7 @@ void parse_geojson(const Value& v, if (!unnest) { sfg_objects++; } - Rcpp::Rcout << "debug: gc unnest sfg_objects2 : " << sfg_objects << std::endl; + //Rcpp::Rcout << "debug: gc unnest sfg_objects2 : " << sfg_objects << std::endl; sfc[i] = res; } else { diff --git a/tests/testthat/test-errors.R b/tests/testthat/test-errors.R index a77b78f..0ce7d8a 100644 --- a/tests/testthat/test-errors.R +++ b/tests/testthat/test-errors.R @@ -2,77 +2,28 @@ context("errors") test_that("errors are handled", { - expect_error( - geojson_sf(1:5), "Geometry could not be determined" - ) - expect_error( - geojson_sf("a"), "Invalid JSON" - ) - expect_error( - geojson_sf() - ) - expect_error( - geojson_sf(NULL) - ) - expect_error( - geojson_sf(NA), "Invalid JSON" - ) - expect_error( - geojson_sf('{ "type" : "Point" }'), "No 'coordinates' member at object index 0 - invalid GeoJSON" - ) - - expect_error( - geojson_sf('{"type" : "MultiPoint","coordinates" : [0, 0] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "Point", "coordinates" : null }'), "No 'array' member at object index 0 - invalid GeoJSON" - ) - expect_error( - geojson_sf('{"type" : "LineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "Polygon", "coordinates" : [0, 0] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }'), "Invalid array object" - ) - expect_error( - geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }'), "Invalid array object" - ) - - expect_error( - geojson_wkt('{"type" : "LineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object" - ) - expect_error( - geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object" - ) - expect_error( - geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }'), "Invalid array object" - ) - expect_error( - geojson_wkt('{"type" : "Polygon", "coordinates" : [0, 0] }'), "Invalid array object" - ) - expect_error( - geojson_wkt('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }'), "Invalid array object" - ) - expect_error( - geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }'), "Invalid array object" - ) - expect_error( - geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }'), "Invalid array object" - ) - - expect_error( - geojson_sf('{"type" : "Point" , "coordinates" : {} }'), "No 'array' member at object index 0 - invalid GeoJSON" - ) + expect_error(geojson_sf(1:5), "Geometry could not be determined") + expect_error(geojson_sf("a"), "Invalid JSON") + expect_error(geojson_sf()) + expect_error(geojson_sf(NULL)) + expect_error(geojson_sf(NA), "Invalid JSON") + expect_error(geojson_sf('{ "type" : "Point" }'), "No 'coordinates' member at object index 0 - invalid GeoJSON") + expect_error(geojson_sf('{"type" : "MultiPoint","coordinates" : [0, 0] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "Point", "coordinates" : null }'), "No 'array' member at object index 0 - invalid GeoJSON") + expect_error(geojson_sf('{"type" : "LineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "Polygon", "coordinates" : [0, 0] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "LineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "Polygon", "coordinates" : [0, 0] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }'), "Invalid array object") + expect_error(geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }'), "Invalid array object") + expect_error(geojson_sf('{"type" : "Point" , "coordinates" : {} }'), "No 'array' member at object index 0 - invalid GeoJSON") }) diff --git a/tests/testthat/test-geojson_validate.R b/tests/testthat/test-geojson_validate.R index e26562f..bf4ee95 100644 --- a/tests/testthat/test-geojson_validate.R +++ b/tests/testthat/test-geojson_validate.R @@ -7,15 +7,14 @@ test_that("Geometry object has correct members", { js <- '{"type":"Point","coordinate":[0,0]}' expect_error( - geojsonsf:::rcpp_geojson_to_sf(js), + geojsonsf:::rcpp_geojson_to_sf(js, F), "No 'coordinates' member at object index 0 - invalid GeoJSON" ) ## no 'type' key js <- '{"geometry":"Point","coordinates":[0,0]}' - expect_error( - geojsonsf:::rcpp_geojson_to_sf(js), + geojsonsf:::rcpp_geojson_to_sf(js, F), "No 'type' member at object index 0 - invalid GeoJSON" ) @@ -39,58 +38,29 @@ test_that("Feature Object has correct members", { ## invalid 'geometry' member js <- ' - { - "type" : "Feature", - "properties" : {}, - "geom" : { - "type" : "Point", - "coordinates" : [ 0, 0] - } - } - ' + {"type" : "Feature","properties" : {}, + "geom" : {"type" : "Point","coordinates" : [ 0, 0]}}' expect_error( - geojsonsf:::rcpp_geojson_to_sf(js), + geojsonsf:::rcpp_geojson_to_sf(js, F), "No 'geometry' member at object index 0 - invalid GeoJSON" ) ## invalid 'properties' member - js <- ' - { - "type" : "Feature", - "property" : {}, - "geometry" : { - "type" : "Point", - "coordinates" : [ 0, 0] - } - } - ' + js <- '{"type" : "Feature","property" : {},"geometry" : {"type" : "Point","coordinates" : [ 0, 0]}}' expect_error( - geojsonsf:::rcpp_geojson_to_sf(js), + geojsonsf:::rcpp_geojson_to_sf(js, F), "No 'properties' member at object index 0 - invalid GeoJSON" ) - js <- ' - [ - { - "type" : "Feature", + js <- '[ + {"type" : "Feature", "properties" : {}, - "geometry" : { - "type" : "Point", - "coordinates" : [ 0, 0] - } - }, - { - "type" : "Feature", - "property" : {}, - "geometry" : { - "type" : "Point", - "coordinates" : [ 0, 0] - } - } - ] - ' + "geometry" : {"type" : "Point","coordinates" : [ 0, 0]} + },{ + "type" : "Feature","property" : {},"geometry" : {"type" : "Point","coordinates" : [ 0, 0]} + }]' expect_error( - geojsonsf:::rcpp_geojson_to_sf(js), + geojsonsf:::rcpp_geojson_to_sf(js, F), "No 'properties' member at object index 1 - invalid GeoJSON" ) }) @@ -126,7 +96,7 @@ test_that("Featurecollection has correct members", { }' expect_error( - geojsonsf:::rcpp_geojson_to_sf(js), + geojsonsf:::rcpp_geojson_to_sf(js, F), "No 'features' member at object index 0 - invalid GeoJSON" ) diff --git a/tests/testthat/test-geometrycollection.R b/tests/testthat/test-geometrycollection.R index 7b52545..274fbff 100644 --- a/tests/testthat/test-geometrycollection.R +++ b/tests/testthat/test-geometrycollection.R @@ -56,6 +56,7 @@ test_that("unnesting preserves properties, ", { {"type": "Polygon","coordinates": [[[-10.0, -10.0],[10.0, -10.0],[10.0, 10.0],[-10.0, -10.0]]]} ]' - geojson_sf(js, unnest = F) + expect_true(nrow(geojson_sf(js, unnest = F)) == 6) + expect_true(nrow(geojson_sf(js, unnest = T)) == 8) }) From 313bbf22470c923e02d2d6f685ca908bc3f0846e Mon Sep 17 00:00:00 2001 From: symbolixau Date: Tue, 24 Apr 2018 20:40:08 +1000 Subject: [PATCH 5/7] documentation --- R/geojson_sf.R | 13 +++++++++++-- man/geojson_sf.Rd | 10 +++++++++- man/geojson_sfc.Rd | 10 +++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/R/geojson_sf.R b/R/geojson_sf.R index 16d35db..890a0b6 100644 --- a/R/geojson_sf.R +++ b/R/geojson_sf.R @@ -3,7 +3,16 @@ #' Extracts geometries from GeoJSON and returns an `sfc` object #' #' @param geojson string or vector of GeoJSON, or a URL or file pointing to a geojson file -#' @param unnest logical indicating whether to unnest GEOMETRYCOLLECTION rows +#' @param unnest logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details +#' +#' @details +#' specifying \code{unnest = TRUE} will expand individual \code{GEOMETRYCOLLECTION} +#' geometries to their own row in the resulting `sf` object. If the geometries are part +#' of a \code{Feature} (i.e., with properties), the properties will be repeated on each row. +#' +#' The \code{GEOMETRYCOLLECTION} information is not kept when using \code{unnest = TRUE}. Therefore, +#' it is not possible to reconstruct the \code{GEOMETRYCOLLECTION} after unnesting it. +#' #' @examples #' #' ## character string of GeoJSON @@ -63,7 +72,7 @@ geojson_sfc.default <- function(geojson, unnest = FALSE) rcpp_geojson_to_sfc(geo #' sf <- geojson_sf(myurl) #'} #' -#' @inheritParams geojson_sfc +#' @inherit geojson_sfc params details #' @export geojson_sf <- function(geojson, unnest = FALSE) UseMethod("geojson_sf") diff --git a/man/geojson_sf.Rd b/man/geojson_sf.Rd index 7604ed8..4b4a828 100644 --- a/man/geojson_sf.Rd +++ b/man/geojson_sf.Rd @@ -9,11 +9,19 @@ geojson_sf(geojson, unnest = FALSE) \arguments{ \item{geojson}{string or vector of GeoJSON, or a URL or file pointing to a geojson file} -\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows} +\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details} } \description{ Converts GeoJSON to an `sf` object } +\details{ +specifying \code{unnest = TRUE} will expand individual \code{GEOMETRYCOLLECTION} +geometries to their own row in the resulting `sf` object. If the geometries are part +of a \code{Feature} (i.e., with properties), the properties will be repeated on each row. + +The \code{GEOMETRYCOLLECTION} information is not kept when using \code{unnest = TRUE}. Therefore, +it is not possible to reconstruct the \code{GEOMETRYCOLLECTION} after unnesting it. +} \examples{ ## character string of GeoJSON diff --git a/man/geojson_sfc.Rd b/man/geojson_sfc.Rd index 1999b60..ed9268e 100644 --- a/man/geojson_sfc.Rd +++ b/man/geojson_sfc.Rd @@ -9,11 +9,19 @@ geojson_sfc(geojson, unnest = FALSE) \arguments{ \item{geojson}{string or vector of GeoJSON, or a URL or file pointing to a geojson file} -\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows} +\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details} } \description{ Extracts geometries from GeoJSON and returns an `sfc` object } +\details{ +specifying \code{unnest = TRUE} will expand individual \code{GEOMETRYCOLLECTION} +geometries to their own row in the resulting `sf` object. If the geometries are part +of a \code{Feature} (i.e., with properties), the properties will be repeated on each row. + +The \code{GEOMETRYCOLLECTION} information is not kept when using \code{unnest = TRUE}. Therefore, +it is not possible to reconstruct the \code{GEOMETRYCOLLECTION} after unnesting it. +} \examples{ ## character string of GeoJSON From aeb7b356cc82331af831d1865d269f2943ea214d Mon Sep 17 00:00:00 2001 From: symbolixau Date: Tue, 24 Apr 2018 21:12:05 +1000 Subject: [PATCH 6/7] tidying --- tests/testthat/test-geometrycollection.R | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-geometrycollection.R b/tests/testthat/test-geometrycollection.R index 274fbff..c538e32 100644 --- a/tests/testthat/test-geometrycollection.R +++ b/tests/testthat/test-geometrycollection.R @@ -24,16 +24,14 @@ test_that("unnesting preserves properties, ", { js <- '[{ "type": "Feature", "properties" : {}, - "geometry": { - "type": "Polygon","coordinates": [[[-10.0, -10.0],[10.0, -10.0],[10.0, 10.0],[-10.0, -10.0]]]} - }, - { + "geometry": {"type": "Polygon","coordinates": [[[-10.0, -10.0],[10.0, -10.0],[10.0, 10.0],[-10.0, -10.0]]]} + },{ "type": "Feature", "properties" : {"id":1}, "geometry": {"type": "MultiPolygon", "coordinates": [[[[180.0, 40.0], [180.0, 50.0], [170.0, 50.0],[170.0, 40.0], [180.0, 40.0]]], [[[-170.0, 40.0], [-170.0, 50.0], [-180.0, 50.0],[-180.0, 40.0], [-170.0, 40.0]]]]} - }, { + },{ "type": "FeatureCollection", "features": [{ "type": "Feature", From bae020ec541928e2adbe1e1f030d9e4195ea532f Mon Sep 17 00:00:00 2001 From: David Cooley Date: Thu, 26 Apr 2018 13:23:36 +1000 Subject: [PATCH 7/7] flatten geometry --- .travis.yml | 1 + R/RcppExports.R | 16 +++--- R/geojson_sf.R | 38 ++++++------- inst/include/geojson_to_sf.h | 18 +++--- man/geojson_sf.Rd | 8 +-- man/geojson_sfc.Rd | 8 +-- src/RcppExports.cpp | 32 +++++------ src/geojson_to_sf.cpp | 70 ++++++++++++------------ src/read_geojson.cpp | 8 +-- tests/testthat/test-geometries.R | 63 +++++---------------- tests/testthat/test-geometry_objects.R | 35 +++--------- tests/testthat/test-geometrycollection.R | 14 +++-- 12 files changed, 128 insertions(+), 183 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc45039..76c307d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r language: r +r: devel sudo: false cache: packages diff --git a/R/RcppExports.R b/R/RcppExports.R index 44f859d..d38beff 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -1,24 +1,24 @@ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 -rcpp_geojson_to_sfc <- function(geojson, unnest) { - .Call(`_geojsonsf_rcpp_geojson_to_sfc`, geojson, unnest) +rcpp_geojson_to_sfc <- function(geojson, flatten_geometries) { + .Call(`_geojsonsf_rcpp_geojson_to_sfc`, geojson, flatten_geometries) } -rcpp_geojson_to_sf <- function(geojson, unnest) { - .Call(`_geojsonsf_rcpp_geojson_to_sf`, geojson, unnest) +rcpp_geojson_to_sf <- function(geojson, flatten_geometries) { + .Call(`_geojsonsf_rcpp_geojson_to_sf`, geojson, flatten_geometries) } rcpp_geojson_to_wkt <- function(geojson) { .Call(`_geojsonsf_rcpp_geojson_to_wkt`, geojson) } -rcpp_read_sfc_file <- function(file, unnest) { - .Call(`_geojsonsf_rcpp_read_sfc_file`, file, unnest) +rcpp_read_sfc_file <- function(file, flatten_geometries) { + .Call(`_geojsonsf_rcpp_read_sfc_file`, file, flatten_geometries) } -rcpp_read_sf_file <- function(file, unnest) { - .Call(`_geojsonsf_rcpp_read_sf_file`, file, unnest) +rcpp_read_sf_file <- function(file, flatten_geometries) { + .Call(`_geojsonsf_rcpp_read_sf_file`, file, flatten_geometries) } rcpp_sfc_to_geojson <- function(sfc) { diff --git a/R/geojson_sf.R b/R/geojson_sf.R index 890a0b6..1d014c8 100644 --- a/R/geojson_sf.R +++ b/R/geojson_sf.R @@ -3,14 +3,14 @@ #' Extracts geometries from GeoJSON and returns an `sfc` object #' #' @param geojson string or vector of GeoJSON, or a URL or file pointing to a geojson file -#' @param unnest logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details +#' @param flatten_geometries logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details #' #' @details -#' specifying \code{unnest = TRUE} will expand individual \code{GEOMETRYCOLLECTION} +#' specifying \code{flatten_geometries = TRUE} will expand individual \code{GEOMETRYCOLLECTION} #' geometries to their own row in the resulting `sf` object. If the geometries are part #' of a \code{Feature} (i.e., with properties), the properties will be repeated on each row. #' -#' The \code{GEOMETRYCOLLECTION} information is not kept when using \code{unnest = TRUE}. Therefore, +#' The \code{GEOMETRYCOLLECTION} information is not kept when using \code{flatten_geometries = TRUE}. Therefore, #' it is not possible to reconstruct the \code{GEOMETRYCOLLECTION} after unnesting it. #' #' @examples @@ -29,28 +29,28 @@ #'} #' #' @export -geojson_sfc <- function(geojson, unnest = FALSE) UseMethod("geojson_sfc") +geojson_sfc <- function(geojson, flatten_geometries = FALSE) UseMethod("geojson_sfc") #' @export -geojson_sfc.character <- function(geojson, unnest = FALSE) { +geojson_sfc.character <- function(geojson, flatten_geometries = FALSE) { if(length(geojson) > 1) { - return(rcpp_geojson_to_sfc(geojson, unnest)) + return(rcpp_geojson_to_sfc(geojson, flatten_geometries)) } if (is_url(geojson)) { - return(geojson_sfc(curl::curl(geojson), unnest)) + return(geojson_sfc(curl::curl(geojson), flatten_geometries)) } else if (file.exists(geojson) ) { - return(rcpp_read_sfc_file(normalizePath(geojson), unnest)) + return(rcpp_read_sfc_file(normalizePath(geojson), flatten_geometries)) } - return(rcpp_geojson_to_sfc(geojson, unnest)) + return(rcpp_geojson_to_sfc(geojson, flatten_geometries)) } #' @export -geojson_sfc.connection <- function(geojson, unnest = FALSE) geojson_sfc(read_url(geojson), unnest) +geojson_sfc.connection <- function(geojson, flatten_geometries = FALSE) geojson_sfc(read_url(geojson), flatten_geometries) #' @export -geojson_sfc.default <- function(geojson, unnest = FALSE) rcpp_geojson_to_sfc(geojson, unnest) +geojson_sfc.default <- function(geojson, flatten_geometries = FALSE) rcpp_geojson_to_sfc(geojson, flatten_geometries) #' Geojson to sf #' @@ -74,28 +74,28 @@ geojson_sfc.default <- function(geojson, unnest = FALSE) rcpp_geojson_to_sfc(geo #' #' @inherit geojson_sfc params details #' @export -geojson_sf <- function(geojson, unnest = FALSE) UseMethod("geojson_sf") +geojson_sf <- function(geojson, flatten_geometries = FALSE) UseMethod("geojson_sf") #' @export -geojson_sf.character <- function(geojson, unnest = FALSE) { +geojson_sf.character <- function(geojson, flatten_geometries = FALSE) { if(length(geojson) > 1) { - return(rcpp_geojson_to_sf(geojson, unnest)) + return(rcpp_geojson_to_sf(geojson, flatten_geometries)) } if (is_url(geojson)) { - return(geojson_sf(curl::curl(geojson), unnest)) + return(geojson_sf(curl::curl(geojson), flatten_geometries)) } else if (file.exists(geojson) ) { - return(rcpp_read_sf_file(normalizePath(geojson), unnest)) + return(rcpp_read_sf_file(normalizePath(geojson), flatten_geometries)) } - return(rcpp_geojson_to_sf(geojson, unnest)) + return(rcpp_geojson_to_sf(geojson, flatten_geometries)) } #' @export -geojson_sf.connection <- function(geojson, unnest = F) geojson_sf(read_url(geojson), unnest) +geojson_sf.connection <- function(geojson, flatten_geometries = F) geojson_sf(read_url(geojson), flatten_geometries) #' @export -geojson_sf.default <- function(geojson, unnest = F) rcpp_geojson_to_sf(geojson, unnest) +geojson_sf.default <- function(geojson, flatten_geometries = F) rcpp_geojson_to_sf(geojson, flatten_geometries) ## TODO: diff --git a/inst/include/geojson_to_sf.h b/inst/include/geojson_to_sf.h index 17da318..91f2782 100644 --- a/inst/include/geojson_to_sf.h +++ b/inst/include/geojson_to_sf.h @@ -18,7 +18,7 @@ Rcpp::List parse_geometry_collection_object(const Value& val, Rcpp::NumericVector& bbox, std::set< std::string >& geometry_types, int& sfg_objects, - bool& unnest); + bool& flatten_geometries); Rcpp::List parse_feature_object(const Value& feature, Rcpp::NumericVector& bbox, @@ -27,7 +27,7 @@ Rcpp::List parse_feature_object(const Value& feature, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest); + bool& flatten_geometries); Rcpp::List parse_feature_collection_object(const Value& fc, Rcpp::NumericVector& bbox, @@ -36,7 +36,7 @@ Rcpp::List parse_feature_collection_object(const Value& fc, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest); + bool& flatten_geometries); void parse_geojson(const Value& v, Rcpp::List& sfc, @@ -48,7 +48,7 @@ void parse_geojson(const Value& v, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest); + bool& flatten_geometries); void parse_geojson_array(Document& d, Rcpp::List& sfc, @@ -60,7 +60,7 @@ void parse_geojson_array(Document& d, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest); + bool& flatten_geometries); void parse_geojson_object(Document& d, Rcpp::List& sfc, @@ -71,7 +71,7 @@ void parse_geojson_object(Document& d, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest); + bool& flatten_geometries); Rcpp::List geojson_to_sf(const char* geojson, Rcpp::NumericVector& bbox, std::set< std::string >& geometry_types, @@ -79,7 +79,7 @@ Rcpp::List geojson_to_sf(const char* geojson, Rcpp::NumericVector& bbox, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest); + bool& flatten_geometries); void setup_property_vectors(std::map< std::string, std::string>& property_types, Rcpp::List& properties, int& sfg_objects); @@ -95,8 +95,8 @@ Rcpp::List construct_sf(Rcpp::List& lst, std::set< std::string >& property_keys, int& sfg_objects, int& row_index); -Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& unnest); +Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& flatten_geometries); -Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& unnest); +Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& flatten_geometries); #endif diff --git a/man/geojson_sf.Rd b/man/geojson_sf.Rd index 4b4a828..4202f14 100644 --- a/man/geojson_sf.Rd +++ b/man/geojson_sf.Rd @@ -4,22 +4,22 @@ \alias{geojson_sf} \title{Geojson to sf} \usage{ -geojson_sf(geojson, unnest = FALSE) +geojson_sf(geojson, flatten_geometries = FALSE) } \arguments{ \item{geojson}{string or vector of GeoJSON, or a URL or file pointing to a geojson file} -\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details} +\item{flatten_geometries}{logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details} } \description{ Converts GeoJSON to an `sf` object } \details{ -specifying \code{unnest = TRUE} will expand individual \code{GEOMETRYCOLLECTION} +specifying \code{flatten_geometries = TRUE} will expand individual \code{GEOMETRYCOLLECTION} geometries to their own row in the resulting `sf` object. If the geometries are part of a \code{Feature} (i.e., with properties), the properties will be repeated on each row. -The \code{GEOMETRYCOLLECTION} information is not kept when using \code{unnest = TRUE}. Therefore, +The \code{GEOMETRYCOLLECTION} information is not kept when using \code{flatten_geometries = TRUE}. Therefore, it is not possible to reconstruct the \code{GEOMETRYCOLLECTION} after unnesting it. } \examples{ diff --git a/man/geojson_sfc.Rd b/man/geojson_sfc.Rd index ed9268e..5017b0a 100644 --- a/man/geojson_sfc.Rd +++ b/man/geojson_sfc.Rd @@ -4,22 +4,22 @@ \alias{geojson_sfc} \title{Geojson to sfc} \usage{ -geojson_sfc(geojson, unnest = FALSE) +geojson_sfc(geojson, flatten_geometries = FALSE) } \arguments{ \item{geojson}{string or vector of GeoJSON, or a URL or file pointing to a geojson file} -\item{unnest}{logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details} +\item{flatten_geometries}{logical indicating whether to unnest GEOMETRYCOLLECTION rows. see details} } \description{ Extracts geometries from GeoJSON and returns an `sfc` object } \details{ -specifying \code{unnest = TRUE} will expand individual \code{GEOMETRYCOLLECTION} +specifying \code{flatten_geometries = TRUE} will expand individual \code{GEOMETRYCOLLECTION} geometries to their own row in the resulting `sf` object. If the geometries are part of a \code{Feature} (i.e., with properties), the properties will be repeated on each row. -The \code{GEOMETRYCOLLECTION} information is not kept when using \code{unnest = TRUE}. Therefore, +The \code{GEOMETRYCOLLECTION} information is not kept when using \code{flatten_geometries = TRUE}. Therefore, it is not possible to reconstruct the \code{GEOMETRYCOLLECTION} after unnesting it. } \examples{ diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 9f24765..2affbf7 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -7,26 +7,26 @@ using namespace Rcpp; // rcpp_geojson_to_sfc -Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& unnest); -RcppExport SEXP _geojsonsf_rcpp_geojson_to_sfc(SEXP geojsonSEXP, SEXP unnestSEXP) { +Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& flatten_geometries); +RcppExport SEXP _geojsonsf_rcpp_geojson_to_sfc(SEXP geojsonSEXP, SEXP flatten_geometriesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::StringVector >::type geojson(geojsonSEXP); - Rcpp::traits::input_parameter< bool& >::type unnest(unnestSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sfc(geojson, unnest)); + Rcpp::traits::input_parameter< bool& >::type flatten_geometries(flatten_geometriesSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sfc(geojson, flatten_geometries)); return rcpp_result_gen; END_RCPP } // rcpp_geojson_to_sf -Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson, bool unnest); -RcppExport SEXP _geojsonsf_rcpp_geojson_to_sf(SEXP geojsonSEXP, SEXP unnestSEXP) { +Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson, bool flatten_geometries); +RcppExport SEXP _geojsonsf_rcpp_geojson_to_sf(SEXP geojsonSEXP, SEXP flatten_geometriesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::StringVector >::type geojson(geojsonSEXP); - Rcpp::traits::input_parameter< bool >::type unnest(unnestSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sf(geojson, unnest)); + Rcpp::traits::input_parameter< bool >::type flatten_geometries(flatten_geometriesSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_geojson_to_sf(geojson, flatten_geometries)); return rcpp_result_gen; END_RCPP } @@ -42,26 +42,26 @@ BEGIN_RCPP END_RCPP } // rcpp_read_sfc_file -Rcpp::List rcpp_read_sfc_file(std::string file, bool unnest); -RcppExport SEXP _geojsonsf_rcpp_read_sfc_file(SEXP fileSEXP, SEXP unnestSEXP) { +Rcpp::List rcpp_read_sfc_file(std::string file, bool flatten_geometries); +RcppExport SEXP _geojsonsf_rcpp_read_sfc_file(SEXP fileSEXP, SEXP flatten_geometriesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::string >::type file(fileSEXP); - Rcpp::traits::input_parameter< bool >::type unnest(unnestSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_read_sfc_file(file, unnest)); + Rcpp::traits::input_parameter< bool >::type flatten_geometries(flatten_geometriesSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_read_sfc_file(file, flatten_geometries)); return rcpp_result_gen; END_RCPP } // rcpp_read_sf_file -Rcpp::List rcpp_read_sf_file(std::string file, bool unnest); -RcppExport SEXP _geojsonsf_rcpp_read_sf_file(SEXP fileSEXP, SEXP unnestSEXP) { +Rcpp::List rcpp_read_sf_file(std::string file, bool flatten_geometries); +RcppExport SEXP _geojsonsf_rcpp_read_sf_file(SEXP fileSEXP, SEXP flatten_geometriesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::string >::type file(fileSEXP); - Rcpp::traits::input_parameter< bool >::type unnest(unnestSEXP); - rcpp_result_gen = Rcpp::wrap(rcpp_read_sf_file(file, unnest)); + Rcpp::traits::input_parameter< bool >::type flatten_geometries(flatten_geometriesSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_read_sf_file(file, flatten_geometries)); return rcpp_result_gen; END_RCPP } diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index 38e8b33..dcd9069 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -69,7 +69,7 @@ Rcpp::List parse_geometry_collection_object(const Value& val, Rcpp::NumericVector& bbox, std::set< std::string >& geometry_types, int& sfg_objects, - bool& unnest) { + bool& flatten_geometries) { std::string geom_type; validate_geometries(val, sfg_objects); auto geometries = val["geometries"].GetArray(); @@ -85,12 +85,12 @@ Rcpp::List parse_geometry_collection_object(const Value& val, parse_geometry_object(geom_collection, i, gcval, bbox, geometry_types, sfg_objects); } - if (!unnest) { + if (!flatten_geometries) { //sfg_objects++; geom_collection.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); } else { sfg_objects+=n; - //Rcpp::Rcout << "debug: unnest gc sfg_objects " << sfg_objects << std::endl; + //Rcpp::Rcout << "debug: flatten_geometries gc sfg_objects " << sfg_objects << std::endl; } return geom_collection; } @@ -102,7 +102,7 @@ Rcpp::List parse_feature_object(const Value& feature, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest) { + bool& flatten_geometries) { validate_geometry(feature, sfg_objects); validate_properties(feature, sfg_objects); @@ -120,7 +120,7 @@ Rcpp::List parse_feature_object(const Value& feature, //Rcpp::Rcout << "debug: type = " << type << std::endl; if (type == "GeometryCollection") { - sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects, unnest); + sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects, flatten_geometries); } else { parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); } @@ -147,16 +147,16 @@ Rcpp::List parse_feature_object(const Value& feature, } // TODO: - // if 'unnest GEOMETRYCOLLECTION', increment this by the number of internal geometries + // if 'flatten_geometries GEOMETRYCOLLECTION', increment this by the number of internal geometries // chuck these steps in a loop, from (i = 0; i < gc.size() || 1; i++) - // IFF GEOMETRYCOLLECTION && UNNEST, we've already added on the objects + // IFF GEOMETRYCOLLECTION && flatten_geometries, we've already added on the objects //Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; if (type != "GeometryCollection") { //Rcpp::Rcout << "debug: type != GC "<< std::endl; sfg_objects++; - } else if (type == "GeometryCollection" && !unnest){ - //Rcpp::Rcout << "debug: type == GC && !unnest "<< std::endl; + } else if (type == "GeometryCollection" && !flatten_geometries){ + //Rcpp::Rcout << "debug: type == GC && !flatten_geometries "<< std::endl; sfg_objects++; } //Rcpp::Rcout << "debug: parse_feature_object sfg_objects: " << sfg_objects << std::endl; @@ -166,11 +166,11 @@ Rcpp::List parse_feature_object(const Value& feature, get_property_types(p, property_types); // TODO: - // - if unnesting GEOMETRYCOLLECTION need to expand the properties by the number of geometries. + // - if flatten_geometriesing GEOMETRYCOLLECTION need to expand the properties by the number of geometries. unsigned int geomsize = 1; unsigned int i; - if (unnest && type == "GeometryCollection") { + if (flatten_geometries && type == "GeometryCollection") { validate_geometries(geometry, sfg_objects); auto geometries = geometry["geometries"].GetArray(); geomsize = geometries.Size(); @@ -179,7 +179,7 @@ Rcpp::List parse_feature_object(const Value& feature, std::string s; for (i = 0; i < geomsize; i++) { //https://stackoverflow.com/a/33473321/5977215 - if (unnest) { + if (flatten_geometries) { s = std::to_string(sfg_objects-i); } else { s = std::to_string(sfg_objects); @@ -201,7 +201,7 @@ Rcpp::List parse_feature_collection_object(const Value& fc, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest) { + bool& flatten_geometries) { // a FeatureCollection MUST have members (array) called features, validate_features(fc, sfg_objects); @@ -216,7 +216,7 @@ Rcpp::List parse_feature_collection_object(const Value& fc, const Value& feature = features[i]; feature_collection[i] = parse_feature_object( feature, bbox, geometry_types, sfg_objects, property_keys, doc_properties, - property_types, unnest + property_types, flatten_geometries ); } return feature_collection; @@ -234,7 +234,7 @@ void parse_geojson(const Value& v, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest) { + bool& flatten_geometries) { Rcpp::List res(1); validate_type(v, sfg_objects); @@ -242,21 +242,21 @@ void parse_geojson(const Value& v, std::string geom_type = v["type"].GetString(); if (geom_type == "Feature") { - res = parse_feature_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + res = parse_feature_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); sfc[i] = res; } else if (geom_type == "FeatureCollection") { - res = parse_feature_collection_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + res = parse_feature_collection_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); sfc[i] = res; } else if (geom_type == "GeometryCollection") { - res = parse_geometry_collection_object(v, bbox, geometry_types, sfg_objects, unnest); - if (!unnest) { + res = parse_geometry_collection_object(v, bbox, geometry_types, sfg_objects, flatten_geometries); + if (!flatten_geometries) { sfg_objects++; } - //Rcpp::Rcout << "debug: gc unnest sfg_objects2 : " << sfg_objects << std::endl; + //Rcpp::Rcout << "debug: gc flatten_geometries sfg_objects2 : " << sfg_objects << std::endl; sfc[i] = res; } else { @@ -275,9 +275,9 @@ void parse_geojson_object(Document& d, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest) { + bool& flatten_geometries) { const Value& v = d; - parse_geojson(v, sfc, properties, 0, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + parse_geojson(v, sfc, properties, 0, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); } void parse_geojson_array(Document& d, @@ -290,9 +290,9 @@ void parse_geojson_array(Document& d, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest) { + bool& flatten_geometries) { const Value& v = d[i]; - parse_geojson(v, sfc, properties, i, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + parse_geojson(v, sfc, properties, i, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); } Rcpp::List geojson_to_sf(const char* geojson, @@ -302,7 +302,7 @@ Rcpp::List geojson_to_sf(const char* geojson, std::set< std::string >& property_keys, Document& doc_properties, std::map< std::string, std::string>& property_types, - bool& unnest) { + bool& flatten_geometries) { Document d; safe_parse(d, geojson); @@ -313,7 +313,7 @@ Rcpp::List geojson_to_sf(const char* geojson, if (d.IsObject()) { Rcpp::List sfg(1); - parse_geojson_object(d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + parse_geojson_object(d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); sfc[0] = sfg; } else if (d.IsArray()) { @@ -321,7 +321,7 @@ Rcpp::List geojson_to_sf(const char* geojson, Rcpp::List sfgs(d.Size()); for (doc_ele = 0; doc_ele < d.Size(); doc_ele++) { - parse_geojson_array(d, sfgs, properties, doc_ele, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + parse_geojson_array(d, sfgs, properties, doc_ele, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); } sfc[0] = sfgs; } @@ -448,7 +448,7 @@ void fill_property_vectors(Document& doc_properties, } } -Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& unnest) { +Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& flatten_geometries) { // iterate over the geojson int n = geojson.size(); int sfg_objects = 0; // keep track of number of objects @@ -465,15 +465,15 @@ Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& unnest) { Rcpp::List sfc(n); for (int geo_ele = 0; geo_ele < n; geo_ele++ ){ - sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); } return construct_sfc(sfg_objects, sfc, bbox, geometry_types); } // [[Rcpp::export]] -Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& unnest) { - return create_sfc(geojson, unnest); +Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& flatten_geometries) { + return create_sfc(geojson, flatten_geometries); } Rcpp::List construct_sf(Rcpp::List& lst, std::set< std::string >& property_keys, @@ -499,7 +499,7 @@ Rcpp::List construct_sf(Rcpp::List& lst, std::set< std::string >& property_keys, return properties; } -Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& unnest) { +Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& flatten_geometries) { // iterate over the geojson int n = geojson.size(); int sfg_objects = 0; // keep track of number of objects @@ -516,7 +516,7 @@ Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& unnest) { Rcpp::List sfc(n); for (int geo_ele = 0; geo_ele < n; geo_ele++ ){ - sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, unnest); + sfc[geo_ele] = geojson_to_sf(geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, flatten_geometries); } @@ -525,6 +525,6 @@ Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& unnest) { } // [[Rcpp::export]] -Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson, bool unnest) { - return generic_geojson_to_sf(geojson, unnest); +Rcpp::List rcpp_geojson_to_sf(Rcpp::StringVector geojson, bool flatten_geometries) { + return generic_geojson_to_sf(geojson, flatten_geometries); } diff --git a/src/read_geojson.cpp b/src/read_geojson.cpp index d9e47eb..1cb2daa 100644 --- a/src/read_geojson.cpp +++ b/src/read_geojson.cpp @@ -20,13 +20,13 @@ Rcpp::StringVector buffer_string(std::string file) { } // [[Rcpp::export]] -Rcpp::List rcpp_read_sfc_file(std::string file, bool unnest) { - return create_sfc(buffer_string(file), unnest); +Rcpp::List rcpp_read_sfc_file(std::string file, bool flatten_geometries) { + return create_sfc(buffer_string(file), flatten_geometries); } // [[Rcpp::export]] -Rcpp::List rcpp_read_sf_file(std::string file, bool unnest) { - return generic_geojson_to_sf(buffer_string(file), unnest); +Rcpp::List rcpp_read_sf_file(std::string file, bool flatten_geometries) { + return generic_geojson_to_sf(buffer_string(file), flatten_geometries); } diff --git a/tests/testthat/test-geometries.R b/tests/testthat/test-geometries.R index f7f52b3..0c4f5f5 100644 --- a/tests/testthat/test-geometries.R +++ b/tests/testthat/test-geometries.R @@ -6,60 +6,35 @@ test_that("geometries are parsed correctly", { ## Point js <- '{"type": "Point", "coordinates": [101.0, 1.0] }' sf <- geojson_sf(js) - - expect_true( - all(c("sfc_POINT", "sfc") %in% attr(sf$geometry, "class")) - ) + expect_true(all(c("sfc_POINT", "sfc") %in% attr(sf$geometry, "class"))) ## MultiPoint js <- '{"type":"MultiPoint","coordinates":[[100.0, 0.0],[101.0, 1.0]]}' sf <- geojson_sf(js) - - expect_true( - all(c("sfc_MULTIPOINT", "sfc") %in% attr(sf$geometry, "class")) - ) + expect_true(all(c("sfc_MULTIPOINT", "sfc") %in% attr(sf$geometry, "class"))) ## LineString js <- '{"type": "LineString","coordinates":[[100.0, 0.0],[101.0, 1.0]]}' sf <- geojson_sf(js) - - expect_true( - all(c("sfc_LINESTRING", "sfc") %in% attr(sf$geometry, "class")) - ) + expect_true(all(c("sfc_LINESTRING", "sfc") %in% attr(sf$geometry, "class"))) ## MultiLineString - js <- '{"type": "MultiLineString","coordinates": [ - [[100.0, 0.0],[101.0, 1.0]],[[102.0, 2.0],[103.0, 3.0]] - ]}' + js <- '{"type": "MultiLineString","coordinates": [[[100.0, 0.0],[101.0, 1.0]],[[102.0, 2.0],[103.0, 3.0]]]}' sf <- geojson_sf(js) - - expect_true( - all(c("sfc_MULTILINESTRING", "sfc") %in% attr(sf$geometry, "class")) - ) + expect_true(all(c("sfc_MULTILINESTRING", "sfc") %in% attr(sf$geometry, "class"))) ## Polygon js <- '{"type": "Polygon","coordinates": [[ [180.0, 40.0], [180.0, 50.0], [170.0, 50.0], - [170.0, 40.0], [180.0, 40.0] - ]]}' + [170.0, 40.0], [180.0, 40.0]]]}' sf <- geojson_sf(js) - - expect_true( - all(c("sfc_POLYGON", "sfc") %in% attr(sf$geometry, "class")) - ) + expect_true(all(c("sfc_POLYGON", "sfc") %in% attr(sf$geometry, "class"))) js <- '{"type": "MultiPolygon","coordinates": [[[ - [180.0, 40.0], [180.0, 50.0], [170.0, 50.0], - [170.0, 40.0], [180.0, 40.0] - ]],[[ - [-170.0, 40.0], [-170.0, 50.0], [-180.0, 50.0], - [-180.0, 40.0], [-170.0, 40.0] - ]]]}' + [180.0, 40.0], [180.0, 50.0], [170.0, 50.0],[170.0, 40.0], [180.0, 40.0]]], + [[[-170.0, 40.0], [-170.0, 50.0], [-180.0, 50.0],[-180.0, 40.0], [-170.0, 40.0]]]]}' sf <- geojson_sf(js) - - expect_true( - all(c("sfc_MULTIPOLYGON", "sfc") %in% attr(sf$geometry, "class")) - ) + expect_true(all(c("sfc_MULTIPOLYGON", "sfc") %in% attr(sf$geometry, "class"))) js <- '{ "type": "GeometryCollection", @@ -72,11 +47,7 @@ test_that("geometries are parsed correctly", { sfc <- geojson_sfc(js) sf <- geojson_sf(js) - - expect_true( - all(c("sfc_GEOMETRY", "sfc") %in% attr(sf$geometry, "class")) - ) - + expect_true(all(c("sfc_GEOMETRY", "sfc") %in% attr(sf$geometry, "class"))) js <- '{ "type": "GeometryCollection", @@ -87,15 +58,7 @@ test_that("geometries are parsed correctly", { ] }' - expect_error( - geojson_sf(js), - "No 'geometries' member at object index 0 - invalid GeoJSON" - ) - - expect_error( - geojson_wkt(js), - "No 'geometries' member at object index 0 - invalid GeoJSON" - ) - + expect_error(geojson_sf(js),"No 'geometries' member at object index 0 - invalid GeoJSON") + expect_error(geojson_wkt(js),"No 'geometries' member at object index 0 - invalid GeoJSON") }) diff --git a/tests/testthat/test-geometry_objects.R b/tests/testthat/test-geometry_objects.R index 1ccdf6d..d8358db 100644 --- a/tests/testthat/test-geometry_objects.R +++ b/tests/testthat/test-geometry_objects.R @@ -4,22 +4,14 @@ context("objects") test_that("individual objects converted to sf", { g <- '{"type": "Point", "coordinates": [100.0, 0.0]}' - - f <- '{ - "type": "Feature", - "properties": null, - "geometry": {"type": "LineString", "coordinates": [[101.0, 0.0], [102.0, 1.0]]} - }' - + f <- '{"type": "Feature","properties": null, + "geometry": {"type": "LineString", "coordinates": [[101.0, 0.0], [102.0, 1.0]]}}' gc <- '{ "type": "GeometryCollection", "geometries": [ {"type": "Point", "coordinates": [100.0, 0.0]}, {"type": "LineString", "coordinates": [[101.0, 0.0], [102.0, 1.0]]}, - {"type" : "MultiPoint", "coordinates" : [[0,0], [1,1], [2,2]]} - ] - }' - + {"type" : "MultiPoint", "coordinates" : [[0,0], [1,1], [2,2]]}]}' fc <- '{ "type": "FeatureCollection", "features": [ @@ -37,21 +29,11 @@ test_that("individual objects converted to sf", { "type": "Feature", "properties": {"foo" : "feature 3.1", "bar" : "feature 3.2"}, "geometry": {"type": "LineString", "coordinates": [[109.0, 0.0], [102.0, 1.0]]} - } - ] - }' - - + }]}' fc1 <- '{ "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": null, - "geometry": {"type": "Point", "coordinates": [100.0, 0.0]} - }] - }' - + "features": [{"type": "Feature","properties": null, + "geometry": {"type": "Point", "coordinates": [100.0, 0.0]}}]}' fgc <- '{ "type" : "Feature", "properties" : {}, @@ -59,10 +41,7 @@ test_that("individual objects converted to sf", { "type": "GeometryCollection", "geometries": [ {"type": "Point", "coordinates": [100.0, 0.0]}, {"type": "LineString", "coordinates": [[101.0, 0.0], [102.0, 1.0]]}, - {"type" : "MultiPoint", "coordinates" : [[0,0], [1,1], [2,2]]} - ] - } - }' + {"type" : "MultiPoint", "coordinates" : [[0,0], [1,1], [2,2]]}]}}' sf_f <- geojson_sf(f) sf_g <- geojson_sf(g) diff --git a/tests/testthat/test-geometrycollection.R b/tests/testthat/test-geometrycollection.R index c538e32..8a4ed4b 100644 --- a/tests/testthat/test-geometrycollection.R +++ b/tests/testthat/test-geometrycollection.R @@ -1,6 +1,6 @@ context("geometrycollection") -test_that("unnesting preserves properties, ", { +test_that("flatten_geometriesing preserves properties, ", { js <- '{ "type": "GeometryCollection", @@ -9,7 +9,8 @@ test_that("unnesting preserves properties, ", { {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]}]}' - expect_true(nrow(geojson_sf(js, unnest = T)) == 3) + expect_true(nrow(geojson_sf(js, flatten_geometries = F)) == 1) + expect_true(nrow(geojson_sf(js, flatten_geometries = T)) == 3) js <- '{"type":"Feature","properties":{"id":1},"geometry":{ "type": "GeometryCollection", @@ -18,8 +19,9 @@ test_that("unnesting preserves properties, ", { {"type": "LineString","coordinates": [[101.0, 0.0],[102.0, 1.0]]}, {"type" : "MultiPoint","coordinates" : [[0,0],[1,1],[2,2]]}]}}' - expect_true(nrow(geojson_sf(js, unnest = T)) == 3) - expect_true(unique(geojson_sf(js, unnest = T)[['id']]) == 1) + expect_true(nrow(geojson_sf(js, flatten_geometries = F)) == 1) + expect_true(nrow(geojson_sf(js, flatten_geometries = T)) == 3) + expect_true(unique(geojson_sf(js, flatten_geometries = T)[['id']]) == 1) js <- '[{ "type": "Feature", @@ -54,7 +56,7 @@ test_that("unnesting preserves properties, ", { {"type": "Polygon","coordinates": [[[-10.0, -10.0],[10.0, -10.0],[10.0, 10.0],[-10.0, -10.0]]]} ]' - expect_true(nrow(geojson_sf(js, unnest = F)) == 6) - expect_true(nrow(geojson_sf(js, unnest = T)) == 8) + expect_true(nrow(geojson_sf(js, flatten_geometries = F)) == 6) + expect_true(nrow(geojson_sf(js, flatten_geometries = T)) == 8) })