diff --git a/DESCRIPTION b/DESCRIPTION index 0de5631..87e73dd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: geojsonsf Type: Package Title: GeoJSON to Simple Feature Converter -Version: 0.3.0003 +Version: 0.3.0004 Authors@R: c( person("David", "Cooley", ,"dcooley@symbolix.com.au", role = c("aut", "cre")) ) diff --git a/NAMESPACE b/NAMESPACE index 85e38cd..0ba29da 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,8 +6,12 @@ S3method(geojson_sf,default) S3method(geojson_sfc,character) S3method(geojson_sfc,connection) S3method(geojson_sfc,default) +S3method(sf_geojson,sf) +S3method(sf_geojson,sfc) +S3method(sf_geojson,sfg) export(geojson_sf) export(geojson_sfc) export(geojson_wkt) +export(sf_geojson) importFrom(Rcpp,sourceCpp) useDynLib(geojsonsf, .registration = TRUE) diff --git a/R/RcppExports.R b/R/RcppExports.R index 2b1c871..ddc4885 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -21,3 +21,7 @@ rcpp_read_sf_file <- function(file) { .Call(`_geojsonsf_rcpp_read_sf_file`, file) } +rcpp_sf_to_geojson <- function(sf, atomise) { + .Call(`_geojsonsf_rcpp_sf_to_geojson`, sf, atomise) +} + diff --git a/R/geojson_sf.R b/R/geojson_sf.R index 1d8f705..39aadee 100644 --- a/R/geojson_sf.R +++ b/R/geojson_sf.R @@ -88,6 +88,36 @@ geojson_sf.connection <- function(geojson) geojson_sf(read_url(geojson)) #' @export geojson_sf.default <- function(geojson) rcpp_geojson_to_sf(geojson) + +## TODO: +## - atomise - logical? +## -- Return a JSON array of objects +## -- Return an R Vector of objects? + +#' sf to GeoJSON +#' +#' Converts `sf`, `sfc` and `sfg` objects to GeoJSON +#' +#' @param sf simple feature object +#' @param atomise logical +#' +#' @export +sf_geojson <- function(sf, atomise = FALSE) UseMethod("sf_geojson") + +#' @export +sf_geojson.sf <- function(sf, atomise) rcpp_sf_to_geojson(sf, atomise) + +#' @export +sf_geojson.sfc <- function(sf, atomise) rcpp_sf_to_geojson(sf, atomise) + +#' @export +sf_geojson.sfg <- function(sf, atomise) rcpp_sf_to_geojson(sf, atomise) + +sf_geojson.default <- function(sf, atomise) stop("Expected an sf object") + + + + is_url <- function(geojson) grepl("^https?://", geojson, useBytes=TRUE) read_url <- function(con) { diff --git a/R/scratch.R b/R/scratch.R index e69de29..d1b79bb 100644 --- a/R/scratch.R +++ b/R/scratch.R @@ -0,0 +1,129 @@ +## sf to GeoJSON + +## stream the data.frame, row by row. +## + +# js <- '{"type" : "Point", "coordinates" : [0, 0]}' +# sf <- geojson_sf(js) +# sf +# sf_geojson(sf) +# +# js <- '{"type" : "MultiPoint", "coordinates" : [ [0, 0], [1, 1] ]}' +# sf <- geojson_sf(js) +# sf +# sf_geojson(sf) +# +# js <- '{"type" : "MultiLineString", "coordinates" : [ [ [0, 0], [1, 1] ] ]}' +# sf <- geojson_sf(js) +# sf +# sf_geojson(sf) +# +# js <- '{"type" : "Polygon", "coordinates" : [ [ [0, 0], [1, 1] ] ]}' +# sf <- geojson_sf(js) +# sf +# sf_geojson(sf) +# +# js <- '[{"type" : "Polygon", "coordinates" : [ [ [0, 0], [1, 1] ] ]}, +# {"type" : "MultiLineString", "coordinates" : [ [ [0, 0], [1, 1] ], [[3,3],[4,4]] ]}]' +# sf <- geojson_sf(js) +# sf +# sf_geojson(sf) + +# 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) +# j <- sf_geojson(sf) +# +# j +# gsub(" |\\n|\\r","",js) + + +# js <- '[{"type" : "Polygon", "coordinates" : [ [ [0, 0], [1, 1] ] ]}, +# {"type" : "MultiLineString", "coordinates" : [ [ [0, 0], [1, 1] ] ]}, +# { +# "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) +# sf +# sf_geojson(sf) + +# 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, "foo" : false, "bar" : "world" }, +# "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]]]] +# } +# } +# ]' +# sf <- geojson_sf(js) +# sf_geojson(sf) +# geojson_sf( sf_geojson(sf) ) + + + +# js <- '{"type":"MultiPolygon","coordinates":[[[[0,0],[0,1],[1,1],[1,0],[0,0]]]]}' +# sf_geojson(geojson_sf(js)) + +# js <- '{ +# "type":"MultiLineString", +# "coordinates":[ +# [ +# [0,0],[0,1],[1,1],[1,0],[0,0] +# ], +# [ +# [2,2],[2,3],[3,3],[3,2],[2,2] +# ] +# ] +# }' +# +# sf_geojson(geojson_sf(js)) + + +# js <- '{ +# "type":"MultiPolygon", +# "coordinates":[ +# [ +# [ +# [0,0],[0,1],[1,1],[1,0],[0,0] +# ], +# [ +# [0.5,0.5],[0.5,0.75],[0.75,0.75],[0.75,0.5],[0.5,0.5] +# ] +# ], +# [ +# [ +# [2,2],[2,3],[3,3],[3,2],[2,2] +# ] +# ] +# ] +# }' +# +# sf_geojson(geojson_sf(js), TRUE) + + diff --git a/inst/include/geojson_wkt.h b/inst/include/geojson_wkt.h index e24c38d..df405f7 100644 --- a/inst/include/geojson_wkt.h +++ b/inst/include/geojson_wkt.h @@ -3,18 +3,21 @@ #define GEOJSON_WKT_H #include "rapidjson/document.h" +using namespace rapidjson; -void coordSeparateWKT(std::ostringstream& os, int i, int n); +void coord_separator(std::ostringstream& os, int i, int n); -void lineSeparateWKT(std::ostringstream& os, int i, int n); +void line_separator_wkt(std::ostringstream& os, int i, int n); -void polygonSeparateWKT(std::ostringstream& os, int i, int n); +void object_separator(std::ostringstream& os); -void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ); +void polygon_separate_wkt(std::ostringstream& os, int i, int n); -void beginWKT(std::ostringstream& os, std::string& geom_type); +void add_lonlat_to_wkt_stream(std::ostringstream& os, float lon, float lat ); -void endWKT(std::ostringstream& os, std::string& geom_type); +void begin_wkt(std::ostringstream& os, std::string& geom_type); + +void end_wkt(std::ostringstream& os, std::string& geom_type); void point_to_wkt(std::ostringstream& os, const Value& coord_array); diff --git a/inst/include/sf_geojson.h b/inst/include/sf_geojson.h new file mode 100644 index 0000000..2f7829a --- /dev/null +++ b/inst/include/sf_geojson.h @@ -0,0 +1,30 @@ +#ifndef SFGEOJSON_H +#define SFGEOJSON_H + +#include +using namespace Rcpp; + + +//void add_geometrycollection_to_stream(std::ostringstream& os, Rcpp::List& gc); + +void begin_geojson_geometry(Rcpp::String& geojson, std::string& geom_type); + +void begin_geojson_geometry(Rcpp::String& geojson, Rcpp::List& sfc, std::string& geom_type); + +void end_geojson_geometry(Rcpp::String& geojson, std::string& geom_type); + +void add_lonlat_to_stream(Rcpp::String& geojson, Rcpp::NumericVector& points); + +void fetch_coordinates(Rcpp::String& geojson, Rcpp::List& sfc, int& object_counter); + +void coord_separator(Rcpp::String& geojson, int i, int n); + +void line_separator_geojson(Rcpp::String& geojson, int i, int n); + +void polygon_separator_geojson(Rcpp::String& geojson, int i, int n); + +void write_geometry(Rcpp::List& sfg, Rcpp::String& geojson); + +void make_gc_type(Rcpp::String& geojson, Rcpp::List& sfg, std::string& geom_type, Rcpp::CharacterVector& cls); + +#endif diff --git a/man/sf_geojson.Rd b/man/sf_geojson.Rd new file mode 100644 index 0000000..778e1c4 --- /dev/null +++ b/man/sf_geojson.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geojson_sf.R +\name{sf_geojson} +\alias{sf_geojson} +\title{sf to GeoJSON} +\usage{ +sf_geojson(sf, atomise = FALSE) +} +\arguments{ +\item{sf}{simple feature object} + +\item{atomise}{logical} +} +\description{ +Converts `sf`, `sfc` and `sfg` objects to GeoJSON +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 35d8e88..463ef02 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -61,6 +61,18 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// rcpp_sf_to_geojson +Rcpp::StringVector rcpp_sf_to_geojson(Rcpp::List sf, bool atomise); +RcppExport SEXP _geojsonsf_rcpp_sf_to_geojson(SEXP sfSEXP, SEXP atomiseSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< Rcpp::List >::type sf(sfSEXP); + Rcpp::traits::input_parameter< bool >::type atomise(atomiseSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_sf_to_geojson(sf, atomise)); + return rcpp_result_gen; +END_RCPP +} static const R_CallMethodDef CallEntries[] = { {"_geojsonsf_rcpp_geojson_to_sfc", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sfc, 1}, @@ -68,6 +80,7 @@ static const R_CallMethodDef CallEntries[] = { {"_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_sf_to_geojson", (DL_FUNC) &_geojsonsf_rcpp_sf_to_geojson, 2}, {NULL, NULL, 0} }; diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index 8c2d785..b5bcc14 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -26,9 +26,10 @@ 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); + std::ostringstream os; + Rcpp::StringVector wkt; + begin_wkt(os, geom_type); + if (geom_type == "Point") { point_to_wkt(os, coord_array); @@ -52,7 +53,7 @@ void parse_geometry_object_wkt(Rcpp::List& sfc, Rcpp::stop("unknown sfg type"); } - endWKT(os, geom_type); + end_wkt(os, geom_type); wkt = os.str(); transform(geom_type.begin(), geom_type.end(), geom_type.begin(), ::toupper); @@ -65,6 +66,7 @@ void parse_geometry_object_wkt(Rcpp::List& sfc, Rcpp::List parse_geometry_collection_object_wkt(const Value& val, std::set< std::string >& geometry_types, int& wkt_objects) { + std::string geom_type; validate_geometries(val, wkt_objects); @@ -88,7 +90,7 @@ Rcpp::List parse_geometry_collection_object_wkt(const Value& val, for (i = 0; i < n; i++) { std::string g = geom_collection[i]; os << g; - coordSeparateWKT(os, i, n); + coord_separator(os, i, n); } os << ")"; diff --git a/src/geojson_wkt.cpp b/src/geojson_wkt.cpp index 9990a0e..d22dd37 100644 --- a/src/geojson_wkt.cpp +++ b/src/geojson_wkt.cpp @@ -7,7 +7,7 @@ using namespace rapidjson; using namespace Rcpp; -void beginWKT(std::ostringstream& os, std::string& geom_type) { +void begin_wkt(std::ostringstream& os, std::string& geom_type) { if (geom_type == "Point") { os << "POINT ("; @@ -26,7 +26,7 @@ void beginWKT(std::ostringstream& os, std::string& geom_type) { } } -void endWKT(std::ostringstream& os, std::string& geom_type) { +void end_wkt(std::ostringstream& os, std::string& geom_type) { if (geom_type == "Point") { os << ")"; @@ -45,26 +45,30 @@ void endWKT(std::ostringstream& os, std::string& geom_type) { } } -void coordSeparateWKT(std::ostringstream& os, int i, int n) { +void object_separator(std::ostringstream& os) { + os << ","; +} + +void coord_separator(std::ostringstream& os, int i, int n) { if (i < (n - 1) ) { - os << ", "; + os << ","; } } -void lineSeparateWKT(std::ostringstream& os, int i, int n) { +void line_separator_wkt(std::ostringstream& os, int i, int n) { if (i < (n - 1) ) { os << "),("; } } -void polygonSeparateWKT(std::ostringstream& os, int i, int n) { +void polygon_separate_wkt(std::ostringstream& os, int i, int n) { if (i < (n - 1) ) { os << ")),(("; } } -void addLonLatToWKTStream(std::ostringstream& os, float lon, float lat ) { +void add_lonlat_to_wkt_stream(std::ostringstream& os, float lon, float lat ) { os << lon << " " << lat; } @@ -72,7 +76,7 @@ void point_to_wkt(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], point[1]); + add_lonlat_to_wkt_stream(os, point[0], point[1]); } @@ -82,7 +86,7 @@ void multi_point_to_wkt(std::ostringstream& os, const Value& coord_array) { for (i = 0; i < n; i++) { validate_array(coord_array[i]); point_to_wkt(os, coord_array[i]); - coordSeparateWKT(os, i, n); + coord_separator(os, i, n); } } @@ -92,7 +96,7 @@ void line_string_to_wkt(std::ostringstream& os, const Value& coord_array) { for (i = 0; i < n; i++) { validate_array(coord_array[i]); point_to_wkt(os, coord_array[i]); - coordSeparateWKT(os, i, n); + coord_separator(os, i, n); } } @@ -102,7 +106,7 @@ void multi_line_string_to_wkt(std::ostringstream& os, const Value& coord_array) for (i = 0; i < n; i++) { validate_array(coord_array[i]); line_string_to_wkt(os, coord_array[i]); - lineSeparateWKT(os, i, n); + line_separator_wkt(os, i, n); } } @@ -112,7 +116,7 @@ void polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { for (i = 0; i < n; i++) { validate_array(coord_array[i]); line_string_to_wkt(os, coord_array[i]); - lineSeparateWKT(os, i, n); + line_separator_wkt(os, i, n); } } @@ -122,7 +126,7 @@ void multi_polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { for (i = 0; i < n; i++) { validate_array(coord_array[i]); polygon_to_wkt(os, coord_array[i]); - polygonSeparateWKT(os, i, n); + polygon_separate_wkt(os, i, n); } } diff --git a/src/sf_geojson.cpp b/src/sf_geojson.cpp new file mode 100644 index 0000000..648816e --- /dev/null +++ b/src/sf_geojson.cpp @@ -0,0 +1,434 @@ +#include "geojsonsf.h" +#include "geojson_wkt.h" +#include "sf_geojson.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +#include +using namespace Rcpp; + +template +Rcpp::CharacterVector sfClass(Vector v) { + return v.attr("class"); +} + +Rcpp::CharacterVector getSfClass(SEXP sf) { + switch( TYPEOF(sf) ) { + case REALSXP: + return sfClass(sf); + case VECSXP: + return sfClass(sf); + case INTSXP: + return sfClass(sf); + default: Rcpp::stop("unknown sf type"); + } + return ""; +} + + +void get_column_type(Rcpp::List& sf, + Rcpp::StringVector& property_names, + Rcpp::StringVector& column_types) { + + for (int i = 0; i < property_names.size(); i++) { + + Rcpp::String col = property_names[i]; + SEXP vec = sf[col]; + + switch(TYPEOF(vec)) { + case REALSXP: + column_types[i] = "Number"; + break; + case INTSXP: + column_types[i] = "Number"; + break; + case LGLSXP: + column_types[i] = "Logical"; + break; + default: { + column_types[i] = "String"; + break; + } + } + } +} + +void begin_geojson_geometry(Rcpp::String& geojson, std::string& geom_type) { + Rcpp::List sfc; + begin_geojson_geometry(geojson, sfc, geom_type); +} + +void begin_geojson_geometry(Rcpp::String& geojson, Rcpp::List& sfc, std::string& geom_type) { + + geojson += "{\"type\":"; + if (geom_type == "POINT") { + geojson += "\"Point\",\"coordinates\":"; + } else if (geom_type == "MULTIPOINT") { + geojson += "\"MultiPoint\",\"coordinates\":["; + } else if (geom_type == "LINESTRING") { + geojson += "\"LineString\",\"coordinates\":["; + } else if (geom_type == "MULTILINESTRING") { + geojson += "\"MultiLineString\",\"coordinates\":[["; + } else if (geom_type == "POLYGON") { + geojson += "\"Polygon\",\"coordinates\":[["; + } else if (geom_type == "MULTIPOLYGON") { + geojson += "\"MultiPolygon\",\"coordinates\":[[["; + } else if (geom_type == "GEOMETRYCOLLECTION") { + geojson += "\"GeometryCollection\",\"geometries\":["; + } +} + +void end_geojson_geometry(Rcpp::String& geojson, std::string& geom_type) { + if (geom_type == "POINT") { + geojson += "}"; + } else if (geom_type == "MULTIPOINT") { + geojson += "]}"; + } else if (geom_type == "LINESTRING") { + geojson += "]}"; + } else if (geom_type == "MULTILINESTRING") { + geojson += "]]}"; + } else if (geom_type == "POLYGON") { + geojson += "]]}"; + } else if (geom_type == "MULTIPOLYGON") { + geojson += "]]]}"; + } else if (geom_type == "GEOMETRYCOLLECTION") { + geojson += "]}"; + } +} + +void coord_separator(Rcpp::String& geojson, int i, int n) { + if (i < (n - 1) ) { + geojson += ","; + } +} + +void object_separator(Rcpp::String& geojson, int i, int n) { + geojson += ","; +} + +void line_separator_geojson(Rcpp::String& geojson, int i, int n) { + if (i < (n - 1) ) { + geojson += "],["; + } +} + +void polygon_separator_geojson(Rcpp::String& geojson, int i, int n) { + if (i < (n - 1) ) { + geojson += "]],[["; + } +} + +void add_lonlat_to_geojson(Rcpp::String& geojson, Rcpp::NumericVector& points) { + + points.attr("dim") = Dimension(points.size() / 2, 2); + Rcpp::NumericMatrix m = as< Rcpp::NumericMatrix >(points); + + for (int i = 0; i < m.nrow(); i++) { + geojson += "["; + geojson += m(i,0); + geojson += ","; + geojson += m(i,1); + geojson += "]"; + coord_separator(geojson, i, m.nrow()); + } +} + +void point_to_geojson(Rcpp::String& geojson, Rcpp::NumericVector& point) { + add_lonlat_to_geojson(geojson, point); +} + +void multi_point_to_geojson(Rcpp::String& geojson, Rcpp::NumericVector& points) { + add_lonlat_to_geojson(geojson, points); +} + +void line_string_to_geojson(Rcpp::String& geojson, Rcpp::NumericVector& line) { + add_lonlat_to_geojson(geojson, line); +} + +void multi_line_string_to_geojson(Rcpp::String& geojson, Rcpp::List& sfg) { + for (int i = 0; i < sfg.size(); i++) { + Rcpp::NumericVector sfgi = sfg[i]; + add_lonlat_to_geojson(geojson, sfgi); + line_separator_geojson(geojson, i, sfg.size()); + } +} + +void polygon_to_geojson(Rcpp::String& geojson, Rcpp::List& sfg) { + for (int i = 0; i < sfg.size(); i++) { + Rcpp::NumericVector sfgi = sfg[i]; + add_lonlat_to_geojson(geojson, sfgi); + line_separator_geojson(geojson, i, sfg.size()); + } +} + +void multi_polygon_to_geojson(Rcpp::String& geojson, Rcpp::List& sfg) { + for (int i = 0; i < sfg.size(); i++) { + Rcpp::List sfgi = sfg[i]; + polygon_to_geojson(geojson, sfgi); + polygon_separator_geojson(geojson, i, sfg.size()); + } +} + +/* +Rcpp::String add_geometry_to_stream(Rcpp::List& sfg) { + + Rcpp::String geojson; + int object_counter = 0; + +// fetch_coordinates(geojson, sfg, object_counter); + + return geojson; +} +*/ + +// if only one object with properties, it's a 'feature' +// if only one object without properties, it's a 'geometry' +// if many objects it's a 'featurecollection' +/* +rapidjson::Value get_json_value(SEXP s, rapidjson::Document::AllocatorType& allocator) { + // TODO: + // switch on R type and return the rapidjson equivalent + rapidjson::Value v1; + switch (TYPEOF(s)) { + case VECSXP: { + rapidjson::Value v(rapidjson::kStringType); + std::string st = as< std::string >(s); + v.SetString(st.c_str(), allocator); + return v; + } + default: { + rapidjson::Value v(rapidjson::kStringType); + std::string st = as< std::string >(s); + v.SetString(st.c_str(), allocator); + return v; + } + } + return v1; +} +*/ + +void vector_to_json(Rcpp::StringVector& sv, std::string& this_type, std::string& this_name) { + std::string this_value; + + if (this_type == "Number") { + for (int j = 0; j < sv.size(); j++) { + this_value = sv[j]; + if(this_value == "NA") { + this_value = "null"; + } + sv[j] = "\"" + this_name + "\"" + ":" + this_value; + } + } else if (this_type == "Logical") { + for (int j = 0; j < sv.size(); j++) { + this_value = sv[j]; + if(this_value == "NA") { + this_value = "null"; + } + transform(this_value.begin(), this_value.end(), this_value.begin(), tolower); + sv[j] = "\"" + this_name + "\"" + ":" + this_value; + } + } else { + for (int j = 0; j < sv.size(); j++) { + this_value = sv[j]; + if (this_value == "NA") { + this_value = "null"; + } else { + this_value = "\"" + this_value + "\""; + } + sv[j] = "\"" + this_name + "\"" + ":" + this_value; + } + } +} + +void write_geojson(Rcpp::String& geojson, SEXP sfg, + std::string& geom_type, Rcpp::CharacterVector& cls) { + + //geometry_json[i] = add_geometry_to_stream(sfg); + if (geom_type == "POINT") { + + Rcpp::NumericVector point = as(sfg); + point_to_geojson(geojson, point); + } else if (geom_type == "MULTIPOINT") { + + Rcpp::NumericVector multipoint = as(sfg); + multi_point_to_geojson(geojson, multipoint); + } else if (geom_type == "LINESTRING") { + + Rcpp::NumericVector line = as(sfg); + line_string_to_geojson(geojson, line); + } else if (geom_type == "MULTILINESTRING") { + + Rcpp::List multiline = as(sfg); + multi_line_string_to_geojson(geojson, multiline); + } else if (geom_type == "POLYGON") { + + Rcpp::List polygon = as(sfg); + polygon_to_geojson(geojson, polygon); + } else if (geom_type == "MULTIPOLYGON") { + + Rcpp::List multipolygon = as(sfg); + multi_polygon_to_geojson(geojson, multipolygon); + } else if (geom_type == "GEOMETRYCOLLECTION") { + + Rcpp::List gc = as(sfg); + Rcpp::List sfgi(1); + for (int i = 0; i < gc.size(); i++) { + sfgi[0] = gc[i]; + make_gc_type(geojson, sfgi, geom_type, cls); + coord_separator(geojson, i, gc.size()); + } + } +} + +void make_gc_type(Rcpp::String& geojson, Rcpp::List& sfg, + std::string& geom_type, Rcpp::CharacterVector& cls) { + + for (Rcpp::List::iterator it = sfg.begin(); it != sfg.end(); it++) { + + switch( TYPEOF(*it) ) { + case VECSXP: { + Rcpp::List tmp = as(*it); + if (!Rf_isNull(tmp.attr("class"))) { + + cls = tmp.attr("class"); + geom_type = cls[1]; // TODO: error handle (there should aways be 3 elements as we're workgin wtih sfg objects)\ + begin_geojson_geometry(geojson, geom_type); + write_geojson(geojson, tmp, geom_type, cls); + end_geojson_geometry(geojson, geom_type); + } else { + make_gc_type(geojson, tmp, geom_type, cls); + } + break; + } + case REALSXP: { + Rcpp::NumericVector tmp = as(*it); + if (!Rf_isNull(tmp.attr("class"))) { + + cls = tmp.attr("class"); + geom_type = cls[1]; + begin_geojson_geometry(geojson, geom_type); + write_geojson(geojson, tmp, geom_type, cls); + end_geojson_geometry(geojson, geom_type); + } + break; + } + default: { + Rcpp::stop("Coordinates could not be found"); + } + } + } +} + +void write_geometry(Rcpp::List& sfg, Rcpp::String& geojson) { + + Rcpp::CharacterVector cls = getSfClass(sfg); + Rcpp::String g = cls[1]; + std::string geom_type = g; + begin_geojson_geometry(geojson, geom_type); + write_geojson(geojson, sfg, geom_type, cls); + end_geojson_geometry(geojson, geom_type); +} + +void geometry_vector_to_geojson(Rcpp::StringVector& geometry_json, Rcpp::List& sfc) { + + Rcpp::List sfg; + for (int i = 0; i < sfc.size(); i++) { + Rcpp::String geojson; + sfg = sfc[i]; + write_geometry(sfg, geojson); + geometry_json[i] = geojson; + } +} + +Rcpp::String matrix_row_to_json(Rcpp::StringMatrix& json_mat, int i) { + std::ostringstream os; + os << "{"; + int n = json_mat.ncol(); + //if (n > 1) { + os << "\"type\":\"Feature\",\"properties\":{"; + for (int j = 0; j < (n-1); j++) { + os << json_mat(i, j); + coord_separator(os, j, (n-1)); + } + os << "},"; + //} + os << "\"geometry\":"; + os << json_mat(i, (n-1)); + os << "}"; + + Rcpp::String res = os.str(); + return res; +} + +// [[Rcpp::export]] +Rcpp::StringVector rcpp_sf_to_geojson(Rcpp::List sf, bool atomise) { + + //std::ostringstream os; + Rcpp::List sf_copy = clone(sf); + // If it contains properties + // it's a 'feature' (or featureCollection) + // + // if 'atomise', return one object per row + + Rcpp::StringVector column_types(sf_copy.size() - 1); + Rcpp::StringVector property_names(sf_copy.size() - 1); + + std::string geom_column = sf_copy.attr("sf_column"); + Rcpp::StringVector col_names = sf_copy.names(); + + // fill 'property_names' with all the columns which aren't 'sf_column' + int property_counter = 0; + for (int i = 0; i < sf.length(); i++) { + if (col_names[i] != geom_column) { + property_names[property_counter] = col_names[i]; + property_counter++; + } + } + + get_column_type(sf_copy, property_names, column_types); + Rcpp::List sfc = sf_copy[geom_column]; + Rcpp::List properties; + + // TODO: + // construct a StringMatrix with the dimensions of sf + // then I can fill a column at a time with a string of JSON... + // then can manipulate it as I want at the end; either atomising or combining + Rcpp::StringMatrix json_mat(sfc.length(), col_names.size()); // row x cols + std::string this_name; + std::string this_type; + std::string this_value; + Rcpp::StringVector this_vector; + + for (int i = 0; i < property_names.length(); i++) { + // iterate the list elements + this_name = property_names[i]; + this_type = column_types[i]; + this_vector = as< Rcpp::StringVector >(sf_copy[this_name]); + vector_to_json(this_vector, this_type, this_name); + json_mat(_, i) = this_vector; + // TODO: what if there's a mssing element? + } + + Rcpp::StringVector geometry_json(sfc.length()); + geometry_vector_to_geojson(geometry_json, sfc); + json_mat(_, (json_mat.ncol() - 1) ) = geometry_json; + + Rcpp::StringVector res(json_mat.nrow()); + + // If properties, do this bit. else return a vector (column of matrix) + if (json_mat.ncol() > 1) { + for (int i = 0; i < res.length(); i++) { + res[i] = matrix_row_to_json(json_mat, i); + } + } else { + res = json_mat(_, 0); + } + + if(atomise) { + return res; + } + + // TODO: convert to FeatureCollection + return res; + +} diff --git a/tests/testthat/test-geojson_properties.R b/tests/testthat/test-geojson_properties.R index 0362b53..c705866 100644 --- a/tests/testthat/test-geojson_properties.R +++ b/tests/testthat/test-geojson_properties.R @@ -11,25 +11,12 @@ 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" - ) + 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 <- '[ { @@ -131,24 +118,12 @@ 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" - ) + 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") }) @@ -163,8 +138,6 @@ test_that("sf and sfc created equally", { sf <- geojson_sf(f) sfc <- geojson_sfc(f) - expect_true( - all(class(sf$geometry) == class(sfc)) - ) + expect_true(all(class(sf$geometry) == class(sfc))) })