From fe454dc7231c4bc07aec2f2cfe1a6d5df0332200 Mon Sep 17 00:00:00 2001 From: David Cooley Date: Thu, 5 Apr 2018 16:13:55 +1000 Subject: [PATCH 1/7] setting up wkt --- NAMESPACE | 1 + R/RcppExports.R | 4 ++ R/gsf_wkt.R | 10 ++++ inst/include/geojsonsf.h | 9 ++- man/geojson_wkt.Rd | 14 +++++ src/RcppExports.cpp | 12 ++++ src/geojson_wkt.cpp | 126 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 R/gsf_wkt.R create mode 100644 man/geojson_wkt.Rd create mode 100644 src/geojson_wkt.cpp diff --git a/NAMESPACE b/NAMESPACE index 9110a76..152f0fb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,5 +2,6 @@ export(geojson_sf) export(geojson_sfc) +export(geojson_wkt) importFrom(Rcpp,sourceCpp) useDynLib(geojsonsf, .registration = TRUE) diff --git a/R/RcppExports.R b/R/RcppExports.R index 5789200..871d368 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -9,6 +9,10 @@ rcpp_geojson_to_sf <- function(geojson) { .Call(`_geojsonsf_rcpp_geojson_to_sf`, geojson) } +rcpp_geojson_to_wkt <- function(geojson) { + .Call(`_geojsonsf_rcpp_geojson_to_wkt`, geojson) +} + rcpp_stream_in <- function(url) { invisible(.Call(`_geojsonsf_rcpp_stream_in`, url)) } diff --git a/R/gsf_wkt.R b/R/gsf_wkt.R new file mode 100644 index 0000000..ca9cfe5 --- /dev/null +++ b/R/gsf_wkt.R @@ -0,0 +1,10 @@ +#' Geojson to WKT +#' +#' Converts GeoJSON to Well-Known Text +#' +#' @param geojson +#' +#' @export +geojson_wkt <- function(geojson) { + rcpp_geojson_to_wkt(geojson) +} diff --git a/inst/include/geojsonsf.h b/inst/include/geojsonsf.h index d4ce79c..6dd27be 100644 --- a/inst/include/geojsonsf.h +++ b/inst/include/geojsonsf.h @@ -12,9 +12,14 @@ namespace geojsonsf { static const char* ARRAY_TYPES[] = { "Null", "False", "True", "Object", "Array", "String", "Number" }; - } - +#define UNKNOWN 0 +#define POINT 1 +#define MULTIPOINT 2 +#define LINESTRING 3 +#define MULTILINESTRING 4 +#define POLYGON 5 +#define MULTIPOLYGON 6 #endif diff --git a/man/geojson_wkt.Rd b/man/geojson_wkt.Rd new file mode 100644 index 0000000..5d39133 --- /dev/null +++ b/man/geojson_wkt.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gsf_wkt.R +\name{geojson_wkt} +\alias{geojson_wkt} +\title{Geojson to WKT} +\usage{ +geojson_wkt(geojson) +} +\arguments{ +\item{geojson}{} +} +\description{ +Converts GeoJSON to Well-Known Text +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index f032acd..2b38b0a 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -28,6 +28,17 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// rcpp_geojson_to_wkt +Rcpp::StringVector rcpp_geojson_to_wkt(Rcpp::StringVector geojson); +RcppExport SEXP _geojsonsf_rcpp_geojson_to_wkt(SEXP geojsonSEXP) { +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_wkt(geojson)); + return rcpp_result_gen; +END_RCPP +} // rcpp_stream_in void rcpp_stream_in(std::string url); RcppExport SEXP _geojsonsf_rcpp_stream_in(SEXP urlSEXP) { @@ -42,6 +53,7 @@ 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_wkt", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_wkt, 1}, {"_geojsonsf_rcpp_stream_in", (DL_FUNC) &_geojsonsf_rcpp_stream_in, 1}, {NULL, NULL, 0} }; diff --git a/src/geojson_wkt.cpp b/src/geojson_wkt.cpp new file mode 100644 index 0000000..e43ed88 --- /dev/null +++ b/src/geojson_wkt.cpp @@ -0,0 +1,126 @@ + +#include "rapidjson/document.h" +#include "geojsonsf.h" +#include "geojson_validate.h" +#include + +using namespace rapidjson; +using namespace Rcpp; + + +void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ) { + os << lon << " " << lat; +} + +void geom_type(const char *cls, int *tp = NULL) { +/* + int type = 0; + if (strcmp(cls, "POINT") == 0) + type = POINT; + else if (strcmp(cls, "MULTIPOINT") == 0) + type = MULTIPOINT; + else if (strcmp(cls, "LINESTRING") == 0) + type = LINESTRING; + else if (strcmp(cls, "POLYGON") == 0) + type = POLYGON; + else if (strcmp(cls, "MULTILINESTRING") == 0) + type = MULTILINESTRING; + else if (strcmp(cls, "MULTIPOLYGON") == 0) + type = MULTIPOLYGON; + else + type = UNKNOWN; + if (tp != NULL) + *tp = type; + */ +} + +void beginWKT(std::ostringstream& os, Rcpp::CharacterVector cls) { + + int tp; + geom_type(cls[1], &tp); + + switch( tp ) { + case POINT: + os << "POINT "; + break; + case MULTIPOINT: + os << "MULTIPOINT ("; + break; + case LINESTRING: + os << "LINESTRING "; + break; + case MULTILINESTRING: + os << "MULTILINESTRING ("; + break; + case POLYGON: + os << "POLYGON ("; + break; + case MULTIPOLYGON: + os << "MULTIPOLYGON (("; + break; + default: { + Rcpp::stop("Unknown geometry type"); + } + } +} + +void endWKT(std::ostringstream& os, Rcpp::CharacterVector cls) { + + int tp; + geom_type(cls[1], &tp); + + switch( tp ) { + case POINT: + os << ""; + break; + case MULTIPOINT: + os << ")"; + break; + case LINESTRING: + os << ""; + break; + case MULTILINESTRING: + os << ")"; + break; + case POLYGON: + os << ")"; + break; + case MULTIPOLYGON: + os << "))"; + break; + default: { + Rcpp::stop("Unknown geometry type"); + } + } +} + +void coordSeparateWKT(std::ostringstream& os) { + os << ", "; +} + +void geojson_to_wkt(const char* geojson) { + // TODO: + // grab the geometry data and stream to WKT + Document d; + safe_parse(d, geojson); + + /* + for (int i = 0; i < n; i++) { + + std::ostringstream os; + Rcpp::String wkt; + + } + */ + + +} + +// [[Rcpp::export]] +Rcpp::StringVector rcpp_geojson_to_wkt(Rcpp::StringVector geojson) { + Rcpp::StringVector wkt(geojson.size()); + + + + return wkt; +} From 4037fad79f6096d52016408489bd9ee751cb15a2 Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Thu, 5 Apr 2018 20:05:04 +1000 Subject: [PATCH 2/7] wkt structure --- src/geojson_to_sf.cpp | 6 ++-- src/{geojson_wkt.cpp => geojson_to_wkt.cpp} | 40 ++++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) rename src/{geojson_wkt.cpp => geojson_to_wkt.cpp} (68%) diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index 7b6c8d5..d206c32 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -153,11 +153,9 @@ void parse_geojson(const Value& v, std::map< std::string, std::string>& property_types) { Rcpp::List res(1); - std::string geom_type; - validate_type(v, sfg_objects); - geom_type = v["type"].GetString(); + std::string geom_type = v["type"].GetString();; if (geom_type == "Feature") { @@ -223,7 +221,7 @@ Rcpp::List geojson_to_sf(const char* geojson, Rcpp::List sfc(1); Rcpp::List properties(1); - if (d.IsObject() ) { + if (d.IsObject()) { Rcpp::List sfg(1); parse_geojson_object(d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types); diff --git a/src/geojson_wkt.cpp b/src/geojson_to_wkt.cpp similarity index 68% rename from src/geojson_wkt.cpp rename to src/geojson_to_wkt.cpp index e43ed88..b6641f5 100644 --- a/src/geojson_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -98,28 +98,52 @@ void coordSeparateWKT(std::ostringstream& os) { os << ", "; } -void geojson_to_wkt(const char* geojson) { +void parse_geojson_wkt(const Value& v, int i, int& wkt_objects) { + + validate_type(v, wkt_objects); + + std::string geom_type = v["type"].GetString(); +} + +void parse_geojson_object_wkt(Document& d, int& wkt_objects, int i) { + const Value& v = d; + parse_geojson_wkt(v, 0, wkt_objects); +} + +void parse_geojson_array_wkt(Document& d, int i, int& wkt_objects) { + const Value& v = d[i]; + parse_geojson_wkt(v, i, wkt_objects); +} + +Rcpp::List geojson_to_wkt(const char* geojson, int& wkt_objects) { // TODO: // grab the geometry data and stream to WKT Document d; safe_parse(d, geojson); + Rcpp::List wkt(1); - /* - for (int i = 0; i < n; i++) { + std::ostringstream os; + // Need to 'recurse' into the GeoJSON like what i did for geo_sf + // because it can be arrays, objects, vectors. + if (d.IsObject()) { - std::ostringstream os; - Rcpp::String wkt; + } else if (d.IsArray()) { - } - */ + } + return wkt; } // [[Rcpp::export]] Rcpp::StringVector rcpp_geojson_to_wkt(Rcpp::StringVector geojson) { - Rcpp::StringVector wkt(geojson.size()); + int n = geojson.size(); + Rcpp::StringVector wkt(n); + int wkt_objects = 0; + for (int i = 0; i < n; i++) { + wkt[i] = geojson_to_wkt(geojson[i], wkt_objects); + } return wkt; From cf13391482666e3f019298177c2582a02c9b9adb Mon Sep 17 00:00:00 2001 From: David Cooley Date: Fri, 6 Apr 2018 15:47:46 +1000 Subject: [PATCH 3/7] parsing wkt --- src/geojson_to_sf.cpp | 2 +- src/geojson_to_wkt.cpp | 245 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 229 insertions(+), 18 deletions(-) diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index d206c32..65895f9 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -230,7 +230,7 @@ Rcpp::List geojson_to_sf(const char* geojson, } else if (d.IsArray()) { Rcpp::List sfgs(d.Size()); - Rcpp::LogicalVector doc_ele_properties(d.Size()); + // Rcpp::LogicalVector doc_ele_properties(d.Size()); for (int 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); diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index b6641f5..f8b055f 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -1,8 +1,11 @@ #include "rapidjson/document.h" +#include #include "geojsonsf.h" +#include "geojson_sfc.h" +#include "geojson_sfg.h" #include "geojson_validate.h" -#include +#include "geojson_properties.h" using namespace rapidjson; using namespace Rcpp; @@ -12,6 +15,24 @@ void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ) { os << lon << " " << lat; } +void encode_wkt_point(std::ostringstream& os, const Value& coord_array) { + Rcpp::NumericVector point(2); + point[0] = get_lon(coord_array); + point[1] = get_lat(coord_array); + + addLonLatToWKTStream(os, point[0]*(float)1e-5, point[1]*(float)1e-5); +} + +void parse_line_to_wkt(std::ostringstream& os, const Value& coord_array) { + size_t n = coord_array.Size(); + + for (int i = 0; i < n; i++) { + encode_wkt_point(os, coord_array[i]); + } +} + + + void geom_type(const char *cls, int *tp = NULL) { /* int type = 0; @@ -98,53 +119,243 @@ void coordSeparateWKT(std::ostringstream& os) { os << ", "; } -void parse_geojson_wkt(const Value& v, int i, int& wkt_objects) { +void parse_geometry_object_wkt(Rcpp::List& sfc, + int i, + const Value& geometry, + std::set< std::string >& geometry_types, + int& wkt_objects) { + + validate_type(geometry, wkt_objects); + validate_coordinates(geometry, wkt_objects); + + std::string geom_type = geometry["type"].GetString(); + const Value& coord_array = geometry["coordinates"]; + geometry_types.insert(geom_type); + + if (geom_type == "Point") { + sfc[i] = get_point(coord_array); + //sfc_classes[counter] = "POINT"; + + } else if (geom_type == "MultiPoint") { + sfc[i] = get_multi_point(coord_array); + //sfc_classes[counter] = "MULTIPOINT"; + + } else if (geom_type == "LineString") { + sfc[i] = get_line_string(coord_array); + //sfc_classes[counter] = "LINESTRING"; + + } else if (geom_type == "MultiLineString") { + sfc[i] = get_multi_line_string(coord_array); + //sfc_classes[counter] = "MULTILINESTRING"; + + } else if (geom_type == "Polygon") { + sfc[i] = get_polygon(coord_array); + //sfc_classes[counter] = "POLYGON"; + + } else if (geom_type == "MultiPolygon") { + sfc[i] = get_multi_polygon(coord_array); + //sfc_classes[counter] = "MULTIPOLYGON"; + + } else { + Rcpp::stop("unknown sfg type"); + } +} + +Rcpp::List parse_geometry_collection_object_wkt(const Value& val, + std::set< std::string >& geometry_types, + int& wkt_objects) { + std::string geom_type; + + auto geometries = val["geometries"].GetArray(); + int n = geometries.Size(); + + Rcpp::List geom_collection(n); + + for (int i = 0; i < n; i++) { + const Value& gcval = geometries[i]; + geom_type = gcval["type"].GetString(); + parse_geometry_object_wkt(geom_collection, i, gcval, geometry_types, wkt_objects); + } + geom_collection.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); + + return geom_collection; +} + + + +Rcpp::List parse_feature_object_wkt(const Value& feature, + std::set< std::string >& geometry_types, + int& wkt_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string>& property_types) { + + validate_geometry(feature, wkt_objects); + validate_properties(feature, wkt_objects); + + const Value& geometry = feature["geometry"]; + Rcpp::List sfc(1); + parse_geometry_object_wkt(sfc, 0, geometry, geometry_types, wkt_objects); + wkt_objects++; + // get property keys + 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: is this method deep-cloning? + Value properties(feature["properties"], doc_properties.GetAllocator()); + doc_properties.AddMember(n, properties, doc_properties.GetAllocator()); + + return sfc; +} + + +Rcpp::List parse_feature_collection_object_wkt(const Value& fc, + std::set< std::string >& geometry_types, + int& wkt_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string>& property_types) { + // a FeatureCollection MUST have members called features, + // which is an array + validate_features(fc, wkt_objects); + + auto features = fc["features"].GetArray(); + + int n = features.Size(); // number of features + Rcpp::List feature_collection(n); + //std::string geom_type = "FeatureCollection"; + + for (int i = 0; i < n; i++) { + const Value& feature = features[i]; + feature_collection[i] = parse_feature_object_wkt(feature, geometry_types, wkt_objects, property_keys, doc_properties, property_types); + } + return feature_collection; +} + + +void parse_geojson_wkt(const Value& v, + Rcpp::List& sfc, + Rcpp::List& properties, + int i, + std::set< std::string >& geometry_types, + int& wkt_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string >& property_types + ) { + Rcpp::List res(1); validate_type(v, wkt_objects); std::string geom_type = v["type"].GetString(); + + if (geom_type == "Feature") { + + res = parse_feature_object_wkt(v, geometry_types, wkt_objects, property_keys, doc_properties, property_types); + sfc[i] = res; + + } else if (geom_type == "FeatureCollection") { + + res = parse_feature_collection_object_wkt(v, geometry_types, wkt_objects, property_keys, doc_properties, property_types); + sfc[i] = res; + + } else if (geom_type == "GeometryCollection") { + + res = parse_geometry_collection_object_wkt(v, geometry_types, wkt_objects); + wkt_objects++; + sfc[i] = res; + + } else { + + parse_geometry_object_wkt(sfc, i, v, geometry_types, wkt_objects); + wkt_objects++; + } + } -void parse_geojson_object_wkt(Document& d, int& wkt_objects, int i) { +void parse_geojson_object_wkt(Document& d, + Rcpp::List& sfc, + Rcpp::List& properties, + std::set< std::string >& geometry_types, + int& wkt_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string>& property_types + ) { const Value& v = d; - parse_geojson_wkt(v, 0, wkt_objects); + parse_geojson_wkt(v, sfc, properties, 0, geometry_types, wkt_objects, property_keys, doc_properties, property_types); } -void parse_geojson_array_wkt(Document& d, int i, int& wkt_objects) { +void parse_geojson_array_wkt(Document& d, + Rcpp::List& sfc, + Rcpp::List& properties, + int i, + std::set< std::string >& geometry_types, + int& wkt_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string>& property_types + ) { const Value& v = d[i]; - parse_geojson_wkt(v, i, wkt_objects); + parse_geojson_wkt(v, sfc, properties, i, geometry_types, wkt_objects, property_keys, doc_properties, property_types); } -Rcpp::List geojson_to_wkt(const char* geojson, int& wkt_objects) { +Rcpp::List geojson_to_wkt(const char* geojson, + std::set< std::string >& geometry_types, + int& wkt_objects, + std::set< std::string >& property_keys, + Document& doc_properties, + std::map< std::string, std::string >& property_types + ) { // TODO: // grab the geometry data and stream to WKT Document d; safe_parse(d, geojson); - Rcpp::List wkt(1); + Rcpp::List sf(1); + Rcpp::List sfc(1); + Rcpp::List properties(1); std::ostringstream os; // Need to 'recurse' into the GeoJSON like what i did for geo_sf // because it can be arrays, objects, vectors. if (d.IsObject()) { + Rcpp::List sfg(1); + parse_geojson_object_wkt(d, sfg, properties, geometry_types, wkt_objects, property_keys, doc_properties, property_types); + sfc[0] = sfg; } else if (d.IsArray()) { } - - return wkt; - + return sfc; } // [[Rcpp::export]] -Rcpp::StringVector rcpp_geojson_to_wkt(Rcpp::StringVector geojson) { +Rcpp::List rcpp_geojson_to_wkt(Rcpp::StringVector geojson) { + + // iterate over the geojson int n = geojson.size(); - Rcpp::StringVector wkt(n); - int wkt_objects = 0; + int wkt_objects = 0; // keep track of number of objects + int row_index; - for (int i = 0; i < n; i++) { - wkt[i] = geojson_to_wkt(geojson[i], wkt_objects); + // Attributes to keep track of along the way + std::set< std::string > geometry_types = start_geometry_types(); + std::set< std::string > property_keys; // storing all the 'key' values from 'properties' + std::map< std::string, std::string> property_types; + + Document doc_properties; // Document to store the 'properties' + doc_properties.SetObject(); + Rcpp::List sfc(n); + + for (int geo_ele = 0; geo_ele < n; geo_ele++) { + sfc[geo_ele] = geojson_to_wkt(geojson[geo_ele], geometry_types, wkt_objects, property_keys, doc_properties, property_types); } - return wkt; + return sfc; } From 050775d36cf6605b06dc291517674f5eba281ad6 Mon Sep 17 00:00:00 2001 From: David Cooley Date: Fri, 6 Apr 2018 15:48:15 +1000 Subject: [PATCH 4/7] forgot to save --- src/geojson_to_wkt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index f8b055f..2ce5079 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -205,7 +205,7 @@ Rcpp::List parse_feature_object_wkt(const Value& feature, //https://stackoverflow.com/a/33473321/5977215 - std::string s = std::to_string(sfg_objects); + std::string s = std::to_string(wkt_objects); Value n(s.c_str(), doc_properties.GetAllocator()); // TODO: is this method deep-cloning? From fefa9e873013860c67de8b9ebf43e87720890ca5 Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Sat, 7 Apr 2018 16:42:11 +1000 Subject: [PATCH 5/7] wkt --- R/scratch.R | 65 ++++++++++ inst/include/geojson_sfg.h | 16 +++ inst/include/geojsonsf.h | 15 +-- src/RcppExports.cpp | 2 +- src/geojson_sfc.cpp | 13 ++ src/geojson_sfg.cpp | 82 ++++++++++-- src/geojson_to_sf.cpp | 27 +++- src/geojson_to_wkt.cpp | 206 ++++++++++++------------------- src/geojson_wkt.cpp | 124 +++++++++++++++++++ src/geojson_wkt.h | 32 +++++ tests/testthat/test-geometries.R | 14 +++ 11 files changed, 445 insertions(+), 151 deletions(-) create mode 100644 src/geojson_wkt.cpp create mode 100644 src/geojson_wkt.h diff --git a/R/scratch.R b/R/scratch.R index b93940e..bd94c21 100644 --- a/R/scratch.R +++ b/R/scratch.R @@ -3,6 +3,8 @@ ## - test erroneous inputs ## - no need to use / call / create 'properties' in construct_sfc +## - don't attach 'sfc' or 'sf' attributes + ## ERRORS # geojson_sf(1:5) # geojson_sf("a") @@ -10,3 +12,66 @@ # geojson_sf(NULL) # geojson_sf(NA) # geojson_sf('{ "type" : "Point" }') + + +# p <- '{ "type" : "Point", "coordinates" : [0, 0] }' +# +# mp <- '{ "type" : "MultiPoint", "coordinates" : [ [0, 0], [2.324, 2] ] }' +# +# ls <- '{ "type" : "LineString", "coordinates" : [ [0, 0], [1, 1] ] }' +# +# ml <- '{"type": "MultiLineString","coordinates": [ +# [[100.0, 0.0],[101.0, 1.0]],[[102.0, 2.0],[103.0, 3.0]] +# ]}' +# +# poly <- '{"type": "Polygon","coordinates": [[ +# [180.0, 40.0], [180.0, 50.0], [170.0, 50.0], +# [170.0, 40.0], [180.0, 40.0] +# ]]}' +# +# mpoly <- '{ +# "type": "MultiPolygon", +# "coordinates": [ +# [ +# [ +# [180.0, 40.0], +# [180.0, 50.0], +# [170.0, 50.0], +# [170.0, 40.0], +# [180.0, 40.0] +# ], +# [ +# [0, 0], +# [1, 1], +# [2, 2], +# [2, 0] +# ] +# ], +# [ +# [ +# [-170.0, 40.0], +# [-170.0, 50.0], +# [-180.0, 50.0], +# [-180.0, 40.0], +# [-170.0, 40.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]]} +# ] +# }' + +# geojson_wkt(p) +# geojson_wkt(mp) +# geojson_wkt(ls) +# geojson_wkt(ml) +# geojson_wkt(poly) +# geojson_wkt(mpoly) +# geojson_wkt(gc) diff --git a/inst/include/geojson_sfg.h b/inst/include/geojson_sfg.h index a990f9b..5003853 100644 --- a/inst/include/geojson_sfg.h +++ b/inst/include/geojson_sfg.h @@ -10,20 +10,36 @@ using namespace rapidjson; double get_lon(const Value& coord_array); double get_lat(const Value& coord_array); +Rcpp::NumericVector parse_point(const Value& coord_array); + Rcpp::NumericVector parse_point(const Value& coord_array, Rcpp::NumericVector& bbox); +Rcpp::NumericMatrix parse_line(const Value& coord_array); + Rcpp::NumericMatrix parse_line(const Value& coord_array, Rcpp::NumericVector& bbox); +Rcpp::NumericVector get_point(const Value& point_array); + Rcpp::NumericVector get_point(const Value& point_array, Rcpp::NumericVector& bbox); +Rcpp::NumericMatrix get_multi_point(const Value& multi_point_array); + Rcpp::NumericMatrix get_multi_point(const Value& multi_point_array, Rcpp::NumericVector& bbox); +Rcpp::NumericMatrix get_line_string(const Value& line_array); + Rcpp::NumericMatrix get_line_string(const Value& line_array, Rcpp::NumericVector& bbox); +Rcpp::List get_multi_line_string(const Value& multi_line_array); + Rcpp::List get_multi_line_string(const Value& multi_line_array, Rcpp::NumericVector& bbox); +Rcpp::List get_polygon(const Value& polygon_array); + Rcpp::List get_polygon(const Value& polygon_array, Rcpp::NumericVector& bbox); +Rcpp::List get_multi_polygon(const Value& multi_polygon_array); + Rcpp::List get_multi_polygon(const Value& multi_polygon_array, Rcpp::NumericVector& bbox); diff --git a/inst/include/geojsonsf.h b/inst/include/geojsonsf.h index 6dd27be..fd99e8e 100644 --- a/inst/include/geojsonsf.h +++ b/inst/include/geojsonsf.h @@ -14,12 +14,13 @@ namespace geojsonsf { { "Null", "False", "True", "Object", "Array", "String", "Number" }; } -#define UNKNOWN 0 -#define POINT 1 -#define MULTIPOINT 2 -#define LINESTRING 3 -#define MULTILINESTRING 4 -#define POLYGON 5 -#define MULTIPOLYGON 6 +#define UNKNOWN 0 +#define POINT 1 +#define MULTIPOINT 2 +#define LINESTRING 3 +#define MULTILINESTRING 4 +#define POLYGON 5 +#define MULTIPOLYGON 6 +#define GEOMETRYCOLLECTION 7 #endif diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 2b38b0a..5379b28 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -29,7 +29,7 @@ BEGIN_RCPP END_RCPP } // rcpp_geojson_to_wkt -Rcpp::StringVector rcpp_geojson_to_wkt(Rcpp::StringVector geojson); +Rcpp::List rcpp_geojson_to_wkt(Rcpp::StringVector geojson); RcppExport SEXP _geojsonsf_rcpp_geojson_to_wkt(SEXP geojsonSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; diff --git a/src/geojson_sfc.cpp b/src/geojson_sfc.cpp index 9593715..a95b505 100644 --- a/src/geojson_sfc.cpp +++ b/src/geojson_sfc.cpp @@ -92,6 +92,7 @@ void fetch_geometries(Rcpp::List& sf, Rcpp::List& res, int& sfg_counter) { std::string geom_attr; + for (Rcpp::List::iterator it = sf.begin(); it != sf.end(); it++) { switch( TYPEOF(*it) ) { @@ -129,6 +130,18 @@ void fetch_geometries(Rcpp::List& sf, Rcpp::List& res, int& sfg_counter) { } break; } + case STRSXP: { + Rcpp::StringVector tmp = as(*it); + if(Rf_isNull(tmp.attr("class"))){ + // TODO: + //handle missing geo_type in vector + Rcpp::stop("Geometry could not be determined"); + } else { + res[sfg_counter] = tmp; + sfg_counter++; + } + break; + } default: { Rcpp::stop("Geometry could not be determined"); } diff --git a/src/geojson_sfg.cpp b/src/geojson_sfg.cpp index d5329df..6fb4c3d 100644 --- a/src/geojson_sfg.cpp +++ b/src/geojson_sfg.cpp @@ -19,20 +19,39 @@ double get_lat(const Value& coord_array) { return coord_array[1].GetDouble(); } +Rcpp::NumericVector parse_point(const Value& coord_array) { + Rcpp::NumericVector point(2); + point[0] = get_lon(coord_array); + point[1] = get_lat(coord_array); + return point; +} + Rcpp::NumericVector parse_point(const Value& coord_array, Rcpp::NumericVector& bbox) { - Rcpp::NumericVector point(2); - point[0] = get_lon(coord_array); - point[1] = get_lat(coord_array); + Rcpp::NumericVector point = parse_point(coord_array); calculate_bbox(bbox, point); return point; } +Rcpp::NumericVector get_point(const Value& point_array) { + Rcpp::NumericVector point = parse_point(point_array); + return point; +} + Rcpp::NumericVector get_point(const Value& point_array, Rcpp::NumericVector& bbox) { Rcpp::NumericVector point = parse_point(point_array, bbox); - point.attr("class") = sfg_attributes("POINT"); return point; } + +Rcpp::NumericMatrix parse_line(const Value& coord_array) { + size_t n = coord_array.Size(); + Rcpp::NumericMatrix line_string(n, 2); + for (int i = 0; i < n; i++) { + line_string(i, _) = parse_point(coord_array[i]); + } + return line_string; +} + Rcpp::NumericMatrix parse_line(const Value& coord_array, Rcpp::NumericVector& bbox) { size_t n = coord_array.Size(); Rcpp::NumericMatrix line_string(n, 2); @@ -42,18 +61,36 @@ Rcpp::NumericMatrix parse_line(const Value& coord_array, Rcpp::NumericVector& bb return line_string; } +Rcpp::NumericMatrix get_multi_point(const Value& multi_point_array) { + Rcpp::NumericMatrix multi_point = parse_line(multi_point_array); + return multi_point; +} + Rcpp::NumericMatrix get_multi_point(const Value& multi_point_array, Rcpp::NumericVector& bbox) { Rcpp::NumericMatrix multi_point = parse_line(multi_point_array, bbox); - multi_point.attr("class") = sfg_attributes("MULTIPOINT"); return multi_point; } +Rcpp::NumericMatrix get_line_string(const Value& line_array) { + Rcpp::NumericMatrix line_string = parse_line(line_array); + return line_string; +} + Rcpp::NumericMatrix get_line_string(const Value& line_array, Rcpp::NumericVector& bbox) { Rcpp::NumericMatrix line_string = parse_line(line_array, bbox); - line_string.attr("class") = sfg_attributes("LINESTRING"); return line_string; } +Rcpp::List get_multi_line_string(const Value& multi_line_array) { + size_t n = multi_line_array.Size(); + Rcpp::List multi_line(n); + + for (int i = 0; i < n; i++) { + multi_line[i] = parse_line(multi_line_array[i]); + } + return multi_line; +} + Rcpp::List get_multi_line_string(const Value& multi_line_array, Rcpp::NumericVector& bbox) { size_t n = multi_line_array.Size(); Rcpp::List multi_line(n); @@ -61,10 +98,19 @@ Rcpp::List get_multi_line_string(const Value& multi_line_array, Rcpp::NumericVec for (int i = 0; i < n; i++) { multi_line[i] = parse_line(multi_line_array[i], bbox); } - multi_line.attr("class") = sfg_attributes("MULTILINESTRING"); return multi_line; } +Rcpp::List get_polygon(const Value& polygon_array) { + size_t n = polygon_array.Size(); + Rcpp::List polygon(n); + + for (int i = 0; i < n; i++) { + polygon[i] = parse_line(polygon_array[i]); + } + return polygon; +} + Rcpp::List get_polygon(const Value& polygon_array, Rcpp::NumericVector& bbox) { size_t n = polygon_array.Size(); Rcpp::List polygon(n); @@ -72,17 +118,31 @@ Rcpp::List get_polygon(const Value& polygon_array, Rcpp::NumericVector& bbox) { for (int i = 0; i < n; i++) { polygon[i] = parse_line(polygon_array[i], bbox); } - - polygon.attr("class") = sfg_attributes("POLYGON"); return polygon; } +Rcpp::List get_multi_polygon(const Value& multi_polygon_array) { + size_t n = multi_polygon_array.Size(); + Rcpp::List multi_polygon(n); + + for (int i = 0; i < n; i++) { + size_t np = multi_polygon_array[i].Size(); + Rcpp::List polygon(np); + const Value& polygon_array = multi_polygon_array[i]; + + for (int j = 0; j < np; j++) { + polygon[j] = parse_line(polygon_array[j]); + } + multi_polygon[i] = polygon; + } + return multi_polygon; +} + Rcpp::List get_multi_polygon(const Value& multi_polygon_array, Rcpp::NumericVector& bbox) { size_t n = multi_polygon_array.Size(); Rcpp::List multi_polygon(n); for (int i = 0; i < n; i++) { - size_t np = multi_polygon_array[i].Size(); Rcpp::List polygon(np); const Value& polygon_array = multi_polygon_array[i]; @@ -92,8 +152,6 @@ Rcpp::List get_multi_polygon(const Value& multi_polygon_array, Rcpp::NumericVect } multi_polygon[i] = polygon; } - - multi_polygon.attr("class") = sfg_attributes("MULTIPOLYGON"); return multi_polygon; } diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index 65895f9..a229af8 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -31,28 +31,40 @@ void parse_geometry_object(Rcpp::List& sfc, geometry_types.insert(geom_type); if (geom_type == "Point") { - sfc[i] = get_point(coord_array, bbox); + Rcpp::NumericVector point = get_point(coord_array, bbox); + point.attr("class") = sfg_attributes("POINT"); + sfc[i] = point; //sfc_classes[counter] = "POINT"; } else if (geom_type == "MultiPoint") { - sfc[i] = get_multi_point(coord_array, bbox); + Rcpp::NumericMatrix multi_point = get_multi_point(coord_array, bbox); + multi_point.attr("class") = sfg_attributes("MULTIPOINT"); //sfc_classes[counter] = "MULTIPOINT"; + sfc[i] = multi_point; } else if (geom_type == "LineString") { - sfc[i] = get_line_string(coord_array, bbox); + Rcpp::NumericMatrix line_string = get_line_string(coord_array, bbox); + line_string.attr("class") = sfg_attributes("LINESTRING"); + sfc[i] = line_string; //sfc_classes[counter] = "LINESTRING"; } else if (geom_type == "MultiLineString") { - sfc[i] = get_multi_line_string(coord_array, bbox); + Rcpp::List multi_line = get_multi_line_string(coord_array, bbox); + multi_line.attr("class") = sfg_attributes("MULTILINESTRING"); //sfc_classes[counter] = "MULTILINESTRING"; + sfc[i] = multi_line; } else if (geom_type == "Polygon") { - sfc[i] = get_polygon(coord_array, bbox); + Rcpp::List polygon = get_polygon(coord_array, bbox); + polygon.attr("class") = sfg_attributes("POLYGON"); //sfc_classes[counter] = "POLYGON"; + sfc[i] = polygon; } else if (geom_type == "MultiPolygon") { - sfc[i] = get_multi_polygon(coord_array, bbox); + Rcpp::List multi_polygon = get_multi_polygon(coord_array, bbox); + multi_polygon.attr("class") = sfg_attributes("MULTIPOLYGON"); //sfc_classes[counter] = "MULTIPOLYGON"; + sfc[i] = multi_polygon; } else { Rcpp::stop("unknown sfg type"); @@ -65,6 +77,8 @@ Rcpp::List parse_geometry_collection_object(const Value& val, int& sfg_objects) { std::string geom_type; + // TODO: + // - validate geometries element auto geometries = val["geometries"].GetArray(); int n = geometries.Size(); @@ -72,6 +86,7 @@ Rcpp::List parse_geometry_collection_object(const Value& val, for (int i = 0; i < n; i++) { const Value& gcval = geometries[i]; + validate_type(gcval, sfg_objects); geom_type = gcval["type"].GetString(); parse_geometry_object(geom_collection, i, gcval, bbox, geometry_types, sfg_objects); } diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index 2ce5079..ac60d58 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -4,122 +4,14 @@ #include "geojsonsf.h" #include "geojson_sfc.h" #include "geojson_sfg.h" +#include "geojson_to_sf.h" #include "geojson_validate.h" #include "geojson_properties.h" +#include "geojson_wkt.h" using namespace rapidjson; using namespace Rcpp; - -void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ) { - os << lon << " " << lat; -} - -void encode_wkt_point(std::ostringstream& os, const Value& coord_array) { - Rcpp::NumericVector point(2); - point[0] = get_lon(coord_array); - point[1] = get_lat(coord_array); - - addLonLatToWKTStream(os, point[0]*(float)1e-5, point[1]*(float)1e-5); -} - -void parse_line_to_wkt(std::ostringstream& os, const Value& coord_array) { - size_t n = coord_array.Size(); - - for (int i = 0; i < n; i++) { - encode_wkt_point(os, coord_array[i]); - } -} - - - -void geom_type(const char *cls, int *tp = NULL) { -/* - int type = 0; - if (strcmp(cls, "POINT") == 0) - type = POINT; - else if (strcmp(cls, "MULTIPOINT") == 0) - type = MULTIPOINT; - else if (strcmp(cls, "LINESTRING") == 0) - type = LINESTRING; - else if (strcmp(cls, "POLYGON") == 0) - type = POLYGON; - else if (strcmp(cls, "MULTILINESTRING") == 0) - type = MULTILINESTRING; - else if (strcmp(cls, "MULTIPOLYGON") == 0) - type = MULTIPOLYGON; - else - type = UNKNOWN; - if (tp != NULL) - *tp = type; - */ -} - -void beginWKT(std::ostringstream& os, Rcpp::CharacterVector cls) { - - int tp; - geom_type(cls[1], &tp); - - switch( tp ) { - case POINT: - os << "POINT "; - break; - case MULTIPOINT: - os << "MULTIPOINT ("; - break; - case LINESTRING: - os << "LINESTRING "; - break; - case MULTILINESTRING: - os << "MULTILINESTRING ("; - break; - case POLYGON: - os << "POLYGON ("; - break; - case MULTIPOLYGON: - os << "MULTIPOLYGON (("; - break; - default: { - Rcpp::stop("Unknown geometry type"); - } - } -} - -void endWKT(std::ostringstream& os, Rcpp::CharacterVector cls) { - - int tp; - geom_type(cls[1], &tp); - - switch( tp ) { - case POINT: - os << ""; - break; - case MULTIPOINT: - os << ")"; - break; - case LINESTRING: - os << ""; - break; - case MULTILINESTRING: - os << ")"; - break; - case POLYGON: - os << ")"; - break; - case MULTIPOLYGON: - os << "))"; - break; - default: { - Rcpp::stop("Unknown geometry type"); - } - } -} - -void coordSeparateWKT(std::ostringstream& os) { - os << ", "; -} - - void parse_geometry_object_wkt(Rcpp::List& sfc, int i, const Value& geometry, @@ -133,53 +25,78 @@ void parse_geometry_object_wkt(Rcpp::List& sfc, const Value& coord_array = geometry["coordinates"]; geometry_types.insert(geom_type); + std::ostringstream os; + Rcpp::StringVector wkt; + beginWKT(os, geom_type); + if (geom_type == "Point") { - sfc[i] = get_point(coord_array); - //sfc_classes[counter] = "POINT"; + point_to_wkt(os, coord_array); } else if (geom_type == "MultiPoint") { - sfc[i] = get_multi_point(coord_array); - //sfc_classes[counter] = "MULTIPOINT"; + multi_point_to_wkt(os, coord_array); } else if (geom_type == "LineString") { - sfc[i] = get_line_string(coord_array); - //sfc_classes[counter] = "LINESTRING"; + line_string_to_wkt(os, coord_array); } else if (geom_type == "MultiLineString") { - sfc[i] = get_multi_line_string(coord_array); - //sfc_classes[counter] = "MULTILINESTRING"; + multi_line_string_to_wkt(os, coord_array); } else if (geom_type == "Polygon") { - sfc[i] = get_polygon(coord_array); - //sfc_classes[counter] = "POLYGON"; + polygon_to_wkt(os, coord_array); } else if (geom_type == "MultiPolygon") { - sfc[i] = get_multi_polygon(coord_array); - //sfc_classes[counter] = "MULTIPOLYGON"; + multi_polygon_to_wkt(os, coord_array); } else { Rcpp::stop("unknown sfg type"); } + + endWKT(os, geom_type); + + wkt = os.str(); + transform(geom_type.begin(), geom_type.end(), geom_type.begin(), ::toupper); + wkt.attr("class") = sfg_attributes(geom_type); + sfc[i] = wkt; + } + Rcpp::List parse_geometry_collection_object_wkt(const Value& val, std::set< std::string >& geometry_types, int& wkt_objects) { std::string geom_type; + // TODO: + // - validate geometries + //validate_geometry(val, wkt_objects); auto geometries = val["geometries"].GetArray(); int n = geometries.Size(); Rcpp::List geom_collection(n); + Rcpp::List geom_collection_wkt(1); for (int i = 0; i < n; i++) { const Value& gcval = geometries[i]; + validate_type(gcval, wkt_objects); geom_type = gcval["type"].GetString(); parse_geometry_object_wkt(geom_collection, i, gcval, geometry_types, wkt_objects); } - geom_collection.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); - return geom_collection; + // collapse into a single WKT string + std::ostringstream os; + os << "GEOMETRYCOLLECTION ("; + for (int i = 0; i < n; i++) { + std::string g = geom_collection[i]; + os << g; + coordSeparateWKT(os, i, n); + } + os << ")"; + + geom_collection_wkt = os.str(); + + geom_collection_wkt.attr("class") = sfg_attributes("GEOMETRYCOLLECTION"); + + return geom_collection_wkt; } @@ -335,6 +252,42 @@ Rcpp::List geojson_to_wkt(const char* geojson, return sfc; } +Rcpp::List construct_wkt(int& sfg_objects, + Rcpp::List& sf, + std::set< std::string >& geometry_types) { + + Rcpp::List sfc_output(sfg_objects); + std::string geom_attr; + + int sfg_counter = 0; + fetch_geometries(sf, sfc_output, sfg_counter); + return sfc_output; +} + + +Rcpp::List construct_wkt_df(Rcpp::List& lst, std::set< std::string >& property_keys, + std::map< std::string, std::string>& property_types, + Document& doc_properties, + int& wkt_objects, + int& row_index) { + + Rcpp::List properties(property_keys.size() + 1); // expand to include geometry + + property_keys.insert("geometry"); + properties.names() = property_keys; + properties["geometry"] = lst; + + setup_property_vectors(property_types, properties, wkt_objects); + fill_property_vectors(doc_properties, property_types, properties, row_index); + + Rcpp::IntegerVector nv = seq(1, wkt_objects); + properties.attr("class") = Rcpp::CharacterVector::create("data.frame"); + properties.attr("wkt_column") = "geometry"; + properties.attr("row.names") = nv; + + return properties; +} + // [[Rcpp::export]] Rcpp::List rcpp_geojson_to_wkt(Rcpp::StringVector geojson) { @@ -356,6 +309,9 @@ Rcpp::List rcpp_geojson_to_wkt(Rcpp::StringVector geojson) { sfc[geo_ele] = geojson_to_wkt(geojson[geo_ele], geometry_types, wkt_objects, property_keys, doc_properties, property_types); } + Rcpp::List res = construct_wkt(wkt_objects, sfc, geometry_types); + Rcpp::List wkt = construct_wkt_df(res, property_keys, property_types, doc_properties, wkt_objects, row_index); - return sfc; + return wkt; } + diff --git a/src/geojson_wkt.cpp b/src/geojson_wkt.cpp new file mode 100644 index 0000000..aee7480 --- /dev/null +++ b/src/geojson_wkt.cpp @@ -0,0 +1,124 @@ + +#include "rapidjson/document.h" +#include +#include "geojsonsf.h" +#include "geojson_sfg.h" +using namespace rapidjson; +using namespace Rcpp; + +void beginWKT(std::ostringstream& os, std::string& geom_type) { + + if (geom_type == "Point") { + os << "POINT ("; + } else if (geom_type == "MultiPoint") { + os << "MULTIPOINT (("; + } else if (geom_type == "LineString") { + os << "LINESTRING ("; + } else if (geom_type == "MultiLineString") { + os << "MULTILINESTRING (("; + } else if (geom_type == "Polygon") { + os << "POLYGON (("; + } else if (geom_type == "MultiPolygon") { + os << "MULTIPOLYGON ((("; + } else if (geom_type == "GeometryCollection") { + os << "GEOMETRYCOLLECTION ("; + } +} + +void endWKT(std::ostringstream& os, std::string& geom_type) { + + if (geom_type == "Point") { + os << ")"; + } else if (geom_type == "MultiPoint") { + os << "))"; + } else if (geom_type == "LineString") { + os << ")"; + } else if (geom_type == "MultiLineString") { + os << "))"; + } else if (geom_type == "Polygon") { + os << "))"; + } else if (geom_type == "MultiPolygon") { + os << ")))"; + } else if (geom_type == "GeometryCollection") { + os << ")"; + } +} + +void coordSeparateWKT(std::ostringstream& os, int i, int n) { + if (i < (n - 1) ) { + os << ", "; + } +} + +void lineSeparateWKT(std::ostringstream& os, int i, int n) { + if (i < (n - 1) ) { + os << "),("; + } +} + +void polygonSeparateWKT(std::ostringstream& os, int i, int n) { + if (i < (n - 1) ) { + os << ")),(("; + } +} + + +void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ) { + os << lon << " " << lat; +} + +void point_to_wkt(std::ostringstream& os, const Value& coord_array) { + // TODO: + // return the WKT representation of a point + Rcpp::NumericVector point(2); + point[0] = get_lon(coord_array); + point[1] = get_lat(coord_array); + addLonLatToWKTStream(os, point[0], point[1]); +} + + +void multi_point_to_wkt(std::ostringstream& os, const Value& coord_array) { + size_t n = coord_array.Size(); + for (int i = 0; i < n; i++) { + point_to_wkt(os, coord_array[i]); + coordSeparateWKT(os, i, n); + } +} + +void line_string_to_wkt(std::ostringstream& os, const Value& coord_array) { + size_t n = coord_array.Size(); + + for (int i = 0; i < n; i++) { + point_to_wkt(os, coord_array[i]); + coordSeparateWKT(os, i, n); + } +} + +void multi_line_string_to_wkt(std::ostringstream& os, const Value& coord_array) { + size_t n = coord_array.Size(); + + for (int i = 0; i < n; i++) { + line_string_to_wkt(os, coord_array[i]); + lineSeparateWKT(os, i, n); + } + +} + +void polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { + size_t n = coord_array.Size(); + + for (int i = 0; i < n; i++) { + line_string_to_wkt(os, coord_array[i]); + lineSeparateWKT(os, i, n); + } +} + +void multi_polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { + size_t n = coord_array.Size(); + for (int i = 0; i < n; i++) { + polygon_to_wkt(os, coord_array[i]); + polygonSeparateWKT(os, i, n); + } +} + + diff --git a/src/geojson_wkt.h b/src/geojson_wkt.h new file mode 100644 index 0000000..e24c38d --- /dev/null +++ b/src/geojson_wkt.h @@ -0,0 +1,32 @@ + +#ifndef GEOJSON_WKT_H +#define GEOJSON_WKT_H + +#include "rapidjson/document.h" + +void coordSeparateWKT(std::ostringstream& os, int i, int n); + +void lineSeparateWKT(std::ostringstream& os, int i, int n); + +void polygonSeparateWKT(std::ostringstream& os, int i, int n); + +void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ); + +void beginWKT(std::ostringstream& os, std::string& geom_type); + +void endWKT(std::ostringstream& os, std::string& geom_type); + +void point_to_wkt(std::ostringstream& os, const Value& coord_array); + +void multi_point_to_wkt(std::ostringstream& os, const Value& coord_array); + +void line_string_to_wkt(std::ostringstream& os, const Value& coord_array); + +void multi_line_string_to_wkt(std::ostringstream& os, const Value& coord_array); + +void polygon_to_wkt(std::ostringstream& os, const Value& coord_array); + +void multi_polygon_to_wkt(std::ostringstream& os, const Value& coord_array); + +#endif + diff --git a/tests/testthat/test-geometries.R b/tests/testthat/test-geometries.R index 75605d8..152666e 100644 --- a/tests/testthat/test-geometries.R +++ b/tests/testthat/test-geometries.R @@ -61,5 +61,19 @@ test_that("geometries are parsed correctly", { all(c("sfc_MULTIPOLYGON", "sfc") %in% attr(sf$geometry, "class")) ) + 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]]} + ] + }' + + sf <- geojson_sf(js) + expect_true( + all(c("sfc_GEOMETRY", "sfc") %in% attr(sf$geometry, "class")) + ) + }) From d1c001bfac8013a0681f17c17c9af2c751816565 Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Sat, 7 Apr 2018 18:32:18 +1000 Subject: [PATCH 6/7] wkt tests --- R/scratch.R | 34 +++++++++++---- inst/include/geojson_validate.h | 4 ++ src/geojson_sfc.cpp | 2 - src/geojson_sfg.cpp | 74 ++++----------------------------- src/geojson_to_sf.cpp | 1 + src/geojson_to_wkt.cpp | 1 + src/geojson_validate.cpp | 18 ++++++++ src/geojson_wkt.cpp | 6 +++ tests/testthat/test-errors.R | 32 ++++++++++++++ 9 files changed, 96 insertions(+), 76 deletions(-) create mode 100644 tests/testthat/test-errors.R diff --git a/R/scratch.R b/R/scratch.R index bd94c21..f37fc4c 100644 --- a/R/scratch.R +++ b/R/scratch.R @@ -5,13 +5,9 @@ ## - don't attach 'sfc' or 'sf' attributes -## ERRORS -# geojson_sf(1:5) -# geojson_sf("a") -# geojson_sf() -# geojson_sf(NULL) -# geojson_sf(NA) -# geojson_sf('{ "type" : "Point" }') +## TODO: +## - handle incorrectly formed geometry type +## -- e.g., MultiPoint with only one-nested array # p <- '{ "type" : "Point", "coordinates" : [0, 0] }' @@ -75,3 +71,27 @@ # geojson_wkt(poly) # geojson_wkt(mpoly) # geojson_wkt(gc) + +## wellknown can't handle properties? +# install.packages("wellknown") +# +# library(RCurl) +# myurl <- "http://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_050_00_500k.json" +# geo <- readLines(url(myurl)) +# geo <- paste0(geo, collapse = "") +# +# library(microbenchmark) +# +# str <- '{"type":"LineString","coordinates":[[0,0,10],[2,1,20],[4,2,30],[5,4,40]]}' +# wellknown::geojson2wkt(str) +# +# microbenchmark( +# geojsonsf = { +# geojson_wkt(geo) +# }, +# wellknown = { +# wellknown::geojson2wkt(geo) +# }, +# times = 2 +# ) + diff --git a/inst/include/geojson_validate.h b/inst/include/geojson_validate.h index 09d179d..040a70e 100644 --- a/inst/include/geojson_validate.h +++ b/inst/include/geojson_validate.h @@ -10,6 +10,10 @@ void safe_parse(Document& d, const char* geojson); void validate_type(const Value& v, int& sfg_objects); +void validate_array(const Value& v); + +void validate_array(const Value& v, int& sfg_objects); + void validate_features(const Value& v, int& sfg_objects); void validate_feature(const Value& v, int& sfg_objects); diff --git a/src/geojson_sfc.cpp b/src/geojson_sfc.cpp index a95b505..352e519 100644 --- a/src/geojson_sfc.cpp +++ b/src/geojson_sfc.cpp @@ -150,8 +150,6 @@ void fetch_geometries(Rcpp::List& sf, Rcpp::List& res, int& sfg_counter) { } - - Rcpp::List construct_sfc(int& sfg_objects, Rcpp::List& sf, Rcpp::NumericVector& bbox, diff --git a/src/geojson_sfg.cpp b/src/geojson_sfg.cpp index 6fb4c3d..226dc80 100644 --- a/src/geojson_sfg.cpp +++ b/src/geojson_sfg.cpp @@ -3,6 +3,7 @@ #include #include "geojson_sfg.h" #include "geojson_sfc.h" +#include "geojson_validate.h" using namespace Rcpp; using namespace rapidjson; @@ -19,135 +20,74 @@ double get_lat(const Value& coord_array) { return coord_array[1].GetDouble(); } -Rcpp::NumericVector parse_point(const Value& coord_array) { +Rcpp::NumericVector parse_point(const Value& coord_array, Rcpp::NumericVector& bbox) { Rcpp::NumericVector point(2); point[0] = get_lon(coord_array); point[1] = get_lat(coord_array); - return point; -} - -Rcpp::NumericVector parse_point(const Value& coord_array, Rcpp::NumericVector& bbox) { - Rcpp::NumericVector point = parse_point(coord_array); calculate_bbox(bbox, point); return point; } -Rcpp::NumericVector get_point(const Value& point_array) { - Rcpp::NumericVector point = parse_point(point_array); - return point; -} - Rcpp::NumericVector get_point(const Value& point_array, Rcpp::NumericVector& bbox) { Rcpp::NumericVector point = parse_point(point_array, bbox); return point; } - -Rcpp::NumericMatrix parse_line(const Value& coord_array) { - size_t n = coord_array.Size(); - Rcpp::NumericMatrix line_string(n, 2); - for (int i = 0; i < n; i++) { - line_string(i, _) = parse_point(coord_array[i]); - } - return line_string; -} - Rcpp::NumericMatrix parse_line(const Value& coord_array, Rcpp::NumericVector& bbox) { size_t n = coord_array.Size(); Rcpp::NumericMatrix line_string(n, 2); for (int i = 0; i < n; i++) { + validate_array(coord_array[i]); line_string(i, _) = parse_point(coord_array[i], bbox); } return line_string; } -Rcpp::NumericMatrix get_multi_point(const Value& multi_point_array) { - Rcpp::NumericMatrix multi_point = parse_line(multi_point_array); - return multi_point; -} Rcpp::NumericMatrix get_multi_point(const Value& multi_point_array, Rcpp::NumericVector& bbox) { Rcpp::NumericMatrix multi_point = parse_line(multi_point_array, bbox); return multi_point; } -Rcpp::NumericMatrix get_line_string(const Value& line_array) { - Rcpp::NumericMatrix line_string = parse_line(line_array); - return line_string; -} - Rcpp::NumericMatrix get_line_string(const Value& line_array, Rcpp::NumericVector& bbox) { Rcpp::NumericMatrix line_string = parse_line(line_array, bbox); return line_string; } -Rcpp::List get_multi_line_string(const Value& multi_line_array) { - size_t n = multi_line_array.Size(); - Rcpp::List multi_line(n); - - for (int i = 0; i < n; i++) { - multi_line[i] = parse_line(multi_line_array[i]); - } - return multi_line; -} - Rcpp::List get_multi_line_string(const Value& multi_line_array, Rcpp::NumericVector& bbox) { size_t n = multi_line_array.Size(); Rcpp::List multi_line(n); for (int i = 0; i < n; i++) { + validate_array(multi_line_array[i]); multi_line[i] = parse_line(multi_line_array[i], bbox); } return multi_line; } -Rcpp::List get_polygon(const Value& polygon_array) { - size_t n = polygon_array.Size(); - Rcpp::List polygon(n); - - for (int i = 0; i < n; i++) { - polygon[i] = parse_line(polygon_array[i]); - } - return polygon; -} - Rcpp::List get_polygon(const Value& polygon_array, Rcpp::NumericVector& bbox) { size_t n = polygon_array.Size(); Rcpp::List polygon(n); for (int i = 0; i < n; i++) { + validate_array(polygon_array[i]); polygon[i] = parse_line(polygon_array[i], bbox); } return polygon; } -Rcpp::List get_multi_polygon(const Value& multi_polygon_array) { - size_t n = multi_polygon_array.Size(); - Rcpp::List multi_polygon(n); - - for (int i = 0; i < n; i++) { - size_t np = multi_polygon_array[i].Size(); - Rcpp::List polygon(np); - const Value& polygon_array = multi_polygon_array[i]; - - for (int j = 0; j < np; j++) { - polygon[j] = parse_line(polygon_array[j]); - } - multi_polygon[i] = polygon; - } - return multi_polygon; -} - Rcpp::List get_multi_polygon(const Value& multi_polygon_array, Rcpp::NumericVector& bbox) { size_t n = multi_polygon_array.Size(); Rcpp::List multi_polygon(n); for (int i = 0; i < n; i++) { + validate_array(multi_polygon_array[i]); size_t np = multi_polygon_array[i].Size(); Rcpp::List polygon(np); const Value& polygon_array = multi_polygon_array[i]; for (int j = 0; j < np; j++) { + validate_array(polygon_array[j]); polygon[j] = parse_line(polygon_array[j], bbox); } multi_polygon[i] = polygon; diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index a229af8..c5bb1b9 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -25,6 +25,7 @@ void parse_geometry_object(Rcpp::List& sfc, validate_type(geometry, sfg_objects); validate_coordinates(geometry, sfg_objects); + validate_array(geometry["coordinates"], sfg_objects); std::string geom_type = geometry["type"].GetString(); const Value& coord_array = geometry["coordinates"]; diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index ac60d58..f7dc3d0 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -20,6 +20,7 @@ void parse_geometry_object_wkt(Rcpp::List& sfc, validate_type(geometry, wkt_objects); validate_coordinates(geometry, wkt_objects); + validate_array(geometry["coordinates"], wkt_objects); std::string geom_type = geometry["type"].GetString(); const Value& coord_array = geometry["coordinates"]; diff --git a/src/geojson_validate.cpp b/src/geojson_validate.cpp index ad128d4..b19585e 100644 --- a/src/geojson_validate.cpp +++ b/src/geojson_validate.cpp @@ -5,6 +5,11 @@ using namespace Rcpp; using namespace rapidjson; +void geojson_object_error(std::string key) { + std::string err = "Invalid " + key + " object"; + Rcpp::stop(err); +} + void geojson_object_error(std::string key, int sfg_number) { std::string err = "No '" + key + "' member at object index " + std::to_string(sfg_number) + " - invalid GeoJSON"; Rcpp::stop(err); @@ -17,6 +22,19 @@ void safe_parse(Document& d, const char* geojson) { } } +void validate_array(const Value& v) { + if ( v.IsArray() == FALSE) { + geojson_object_error("array"); + } +} + +void validate_array(const Value& v, int& sfg_objects) { + if ( v.IsArray() == FALSE) { + geojson_object_error("array", sfg_objects); + } +} + + void validate_type(const Value& v, int& sfg_objects) { if (v.HasMember("type") == FALSE) { geojson_object_error("type", sfg_objects); diff --git a/src/geojson_wkt.cpp b/src/geojson_wkt.cpp index aee7480..fce921d 100644 --- a/src/geojson_wkt.cpp +++ b/src/geojson_wkt.cpp @@ -3,6 +3,7 @@ #include #include "geojsonsf.h" #include "geojson_sfg.h" +#include "geojson_validate.h" using namespace rapidjson; using namespace Rcpp; @@ -80,6 +81,7 @@ void point_to_wkt(std::ostringstream& os, const Value& coord_array) { void multi_point_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); for (int i = 0; i < n; i++) { + validate_array(coord_array[i]); point_to_wkt(os, coord_array[i]); coordSeparateWKT(os, i, n); } @@ -89,6 +91,7 @@ void line_string_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); for (int i = 0; i < n; i++) { + validate_array(coord_array[i]); point_to_wkt(os, coord_array[i]); coordSeparateWKT(os, i, n); } @@ -98,6 +101,7 @@ void multi_line_string_to_wkt(std::ostringstream& os, const Value& coord_array) size_t n = coord_array.Size(); for (int i = 0; i < n; i++) { + validate_array(coord_array[i]); line_string_to_wkt(os, coord_array[i]); lineSeparateWKT(os, i, n); } @@ -108,6 +112,7 @@ void polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); for (int i = 0; i < n; i++) { + validate_array(coord_array[i]); line_string_to_wkt(os, coord_array[i]); lineSeparateWKT(os, i, n); } @@ -116,6 +121,7 @@ void polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { void multi_polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); for (int i = 0; i < n; i++) { + validate_array(coord_array[i]); polygon_to_wkt(os, coord_array[i]); polygonSeparateWKT(os, i, n); } diff --git a/tests/testthat/test-errors.R b/tests/testthat/test-errors.R new file mode 100644 index 0000000..10406b0 --- /dev/null +++ b/tests/testthat/test-errors.R @@ -0,0 +1,32 @@ +context("errors") + +test_that("errors are handled", { + + expect_error(geojson_sf(1:5)) + expect_error(geojson_sf("a")) + expect_error(geojson_sf()) + expect_error(geojson_sf(NULL)) + expect_error(geojson_sf(NA)) + expect_error(geojson_sf('{ "type" : "Point" }')) + + expect_error(geojson_sf('{"type" : "MultiPoint","coordinates" : [0, 0] }')) + expect_error(geojson_sf('{"type" : "Point", "coordinates" : null }')) + expect_error(geojson_sf('{"type" : "LineString", "coordinates" : [ 0, 0 ] }')) + expect_error(geojson_sf('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }')) + expect_error(geojson_sf('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }')) + expect_error(geojson_sf('{"type" : "Polygon", "coordinates" : [0, 0] }')) + expect_error(geojson_sf('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }')) + expect_error(geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }')) + expect_error(geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }')) + + expect_error(geojson_wkt('{"type" : "LineString", "coordinates" : [ 0, 0 ] }')) + expect_error(geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }')) + expect_error(geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }')) + expect_error(geojson_wkt('{"type" : "Polygon", "coordinates" : [0, 0] }')) + expect_error(geojson_wkt('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }')) + expect_error(geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }')) + expect_error(geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }')) + + expect_error(geojson_sf('{"type" : "Point" , "coordinates" : {} }')) + +}) From baa6c45ba4b2864b1beb66f757b130ea74291c8c Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Sat, 7 Apr 2018 18:48:29 +1000 Subject: [PATCH 7/7] wkt tests --- R/scratch.R | 65 ----------------- src/geojson_to_sf.cpp | 5 -- src/geojson_to_wkt.cpp | 7 ++ tests/testthat/test-errors.R | 92 ++++++++++++++++++------ tests/testthat/test-geojson_properties.R | 21 ++++++ tests/testthat/test-geojson_wkt.R | 87 ++++++++++++++++++++++ 6 files changed, 184 insertions(+), 93 deletions(-) create mode 100644 tests/testthat/test-geojson_wkt.R diff --git a/R/scratch.R b/R/scratch.R index f37fc4c..bc32855 100644 --- a/R/scratch.R +++ b/R/scratch.R @@ -5,73 +5,8 @@ ## - don't attach 'sfc' or 'sf' attributes -## TODO: -## - handle incorrectly formed geometry type -## -- e.g., MultiPoint with only one-nested array -# p <- '{ "type" : "Point", "coordinates" : [0, 0] }' -# -# mp <- '{ "type" : "MultiPoint", "coordinates" : [ [0, 0], [2.324, 2] ] }' -# -# ls <- '{ "type" : "LineString", "coordinates" : [ [0, 0], [1, 1] ] }' -# -# ml <- '{"type": "MultiLineString","coordinates": [ -# [[100.0, 0.0],[101.0, 1.0]],[[102.0, 2.0],[103.0, 3.0]] -# ]}' -# -# poly <- '{"type": "Polygon","coordinates": [[ -# [180.0, 40.0], [180.0, 50.0], [170.0, 50.0], -# [170.0, 40.0], [180.0, 40.0] -# ]]}' -# -# mpoly <- '{ -# "type": "MultiPolygon", -# "coordinates": [ -# [ -# [ -# [180.0, 40.0], -# [180.0, 50.0], -# [170.0, 50.0], -# [170.0, 40.0], -# [180.0, 40.0] -# ], -# [ -# [0, 0], -# [1, 1], -# [2, 2], -# [2, 0] -# ] -# ], -# [ -# [ -# [-170.0, 40.0], -# [-170.0, 50.0], -# [-180.0, 50.0], -# [-180.0, 40.0], -# [-170.0, 40.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]]} -# ] -# }' - -# geojson_wkt(p) -# geojson_wkt(mp) -# geojson_wkt(ls) -# geojson_wkt(ml) -# geojson_wkt(poly) -# geojson_wkt(mpoly) -# geojson_wkt(gc) - ## wellknown can't handle properties? # install.packages("wellknown") # diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index c5bb1b9..9986f9f 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -11,11 +11,6 @@ using namespace Rcpp; -// TODO: -// - handle incorrectly formed geometry type -// -- e.g., MultiPoint with only one-nested array - - void parse_geometry_object(Rcpp::List& sfc, int i, const Value& geometry, diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index f7dc3d0..faf5adb 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -243,12 +243,19 @@ Rcpp::List geojson_to_wkt(const char* geojson, // Need to 'recurse' into the GeoJSON like what i did for geo_sf // because it can be arrays, objects, vectors. if (d.IsObject()) { + Rcpp::List sfg(1); parse_geojson_object_wkt(d, sfg, properties, geometry_types, wkt_objects, property_keys, doc_properties, property_types); sfc[0] = sfg; } else if (d.IsArray()) { + Rcpp::List sfgs(d.Size()); + // Rcpp::LogicalVector doc_ele_properties(d.Size()); + for (int doc_ele = 0; doc_ele < d.Size(); doc_ele++) { + parse_geojson_array_wkt(d, sfgs, properties, doc_ele, geometry_types, wkt_objects, property_keys, doc_properties, property_types); + } + sfc[0] = sfgs; } return sfc; } diff --git a/tests/testthat/test-errors.R b/tests/testthat/test-errors.R index 10406b0..a77b78f 100644 --- a/tests/testthat/test-errors.R +++ b/tests/testthat/test-errors.R @@ -2,31 +2,77 @@ context("errors") test_that("errors are handled", { - expect_error(geojson_sf(1:5)) - expect_error(geojson_sf("a")) - expect_error(geojson_sf()) - expect_error(geojson_sf(NULL)) - expect_error(geojson_sf(NA)) - expect_error(geojson_sf('{ "type" : "Point" }')) + 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] }')) - expect_error(geojson_sf('{"type" : "Point", "coordinates" : null }')) - expect_error(geojson_sf('{"type" : "LineString", "coordinates" : [ 0, 0 ] }')) - expect_error(geojson_sf('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }')) - expect_error(geojson_sf('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }')) - expect_error(geojson_sf('{"type" : "Polygon", "coordinates" : [0, 0] }')) - expect_error(geojson_sf('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }')) - expect_error(geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }')) - expect_error(geojson_sf('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }')) + 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 ] }')) - expect_error(geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ 0, 0 ] }')) - expect_error(geojson_wkt('{"type" : "MultiLineString", "coordinates" : [ [0, 0] ] }')) - expect_error(geojson_wkt('{"type" : "Polygon", "coordinates" : [0, 0] }')) - expect_error(geojson_wkt('{"type" : "Polygon", "coordinates" : [ [ 0, 0 ] ] }')) - expect_error(geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [0, 0] ] }')) - expect_error(geojson_wkt('{"type" : "MultiPolygon", "coordinates" : [ [ [0, 0] ] ] }')) + 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" : {} }')) + expect_error( + geojson_sf('{"type" : "Point" , "coordinates" : {} }'), "No 'array' member at object index 0 - invalid GeoJSON" + ) }) diff --git a/tests/testthat/test-geojson_properties.R b/tests/testthat/test-geojson_properties.R index 42b8ab9..1750091 100644 --- a/tests/testthat/test-geojson_properties.R +++ b/tests/testthat/test-geojson_properties.R @@ -9,16 +9,27 @@ test_that("properties captured correctly", { }' sf <- geojson_sf(f) + wkt <- geojson_wkt(f) expect_true( all(names(sf) == c("geometry", "id", "name")) ) + expect_true( + all(names(wkt) == c("geometry", "id", "name")) + ) + expect_true( sf$id == 1 ) + expect_true( + wkt$id == 1 + ) expect_true( sf$name == "foo" ) + expect_true( + wkt$name == "foo" + ) js <- '[ { @@ -118,16 +129,26 @@ test_that("properties captured correctly", { } ]' sf <- geojson_sf(js) + wkt <- geojson_wkt(js) expect_true( ncol(sf) == 3 ) + expect_true( + ncol(wkt) == 3 + ) expect_true( sum(sf$id, na.rm = T) == 3 ) + expect_true( + sum(wkt$id, na.rm = T) == 3 + ) expect_true( sf$value[!is.na(sf$value)] == "foo" ) + expect_true( + wkt$value[!is.na(wkt$value)] == "foo" + ) }) diff --git a/tests/testthat/test-geojson_wkt.R b/tests/testthat/test-geojson_wkt.R new file mode 100644 index 0000000..9097aa6 --- /dev/null +++ b/tests/testthat/test-geojson_wkt.R @@ -0,0 +1,87 @@ +context("wkt") + +test_that("wkt created correctly", { + + + p <- '{ "type" : "Point", "coordinates" : [0, 0] }' + + mp <- '{ "type" : "MultiPoint", "coordinates" : [ [0, 0], [2.324, 2] ] }' + + ls <- '{ "type" : "LineString", "coordinates" : [ [0, 0], [1, 1] ] }' + + ml <- '{"type": "MultiLineString","coordinates": [ + [[100.0, 0.0],[101.0, 1.0]],[[102.0, 2.0],[103.0, 3.0]] + ]}' + + poly <- '{"type": "Polygon","coordinates": [[ + [180.0, 40.0], [180.0, 50.0], [170.0, 50.0], + [170.0, 40.0], [180.0, 40.0] + ]]}' + + mpoly <- '{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [180.0, 40.0], + [180.0, 50.0], + [170.0, 50.0], + [170.0, 40.0], + [180.0, 40.0] + ], + [ + [0, 0], + [1, 1], + [2, 2], + [2, 0] + ] + ], + [ + [ + [-170.0, 40.0], + [-170.0, 50.0], + [-180.0, 50.0], + [-180.0, 40.0], + [-170.0, 40.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]]} + ] + }' + + p <- geojson_wkt(p) + mp <- geojson_wkt(mp) + ls <- geojson_wkt(ls) + ml <- geojson_wkt(ml) + poly <- geojson_wkt(poly) + mpoly <- geojson_wkt(mpoly) + gc <- geojson_wkt(gc) + + expect_true( + class(p) == "data.frame" + ) + expect_true( + class(mp) == "data.frame" + ) + expect_true( + class(ml) == "data.frame" + ) + expect_true( + class(poly) == "data.frame" + ) + expect_true( + class(mpoly) == "data.frame" + ) + expect_true( + class(gc) == "data.frame" + ) + +})