From a9111ca48e998dfb3236eedaafbccc266ba2f600 Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Mon, 24 Dec 2018 12:56:48 +1100 Subject: [PATCH 1/4] restructuring --- DESCRIPTION | 4 +- inst/include/geojsonsf/geojson/api/df_api.hpp | 191 ++++++++++++++++ inst/include/geojsonsf/geojson/api/sf_api.hpp | 172 +++++++++++++++ .../geojsonsf/geojson/write_geometry.hpp | 131 +++++++++++ inst/include/geojsonsf/geojsonsf.h | 3 + .../geometrycollection/geometrycollection.hpp | 80 +++---- inst/include/geojsonsf/utils/where/where.hpp | 50 ++--- inst/include/geojsonsf/write_geojson.hpp | 172 ++++++++------- src/df_geojson.cpp | 204 +----------------- src/sf_geojson.cpp | 185 +--------------- 10 files changed, 656 insertions(+), 536 deletions(-) create mode 100644 inst/include/geojsonsf/geojson/api/df_api.hpp create mode 100644 inst/include/geojsonsf/geojson/api/sf_api.hpp create mode 100644 inst/include/geojsonsf/geojson/write_geometry.hpp diff --git a/DESCRIPTION b/DESCRIPTION index 25ea96e..c910baf 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: geojsonsf Type: Package Title: GeoJSON to Simple Feature Converter -Version: 1.2.1 -Date: 2018-11-19 +Version: 1.2.2 +Date: 2019-01-02 Authors@R: c( person("David", "Cooley", ,"dcooley@symbolix.com.au", role = c("aut", "cre")) ) diff --git a/inst/include/geojsonsf/geojson/api/df_api.hpp b/inst/include/geojsonsf/geojson/api/df_api.hpp new file mode 100644 index 0000000..601dbd2 --- /dev/null +++ b/inst/include/geojsonsf/geojson/api/df_api.hpp @@ -0,0 +1,191 @@ +#ifndef GEOJSONSF_GEOJSON_DF_API_H +#define GEOJSONSF_GEOJSON_DF_API_H + +#include "jsonify/jsonify.hpp" +#include "jsonify/to_json/dataframe.hpp" + +#include "geojsonsf/geojsonsf.h" +#include "geojsonsf/utils/where/where.hpp" +#include "geojsonsf/geojson/write_geometry.hpp" + +namespace geojsonsf { +namespace api { + + inline Rcpp::StringVector df_to_geojson( + Rcpp::DataFrame& df, + Rcpp::StringVector& geometry_columns, + int& digits ) { + + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + + size_t n_cols = df.ncol(); + size_t n_rows = df.nrows(); + size_t i, j; + Rcpp::StringVector column_names = df.names(); + + // the sfc_POINT + size_t n_geometry_columns = geometry_columns.size(); + Rcpp::List geometry_vectors( n_geometry_columns ); + + size_t n_properties = n_cols - n_geometry_columns; + Rcpp::StringVector property_names( n_properties ); + + for ( i = 0; i < n_geometry_columns; i++ ) { + Rcpp::String this_geometry = geometry_columns[i]; + geometry_vectors[i] = df[ this_geometry ]; + } + + std::string dim = geojsonsf::utils::make_dimension( n_geometry_columns ); + Rcpp::CharacterVector cls = Rcpp::CharacterVector::create( dim , "POINT", "sfg"); + + int property_counter = 0; + + for ( int i = 0; i < df.length(); i++ ) { + + Rcpp::String this_column = column_names[i]; + int idx = geojsonsf::utils::where::where_is( this_column, geometry_columns ); + + if ( idx == -1 ) { // i.e. it's not in the vector + property_names[property_counter] = column_names[i]; + property_counter++; + } + } + + writer.StartObject(); + geojsonsf::writers::start_feature_collection( writer ); + + writer.StartArray(); + + for( i = 0; i < n_rows; i++ ) { + + writer.StartObject(); + + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); + writer.StartObject(); + // properties first, then sfc + + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; + SEXP this_vec = df[ h ]; + + jsonify::writers::write_value( writer, h ); + jsonify::dataframe::dataframe_cell( writer, this_vec, i ); + } + writer.EndObject(); + + writer.String("geometry"); + + Rcpp::NumericVector geom( n_geometry_columns ); + for ( j = 0; j < n_geometry_columns; j++ ) { + Rcpp::NumericVector this_geometry_vector = geometry_vectors[j]; + geom[j] = this_geometry_vector[i]; + } + SEXP sfg = geom; + geojsonsf::write_geometry::write_geometry( writer, sfg, cls, digits ); + + writer.EndObject(); + } + + writer.EndArray(); + writer.EndObject(); + + Rcpp::StringVector geojson = sb.GetString(); + geojsonsf::attach_class( geojson ); + return geojson; + } + + inline Rcpp::StringVector df_to_geojson_atomise( + Rcpp::DataFrame& df, + Rcpp::StringVector& geometry_columns, + int& digits) { + + size_t n_cols = df.ncol(); + size_t n_rows = df.nrows(); + size_t i, j; + Rcpp::StringVector column_names = df.names(); + + Rcpp::StringVector geojson( n_rows ); + + + size_t n_geometry_columns = geometry_columns.size(); + Rcpp::List geometry_vectors( n_geometry_columns ); + + size_t n_properties = n_cols - n_geometry_columns; + Rcpp::StringVector property_names( n_properties ); + + for ( i = 0; i < n_geometry_columns; i++ ) { + Rcpp::String this_geometry = geometry_columns[i]; + geometry_vectors[i] = df[ this_geometry ]; + } + + std::string dim = geojsonsf::utils::make_dimension( n_geometry_columns ); + Rcpp::CharacterVector cls = Rcpp::CharacterVector::create( dim , "POINT", "sfg"); + + int property_counter = 0; + for (int i = 0; i < df.length(); i++) { + + Rcpp::String this_column = column_names[i]; + int idx = geojsonsf::utils::where::where_is( this_column, geometry_columns ); + + if ( idx == -1 ) { // i.e. it's not in the vector + + property_names[property_counter] = column_names[i]; + property_counter++; + } + } + + + for( i = 0; i < n_rows; i++ ) { + + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + + if ( n_properties > 0 ) { + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); + + writer.StartObject(); + + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; + + SEXP this_vec = df[ h ]; + + jsonify::writers::write_value( writer, h ); + jsonify::dataframe::dataframe_cell( writer, this_vec, i ); + } + writer.EndObject(); + } + + // now geometries + if( n_properties > 0 ) { + writer.String("geometry"); + } + + Rcpp::NumericVector geom( n_geometry_columns ); + for ( j = 0; j < n_geometry_columns; j++ ) { + Rcpp::NumericVector this_geometry_vector = geometry_vectors[j]; + geom[j] = this_geometry_vector[i]; + } + SEXP sfg = geom; + geojsonsf::write_geometry::write_geometry( writer, sfg, cls, digits ); + + if( n_properties > 0 ) { + writer.EndObject(); + } + geojson[i] = sb.GetString(); + } + + geojsonsf::attach_class( geojson ); + return geojson; + } + +} // namespace geojsonsf +} // namespace api + + +#endif diff --git a/inst/include/geojsonsf/geojson/api/sf_api.hpp b/inst/include/geojsonsf/geojson/api/sf_api.hpp new file mode 100644 index 0000000..03d7c5e --- /dev/null +++ b/inst/include/geojsonsf/geojson/api/sf_api.hpp @@ -0,0 +1,172 @@ +#ifndef GEOJSONSF_GEOJSON_SF_API_H +#define GEOJSONSF_GEOJSON_SF_API_H + +#include "jsonify/jsonify.hpp" +#include "jsonify/to_json/dataframe.hpp" + +#include "geojsonsf/geojsonsf.h" +#include "geojsonsf/geojson/write_geometry.hpp" + +namespace geojsonsf { +namespace api { + + inline Rcpp::StringVector sfc_to_geojson( Rcpp::List& sfc, int& digits ) { + // atomise - each row is a separate GeoJSON string + + size_t n_rows = sfc.size(); + size_t i; + + Rcpp::StringVector geojson( n_rows ); + + for( i = 0; i < n_rows; i++ ) { + + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + + geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); + geojson[i] = sb.GetString(); + } + geojsonsf::attach_class( geojson ); + return geojson; + } + + /* + * sf_to_geojson + * + * Converts sf object to GeoJSON + */ + inline Rcpp::StringVector sf_to_geojson( Rcpp::DataFrame& sf, int& digits ) { + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + + std::string geom_column = sf.attr("sf_column"); + + size_t n_cols = sf.ncol(); + size_t n_properties = n_cols - 1; + size_t n_rows = sf.nrows(); + size_t i, j; + Rcpp::StringVector column_names = sf.names(); + Rcpp::StringVector property_names(sf.size() - 1); + + int property_counter = 0; + for (int i = 0; i < sf.length(); i++) { + if (column_names[i] != geom_column) { + property_names[property_counter] = column_names[i]; + property_counter++; + } + } + + writer.StartObject(); + geojsonsf::writers::start_feature_collection( writer ); + + writer.StartArray(); + + for( i = 0; i < n_rows; i++ ) { + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); + writer.StartObject(); + + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; + + SEXP this_vec = sf[ h ]; + + jsonify::writers::write_value( writer, h ); + jsonify::dataframe::dataframe_cell( writer, this_vec, i ); + } + writer.EndObject(); + + writer.String("geometry"); + + Rcpp::List sfc = sf[ geom_column ]; + geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); + + writer.EndObject(); + } + + writer.EndArray(); + writer.EndObject(); + + Rcpp::StringVector geojson = sb.GetString(); + geojsonsf::attach_class( geojson ); + return geojson; + } + + /* + * + * sf_to_geojson_atomise + * + * Takes an sf object, converts to atomised GeoJSON + * Where every geometry is turned into an individual array + */ + inline Rcpp::StringVector sf_to_geojson_atomise( Rcpp::DataFrame& sf, int& digits ) { + // atomise - each row is a separate GeoJSON string + + std::string geom_column = sf.attr("sf_column"); + + size_t n_cols = sf.ncol(); + size_t n_properties = n_cols - 1; + size_t n_rows = sf.nrows(); + size_t i, j; + Rcpp::StringVector column_names = sf.names(); + Rcpp::StringVector property_names(sf.size() - 1); + + Rcpp::StringVector geojson( n_rows ); + + int property_counter = 0; + for (int i = 0; i < sf.length(); i++) { + if (column_names[i] != geom_column) { + property_names[property_counter] = column_names[i]; + property_counter++; + } + } + + for( i = 0; i < n_rows; i++ ) { + + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + + if ( n_properties > 0 ) { + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); + + writer.StartObject(); + + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; + + SEXP this_vec = sf[ h ]; + + jsonify::writers::write_value( writer, h ); + jsonify::dataframe::dataframe_cell( writer, this_vec, i ); + } + writer.EndObject(); + } + + // now geometries + if( n_properties > 0 ) { + writer.String("geometry"); + } + + Rcpp::List sfc = sf[ geom_column ]; + geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); + + if( n_properties > 0 ) { + writer.EndObject(); + } + geojson[i] = sb.GetString(); + } + + geojsonsf::attach_class( geojson ); + return geojson; + } + +} // namespace geojsonsf +} // namespace api + + +#endif diff --git a/inst/include/geojsonsf/geojson/write_geometry.hpp b/inst/include/geojsonsf/geojson/write_geometry.hpp new file mode 100644 index 0000000..323f96f --- /dev/null +++ b/inst/include/geojsonsf/geojson/write_geometry.hpp @@ -0,0 +1,131 @@ +#ifndef GEOJSONSF_GEOJSON_H +#define GEOJSONSF_GEOJSON_H + +#include "geojsonsf/writers/writers.hpp" +#include "geojsonsf/write_geojson.hpp" +#include "geojsonsf/utils/utils.hpp" + +namespace geojsonsf { +namespace write_geometry { + + inline void cls_check( Rcpp::CharacterVector& cls ) { + if (cls.size() != 3 ) { + Rcpp::stop("unknown sf class"); + } + } + + /* + * Write Geometry + * + * The input will be a data.frame, so every row should be a POINT (not nested lists), so + * no requriement to keep track of sfg indeces + */ + template< typename Writer > + inline void write_geometry(Writer& writer, SEXP sfg, Rcpp::CharacterVector& cls, int digits ) { + + std::string geom_type; + geom_type = cls[1]; + + int sfglength = geojsonsf::utils::get_sexp_length( sfg ); + + if (sfglength == 0) { + writer.Null(); + } else { + + bool isnull = geojsonsf::utils::is_null_geometry( sfg, geom_type ); + if ( isnull ) { + writer.Null(); + } else { + geojsonsf::writers::begin_geojson_geometry(writer, geom_type); + geojsonsf::write_geojson::write_geojson(writer, sfg, geom_type, cls, digits ); + geojsonsf::writers::end_geojson_geometry( writer, geom_type ); + } + } + } + + + /* + * Write geometry + * + * the standard function for writing GeoJSON geometries + */ + template< typename Writer > + inline void write_geometry(Writer& writer, Rcpp::List& sfc, int sfg_index, int digits) { + + SEXP sfg = sfc[ sfg_index ]; + + std::string geom_type; + Rcpp::CharacterVector cls = geojsonsf::getSfClass(sfg); + cls_check( cls ); + geom_type = cls[1]; + + // need to keep track of GEOMETRYCOLLECTIONs so we can correctly close them + bool isGeometryCollection = (geom_type == "GEOMETRYCOLLECTION") ? true : false; + + int sfglength = geojsonsf::utils::get_sexp_length( sfg ); + + if (sfglength == 0) { + writer.Null(); + } else { + + bool isnull = geojsonsf::utils::is_null_geometry( sfg, geom_type ); + if ( isnull ) { + writer.Null(); + } else { + geojsonsf::writers::begin_geojson_geometry(writer, geom_type); + geojsonsf::write_geojson::write_geojson(writer, sfg, geom_type, cls, digits ); + + geom_type = (isGeometryCollection) ? "GEOMETRYCOLLECTION" : geom_type; + geojsonsf::writers::end_geojson_geometry( writer, geom_type ); + } + } + } + + /* + * write geometry + * down-casting MULTI* geometries to their simpler form + */ + template< typename Writer > + inline void write_geometry(Writer& writer, Rcpp::List& sfc, int sfg_index, int geometry_index, + std::string& geom_type, Rcpp::CharacterVector& cls, int digits ) { + + SEXP sfg = sfc[ sfg_index ]; + std::string downcast_geometry; + + if ( geom_type == "MULTIPOINT") { + downcast_geometry = "POINT"; + } else if ( geom_type == "MULTILINESTRING" ) { + downcast_geometry = "LINESTRING"; + } else if ( geom_type == "MULTIPOLYGON" ) { + downcast_geometry = "POLYGON"; + } else { + downcast_geometry = geom_type; + } + + // need to keep track of GEOMETRYCOLLECTIONs so we can correctly close them + bool isGeometryCollection = (geom_type == "GEOMETRYCOLLECTION") ? true : false; + + int sfglength = geojsonsf::utils::get_sexp_length( sfg ); + + if (sfglength == 0) { + writer.Null(); + } else { + + bool isnull = geojsonsf::utils::is_null_geometry( sfg, geom_type ); + if ( isnull ) { + writer.Null(); + } else { + + geojsonsf::writers::begin_geojson_geometry( writer, downcast_geometry ); + geojsonsf::write_geojson::write_geojson( writer, sfg, geom_type, cls, geometry_index, digits ); + + geom_type = (isGeometryCollection) ? "GEOMETRYCOLLECTION" : geom_type; + geojsonsf::writers::end_geojson_geometry( writer, downcast_geometry ); + } + } + } + +} // namespace geojson +} // namespace geojsonsf + +#endif diff --git a/inst/include/geojsonsf/geojsonsf.h b/inst/include/geojsonsf/geojsonsf.h index e69fbf5..80d1b44 100644 --- a/inst/include/geojsonsf/geojsonsf.h +++ b/inst/include/geojsonsf/geojsonsf.h @@ -29,6 +29,9 @@ namespace geojsonsf { return ""; } + inline void attach_class( Rcpp::StringVector& geojson ) { + geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); + } } #define UNKNOWN 0 diff --git a/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp b/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp index 9a3e9a3..3592a64 100644 --- a/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp +++ b/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp @@ -2,71 +2,51 @@ #define GEOJSONSF_GEOMETRYCOLLECTION_H #include -#include "geojsonsf/geojsonsf.h" #include "geojsonsf/utils/utils.hpp" -#include "geojsonsf/writers/writers.hpp" +namespace geojsonsf { +namespace geometrycollection { -template< typename Writer > -inline void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, Rcpp::CharacterVector& cls, int& digits ); + inline void gc_type( Rcpp::List& sfg, std::string& gc_geom_type, bool& isnull, Rcpp::CharacterVector& cls ) { -#include "geojsonsf/geometrycollection/geometrycollection.hpp" + for (Rcpp::List::iterator it = sfg.begin(); it != sfg.end(); it++) { -template< typename Writer > -inline void make_gc_type(Writer& writer, Rcpp::List& sfg, - std::string& geom_type, Rcpp::CharacterVector& cls, int& digits) { + switch( TYPEOF( *it ) ) { + case VECSXP: { + Rcpp::List tmp = Rcpp::as< Rcpp::List >(*it); + if (!Rf_isNull(tmp.attr("class"))) { - bool isnull = false; + cls = tmp.attr("class"); + // TODO: error handle (there should aways be 3 elements as we're workgin wtih sfg objects) + gc_geom_type = cls[1]; - for (Rcpp::List::iterator it = sfg.begin(); it != sfg.end(); it++) { - - switch( TYPEOF( *it ) ) { - case VECSXP: { - Rcpp::List tmp = Rcpp::as< Rcpp::List >(*it); - if (!Rf_isNull(tmp.attr("class"))) { - - cls = tmp.attr("class"); - // TODO: error handle (there should aways be 3 elements as we're workgin wtih sfg objects) - geom_type = cls[1]; - - SEXP tst = *it; - isnull = geojsonsf::utils::is_null_geometry( tst, geom_type ); - if ( isnull ) { - //writer.Null(); + SEXP tst = *it; + isnull = geojsonsf::utils::is_null_geometry( tst, gc_geom_type ); } else { - geojsonsf::writers::begin_geojson_geometry(writer, geom_type); - write_geojson(writer, tmp, geom_type, cls, digits); - geojsonsf::writers::end_geojson_geometry(writer, geom_type); + gc_type(tmp, gc_geom_type, isnull, cls); } - } else { - make_gc_type(writer, tmp, geom_type, cls, digits); + break; } - break; - } - case REALSXP: { - Rcpp::NumericVector tmp = Rcpp::as< Rcpp::NumericVector >( *it ); - if (!Rf_isNull(tmp.attr("class"))) { + case REALSXP: { + Rcpp::NumericVector tmp = Rcpp::as< Rcpp::NumericVector >( *it ); + if (!Rf_isNull(tmp.attr("class"))) { - cls = tmp.attr("class"); - geom_type = cls[1]; + cls = tmp.attr("class"); + gc_geom_type = cls[1]; - SEXP tst = *it; - isnull = geojsonsf::utils::is_null_geometry( tst, geom_type ); - if ( isnull ) { - //writer.Null(); - } else { - geojsonsf::writers::begin_geojson_geometry(writer, geom_type); - write_geojson(writer, tmp, geom_type, cls, digits); - geojsonsf::writers::end_geojson_geometry(writer, geom_type); + SEXP tst = *it; + isnull = geojsonsf::utils::is_null_geometry( tst, gc_geom_type ); } + break; + } + default: { + Rcpp::stop("Coordinates could not be found"); + } } - break; - } - default: { - Rcpp::stop("Coordinates could not be found"); - } } } -} + +} // namespace geometrycollection +} // namespace geojsonsf #endif diff --git a/inst/include/geojsonsf/utils/where/where.hpp b/inst/include/geojsonsf/utils/where/where.hpp index 3d1e8b8..bb2f0ac 100644 --- a/inst/include/geojsonsf/utils/where/where.hpp +++ b/inst/include/geojsonsf/utils/where/where.hpp @@ -7,36 +7,36 @@ namespace geojsonsf { namespace utils { namespace where { -/* -* find_parameter_index_in_vector -* Finds the location (index) of a string in the list of parameters (as given by the R function call) -*/ -inline int where_is( - Rcpp::String to_find, - Rcpp::StringVector& sv ) { - int n = sv.size(); - int i; - for( i = 0; i < n; i++ ) { - if ( to_find == sv[i] ) { - return i; + /* + * find_parameter_index_in_vector + * Finds the location (index) of a string in the list of parameters (as given by the R function call) + */ + inline int where_is( + Rcpp::String to_find, + Rcpp::StringVector& sv ) { + int n = sv.size(); + int i; + for( i = 0; i < n; i++ ) { + if ( to_find == sv[i] ) { + return i; + } } + return -1; } - return -1; -} -inline Rcpp::IntegerVector where_is( - Rcpp::StringVector& param_value, - Rcpp::StringVector& data_names) { + inline Rcpp::IntegerVector where_is( + Rcpp::StringVector& param_value, + Rcpp::StringVector& data_names) { - int n = param_value.size(); - int i; - Rcpp::IntegerVector res( n ); - for ( i = 0; i < n; i++ ) { - Rcpp::String to_find = param_value[i]; - res[i] = where_is( to_find, data_names ); + int n = param_value.size(); + int i; + Rcpp::IntegerVector res( n ); + for ( i = 0; i < n; i++ ) { + Rcpp::String to_find = param_value[i]; + res[i] = where_is( to_find, data_names ); + } + return res; } - return res; -} } // namespace where } // namespace utils diff --git a/inst/include/geojsonsf/write_geojson.hpp b/inst/include/geojsonsf/write_geojson.hpp index 6b1e8dc..0e4c3b2 100644 --- a/inst/include/geojsonsf/write_geojson.hpp +++ b/inst/include/geojsonsf/write_geojson.hpp @@ -3,89 +3,107 @@ #include #include "geojsonsf/writers/writers.hpp" - -template< typename Writer > -inline void make_gc_type(Writer& writer, Rcpp::List& sfg, - std::string& geom_type, Rcpp::CharacterVector& cls, int& digits); - #include "geojsonsf/geometrycollection/geometrycollection.hpp" -template< typename Writer > -void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, Rcpp::CharacterVector& cls, int& digits ) { - - if (geom_type == "POINT") { - geojsonsf::writers::points_to_geojson( writer, sfg, digits ); - - } else if (geom_type == "MULTIPOINT") { - geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); - - } else if (geom_type == "LINESTRING") { - geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); - - } else if (geom_type == "MULTILINESTRING") { - Rcpp::List multiline = Rcpp::as< Rcpp::List >( sfg ); - geojsonsf::writers::polygon_to_geojson( writer, multiline, digits ); - - } else if (geom_type == "POLYGON") { - Rcpp::List polygon = Rcpp::as< Rcpp::List >(sfg); - geojsonsf::writers::polygon_to_geojson( writer, polygon, digits ); - - } else if (geom_type == "MULTIPOLYGON") { - Rcpp::List multipolygon = Rcpp::as< Rcpp::List >( sfg ); - geojsonsf::writers::multi_polygon_to_geojson( writer, multipolygon, digits ); - - } else if (geom_type == "GEOMETRYCOLLECTION") { - Rcpp::List gc = Rcpp::as< Rcpp::List >( sfg ); - Rcpp::List sfgi(1); - for (int i = 0; i < gc.size(); i++) { - sfgi[0] = gc[i]; - make_gc_type(writer, sfgi, geom_type, cls, digits); +namespace geojsonsf { +namespace write_geojson { + + template< typename Writer > + inline void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, Rcpp::CharacterVector& cls, int& digits ) { + + if (geom_type == "POINT") { + geojsonsf::writers::points_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTIPOINT") { + geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "LINESTRING") { + geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTILINESTRING") { + Rcpp::List multiline = Rcpp::as< Rcpp::List >( sfg ); + geojsonsf::writers::polygon_to_geojson( writer, multiline, digits ); + + } else if (geom_type == "POLYGON") { + Rcpp::List polygon = Rcpp::as< Rcpp::List >(sfg); + geojsonsf::writers::polygon_to_geojson( writer, polygon, digits ); + + } else if (geom_type == "MULTIPOLYGON") { + Rcpp::List multipolygon = Rcpp::as< Rcpp::List >( sfg ); + geojsonsf::writers::multi_polygon_to_geojson( writer, multipolygon, digits ); + + } else if (geom_type == "GEOMETRYCOLLECTION") { + Rcpp::List gc = Rcpp::as< Rcpp::List >( sfg ); + Rcpp::List sfgi(1); + for (int i = 0; i < gc.size(); i++) { + sfgi[0] = gc[i]; + std::string gc_geom_type; + bool isnull = false; + geojsonsf::geometrycollection::gc_type( sfgi, gc_geom_type, isnull, cls ); + if( !isnull ) { + SEXP sfg_gc = gc[i]; + geojsonsf::writers::begin_geojson_geometry(writer, gc_geom_type); + write_geojson( writer, sfg_gc, gc_geom_type, cls, digits ); + geojsonsf::writers::end_geojson_geometry(writer, gc_geom_type); + } + } } } -} - - -/* - * used for down-casting MULTI objects - */ -template< typename Writer > -void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, - Rcpp::CharacterVector& cls, int geometry, int& digits ) { - - if (geom_type == "POINT") { - geojsonsf::writers::points_to_geojson( writer, sfg, digits ); - - } else if (geom_type == "MULTIPOINT") { - Rcpp::NumericMatrix mls = Rcpp::as< Rcpp::NumericMatrix >( sfg ); - Rcpp::NumericVector pts = mls(geometry, Rcpp::_ ); - geojsonsf::writers::points_to_geojson( writer, pts, digits ); - - } else if (geom_type == "LINESTRING") { - geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); - - } else if (geom_type == "MULTILINESTRING") { - Rcpp::List multiline = Rcpp::as< Rcpp::List >( sfg ); - SEXP ml = multiline[ geometry ]; - geojsonsf::writers::linestring_to_geojson( writer, ml, digits ); - - } else if (geom_type == "POLYGON") { - Rcpp::List polygon = Rcpp::as< Rcpp::List >(sfg); - geojsonsf::writers::polygon_to_geojson( writer, polygon, digits ); - - } else if (geom_type == "MULTIPOLYGON") { - Rcpp::List multipolygon = Rcpp::as< Rcpp::List >( sfg ); - Rcpp::List mlp = multipolygon[ geometry ]; - geojsonsf::writers::polygon_to_geojson( writer, mlp, digits ); - - } else if (geom_type == "GEOMETRYCOLLECTION") { - Rcpp::List gc = Rcpp::as< Rcpp::List >( sfg ); - Rcpp::List sfgi(1); - for (int i = 0; i < gc.size(); i++) { - sfgi[0] = gc[i]; - make_gc_type(writer, sfgi, geom_type, cls, digits); + + + /* + * used for down-casting MULTI objects + */ + template< typename Writer > + inline void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, + Rcpp::CharacterVector& cls, int geometry_index, int& digits ) { + + if (geom_type == "POINT") { + geojsonsf::writers::points_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTIPOINT") { + Rcpp::NumericMatrix mls = Rcpp::as< Rcpp::NumericMatrix >( sfg ); + Rcpp::NumericVector pts = mls(geometry_index, Rcpp::_ ); + geojsonsf::writers::points_to_geojson( writer, pts, digits ); + + } else if (geom_type == "LINESTRING") { + geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTILINESTRING") { + Rcpp::List multiline = Rcpp::as< Rcpp::List >( sfg ); + SEXP ml = multiline[ geometry_index ]; + geojsonsf::writers::linestring_to_geojson( writer, ml, digits ); + + } else if (geom_type == "POLYGON") { + Rcpp::List polygon = Rcpp::as< Rcpp::List >(sfg); + geojsonsf::writers::polygon_to_geojson( writer, polygon, digits ); + + } else if (geom_type == "MULTIPOLYGON") { + Rcpp::List multipolygon = Rcpp::as< Rcpp::List >( sfg ); + Rcpp::List mlp = multipolygon[ geometry_index ]; + geojsonsf::writers::polygon_to_geojson( writer, mlp, digits ); + + } else if (geom_type == "GEOMETRYCOLLECTION") { + Rcpp::List gc = Rcpp::as< Rcpp::List >( sfg ); + Rcpp::List sfgi(1); + for (int i = 0; i < gc.size(); i++) { + sfgi[0] = gc[i]; + + std::string gc_geom_type; + bool isnull = false; + geojsonsf::geometrycollection::gc_type( sfgi, gc_geom_type, isnull, cls ); + if( !isnull ) { + SEXP sfg_gc = gc[i]; + geojsonsf::writers::begin_geojson_geometry(writer, gc_geom_type); + write_geojson( writer, sfg_gc, gc_geom_type, cls, digits ); + geojsonsf::writers::end_geojson_geometry(writer, gc_geom_type); + } + + } } } -} +} // namespace geojsonsf +} // namespace write_geojson #endif diff --git a/src/df_geojson.cpp b/src/df_geojson.cpp index 0387ed0..7c5b140 100644 --- a/src/df_geojson.cpp +++ b/src/df_geojson.cpp @@ -1,48 +1,7 @@ #include -#include "geojsonsf/geojsonsf.h" +#include "geojsonsf/geojson/api/df_api.hpp" -#include "geojsonsf/utils/utils.hpp" -#include "geojsonsf/utils/where/where.hpp" -#include "geojsonsf/writers/writers.hpp" -#include "geojsonsf/geometrycollection/geometrycollection.hpp" - -#include "jsonify/jsonify.hpp" -#include "jsonify/to_json/dataframe.hpp" - - -template< typename Writer > -void write_geometry(Writer& writer, SEXP sfg, Rcpp::CharacterVector& cls, int& digits ) { - - std::string geom_type; - geom_type = cls[1]; - - int sfglength = geojsonsf::utils::get_sexp_length( sfg ); - - if (sfglength == 0) { - writer.Null(); - } else { - - bool isnull = geojsonsf::utils::is_null_geometry( sfg, geom_type ); - if ( isnull ) { - writer.Null(); - } else { - geojsonsf::writers::begin_geojson_geometry(writer, geom_type); - write_geojson(writer, sfg, geom_type, cls, digits ); - geojsonsf::writers::end_geojson_geometry( writer, geom_type ); - } - } -} - -template< typename Writer > -void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, Rcpp::CharacterVector& cls, int& digits ) { - - if (geom_type == "POINT") { - geojsonsf::writers::points_to_geojson( writer, sfg, digits ); - } else { - Rcpp::stop("df unknown geometry"); - } -} // [[Rcpp::export]] Rcpp::StringVector rcpp_df_to_geojson_atomise( @@ -50,87 +9,7 @@ Rcpp::StringVector rcpp_df_to_geojson_atomise( Rcpp::StringVector& geometry_columns, int& digits) { - size_t n_cols = df.ncol(); - size_t n_rows = df.nrows(); - size_t i, j; - Rcpp::StringVector column_names = df.names(); - - Rcpp::StringVector geojson( n_rows ); - - - size_t n_geometry_columns = geometry_columns.size(); - Rcpp::List geometry_vectors( n_geometry_columns ); - - size_t n_properties = n_cols - n_geometry_columns; - Rcpp::StringVector property_names( n_properties ); - - for ( i = 0; i < n_geometry_columns; i++ ) { - Rcpp::String this_geometry = geometry_columns[i]; - geometry_vectors[i] = df[ this_geometry ]; - } - - std::string dim = geojsonsf::utils::make_dimension( n_geometry_columns ); - Rcpp::CharacterVector cls = Rcpp::CharacterVector::create( dim , "POINT", "sfg"); - - int property_counter = 0; - for (int i = 0; i < df.length(); i++) { - - Rcpp::String this_column = column_names[i]; - int idx = geojsonsf::utils::where::where_is( this_column, geometry_columns ); - - if ( idx == -1 ) { // i.e. it's not in the vector - - property_names[property_counter] = column_names[i]; - property_counter++; - } - } - - - for( i = 0; i < n_rows; i++ ) { - - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - - if ( n_properties > 0 ) { - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); - - writer.StartObject(); - - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; - - SEXP this_vec = df[ h ]; - - jsonify::writers::write_value( writer, h ); - jsonify::dataframe::dataframe_cell( writer, this_vec, i ); - } - writer.EndObject(); - } - - // now geometries - if( n_properties > 0 ) { - writer.String("geometry"); - } - - Rcpp::NumericVector geom( n_geometry_columns ); - for ( j = 0; j < n_geometry_columns; j++ ) { - Rcpp::NumericVector this_geometry_vector = geometry_vectors[j]; - geom[j] = this_geometry_vector[i]; - } - SEXP sfg = geom; - write_geometry( writer, sfg, cls, digits ); - - if( n_properties > 0 ) { - writer.EndObject(); - } - geojson[i] = sb.GetString(); - } - - geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); - return geojson; + return geojsonsf::api::df_to_geojson_atomise( df, geometry_columns, digits ); } // [[Rcpp::export]] @@ -139,82 +18,5 @@ Rcpp::StringVector rcpp_df_to_geojson( Rcpp::StringVector& geometry_columns, int& digits ) { - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - - size_t n_cols = df.ncol(); - size_t n_rows = df.nrows(); - size_t i, j; - Rcpp::StringVector column_names = df.names(); - - // the sfc_POINT - size_t n_geometry_columns = geometry_columns.size(); - Rcpp::List geometry_vectors( n_geometry_columns ); - - size_t n_properties = n_cols - n_geometry_columns; - Rcpp::StringVector property_names( n_properties ); - - for ( i = 0; i < n_geometry_columns; i++ ) { - Rcpp::String this_geometry = geometry_columns[i]; - geometry_vectors[i] = df[ this_geometry ]; - } - - std::string dim = geojsonsf::utils::make_dimension( n_geometry_columns ); - Rcpp::CharacterVector cls = Rcpp::CharacterVector::create( dim , "POINT", "sfg"); - - int property_counter = 0; - - for ( int i = 0; i < df.length(); i++ ) { - - Rcpp::String this_column = column_names[i]; - int idx = geojsonsf::utils::where::where_is( this_column, geometry_columns ); - - if ( idx == -1 ) { // i.e. it's not in the vector - property_names[property_counter] = column_names[i]; - property_counter++; - } - } - - writer.StartObject(); - geojsonsf::writers::start_feature_collection( writer ); - - writer.StartArray(); - - for( i = 0; i < n_rows; i++ ) { - - writer.StartObject(); - - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); - writer.StartObject(); - // properties first, then sfc - - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; - SEXP this_vec = df[ h ]; - - jsonify::writers::write_value( writer, h ); - jsonify::dataframe::dataframe_cell( writer, this_vec, i ); - } - writer.EndObject(); - - writer.String("geometry"); - - Rcpp::NumericVector geom( n_geometry_columns ); - for ( j = 0; j < n_geometry_columns; j++ ) { - Rcpp::NumericVector this_geometry_vector = geometry_vectors[j]; - geom[j] = this_geometry_vector[i]; - } - SEXP sfg = geom; - write_geometry( writer, sfg, cls, digits ); - - writer.EndObject(); - } - - writer.EndArray(); - writer.EndObject(); - - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); - return geojson; + return geojsonsf::api::df_to_geojson( df, geometry_columns, digits ); } diff --git a/src/sf_geojson.cpp b/src/sf_geojson.cpp index 3a2b32a..d3cff6c 100644 --- a/src/sf_geojson.cpp +++ b/src/sf_geojson.cpp @@ -1,197 +1,20 @@ #include -#include "geojsonsf/geojsonsf.h" - -#include "geojsonsf/utils/utils.hpp" -#include "geojsonsf/writers/writers.hpp" -#include "geojsonsf/geometrycollection/geometrycollection.hpp" - -#include "geojsonsf/write_geojson.hpp" - -#include "geojsonsf/geometries/sizes.hpp" - -#include "jsonify/jsonify.hpp" -#include "jsonify/to_json/dataframe.hpp" - -template< typename Writer > -void write_geometry(Writer& writer, Rcpp::List& sfc, int i, int& digits ) { - - SEXP sfg = sfc[ i ]; - - std::string geom_type; - Rcpp::CharacterVector cls = geojsonsf::getSfClass(sfg); - geom_type = cls[1]; - - // need to keep track of GEOMETRYCOLLECTIONs so we can correctly close them - bool isGeometryCollection = (geom_type == "GEOMETRYCOLLECTION") ? true : false; - - int sfglength = geojsonsf::utils::get_sexp_length( sfg ); - - if (sfglength == 0) { - writer.Null(); - } else { - - bool isnull = geojsonsf::utils::is_null_geometry( sfg, geom_type ); - if ( isnull ) { - writer.Null(); - } else { - - geojsonsf::writers::begin_geojson_geometry( writer, geom_type ); - write_geojson( writer, sfg, geom_type, cls, digits ); - - geom_type = (isGeometryCollection) ? "GEOMETRYCOLLECTION" : geom_type; - geojsonsf::writers::end_geojson_geometry( writer, geom_type ); - } - } -} +#include "geojsonsf/geojson/api/sf_api.hpp" // [[Rcpp::export]] Rcpp::StringVector rcpp_sfc_to_geojson( Rcpp::List& sfc, int& digits ) { - // atomise - each row is a separate GeoJSON string - - size_t n_rows = sfc.size(); - size_t i; - - Rcpp::StringVector geojson( n_rows ); - - for( i = 0; i < n_rows; i++ ) { - - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - - write_geometry( writer, sfc, i, digits ); - geojson[i] = sb.GetString(); - } - geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); - return geojson; + return geojsonsf::api::sfc_to_geojson( sfc, digits ); } // [[Rcpp::export]] Rcpp::StringVector rcpp_sf_to_geojson_atomise( Rcpp::DataFrame& sf, int& digits ) { - - // Rcpp::Rcout << "atomise digits : " << digits << std::endl; - - std::string geom_column = sf.attr("sf_column"); - - size_t n_cols = sf.ncol(); - size_t n_properties = n_cols - 1; - size_t n_rows = sf.nrows(); - size_t i, j; - Rcpp::StringVector column_names = sf.names(); - Rcpp::StringVector property_names(sf.size() - 1); - - Rcpp::StringVector geojson( n_rows ); - - int property_counter = 0; - for (int i = 0; i < sf.length(); i++) { - if (column_names[i] != geom_column) { - property_names[property_counter] = column_names[i]; - property_counter++; - } - } - - for( i = 0; i < n_rows; i++ ) { - - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - - if ( n_properties > 0 ) { - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); - - writer.StartObject(); - - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; - - SEXP this_vec = sf[ h ]; - - jsonify::writers::write_value( writer, h ); - jsonify::dataframe::dataframe_cell( writer, this_vec, i ); - } - writer.EndObject(); - } - - // now geometries - if( n_properties > 0 ) { - writer.String("geometry"); - } - - Rcpp::List sfc = sf[ geom_column ]; - write_geometry( writer, sfc, i, digits ); - - if( n_properties > 0 ) { - writer.EndObject(); - } - geojson[i] = sb.GetString(); - } - - geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); - return geojson; + return geojsonsf::api::sf_to_geojson_atomise( sf, digits ); } // [[Rcpp::export]] Rcpp::StringVector rcpp_sf_to_geojson( Rcpp::DataFrame& sf, int& digits ) { - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - - //Rcpp::Rcout << "digits: " << digits << std::endl; - - std::string geom_column = sf.attr("sf_column"); - - size_t n_cols = sf.ncol(); - size_t n_properties = n_cols - 1; - size_t n_rows = sf.nrows(); - size_t i, j; - Rcpp::StringVector column_names = sf.names(); - Rcpp::StringVector property_names(sf.size() - 1); - - int property_counter = 0; - for (int i = 0; i < sf.length(); i++) { - if (column_names[i] != geom_column) { - property_names[property_counter] = column_names[i]; - property_counter++; - } - } - - writer.StartObject(); - geojsonsf::writers::start_feature_collection( writer ); - - writer.StartArray(); - - for( i = 0; i < n_rows; i++ ) { - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); - writer.StartObject(); - - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; - - SEXP this_vec = sf[ h ]; - - jsonify::writers::write_value( writer, h ); - jsonify::dataframe::dataframe_cell( writer, this_vec, i ); - } - writer.EndObject(); - - writer.String("geometry"); - - Rcpp::List sfc = sf[ geom_column ]; - write_geometry( writer, sfc, i, digits ); - - writer.EndObject(); - } - - writer.EndArray(); - writer.EndObject(); - - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); - return geojson; + return geojsonsf::api::sf_to_geojson( sf, digits ); } From d9ec3e6f68cd8074fc0f3c8325b838b70f92ca75 Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Sat, 29 Dec 2018 19:40:10 +1100 Subject: [PATCH 2/4] restructuring --- DESCRIPTION | 4 +- .../geojsonsf/geojson/geojson_properties.hpp | 250 ++++++++ .../geojsonsf/geojson/geojson_validate.hpp | 111 ++++ inst/include/geojsonsf/geojson/parse.hpp | 279 +++++++++ .../geojsonsf/geojson/write_geometry.hpp | 3 +- .../{ => geojson}/writers/writers.hpp | 0 inst/include/geojsonsf/geojson_properties.h | 41 -- inst/include/geojsonsf/geojson_sfc.h | 20 - inst/include/geojsonsf/geojson_sfg.h | 27 - inst/include/geojsonsf/geojson_to_sf.h | 124 +++- inst/include/geojsonsf/geojson_validate.h | 33 - inst/include/geojsonsf/geojsonsf.h | 7 +- inst/include/geojsonsf/geometries/sizes.hpp | 4 +- .../geometrycollection/geometrycollection.hpp | 9 + inst/include/geojsonsf/sf/sf/construct.hpp | 47 ++ inst/include/geojsonsf/sf/sfc/geojson_sfc.h | 31 + .../geojsonsf/sf/sfc/utils/sfc_utils.hpp | 172 ++++++ inst/include/geojsonsf/sf/sfg/geojson_sfg.h | 311 ++++++++++ inst/include/geojsonsf/write_geojson.hpp | 2 +- src/geojson_properties.cpp | 230 +++---- src/geojson_sfc.cpp | 182 ------ src/geojson_sfg.cpp | 259 -------- src/geojson_to_sf.cpp | 569 +----------------- src/geojson_to_wkt.cpp | 48 +- src/geojson_validate.cpp | 104 ---- src/geojson_wkt.cpp | 19 +- src/read_geojson.cpp | 10 +- src/sf_geojson.cpp | 85 --- 28 files changed, 1485 insertions(+), 1496 deletions(-) create mode 100644 inst/include/geojsonsf/geojson/geojson_properties.hpp create mode 100644 inst/include/geojsonsf/geojson/geojson_validate.hpp create mode 100644 inst/include/geojsonsf/geojson/parse.hpp rename inst/include/geojsonsf/{ => geojson}/writers/writers.hpp (100%) delete mode 100644 inst/include/geojsonsf/geojson_properties.h delete mode 100644 inst/include/geojsonsf/geojson_sfc.h delete mode 100644 inst/include/geojsonsf/geojson_sfg.h delete mode 100644 inst/include/geojsonsf/geojson_validate.h create mode 100644 inst/include/geojsonsf/sf/sf/construct.hpp create mode 100644 inst/include/geojsonsf/sf/sfc/geojson_sfc.h create mode 100644 inst/include/geojsonsf/sf/sfc/utils/sfc_utils.hpp create mode 100644 inst/include/geojsonsf/sf/sfg/geojson_sfg.h delete mode 100644 src/geojson_sfc.cpp delete mode 100644 src/geojson_sfg.cpp delete mode 100644 src/geojson_validate.cpp diff --git a/DESCRIPTION b/DESCRIPTION index c910baf..6bbdc39 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: geojsonsf Type: Package Title: GeoJSON to Simple Feature Converter -Version: 1.2.2 +Version: 1.2.2001 Date: 2019-01-02 Authors@R: c( person("David", "Cooley", ,"dcooley@symbolix.com.au", role = c("aut", "cre")) @@ -12,7 +12,7 @@ Encoding: UTF-8 LazyData: true Depends: R (>= 3.3.0) LinkingTo: - jsonify (>= 0.1.2002), + jsonify (>= 0.1.2003), rapidjsonr (>= 1.1), Rcpp Imports: diff --git a/inst/include/geojsonsf/geojson/geojson_properties.hpp b/inst/include/geojsonsf/geojson/geojson_properties.hpp new file mode 100644 index 0000000..8a58b07 --- /dev/null +++ b/inst/include/geojsonsf/geojson/geojson_properties.hpp @@ -0,0 +1,250 @@ +#ifndef GEOJSONSF_PROPERTIES_H +#define GEOJSONSF_PROPERTIES_H + +#include "rapidjson/document.h" +#include +using namespace rapidjson; + +namespace geojsonsf { +namespace geojson_properties { + + template + inline std::string any_to_string(const T& obj) { + std::stringstream ss; + ss << obj; + return ss.str(); + } + + /* + * Updates a list element to string + */ + inline void vector_to_string(Rcpp::List& lst, std::string& key) { + Rcpp::StringVector sv = Rcpp::as(lst[key]); + lst[key] = sv; + } + + inline void get_property_types(const Value& v, std::unordered_map< std::string, std::string>& property_types) { + + // TODO: move to a header?? + static const char* ARRAY_TYPES[] = + { "Null", "False", "True", "Object", "Array", "String", "Number" }; + + for (Value::ConstMemberIterator iter = v.MemberBegin(); iter != v.MemberEnd(); ++iter) { + std::string property = iter->name.GetString(); + + std::string type = ARRAY_TYPES[iter->value.GetType()]; + + // check if key exists + if (property_types.count(property) == 1) { + // it exists + std::string existing_type = property_types[property]; + + if (existing_type == "String") { + // if it's already a 'String' (JSON type), exit + + } else if (existing_type != type) { + // if it's different, update to be a 'String' + property_types[property] = "String"; + + } + // if it's not different, exit + } else { + // doesn't exist + property_types[property] = type; + + } + // if it doesn't exist, add the key / type to the map + } + } + + + inline void sort_property_names( Rcpp::List& properties, std::unordered_set< std::string >& property_keys) { + + properties.names() = property_keys; + std::vector< std::string > n = properties.names(); + std::reverse( n.begin(), n.end() ); + std::vector< std::string > sv( n.size() ); + unsigned int i; + + for( i = 0; i < n.size(); i++ ) { + sv[i] = n[i]; + } + properties.names() = sv; + + } + + inline void get_property_keys(const Value& v, std::unordered_set< std::string >& property_keys) { + + for ( Value::ConstMemberIterator iter = v.MemberBegin(); iter != v.MemberEnd(); ++iter ) { + + // std::string s = iter->name.GetString(); + // Rcpp::Rcout << s << std::endl; + + property_keys.insert(iter->name.GetString()); + } + // for ( auto it = property_keys.begin(); it != property_keys.end(); it++ ) { + // Rcpp::Rcout << (*it) << std::endl; + // } + } + + inline void update_string_vector(Rcpp::List& sf, std::string& key, const std::string& value, const int& row_index) { + Rcpp::StringVector sv = sf[key]; + sv[row_index] = value; + sf[key] = sv; + } + + inline void update_logical_vector(Rcpp::List& sf, std::string& key, const bool& value, const int& row_index) { + Rcpp::LogicalVector lv = sf[key]; + lv[row_index] = value; + sf[key] = lv; + } + + inline void update_numeric_vector(Rcpp::List& sf, std::string& key, double& value, const int& row_index) { + Rcpp::NumericVector nv = sf[key]; + nv[row_index] = value; + sf[key] = nv; + } + + inline Rcpp::NumericVector na_numeric_vector(const size_t& n_elements) { + Rcpp::NumericVector nv(n_elements, NA_REAL); + return nv; + } + + inline Rcpp::StringVector na_string_vector(const size_t& n_elements) { + Rcpp::StringVector sv(n_elements, Rcpp::StringVector::get_na()); + return sv; + } + + inline Rcpp::LogicalVector na_logical_vector(const size_t& n_elements) { + Rcpp::LogicalVector lv(n_elements, NA_LOGICAL); + return lv; + } + + inline void setup_property_vectors(std::unordered_map< std::string, std::string>& property_types, + Rcpp::List& properties, int& sfg_objects) { + + for (auto keys_it = property_types.begin(); keys_it != property_types.end(); keys_it++ ) { + + std::string this_key = keys_it->first; + std::string this_type = keys_it->second; + + if (this_type == "False" || this_type == "True" ) { + properties[ this_key ] = na_logical_vector( sfg_objects ); + } else if (this_type == "Number") { + properties[ this_key ] = na_numeric_vector( sfg_objects ); + } else { + properties[ this_key ] = na_string_vector( sfg_objects ); + } + } + } + + inline void nested_json_to_string(rapidjson::Value& v, + std::string& type, + Rcpp::List& properties, + int& row_index, + std::string& key) { + + StringBuffer sb; + Writer writer(sb); + v.Accept(writer); + std::string this_value = sb.GetString(); + + if (type != "String") { + std::string value = any_to_string(this_value); + update_string_vector(properties, key, value, row_index -1); + } else { + std::string value = this_value; + update_string_vector(properties, key, value, row_index -1); + } + } + + inline void fill_property_vectors(Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + Rcpp::List& properties, + int& row_index) { + + // TODO(move to header): + static const char* ARRAY_TYPES[] = + { "Null", "False", "True", "Object", "Array", "String", "Number" }; + + for (auto& m : doc_properties.GetObject() ) { + row_index = std::stoi( m.name.GetString() ); + + for (auto& p : m.value.GetObject() ) { + + std::string key = p.name.GetString(); + std::string type = property_types[ key ]; + + std::string value_type = ARRAY_TYPES[p.value.GetType()]; + + if (value_type == "String") { + std::string this_value = p.value.GetString(); + + if (type != "String") { + std::string value = any_to_string(this_value); + update_string_vector(properties, key, value, row_index-1); + } else { + std::string value = this_value; + update_string_vector(properties, key, value, row_index-1); + } + + } else if (value_type == "Number") { + + double this_value = p.value.GetDouble(); + + if (type != "Number") { + std::string value = any_to_string(this_value); + update_string_vector(properties, key, value, row_index-1); + } else { + double value = p.value.GetDouble(); + update_numeric_vector(properties, key, value, row_index-1); + } + + } else if (value_type == "False") { + + bool this_value = p.value.GetBool(); + if (type != "False") { + std::string value = any_to_string(this_value); + update_string_vector(properties, key, value, row_index-1); + } else { + bool value = p.value.GetBool(); + update_logical_vector(properties, key, value, row_index-1); + } + + } else if (value_type == "True") { + + bool this_value = p.value.GetBool(); + if (type != "True") { + std::string value = any_to_string(this_value); + update_string_vector(properties, key, value, row_index-1); + } else { + bool value = p.value.GetBool(); + update_logical_vector(properties, key, value, row_index-1); + } + + } else if (value_type == "Null") { + // don't do anything... + } else if (value_type == "Object") { + + Value v = p.value.GetObject(); + nested_json_to_string(v, type, properties, row_index, key); + + } else if (value_type == "Array") { + + Value v = p.value.GetArray(); + nested_json_to_string(v, type, properties, row_index, key); + + } else { + Rcpp::stop("unknown column data type " + type); + } + } + } + } + + +} // namespace geojson +} // namespace geojsonsf + +#endif + + diff --git a/inst/include/geojsonsf/geojson/geojson_validate.hpp b/inst/include/geojsonsf/geojson/geojson_validate.hpp new file mode 100644 index 0000000..a78dd9a --- /dev/null +++ b/inst/include/geojsonsf/geojson/geojson_validate.hpp @@ -0,0 +1,111 @@ + +#ifndef GEOJSONSF_VALIDATE_H +#define GEOJSONSF_VALIDATE_H + +#include "rapidjson/document.h" + +using namespace rapidjson; + +namespace geojsonsf { +namespace validate { + + inline void geojson_object_error(std::string key) { + std::string err = "Invalid " + key + " object"; + Rcpp::stop(err); + } + + inline 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); + } + + inline void safe_parse(Document& d, const char* geojson) { + d.Parse(geojson); + if( d.Parse(geojson).HasParseError() ) { + Rcpp::stop("Invalid JSON"); + } + } + + inline void validate_array(const Value& v) { + if ( v.IsArray() == false) { + geojson_object_error("array"); + } + } + + inline void validate_array(const Value& v, int& sfg_objects) { + if ( v.IsArray() == false) { + geojson_object_error("array", sfg_objects); + } + } + + + inline void validate_type(const Value& v, int& sfg_objects) { + if (v.HasMember("type") == false ) { + geojson_object_error("type", sfg_objects); + } + if (v["type"].IsNull()) { + geojson_object_error("type", sfg_objects); + } + } + + inline void validate_features(const Value& v, int& sfg_objects) { + if (v.HasMember("features") == false) { + geojson_object_error("features", sfg_objects); + } + } + + inline void validate_feature(const Value& v, int& sfg_objects) { + if (v.HasMember("feature") == false) { + geojson_object_error("feature", sfg_objects); + } + } + + inline void validate_properties(const Value& v, int& sfg_objects) { + if (v.HasMember("properties") == false) { + geojson_object_error("properties", sfg_objects); + } + } + + inline void validate_geometry(const Value& v, int& sfg_objects) { + if (v.HasMember("geometry") == false) { + geojson_object_error("geometry", sfg_objects); + } + } + + inline void validate_geometries(const Value& v, int& sfg_objects) { + if (v.HasMember("geometries") == false) { + geojson_object_error("geometries", sfg_objects); + } + } + + inline void validate_coordinates(const Value& v, int& sfg_objects) { + if (v.HasMember("coordinates") == false) { + geojson_object_error("coordinates", sfg_objects); + } + } + + inline void validate_points(const Value& v) { + if ( v.Size() < 2 || v.Size() > 4 ) { + geojson_object_error("lon/lat"); + } + } + + inline void validate_point(const Value& v) { + // TODO(move to header): + // Rcpp::Rcout << "v.size: " << v.Size() << std::endl; + // Rcpp::Rcout << "v.type: " << v.GetType() << std::endl; + // if ( v.Size() < 2 ) { + // Rcpp::stop("mis-specified geometry"); + // } + + static const char* ARRAY_TYPES[] = + { "Null", "false", "True", "Object", "Array", "String", "Number" }; + if( strncmp(ARRAY_TYPES[v.GetType()], "Num", 3) != 0) { + geojson_object_error("lon/lat"); + } + } + +} // namespace validate +} // namespace geojsonsf + +#endif diff --git a/inst/include/geojsonsf/geojson/parse.hpp b/inst/include/geojsonsf/geojson/parse.hpp new file mode 100644 index 0000000..1ac8a64 --- /dev/null +++ b/inst/include/geojsonsf/geojson/parse.hpp @@ -0,0 +1,279 @@ +#ifndef GEOJSONSF_GEOJSON_PARSE_H +#define GEOJSONSF_GEOJSON_PARSE_H + +#include +#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/geojson/geojson_validate.hpp" + +namespace geojsonsf { +namespace geojson { +namespace parse { + + inline void parse_geometry_object(Rcpp::List& sfc, + int i, + const Value& geometry, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects) { + + geojsonsf::validate::validate_type(geometry, sfg_objects); + geojsonsf::validate::validate_coordinates(geometry, sfg_objects); + geojsonsf::validate::validate_array(geometry["coordinates"], sfg_objects); + + std::string geom_type = geometry["type"].GetString(); + const Value& coord_array = geometry["coordinates"]; + geometry_types.insert( geom_type ); + + + if (geom_type == "Point") { + geojsonsf::sfg::get_points( coord_array, bbox, sfc, i, true, "POINT"); + + } else if (geom_type == "MultiPoint") { + int max_cols = 2; + geojsonsf::sfg::get_line_string( coord_array, bbox, sfc, i, true, "MULTIPOINT", max_cols ); + + } else if (geom_type == "LineString") { + int max_cols = 2; + geojsonsf::sfg::get_line_string( coord_array, bbox, sfc, i, true, "LINESTRING", max_cols ); + + } else if (geom_type == "MultiLineString") { + geojsonsf::sfg::get_multi_line_string( coord_array, bbox, sfc, i, true, "MULTILINESTRING" ); + + } else if (geom_type == "Polygon") { + geojsonsf::sfg::get_polygon( coord_array, bbox, sfc, i, true, "POLYGON" ); + + } else if (geom_type == "MultiPolygon") { + geojsonsf::sfg::get_multi_polygon( coord_array, bbox, sfc, i, true, "MULTIPOLYGON" ); + + } else { + Rcpp::stop("unknown sfg type"); + } + } + + inline Rcpp::List parse_geometry_collection_object(const Value& val, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + bool& expand_geometries) { + std::string geom_type; + geojsonsf::validate::validate_geometries(val, sfg_objects); + auto geometries = val["geometries"].GetArray(); + unsigned int n = geometries.Size(); + unsigned int i; + Rcpp::List geom_collection(n); + + for (i = 0; i < n; i++) { + const Value& gcval = geometries[i]; + geojsonsf::validate::validate_type(gcval, sfg_objects); + geom_type = gcval["type"].GetString(); + + parse_geometry_object(geom_collection, i, gcval, bbox, geometry_types, sfg_objects); + } + geometry_types.insert( "GEOMETRYCOLLECTION" ); + + if ( !expand_geometries ) { + // TODO( dimension ) + std::string dim = "XY"; + std::string attribute = "GEOMETRYCOLLECTION"; + geom_collection.attr("class") = geojsonsf::sfg::sfg_attributes( dim, attribute ); + } else { + sfg_objects+=n; + } + return geom_collection; + } + + inline Rcpp::List parse_feature_object(const Value& feature, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + std::unordered_set< std::string >& property_keys, + Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + bool& expand_geometries, + int& nempty) { + + geojsonsf::validate::validate_geometry(feature, sfg_objects); + + // TODO( null property ==> NULL geometry) + //validate_properties(feature, sfg_objects); + + Rcpp::List sfc(1); + + const Value& geometry = feature["geometry"]; + std::string type; + + if (geometry.Size() > 0) { + + geojsonsf::validate::validate_type( geometry, sfg_objects ); + type = geometry["type"].GetString(); + + if (type == "GeometryCollection") { + sfc[0] = geojsonsf::geojson::parse::parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects, expand_geometries); + } else { + geojsonsf::geojson::parse::parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); + } + } else { + geojsonsf::sfg::create_null_sfg(sfc, geometry_types, nempty); + } + + if (type != "GeometryCollection") { + sfg_objects++; + } else if (type == "GeometryCollection" && !expand_geometries){ + sfg_objects++; + } + + const Value& p = feature["properties"]; + + geojsonsf::geojson_properties::get_property_keys(p, property_keys); + geojsonsf::geojson_properties::get_property_types(p, property_types); + + unsigned int geomsize = 1; + unsigned int i; + + if (expand_geometries && type == "GeometryCollection") { + geojsonsf::validate::validate_geometries( geometry, sfg_objects ); + auto geometries = geometry["geometries"].GetArray(); + geomsize = geometries.Size(); + } + + std::string s; + for ( i = 0; i < geomsize; i++ ) { + //https://stackoverflow.com/a/33473321/5977215 + if ( expand_geometries ) { + s = std::to_string( sfg_objects - i ); + } else { + s = std::to_string( sfg_objects ); + } + Value n( s.c_str(), doc_properties.GetAllocator() ); + + // TODO: is this method deep-cloning? + Value properties( feature["properties"], doc_properties.GetAllocator() ); + doc_properties.AddMember( n, properties, doc_properties.GetAllocator() ); + + } + + return sfc; + } + + inline Rcpp::List parse_feature_collection_object(const Value& fc, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + std::unordered_set< std::string >& property_keys, + Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + bool& expand_geometries, + int& nempty) { + + // a FeatureCollection MUST have members (array) called features, + geojsonsf::validate::validate_features(fc, sfg_objects); + + + auto features = fc["features"].GetArray(); + + unsigned int n = features.Size(); // number of features + unsigned int i; + + Rcpp::List feature_collection(n); + + for ( i = 0; i < n; i++ ) { + const Value& feature = features[i]; + feature_collection[i] = parse_feature_object( + feature, bbox, geometry_types, sfg_objects, property_keys, doc_properties, + property_types, expand_geometries, nempty + ); + } + return feature_collection; + } + + inline void parse_geojson(const Value& v, + Rcpp::List& sfc, + Rcpp::List& properties, + int i, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + std::unordered_set< std::string >& property_keys, + Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + bool& expand_geometries, + int& nempty) { + + Rcpp::List res(1); + geojsonsf::validate::validate_type(v, sfg_objects); + + std::string geom_type = v["type"].GetString(); + + if (geom_type == "Feature") { + res = parse_feature_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, expand_geometries, nempty); + sfc[i] = res; + + } else if (geom_type == "FeatureCollection") { + + res = parse_feature_collection_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, expand_geometries, nempty); + sfc[i] = res; + + } else if (geom_type == "GeometryCollection") { + + res = parse_geometry_collection_object(v, bbox, geometry_types, sfg_objects, expand_geometries); + if (!expand_geometries) { + sfg_objects++; + } + sfc[i] = res; + + } else { + + parse_geometry_object(sfc, i, v, bbox, geometry_types, sfg_objects); + sfg_objects++; + } + } + + inline void parse_geojson_object(Document& d, + Rcpp::List& sfc, + Rcpp::List& properties, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + std::unordered_set< std::string >& property_keys, + Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + bool& expand_geometries, + int& nempty) { + const Value& v = d; + parse_geojson( + v, sfc, properties, 0, bbox, geometry_types, sfg_objects, property_keys, + doc_properties, property_types, expand_geometries, nempty + ); + + // // out of order + // for ( auto it = property_keys.begin(); it != property_keys.end(); it++ ) { + // //const char s = *it->c_str(); + // std::cout << (*it) << std::endl; + // } + + } + + inline void parse_geojson_array(Document& d, + Rcpp::List& sfc, + Rcpp::List& properties, + int i, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + std::unordered_set< std::string >& property_keys, + Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + bool& expand_geometries, + int& nempty) { + const Value& v = d[i]; + parse_geojson( + v, sfc, properties, i, bbox, geometry_types, sfg_objects, property_keys, + doc_properties, property_types, expand_geometries, nempty + ); + } + +} // namespace parse +} // namespace geojson +} // namespace geojsonsf + +#endif diff --git a/inst/include/geojsonsf/geojson/write_geometry.hpp b/inst/include/geojsonsf/geojson/write_geometry.hpp index 323f96f..fd48ac1 100644 --- a/inst/include/geojsonsf/geojson/write_geometry.hpp +++ b/inst/include/geojsonsf/geojson/write_geometry.hpp @@ -1,7 +1,8 @@ #ifndef GEOJSONSF_GEOJSON_H #define GEOJSONSF_GEOJSON_H -#include "geojsonsf/writers/writers.hpp" +#include "geojsonsf/geojsonsf.h" +#include "geojsonsf/geojson/writers/writers.hpp" #include "geojsonsf/write_geojson.hpp" #include "geojsonsf/utils/utils.hpp" diff --git a/inst/include/geojsonsf/writers/writers.hpp b/inst/include/geojsonsf/geojson/writers/writers.hpp similarity index 100% rename from inst/include/geojsonsf/writers/writers.hpp rename to inst/include/geojsonsf/geojson/writers/writers.hpp diff --git a/inst/include/geojsonsf/geojson_properties.h b/inst/include/geojsonsf/geojson_properties.h deleted file mode 100644 index 78750ac..0000000 --- a/inst/include/geojsonsf/geojson_properties.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef GEOJSONSF_PROPERTIES_H -#define GEOJSONSF_PROPERTIES_H - -#include "rapidjson/document.h" -#include -using namespace rapidjson; -using namespace Rcpp; - -void vector_to_string(Rcpp::List& lst, std::string& key); - -//template -//std::string any_to_string(const T& obj); - -template -std::string any_to_string(const T& obj) { - std::stringstream ss; - ss << obj; - return ss.str(); -} - -void sort_property_names( Rcpp::List& properties, std::unordered_set< std::string >& property_keys); - -void get_property_types(const Value& v, std::unordered_map< std::string, std::string>& property_types); - -void get_property_keys(const Value& v, std::unordered_set< std::string >& property_keys); - -void update_string_vector(Rcpp::List& sf, std::string& key, const std::string& value, const int& row_index); - -void update_logical_vector(Rcpp::List& sf, std::string& key, const bool& value, const int& row_index); - -void update_numeric_vector(Rcpp::List& sf, std::string& key, double& value, const int& row_index); - -Rcpp::NumericVector na_numeric_vector(const size_t& n_elements); - -Rcpp::StringVector na_string_vector(const size_t& n_elements); - -Rcpp::LogicalVector na_logical_vector(const size_t& n_elements); - -#endif - - diff --git a/inst/include/geojsonsf/geojson_sfc.h b/inst/include/geojsonsf/geojson_sfc.h deleted file mode 100644 index 6dab3c7..0000000 --- a/inst/include/geojsonsf/geojson_sfc.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef GEOJSONSF_SFC_H -#define GEOJSONSF_SFC_H - -#include - -void fetch_geometries(Rcpp::List& sf, Rcpp::List& res, int& sfg_counter); - -void calculate_bbox(Rcpp::NumericVector& bbox, Rcpp::NumericVector& point); - -Rcpp::NumericVector start_bbox(); - -Rcpp::StringVector start_sfc_classes(size_t collectionCount); - -Rcpp::List construct_sfc(int& sfg_objects, - Rcpp::List& sfc, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& nempty); - -#endif diff --git a/inst/include/geojsonsf/geojson_sfg.h b/inst/include/geojsonsf/geojson_sfg.h deleted file mode 100644 index c3ee423..0000000 --- a/inst/include/geojsonsf/geojson_sfg.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef GEOJSONSF_SFG_H -#define GEOJSONSF_SFG_H - -#include "rapidjson/document.h" - -#include -using namespace rapidjson; - -double get_lon(const Value& coord_array); -double get_lat(const Value& coord_array); - -void get_points( const Value& point_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ); - -void get_line_string( const Value& line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute, int& max_cols ); - -void get_multi_line_string( const Value& line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ); - -void get_polygon( const Value& multi_line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ); - -void get_multi_polygon( const Value& multi_line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ); - -#endif diff --git a/inst/include/geojsonsf/geojson_to_sf.h b/inst/include/geojsonsf/geojson_to_sf.h index b23b5c4..85f22ff 100644 --- a/inst/include/geojsonsf/geojson_to_sf.h +++ b/inst/include/geojsonsf/geojson_to_sf.h @@ -1,29 +1,123 @@ #ifndef GEOJSON_TO_SF_H #define GEOJSON_TO_SF_H -#include "rapidjson/document.h" +#include #include +#include "rapidjson/document.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + +#include "geojsonsf/geojsonsf.h" +#include "geojsonsf/geojson_to_sf.h" +#include "geojsonsf/sf/sf/construct.hpp" +#include "geojsonsf/sf/sfc/geojson_sfc.h" +#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/geojson/geojson_validate.hpp" +#include "geojsonsf/geojson/geojson_properties.hpp" +#include "geojsonsf/geojson/parse.hpp" + using namespace rapidjson; -Rcpp::CharacterVector sfg_attributes( std::string& dimension, std::string& geom_type ); +namespace geojsonsf { +namespace sf { + + inline Rcpp::List geojson_to_sf(const char* geojson, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& sfg_objects, + std::unordered_set< std::string >& property_keys, + Document& doc_properties, + std::unordered_map< std::string, std::string>& property_types, + bool& expand_geometries, + int& nempty) { + + Document d; + geojsonsf::validate::safe_parse(d, geojson); + Rcpp::List sf(1); + Rcpp::List sfc(1); + Rcpp::List properties(1); + unsigned int doc_ele; + + if (d.IsObject()) { + Rcpp::List sfg(1); + geojsonsf::geojson::parse::parse_geojson_object( + d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, + doc_properties, property_types, expand_geometries, nempty + ); + sfc[0] = sfg; + + } else if (d.IsArray()) { + + Rcpp::List sfgs(d.Size()); + + for (doc_ele = 0; doc_ele < d.Size(); doc_ele++) { + geojsonsf::geojson::parse::parse_geojson_array( + d, sfgs, properties, doc_ele, bbox, geometry_types, sfg_objects, + property_keys, doc_properties, property_types, expand_geometries, nempty + ); + } + sfc[0] = sfgs; + } + return sfc; + } + + inline Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& expand_geometries) { + // iterate over the geojson + int n = geojson.size(); + int sfg_objects = 0; // keep track of number of objects + int nempty = 0; + //int row_index; + + // Attributes to keep track of along the way + Rcpp::NumericVector bbox = geojsonsf::sfc::utils::start_bbox(); + std::unordered_set< std::string > geometry_types; + std::unordered_set< std::string > property_keys; // storing all the 'key' values from 'properties' + std::unordered_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_sf( + geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, + doc_properties, property_types, expand_geometries, nempty + ); + } + + return geojsonsf::sfc::construct_sfc(sfg_objects, sfc, bbox, geometry_types, nempty); + } + + inline Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& expand_geometries) { + // iterate over the geojson + int n = geojson.size(); + int sfg_objects = 0; // keep track of number of objects + int row_index = 0; + int nempty = 0; -void setup_property_vectors(std::unordered_map< std::string, std::string>& property_types, - Rcpp::List& properties, int& sfg_objects); + // Attributes to keep track of along the way + Rcpp::NumericVector bbox = geojsonsf::sfc::utils::start_bbox(); + std::unordered_set< std::string > geometry_types; + std::unordered_set< std::string > property_keys; // storing all the 'key' values from 'properties' + std::unordered_map< std::string, std::string > property_types; -void fill_property_vectors(Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - Rcpp::List& properties, - int& row_index); + Document doc_properties; // Document to store the 'properties' + doc_properties.SetObject(); + Rcpp::List sfc( n ); -Rcpp::List construct_sf(Rcpp::List& lst, std::unordered_set< std::string >& property_keys, - std::unordered_map< std::string, std::string>& property_types, - Document& doc_properties, - int& sfg_objects, - int& row_index); + for ( int geo_ele = 0; geo_ele < n; geo_ele++ ){ + sfc[geo_ele] = geojson_to_sf( + geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, + doc_properties, property_types, expand_geometries, nempty + ); + } -Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& expand_geometries); + Rcpp::List res = geojsonsf::sfc::construct_sfc( sfg_objects, sfc, bbox, geometry_types, nempty ); + return geojsonsf::sf::construct_sf( res, property_keys, property_types, doc_properties, sfg_objects, row_index ); + } -Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& expand_geometries); +} // namesapce sf +} // namespace geojsonsf #endif diff --git a/inst/include/geojsonsf/geojson_validate.h b/inst/include/geojsonsf/geojson_validate.h deleted file mode 100644 index b57ec26..0000000 --- a/inst/include/geojsonsf/geojson_validate.h +++ /dev/null @@ -1,33 +0,0 @@ - -#ifndef GEOJSONSF_VALIDATE_H -#define GEOJSONSF_VALIDATE_H - -#include "rapidjson/document.h" - -using namespace rapidjson; - -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); - -void validate_properties(const Value& v, int& sfg_objects); - -void validate_geometry(const Value& v, int& sfg_objects); - -void validate_geometries(const Value& v, int& sfg_objects); - -void validate_coordinates(const Value& v, int& sfg_objects); - -void validate_points(const Value& v); - -void validate_point(const Value& v); - -#endif diff --git a/inst/include/geojsonsf/geojsonsf.h b/inst/include/geojsonsf/geojsonsf.h index 80d1b44..f366788 100644 --- a/inst/include/geojsonsf/geojsonsf.h +++ b/inst/include/geojsonsf/geojsonsf.h @@ -9,6 +9,9 @@ namespace geojsonsf { const int EPSG = 4326; const std::string PROJ4STRING = "+proj=longlat +datum=WGS84 +no_defs"; + inline void attach_class( Rcpp::StringVector& geojson ) { + geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); + } template inline Rcpp::CharacterVector sfClass(Rcpp::Vector v) { @@ -28,10 +31,6 @@ namespace geojsonsf { } return ""; } - - inline void attach_class( Rcpp::StringVector& geojson ) { - geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); - } } #define UNKNOWN 0 diff --git a/inst/include/geojsonsf/geometries/sizes.hpp b/inst/include/geojsonsf/geometries/sizes.hpp index c562ea4..d6094bf 100644 --- a/inst/include/geojsonsf/geometries/sizes.hpp +++ b/inst/include/geojsonsf/geometries/sizes.hpp @@ -6,7 +6,7 @@ namespace geojsonsf { namespace sizes { - // determines the size of teh geometry for MULTI objects + // determines the size of the geometry for MULTI objects // MULTIPOINT - number of points (vector size) // MULTILINESTRING - number of lines ( matrix rows ) // MULTIPOLYGON - number of polygons ( list size? ) @@ -28,7 +28,7 @@ namespace sizes { } } // namespace sizes -} //namespace geojsonsf +} // namespace geojsonsf #endif diff --git a/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp b/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp index 3592a64..db29865 100644 --- a/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp +++ b/inst/include/geojsonsf/geometrycollection/geometrycollection.hpp @@ -4,6 +4,7 @@ #include #include "geojsonsf/utils/utils.hpp" + namespace geojsonsf { namespace geometrycollection { @@ -49,4 +50,12 @@ namespace geometrycollection { } // namespace geometrycollection } // namespace geojsonsf + +// for backwards compability until spatialwidget v0.2 is on CRAN +template< typename Writer > +inline void make_gc_type(Writer& writer, Rcpp::List& sfg, + std::string& geom_type, Rcpp::CharacterVector& cls){ + Rcpp::stop(""); +}; + #endif diff --git a/inst/include/geojsonsf/sf/sf/construct.hpp b/inst/include/geojsonsf/sf/sf/construct.hpp new file mode 100644 index 0000000..e3d08a9 --- /dev/null +++ b/inst/include/geojsonsf/sf/sf/construct.hpp @@ -0,0 +1,47 @@ +#ifndef GEOJSONSF_SF_CONSTRUCTORS_H +#define GEOJSONSF_SF_CONSTRUCTORS_H + +#include +#include "geojsonsf/geojson/geojson_properties.hpp" + +namespace geojsonsf { +namespace sf { + + inline Rcpp::List construct_sf( Rcpp::List& lst, std::unordered_set< std::string >& property_keys, + std::unordered_map< std::string, std::string>& property_types, + Document& doc_properties, + int& sfg_objects, + int& row_index ) { + + int n_cols = property_keys.size(); + if ( sfg_objects > 0 ) { + property_keys.insert("geometry"); + n_cols++; // expand to include geometry + } + + Rcpp::List properties( n_cols ); + + geojsonsf::geojson_properties::sort_property_names(properties, property_keys); + + properties["geometry"] = lst; + + geojsonsf::geojson_properties::setup_property_vectors( property_types, properties, sfg_objects ); + geojsonsf::geojson_properties::fill_property_vectors( doc_properties, property_types, properties, row_index ); + + if (sfg_objects > 0 ) { + Rcpp::IntegerVector nv = Rcpp::seq( 1, sfg_objects ); + properties.attr("row.names") = nv; + } else { + properties.attr("row.names") = Rcpp::IntegerVector(0); + } + properties.attr("class") = Rcpp::CharacterVector::create("sf", "data.frame"); + properties.attr("sf_column") = "geometry"; + + return properties; + } + +} // namespace sf +} // namespace geojsonsf + + +#endif diff --git a/inst/include/geojsonsf/sf/sfc/geojson_sfc.h b/inst/include/geojsonsf/sf/sfc/geojson_sfc.h new file mode 100644 index 0000000..7ce1f93 --- /dev/null +++ b/inst/include/geojsonsf/sf/sfc/geojson_sfc.h @@ -0,0 +1,31 @@ +#ifndef GEOJSONSF_SFC_H +#define GEOJSONSF_SFC_H + +#include +//#include "geojsonsf/geojsonsf.h" +#include "geojsonsf/sf/sfc/utils/sfc_utils.hpp" + +namespace geojsonsf { +namespace sfc { + + inline Rcpp::List construct_sfc(int& sfg_objects, + Rcpp::List& sf, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& nempty) { + + Rcpp::List sfc_output( sfg_objects ); + std::string geom_attr; + + int sfg_counter = 0; + + geojsonsf::sfc::utils::fetch_geometries( sf, sfc_output, sfg_counter ); + geojsonsf::sfc::utils::attach_sfc_attributes( sfc_output, geom_attr, bbox, geometry_types, nempty ); + + return sfc_output; + } + +} // namespace sfc +} // namespace geojsonsf + +#endif diff --git a/inst/include/geojsonsf/sf/sfc/utils/sfc_utils.hpp b/inst/include/geojsonsf/sf/sfc/utils/sfc_utils.hpp new file mode 100644 index 0000000..2524f3e --- /dev/null +++ b/inst/include/geojsonsf/sf/sfc/utils/sfc_utils.hpp @@ -0,0 +1,172 @@ +#ifndef GEOJSONSF_SFC_UTILS_H +#define GEOJSONSF_SFC_UTILS_H + +#include "geojsonsf/geojsonsf.h" + +namespace geojsonsf { +namespace sfc { +namespace utils { + + inline Rcpp::NumericVector start_bbox() { + Rcpp::NumericVector bbox(4); // xmin, ymin, xmax, ymax + bbox(0) = bbox(1) = bbox(2) = bbox(3) = NA_REAL; + return bbox; + } + + inline Rcpp::StringVector start_sfc_classes(size_t collectionCount) { + Rcpp::StringVector sfc_classes(collectionCount); + return sfc_classes; + } + + inline void calculate_bbox(Rcpp::NumericVector& bbox, Rcpp::NumericVector& point) { + //xmin, ymin, xmax, ymax + bbox[0] = std::min(point[0], bbox[0]); + bbox[2] = std::max(point[0], bbox[2]); + + bbox[1] = std::min(point[1], bbox[1]); + bbox[3] = std::max(point[1], bbox[3]); + } + + + inline std::string attach_class(Rcpp::List& sfc, + std::string geom_type, + std::unordered_set< std::string >& geometry_types) { + + std::string geometry_class; + + // handle no features + // '{"type":"FeatureCollection","features":[]}' + if (geometry_types.size() == 0 ) { + return "GEOMETRY"; + } + + if (geom_type == "GEOMETRYCOLLECTION") { + geometry_class = "GEOMETRYCOLLECTION"; + } else { + + if (geometry_types.size() > 1) { + geometry_class = "GEOMETRY"; + + Rcpp::StringVector sfc_classes = start_sfc_classes(sfc.size()); + for (int i = 0; i < sfc.size(); i++) { + SEXP sfci = sfc[i]; + Rcpp::CharacterVector cls = geojsonsf::getSfClass(sfci); + sfc_classes[i] = cls[1]; + } + + // attribute::classes + sfc.attr("classes") = sfc_classes; + + } else { + std::string type = *geometry_types.begin(); + transform(type.begin(), type.end(), type.begin(), toupper); + geometry_class = type; + } + } + return geometry_class; + } + + inline void attach_sfc_attributes(Rcpp::List& sfc, + std::string& type, + Rcpp::NumericVector& bbox, + std::unordered_set< std::string >& geometry_types, + int& nempty) { + + std::string geometry_class = attach_class(sfc, type, geometry_types); + sfc.attr("class") = Rcpp::CharacterVector::create("sfc_" + geometry_class, "sfc"); + + double prec = 0; + + // attribute::crs + Rcpp::List crs = Rcpp::List::create( + Rcpp::Named("epsg") = geojsonsf::EPSG, + Rcpp::Named("proj4string") = geojsonsf::PROJ4STRING + ); + + crs.attr("class") = Rcpp::CharacterVector::create("crs"); + sfc.attr("crs") = crs; + + // attribute::precision + sfc.attr("precision") = prec; + + // attribute::n_empty + sfc.attr("n_empty") = nempty; + + // attribute::bbox + bbox.attr("class") = Rcpp::CharacterVector::create("bbox"); + bbox.attr("names") = Rcpp::CharacterVector::create("xmin", "ymin", "xmax", "ymax"); + sfc.attr("bbox") = bbox; + } + + inline Rcpp::List create_null_sfc() { + Rcpp::List empty_sfc(0); + + std::string type = "GEOMETRY"; + Rcpp::NumericVector bbox = start_bbox(); + int n_empty = 0; + std::unordered_set< std::string > geometry_types{"GEOMETRY"}; + attach_sfc_attributes(empty_sfc, type, bbox, geometry_types, n_empty); + return empty_sfc; + } + + inline 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) ) { + + case VECSXP: { + Rcpp::List tmp = Rcpp::as< Rcpp::List >( *it ); + if(Rf_isNull(tmp.attr("class"))) { + fetch_geometries(tmp, res, sfg_counter); + } else { + res[sfg_counter] = tmp; + sfg_counter++; + } + break; + } + case REALSXP: { + Rcpp::NumericVector tmp = Rcpp::as< Rcpp::NumericVector >( *it ); + if(Rf_isNull(tmp.attr("class"))) { + Rcpp::stop("Geometry could not be determined"); + } else { + res[sfg_counter] = tmp; + sfg_counter++; + } + break; + } + case INTSXP: { + Rcpp::IntegerVector tmp = Rcpp::as< Rcpp::IntegerVector >( *it ); + if(Rf_isNull( tmp.attr( "class" ) ) ){ + Rcpp::stop("Geometry could not be determined"); + } else { + res[sfg_counter] = tmp; + sfg_counter++; + } + break; + } + case STRSXP: { + Rcpp::StringVector tmp = Rcpp::as< Rcpp::StringVector >( *it ); + if(Rf_isNull( tmp.attr( "class" ) ) ) { + Rcpp::stop("Geometry could not be determined"); + } else { + res[sfg_counter] = tmp; + sfg_counter++; + } + break; + } + default: { + res[0] = create_null_sfc(); + //Rcpp::stop("Geometry could not be determined"); + } + } + } + } + +} // namespace utils +} // namespace sfc +} // namespace geojsonsf + +#endif diff --git a/inst/include/geojsonsf/sf/sfg/geojson_sfg.h b/inst/include/geojsonsf/sf/sfg/geojson_sfg.h new file mode 100644 index 0000000..f0289b3 --- /dev/null +++ b/inst/include/geojsonsf/sf/sfg/geojson_sfg.h @@ -0,0 +1,311 @@ +#ifndef GEOJSONSF_SFG_H +#define GEOJSONSF_SFG_H + + +#include +#include "rapidjson/document.h" +#include "geojsonsf/sf/sfc/geojson_sfc.h" +#include "geojsonsf/geojson/geojson_validate.hpp" +#include "geojsonsf/utils/utils.hpp" +#include "geojsonsf/utils/where/where.hpp" + +using namespace rapidjson; + +namespace geojsonsf { +namespace sfg { + + + + inline Rcpp::CharacterVector sfg_attributes( std::string& dimension, std::string& geom_type ) { + return Rcpp::CharacterVector::create( dimension, geom_type, "sfg" ); + } + + inline double get_lon(const Value& coord_array) { + geojsonsf::validate::validate_point(coord_array[0]); + return coord_array[0].GetDouble(); + } + + inline double get_lat(const Value& coord_array) { + geojsonsf::validate::validate_point(coord_array[1]); + return coord_array[1].GetDouble(); + } + + // inline std::string make_dimension( int n ) { + // switch( n ) { + // case 2: { + // return "XY"; + // break; + // } + // case 3: { + // return "XYZ"; + // break; + // } + // case 4: { + // return "XYZM"; + // break; + // } + // default: { + // Rcpp::stop("unknown dimension attribute"); + // } + // } + // return "XY"; // never reached + // } + + inline void get_integer_points( const Value& point_array, int& n, Rcpp::IntegerVector iv ) { + int i; + for ( i = 0; i < n; i++ ) { + iv[i] = point_array[i].GetDouble(); + } + } + + inline void get_numeric_points( const Value& point_array, int& n, Rcpp::NumericVector nv, + Rcpp::NumericVector& bbox ) { + int i; + // if (point_array.Size() < 0 ) { + // Rcpp::stop("mis-specified geometry"); + // } + + // if ( n < 0 || point_array.Size() == 0 ) { + // Rcpp::stop("mis-specified geometry"); + // } + + // Rcpp::Rcout << "n: " << n << std::endl; + // Rcpp::Rcout << "points_size: " << point_array.Size() << std::endl; + // const Value& v = point_array[0]; + // Rcpp::Rcout << "v.type: " << v.GetType() << std::endl; + + for ( i = 0; i < n; i++ ) { + geojsonsf::validate::validate_point(point_array[i]); + nv[i] = point_array[i].GetDouble(); + } + geojsonsf::sfc::utils::calculate_bbox(bbox, nv); + } + + + + inline void get_points( const Value& point_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, + bool requires_attribute, std::string attribute ) { + int n = point_array.Size(); + geojsonsf::validate::validate_points(point_array); + + //int r_type; + + // r_type = make_type( point_array ); + // + // switch ( r_type ) { + // case INTSXP: { + // Rcpp::IntegerVector iv( n ); + // get_integer_points( point_array, n, iv ); + // if ( requires_attribute ) { + // iv.attr("class") = sfg_attributes( attribute ); + // } + // sfc[i] = iv; + // break; + // } + // case REALSXP: { + Rcpp::NumericVector nv( n ); + get_numeric_points( point_array, n, nv, bbox ); + + if ( requires_attribute ) { + std::string dim = geojsonsf::utils::make_dimension( n ); + nv.attr("class") = sfg_attributes( dim, attribute ); + } + sfc[i] = nv; + // break; + // } + // default: { + // Rcpp::stop("unknown coordinate type"); + // } + // } + } + + inline void get_line_string( const Value& line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, + bool requires_attribute, std::string attribute, int& max_cols ) { + int n = line_array.Size(); + //int max_cols = 2; + int row; + + //int r_type; + // // TODO( does this take up too much time )? + // r_type = make_type( line_array ); + // + // switch ( r_type ) { + // case INTSXP: { + // Rcpp::IntegerMatrix im( n, 4 ); + // for( row = 0; row < n; row++ ) { + // const Value& coord_array = line_array[ row ]; + // int n_points = coord_array.Size(); + // if( n_points > max_cols ) { + // max_cols = n_points; + // } + // Rcpp::IntegerVector iv( n_points ); + // get_integer_points( coord_array, n_points, iv ); + // im( row, Rcpp::_ ) = iv; + // } + // im = im( Rcpp::_, Rcpp::Range(0, ( max_cols - 1 ) ) ); + // + // if ( requires_attribute ) { + // im.attr("class") = sfg_attributes( attribute ); + // } + // sfc[i] = im; + // break; + // } + // case REALSXP: { + + Rcpp::NumericMatrix nm( n, 4 ); + + for( row = 0; row < n; row++ ) { + const Value& coord_array = line_array[ row ]; + int n_points = coord_array.Size(); + + if( n_points <= 1 ) { + Rcpp::stop("mis-specified geometry"); + } + + if( n_points > max_cols ) { + max_cols = n_points; + } + //Rcpp::Rcout << "n_points: " << n_points << std::endl; + + Rcpp::NumericVector nv( 4 ); // initialise with ZM , we remove later + get_numeric_points( coord_array, n_points, nv, bbox ); + nm( row, Rcpp::_ ) = nv; + } + + nm = nm( Rcpp::_, Rcpp::Range(0, ( max_cols - 1 ) ) ); + + if ( requires_attribute ) { + std::string dim = geojsonsf::utils::make_dimension( max_cols ); + nm.attr("class") = sfg_attributes( dim, attribute ); + } + sfc[i] = nm; + // + // break; + // } + // default: { + // Rcpp::stop("unknown coordinate type"); + // } + // } + + } + + inline void get_multi_line_string( const Value& multi_line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, + bool requires_attribute, std::string attribute ) { + int n = multi_line_array.Size(); + Rcpp::List ml( n ); + int j; + int max_dimension = 2; + for ( j = 0; j < n; j++ ) { + int max_cols = 2; + geojsonsf::validate::validate_array( multi_line_array[j] ); + get_line_string( multi_line_array[j], bbox, ml, j, false, attribute, max_cols ); + if( max_cols > max_dimension ) { + max_dimension = max_cols; + } + } + if( requires_attribute ) { + std::string dim = geojsonsf::utils::make_dimension( max_dimension ); + ml.attr("class") = sfg_attributes( dim, attribute ); + } + sfc[i] = ml; + } + + inline void get_polygon( const Value& polygon_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, + bool requires_attribute, std::string attribute ) { + + int n = polygon_array.Size(); + Rcpp::List pl( n ); + int j; + int max_dimension = 2; + for ( j = 0; j < n; j++ ) { + int max_cols = 2; + geojsonsf::validate::validate_array( polygon_array[j] ); + get_line_string( polygon_array[j], bbox, pl, j, false, "", max_cols ); + if ( max_cols > max_dimension ) { + max_dimension = max_cols; + } + } + + + if( requires_attribute ) { + std::string dim = geojsonsf::utils::make_dimension( max_dimension ); + pl.attr("class") = sfg_attributes( dim, attribute ); + } + sfc[i] = pl; + } + + inline void get_multi_polygon( const Value& multi_polygon_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, + bool requires_attribute, std::string attribute ) { + int n = multi_polygon_array.Size(); + Rcpp::List mp( n ); + int j, k; + int max_dimension = 2; + + for ( j = 0; j < n; j++ ) { + const Value& polygon_array = multi_polygon_array[j]; + geojsonsf::validate::validate_array( polygon_array ); + int np = polygon_array.Size(); + Rcpp::List p( np ); + + for ( k = 0; k < np; k++ ) { + int max_cols = 2; + geojsonsf::validate::validate_array( polygon_array[k] ); + get_line_string( polygon_array[k], bbox, p, k, false, "", max_cols ); + if( max_cols > max_dimension ) { + max_dimension = max_cols; + } + } + mp[j] = p; + } + + if( requires_attribute ) { + std::string dim = geojsonsf::utils::make_dimension( max_dimension ); + mp.attr("class") = sfg_attributes( dim, attribute ); + } + sfc[i] = mp; + } + + inline void create_null_sfg(Rcpp::List& sfc, + std::unordered_set< std::string >& geometry_types, + int& nempty) { + std::string geom_type; + std::string dim = "XY"; + if (geometry_types.size() == 0) { + geom_type = "Point"; + } else { + geom_type = *geometry_types.begin(); + // the 'set' stores the geometries alphabetically + // If there are more than one geometries, does it really matter which one is + // selected? + // if there is only one type, we want to use that one, so selecting the 'begin' + // is as good a method as any + } + geometry_types.insert(geom_type); + transform(geom_type.begin(), geom_type.end(), geom_type.begin(), toupper); + + if (geom_type == "POINT" ) { + + Rcpp::NumericVector nullObj(2, NA_REAL); + nullObj.attr("class") = sfg_attributes(dim, geom_type); + sfc[0] = nullObj; + + } else if (geom_type == "MULTIPOINT" || geom_type == "LINESTRING") { + + Rcpp::NumericMatrix nullObj; + nullObj.attr("class") = sfg_attributes(dim, geom_type); + sfc[0] = nullObj; + + } else { + + Rcpp::List nullObj; + nullObj.attr("class") = geojsonsf::sfg::sfg_attributes(dim, geom_type); + sfc[0] = nullObj; + } + nempty++; + } + + +} // namespace sfg +} // namespace geojsonsf + +#endif diff --git a/inst/include/geojsonsf/write_geojson.hpp b/inst/include/geojsonsf/write_geojson.hpp index 0e4c3b2..d05db63 100644 --- a/inst/include/geojsonsf/write_geojson.hpp +++ b/inst/include/geojsonsf/write_geojson.hpp @@ -2,7 +2,7 @@ #define GEOJSONSF_WRITE_GEOJSON_H #include -#include "geojsonsf/writers/writers.hpp" +#include "geojsonsf/geojson/writers/writers.hpp" #include "geojsonsf/geometrycollection/geometrycollection.hpp" namespace geojsonsf { diff --git a/src/geojson_properties.cpp b/src/geojson_properties.cpp index 949d7cb..5443614 100644 --- a/src/geojson_properties.cpp +++ b/src/geojson_properties.cpp @@ -1,117 +1,117 @@ - -#include "rapidjson/document.h" -#include -#include -#include "geojsonsf/geojsonsf.h" -using namespace rapidjson; -using namespace Rcpp; - - - -/* - * Updates a list element to string - */ -void vector_to_string(Rcpp::List& lst, std::string& key) { - Rcpp::StringVector sv = as(lst[key]); - lst[key] = sv; -} - - - -void get_property_types(const Value& v, std::unordered_map< std::string, std::string>& property_types) { - - // TODO: move to a header?? - static const char* ARRAY_TYPES[] = - { "Null", "False", "True", "Object", "Array", "String", "Number" }; - - for (Value::ConstMemberIterator iter = v.MemberBegin(); iter != v.MemberEnd(); ++iter) { - std::string property = iter->name.GetString(); - - std::string type = ARRAY_TYPES[iter->value.GetType()]; - - // check if key exists - if (property_types.count(property) == 1) { - // it exists - std::string existing_type = property_types[property]; - - if (existing_type == "String") { - // if it's already a 'String' (JSON type), exit - - } else if (existing_type != type) { - // if it's different, update to be a 'String' - property_types[property] = "String"; - - } - // if it's not different, exit - } else { - // doesn't exist - property_types[property] = type; - - } - // if it doesn't exist, add the key / type to the map - } -} - - -void sort_property_names( Rcpp::List& properties, std::unordered_set< std::string >& property_keys) { - - properties.names() = property_keys; - std::vector< std::string > n = properties.names(); - std::reverse( n.begin(), n.end() ); - std::vector< std::string > sv( n.size() ); - unsigned int i; - - for( i = 0; i < n.size(); i++ ) { - sv[i] = n[i]; - } - properties.names() = sv; - -} - -void get_property_keys(const Value& v, std::unordered_set< std::string >& property_keys) { - - for ( Value::ConstMemberIterator iter = v.MemberBegin(); iter != v.MemberEnd(); ++iter ) { - -// std::string s = iter->name.GetString(); -// Rcpp::Rcout << s << std::endl; - - property_keys.insert(iter->name.GetString()); - } -// for ( auto it = property_keys.begin(); it != property_keys.end(); it++ ) { -// Rcpp::Rcout << (*it) << std::endl; +// +// #include "rapidjson/document.h" +// #include +// #include +// #include "geojsonsf/geojsonsf.h" +// using namespace rapidjson; +// using namespace Rcpp; +// +// +// +// /* +// * Updates a list element to string +// */ +// void vector_to_string(Rcpp::List& lst, std::string& key) { +// Rcpp::StringVector sv = as(lst[key]); +// lst[key] = sv; +// } +// +// +// +// void get_property_types(const Value& v, std::unordered_map< std::string, std::string>& property_types) { +// +// // TODO: move to a header?? +// static const char* ARRAY_TYPES[] = +// { "Null", "False", "True", "Object", "Array", "String", "Number" }; +// +// for (Value::ConstMemberIterator iter = v.MemberBegin(); iter != v.MemberEnd(); ++iter) { +// std::string property = iter->name.GetString(); +// +// std::string type = ARRAY_TYPES[iter->value.GetType()]; +// +// // check if key exists +// if (property_types.count(property) == 1) { +// // it exists +// std::string existing_type = property_types[property]; +// +// if (existing_type == "String") { +// // if it's already a 'String' (JSON type), exit +// +// } else if (existing_type != type) { +// // if it's different, update to be a 'String' +// property_types[property] = "String"; +// +// } +// // if it's not different, exit +// } else { +// // doesn't exist +// property_types[property] = type; +// +// } +// // if it doesn't exist, add the key / type to the map // } -} - -void update_string_vector(Rcpp::List& sf, std::string& key, const std::string& value, const int& row_index) { - Rcpp::StringVector sv = sf[key]; - sv[row_index] = value; - sf[key] = sv; -} - -void update_logical_vector(Rcpp::List& sf, std::string& key, const bool& value, const int& row_index) { - Rcpp::LogicalVector lv = sf[key]; - lv[row_index] = value; - sf[key] = lv; -} - -void update_numeric_vector(Rcpp::List& sf, std::string& key, double& value, const int& row_index) { - Rcpp::NumericVector nv = sf[key]; - nv[row_index] = value; - sf[key] = nv; -} - -Rcpp::NumericVector na_numeric_vector(const size_t& n_elements) { - Rcpp::NumericVector nv(n_elements, NA_REAL); - return nv; -} - -Rcpp::StringVector na_string_vector(const size_t& n_elements) { - Rcpp::StringVector sv(n_elements, Rcpp::StringVector::get_na()); - return sv; -} - -Rcpp::LogicalVector na_logical_vector(const size_t& n_elements) { - Rcpp::LogicalVector lv(n_elements, NA_LOGICAL); - return lv; -} +// } +// +// +// void sort_property_names( Rcpp::List& properties, std::unordered_set< std::string >& property_keys) { +// +// properties.names() = property_keys; +// std::vector< std::string > n = properties.names(); +// std::reverse( n.begin(), n.end() ); +// std::vector< std::string > sv( n.size() ); +// unsigned int i; +// +// for( i = 0; i < n.size(); i++ ) { +// sv[i] = n[i]; +// } +// properties.names() = sv; +// +// } +// +// void get_property_keys(const Value& v, std::unordered_set< std::string >& property_keys) { +// +// for ( Value::ConstMemberIterator iter = v.MemberBegin(); iter != v.MemberEnd(); ++iter ) { +// +// // std::string s = iter->name.GetString(); +// // Rcpp::Rcout << s << std::endl; +// +// property_keys.insert(iter->name.GetString()); +// } +// // for ( auto it = property_keys.begin(); it != property_keys.end(); it++ ) { +// // Rcpp::Rcout << (*it) << std::endl; +// // } +// } +// +// void update_string_vector(Rcpp::List& sf, std::string& key, const std::string& value, const int& row_index) { +// Rcpp::StringVector sv = sf[key]; +// sv[row_index] = value; +// sf[key] = sv; +// } +// +// void update_logical_vector(Rcpp::List& sf, std::string& key, const bool& value, const int& row_index) { +// Rcpp::LogicalVector lv = sf[key]; +// lv[row_index] = value; +// sf[key] = lv; +// } +// +// void update_numeric_vector(Rcpp::List& sf, std::string& key, double& value, const int& row_index) { +// Rcpp::NumericVector nv = sf[key]; +// nv[row_index] = value; +// sf[key] = nv; +// } +// +// Rcpp::NumericVector na_numeric_vector(const size_t& n_elements) { +// Rcpp::NumericVector nv(n_elements, NA_REAL); +// return nv; +// } +// +// Rcpp::StringVector na_string_vector(const size_t& n_elements) { +// Rcpp::StringVector sv(n_elements, Rcpp::StringVector::get_na()); +// return sv; +// } +// +// Rcpp::LogicalVector na_logical_vector(const size_t& n_elements) { +// Rcpp::LogicalVector lv(n_elements, NA_LOGICAL); +// return lv; +// } diff --git a/src/geojson_sfc.cpp b/src/geojson_sfc.cpp deleted file mode 100644 index 1049c5c..0000000 --- a/src/geojson_sfc.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include "geojsonsf/geojsonsf.h" -#include "geojsonsf/geojson_sfc.h" - -void calculate_bbox(Rcpp::NumericVector& bbox, Rcpp::NumericVector& point) { - //xmin, ymin, xmax, ymax - bbox[0] = std::min(point[0], bbox[0]); - bbox[2] = std::max(point[0], bbox[2]); - - bbox[1] = std::min(point[1], bbox[1]); - bbox[3] = std::max(point[1], bbox[3]); -} - - -std::string attach_class(Rcpp::List& sfc, - std::string geom_type, - std::unordered_set< std::string >& geometry_types) { - - std::string geometry_class; - - // handle no features - // '{"type":"FeatureCollection","features":[]}' - if (geometry_types.size() == 0 ) { - return "GEOMETRY"; - } - - if (geom_type == "GEOMETRYCOLLECTION") { - geometry_class = "GEOMETRYCOLLECTION"; - } else { - - if (geometry_types.size() > 1) { - geometry_class = "GEOMETRY"; - - Rcpp::StringVector sfc_classes = start_sfc_classes(sfc.size()); - for (int i = 0; i < sfc.size(); i++) { - SEXP sfci = sfc[i]; - Rcpp::CharacterVector cls = geojsonsf::getSfClass(sfci); - sfc_classes[i] = cls[1]; - } - - // attribute::classes - sfc.attr("classes") = sfc_classes; - - } else { - std::string type = *geometry_types.begin(); - transform(type.begin(), type.end(), type.begin(), toupper); - geometry_class = type; - } - } - return geometry_class; -} - -void attach_sfc_attributes(Rcpp::List& sfc, - std::string& type, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& nempty) { - - std::string geometry_class = attach_class(sfc, type, geometry_types); - sfc.attr("class") = Rcpp::CharacterVector::create("sfc_" + geometry_class, "sfc"); - - double prec = 0; - - // attribute::crs - Rcpp::List crs = Rcpp::List::create( - Rcpp::Named("epsg") = geojsonsf::EPSG, - Rcpp::Named("proj4string") = geojsonsf::PROJ4STRING - ); - - crs.attr("class") = Rcpp::CharacterVector::create("crs"); - sfc.attr("crs") = crs; - - // attribute::precision - sfc.attr("precision") = prec; - - // attribute::n_empty - sfc.attr("n_empty") = nempty; - - // attribute::bbox - bbox.attr("class") = Rcpp::CharacterVector::create("bbox"); - bbox.attr("names") = Rcpp::CharacterVector::create("xmin", "ymin", "xmax", "ymax"); - sfc.attr("bbox") = bbox; -} - -Rcpp::NumericVector start_bbox() { - Rcpp::NumericVector bbox(4); // xmin, ymin, xmax, ymax - bbox(0) = bbox(1) = bbox(2) = bbox(3) = NA_REAL; - return bbox; -} - -Rcpp::StringVector start_sfc_classes(size_t collectionCount) { - Rcpp::StringVector sfc_classes(collectionCount); - return sfc_classes; -} - -Rcpp::List create_null_sfc() { - Rcpp::List empty_sfc(0); - - std::string type = "GEOMETRY"; - Rcpp::NumericVector bbox = start_bbox(); - int n_empty = 0; - std::unordered_set< std::string > geometry_types{"GEOMETRY"}; - attach_sfc_attributes(empty_sfc, type, bbox, geometry_types, n_empty); - return empty_sfc; -} - -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) ) { - - case VECSXP: { - Rcpp::List tmp = Rcpp::as< Rcpp::List >( *it ); - if(Rf_isNull(tmp.attr("class"))) { - fetch_geometries(tmp, res, sfg_counter); - } else { - res[sfg_counter] = tmp; - sfg_counter++; - } - break; - } - case REALSXP: { - Rcpp::NumericVector tmp = Rcpp::as< Rcpp::NumericVector >( *it ); - if(Rf_isNull(tmp.attr("class"))) { - Rcpp::stop("Geometry could not be determined"); - } else { - res[sfg_counter] = tmp; - sfg_counter++; - } - break; - } - case INTSXP: { - Rcpp::IntegerVector tmp = Rcpp::as< Rcpp::IntegerVector >( *it ); - if(Rf_isNull( tmp.attr( "class" ) ) ){ - Rcpp::stop("Geometry could not be determined"); - } else { - res[sfg_counter] = tmp; - sfg_counter++; - } - break; - } - case STRSXP: { - Rcpp::StringVector tmp = Rcpp::as< Rcpp::StringVector >( *it ); - if(Rf_isNull( tmp.attr( "class" ) ) ) { - Rcpp::stop("Geometry could not be determined"); - } else { - res[sfg_counter] = tmp; - sfg_counter++; - } - break; - } - default: { - res[0] = create_null_sfc(); - //Rcpp::stop("Geometry could not be determined"); - } - } - } -} - - -Rcpp::List construct_sfc(int& sfg_objects, - Rcpp::List& sf, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& nempty) { - - Rcpp::List sfc_output( sfg_objects ); - std::string geom_attr; - - int sfg_counter = 0; - - fetch_geometries( sf, sfc_output, sfg_counter ); - - attach_sfc_attributes( sfc_output, geom_attr, bbox, geometry_types, nempty ); - - return sfc_output; -} - - diff --git a/src/geojson_sfg.cpp b/src/geojson_sfg.cpp deleted file mode 100644 index dcb13e7..0000000 --- a/src/geojson_sfg.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "rapidjson/document.h" - -#include -#include "geojsonsf/geojson_sfg.h" -#include "geojsonsf/geojson_sfc.h" -#include "geojsonsf/geojson_validate.h" -#include "geojsonsf/utils/utils.hpp" -#include "geojsonsf/utils/where/where.hpp" - -using namespace rapidjson; - -Rcpp::CharacterVector sfg_attributes( std::string& dimension, std::string& geom_type ) { - return Rcpp::CharacterVector::create( dimension, geom_type, "sfg" ); -} - -double get_lon(const Value& coord_array) { - validate_point(coord_array[0]); - return coord_array[0].GetDouble(); -} - -double get_lat(const Value& coord_array) { - validate_point(coord_array[1]); - return coord_array[1].GetDouble(); -} - -// std::string make_dimension( int n ) { -// switch( n ) { -// case 2: { -// return "XY"; -// break; -// } -// case 3: { -// return "XYZ"; -// break; -// } -// case 4: { -// return "XYZM"; -// break; -// } -// default: { -// Rcpp::stop("unknown dimension attribute"); -// } -// } -// return "XY"; // never reached -// } - -void get_integer_points( const Value& point_array, int& n, Rcpp::IntegerVector iv ) { - int i; - for ( i = 0; i < n; i++ ) { - iv[i] = point_array[i].GetDouble(); - } -} - -void get_numeric_points( const Value& point_array, int& n, Rcpp::NumericVector nv, - Rcpp::NumericVector& bbox ) { - int i; - // if (point_array.Size() < 0 ) { - // Rcpp::stop("mis-specified geometry"); - // } - - // if ( n < 0 || point_array.Size() == 0 ) { - // Rcpp::stop("mis-specified geometry"); - // } - - // Rcpp::Rcout << "n: " << n << std::endl; - // Rcpp::Rcout << "points_size: " << point_array.Size() << std::endl; - // const Value& v = point_array[0]; - // Rcpp::Rcout << "v.type: " << v.GetType() << std::endl; - - for ( i = 0; i < n; i++ ) { - validate_point(point_array[i]); - nv[i] = point_array[i].GetDouble(); - } - calculate_bbox(bbox, nv); -} - - - -void get_points( const Value& point_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ) { - int n = point_array.Size(); - validate_points(point_array); - - //int r_type; - - // r_type = make_type( point_array ); - // - // switch ( r_type ) { - // case INTSXP: { - // Rcpp::IntegerVector iv( n ); - // get_integer_points( point_array, n, iv ); - // if ( requires_attribute ) { - // iv.attr("class") = sfg_attributes( attribute ); - // } - // sfc[i] = iv; - // break; - // } - // case REALSXP: { - Rcpp::NumericVector nv( n ); - get_numeric_points( point_array, n, nv, bbox ); - - if ( requires_attribute ) { - std::string dim = geojsonsf::utils::make_dimension( n ); - nv.attr("class") = sfg_attributes( dim, attribute ); - } - sfc[i] = nv; - // break; - // } - // default: { - // Rcpp::stop("unknown coordinate type"); - // } - // } -} - -void get_line_string( const Value& line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute, int& max_cols ) { - int n = line_array.Size(); - //int max_cols = 2; - int row; - - //int r_type; - // // TODO( does this take up too much time )? - // r_type = make_type( line_array ); - // - // switch ( r_type ) { - // case INTSXP: { - // Rcpp::IntegerMatrix im( n, 4 ); - // for( row = 0; row < n; row++ ) { - // const Value& coord_array = line_array[ row ]; - // int n_points = coord_array.Size(); - // if( n_points > max_cols ) { - // max_cols = n_points; - // } - // Rcpp::IntegerVector iv( n_points ); - // get_integer_points( coord_array, n_points, iv ); - // im( row, Rcpp::_ ) = iv; - // } - // im = im( Rcpp::_, Rcpp::Range(0, ( max_cols - 1 ) ) ); - // - // if ( requires_attribute ) { - // im.attr("class") = sfg_attributes( attribute ); - // } - // sfc[i] = im; - // break; - // } - // case REALSXP: { - - Rcpp::NumericMatrix nm( n, 4 ); - - for( row = 0; row < n; row++ ) { - const Value& coord_array = line_array[ row ]; - int n_points = coord_array.Size(); - - if( n_points <= 1 ) { - Rcpp::stop("mis-specified geometry"); - } - - if( n_points > max_cols ) { - max_cols = n_points; - } - //Rcpp::Rcout << "n_points: " << n_points << std::endl; - - Rcpp::NumericVector nv( 4 ); // initialise with ZM , we remove later - get_numeric_points( coord_array, n_points, nv, bbox ); - nm( row, Rcpp::_ ) = nv; - } - - nm = nm( Rcpp::_, Rcpp::Range(0, ( max_cols - 1 ) ) ); - - if ( requires_attribute ) { - std::string dim = geojsonsf::utils::make_dimension( max_cols ); - nm.attr("class") = sfg_attributes( dim, attribute ); - } - sfc[i] = nm; -// -// break; -// } -// default: { -// Rcpp::stop("unknown coordinate type"); -// } -// } - -} - -void get_multi_line_string( const Value& multi_line_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ) { - int n = multi_line_array.Size(); - Rcpp::List ml( n ); - int j; - int max_dimension = 2; - for ( j = 0; j < n; j++ ) { - int max_cols = 2; - validate_array( multi_line_array[j] ); - get_line_string( multi_line_array[j], bbox, ml, j, false, attribute, max_cols ); - if( max_cols > max_dimension ) { - max_dimension = max_cols; - } - } - if( requires_attribute ) { - std::string dim = geojsonsf::utils::make_dimension( max_dimension ); - ml.attr("class") = sfg_attributes( dim, attribute ); - } - sfc[i] = ml; -} - -void get_polygon( const Value& polygon_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ) { - - int n = polygon_array.Size(); - Rcpp::List pl( n ); - int j; - int max_dimension = 2; - for ( j = 0; j < n; j++ ) { - int max_cols = 2; - validate_array( polygon_array[j] ); - get_line_string( polygon_array[j], bbox, pl, j, false, "", max_cols ); - if ( max_cols > max_dimension ) { - max_dimension = max_cols; - } - } - - - if( requires_attribute ) { - std::string dim = geojsonsf::utils::make_dimension( max_dimension ); - pl.attr("class") = sfg_attributes( dim, attribute ); - } - sfc[i] = pl; -} - -void get_multi_polygon( const Value& multi_polygon_array, Rcpp::NumericVector& bbox, Rcpp::List& sfc, int& i, - bool requires_attribute, std::string attribute ) { - int n = multi_polygon_array.Size(); - Rcpp::List mp( n ); - int j, k; - int max_dimension = 2; - - for ( j = 0; j < n; j++ ) { - const Value& polygon_array = multi_polygon_array[j]; - validate_array( polygon_array ); - int np = polygon_array.Size(); - Rcpp::List p( np ); - - for ( k = 0; k < np; k++ ) { - int max_cols = 2; - validate_array( polygon_array[k] ); - get_line_string( polygon_array[k], bbox, p, k, false, "", max_cols ); - if( max_cols > max_dimension ) { - max_dimension = max_cols; - } - } - mp[j] = p; - } - - if( requires_attribute ) { - std::string dim = geojsonsf::utils::make_dimension( max_dimension ); - mp.attr("class") = sfg_attributes( dim, attribute ); - } - sfc[i] = mp; -} diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index 7ed55fb..ca98abf 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -1,577 +1,12 @@ -#include -#include -#include "rapidjson/document.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/writer.h" - -#include "geojsonsf/geojsonsf.h" #include "geojsonsf/geojson_to_sf.h" -#include "geojsonsf/geojson_sfc.h" -#include "geojsonsf/geojson_sfg.h" -#include "geojsonsf/geojson_validate.h" -#include "geojsonsf/geojson_properties.h" - -void parse_geometry_object(Rcpp::List& sfc, - int i, - const Value& geometry, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects) { - - 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"]; - geometry_types.insert( geom_type ); - - - if (geom_type == "Point") { - get_points( coord_array, bbox, sfc, i, true, "POINT"); - - } else if (geom_type == "MultiPoint") { - int max_cols = 2; - get_line_string( coord_array, bbox, sfc, i, true, "MULTIPOINT", max_cols ); - - } else if (geom_type == "LineString") { - int max_cols = 2; - get_line_string( coord_array, bbox, sfc, i, true, "LINESTRING", max_cols ); - - } else if (geom_type == "MultiLineString") { - get_multi_line_string( coord_array, bbox, sfc, i, true, "MULTILINESTRING" ); - - } else if (geom_type == "Polygon") { - get_polygon( coord_array, bbox, sfc, i, true, "POLYGON" ); - - } else if (geom_type == "MultiPolygon") { - get_multi_polygon( coord_array, bbox, sfc, i, true, "MULTIPOLYGON" ); - - } else { - Rcpp::stop("unknown sfg type"); - } -} - -Rcpp::List parse_geometry_collection_object(const Value& val, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - bool& expand_geometries) { - std::string geom_type; - validate_geometries(val, sfg_objects); - auto geometries = val["geometries"].GetArray(); - unsigned int n = geometries.Size(); - unsigned int i; - Rcpp::List geom_collection(n); - - for (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); - } - geometry_types.insert( "GEOMETRYCOLLECTION" ); - - if ( !expand_geometries ) { - // TODO( dimension ) - std::string dim = "XY"; - std::string attribute = "GEOMETRYCOLLECTION"; - geom_collection.attr("class") = sfg_attributes( dim, attribute ); - } else { - sfg_objects+=n; - } - return geom_collection; -} - -void create_null_sfg(Rcpp::List& sfc, - std::unordered_set< std::string >& geometry_types, - int& nempty) { - std::string geom_type; - std::string dim = "XY"; - if (geometry_types.size() == 0) { - geom_type = "Point"; - } else { - geom_type = *geometry_types.begin(); - // the 'set' stores the geometries alphabetically - // If there are more than one geometries, does it really matter which one is - // selected? - // if there is only one type, we want to use that one, so selecting the 'begin' - // is as good a method as any - } - geometry_types.insert(geom_type); - transform(geom_type.begin(), geom_type.end(), geom_type.begin(), toupper); - - if (geom_type == "POINT" ) { - - Rcpp::NumericVector nullObj(2, NA_REAL); - nullObj.attr("class") = sfg_attributes(dim, geom_type); - sfc[0] = nullObj; - - } else if (geom_type == "MULTIPOINT" || geom_type == "LINESTRING") { - - Rcpp::NumericMatrix nullObj; - nullObj.attr("class") = sfg_attributes(dim, geom_type); - sfc[0] = nullObj; - - } else { - - Rcpp::List nullObj; - nullObj.attr("class") = sfg_attributes(dim, geom_type); - sfc[0] = nullObj; - } - nempty++; -} - -Rcpp::List parse_feature_object(const Value& feature, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - std::unordered_set< std::string >& property_keys, - Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - bool& expand_geometries, - int& nempty) { - - validate_geometry(feature, sfg_objects); - - // TODO( null property ==> NULL geometry) - //validate_properties(feature, sfg_objects); - - Rcpp::List sfc(1); - - const Value& geometry = feature["geometry"]; - std::string type; - - if (geometry.Size() > 0) { - - validate_type( geometry, sfg_objects ); - type = geometry["type"].GetString(); - - if (type == "GeometryCollection") { - sfc[0] = parse_geometry_collection_object(geometry, bbox, geometry_types, sfg_objects, expand_geometries); - } else { - parse_geometry_object(sfc, 0, geometry, bbox, geometry_types, sfg_objects); - } - } else { - create_null_sfg(sfc, geometry_types, nempty); - } - - if (type != "GeometryCollection") { - sfg_objects++; - } else if (type == "GeometryCollection" && !expand_geometries){ - sfg_objects++; - } - - const Value& p = feature["properties"]; - - get_property_keys(p, property_keys); - get_property_types(p, property_types); - - unsigned int geomsize = 1; - unsigned int i; - - if (expand_geometries && type == "GeometryCollection") { - validate_geometries( geometry, sfg_objects ); - auto geometries = geometry["geometries"].GetArray(); - geomsize = geometries.Size(); - } - - std::string s; - for ( i = 0; i < geomsize; i++ ) { - //https://stackoverflow.com/a/33473321/5977215 - if ( expand_geometries ) { - s = std::to_string( sfg_objects - i ); - } else { - s = std::to_string( sfg_objects ); - } - Value n( s.c_str(), doc_properties.GetAllocator() ); - - // TODO: is this method deep-cloning? - Value properties( feature["properties"], doc_properties.GetAllocator() ); - doc_properties.AddMember( n, properties, doc_properties.GetAllocator() ); - - } - - return sfc; -} - -Rcpp::List parse_feature_collection_object(const Value& fc, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - std::unordered_set< std::string >& property_keys, - Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - bool& expand_geometries, - int& nempty) { - - // a FeatureCollection MUST have members (array) called features, - validate_features(fc, sfg_objects); - - - auto features = fc["features"].GetArray(); - - unsigned int n = features.Size(); // number of features - unsigned int i; - - Rcpp::List feature_collection(n); - - for ( i = 0; i < n; i++ ) { - const Value& feature = features[i]; - feature_collection[i] = parse_feature_object( - feature, bbox, geometry_types, sfg_objects, property_keys, doc_properties, - property_types, expand_geometries, nempty - ); - } - return feature_collection; -} - - - -void parse_geojson(const Value& v, - Rcpp::List& sfc, - Rcpp::List& properties, - int i, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - std::unordered_set< std::string >& property_keys, - Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - bool& expand_geometries, - int& nempty) { - - Rcpp::List res(1); - validate_type(v, sfg_objects); - - std::string geom_type = v["type"].GetString(); - - if (geom_type == "Feature") { - res = parse_feature_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, expand_geometries, nempty); - sfc[i] = res; - - } else if (geom_type == "FeatureCollection") { - - res = parse_feature_collection_object(v, bbox, geometry_types, sfg_objects, property_keys, doc_properties, property_types, expand_geometries, nempty); - sfc[i] = res; - - } else if (geom_type == "GeometryCollection") { - - res = parse_geometry_collection_object(v, bbox, geometry_types, sfg_objects, expand_geometries); - if (!expand_geometries) { - sfg_objects++; - } - sfc[i] = res; - - } else { - - parse_geometry_object(sfc, i, v, bbox, geometry_types, sfg_objects); - sfg_objects++; - } -} - -void parse_geojson_object(Document& d, - Rcpp::List& sfc, - Rcpp::List& properties, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - std::unordered_set< std::string >& property_keys, - Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - bool& expand_geometries, - int& nempty) { - const Value& v = d; - parse_geojson( - v, sfc, properties, 0, bbox, geometry_types, sfg_objects, property_keys, - doc_properties, property_types, expand_geometries, nempty - ); - - // // out of order - // for ( auto it = property_keys.begin(); it != property_keys.end(); it++ ) { - // //const char s = *it->c_str(); - // std::cout << (*it) << std::endl; - // } - -} - -void parse_geojson_array(Document& d, - Rcpp::List& sfc, - Rcpp::List& properties, - int i, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - std::unordered_set< std::string >& property_keys, - Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - bool& expand_geometries, - int& nempty) { - const Value& v = d[i]; - parse_geojson( - v, sfc, properties, i, bbox, geometry_types, sfg_objects, property_keys, - doc_properties, property_types, expand_geometries, nempty - ); -} - -Rcpp::List geojson_to_sf(const char* geojson, - Rcpp::NumericVector& bbox, - std::unordered_set< std::string >& geometry_types, - int& sfg_objects, - std::unordered_set< std::string >& property_keys, - Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - bool& expand_geometries, - int& nempty) { - - Document d; - safe_parse(d, geojson); - Rcpp::List sf(1); - Rcpp::List sfc(1); - Rcpp::List properties(1); - unsigned int doc_ele; - - if (d.IsObject()) { - Rcpp::List sfg(1); - parse_geojson_object( - d, sfg, properties, bbox, geometry_types, sfg_objects, property_keys, - doc_properties, property_types, expand_geometries, nempty - ); - sfc[0] = sfg; - - } else if (d.IsArray()) { - - Rcpp::List sfgs(d.Size()); - - for (doc_ele = 0; doc_ele < d.Size(); doc_ele++) { - parse_geojson_array( - d, sfgs, properties, doc_ele, bbox, geometry_types, sfg_objects, - property_keys, doc_properties, property_types, expand_geometries, nempty - ); - } - sfc[0] = sfgs; - } - return sfc; -} - -void setup_property_vectors(std::unordered_map< std::string, std::string>& property_types, - Rcpp::List& properties, int& sfg_objects) { - - for (auto keys_it = property_types.begin(); keys_it != property_types.end(); keys_it++ ) { - - std::string this_key = keys_it->first; - std::string this_type = keys_it->second; - - if (this_type == "False" || this_type == "True" ) { - properties[ this_key ] = na_logical_vector( sfg_objects ); - } else if (this_type == "Number") { - properties[ this_key ] = na_numeric_vector( sfg_objects ); - } else { - properties[ this_key ] = na_string_vector( sfg_objects ); - } - } -} - - -void nested_json_to_string(rapidjson::Value& v, - std::string& type, - Rcpp::List& properties, - int& row_index, - std::string& key) { - - StringBuffer sb; - Writer writer(sb); - v.Accept(writer); - std::string this_value = sb.GetString(); - - if (type != "String") { - std::string value = any_to_string(this_value); - update_string_vector(properties, key, value, row_index -1); - } else { - std::string value = this_value; - update_string_vector(properties, key, value, row_index -1); - } -} - -void fill_property_vectors(Document& doc_properties, - std::unordered_map< std::string, std::string>& property_types, - Rcpp::List& properties, - int& row_index) { - - // TODO(move to header): - static const char* ARRAY_TYPES[] = - { "Null", "False", "True", "Object", "Array", "String", "Number" }; - - for (auto& m : doc_properties.GetObject() ) { - row_index = std::stoi( m.name.GetString() ); - - for (auto& p : m.value.GetObject() ) { - - std::string key = p.name.GetString(); - std::string type = property_types[ key ]; - - std::string value_type = ARRAY_TYPES[p.value.GetType()]; - - if (value_type == "String") { - std::string this_value = p.value.GetString(); - - if (type != "String") { - std::string value = any_to_string(this_value); - update_string_vector(properties, key, value, row_index-1); - } else { - std::string value = this_value; - update_string_vector(properties, key, value, row_index-1); - } - - } else if (value_type == "Number") { - - double this_value = p.value.GetDouble(); - - if (type != "Number") { - std::string value = any_to_string(this_value); - update_string_vector(properties, key, value, row_index-1); - } else { - double value = p.value.GetDouble(); - update_numeric_vector(properties, key, value, row_index-1); - } - - } else if (value_type == "False") { - - bool this_value = p.value.GetBool(); - if (type != "False") { - std::string value = any_to_string(this_value); - update_string_vector(properties, key, value, row_index-1); - } else { - bool value = p.value.GetBool(); - update_logical_vector(properties, key, value, row_index-1); - } - - } else if (value_type == "True") { - - bool this_value = p.value.GetBool(); - if (type != "True") { - std::string value = any_to_string(this_value); - update_string_vector(properties, key, value, row_index-1); - } else { - bool value = p.value.GetBool(); - update_logical_vector(properties, key, value, row_index-1); - } - - } else if (value_type == "Null") { - // don't do anything... - } else if (value_type == "Object") { - - Value v = p.value.GetObject(); - nested_json_to_string(v, type, properties, row_index, key); - - } else if (value_type == "Array") { - - Value v = p.value.GetArray(); - nested_json_to_string(v, type, properties, row_index, key); - - } else { - Rcpp::stop("unknown column data type " + type); - } - } - } -} - -Rcpp::List create_sfc(Rcpp::StringVector geojson, bool& expand_geometries) { - // iterate over the geojson - int n = geojson.size(); - int sfg_objects = 0; // keep track of number of objects - int nempty = 0; - //int row_index; - - // Attributes to keep track of along the way - Rcpp::NumericVector bbox = start_bbox(); - std::unordered_set< std::string > geometry_types; - std::unordered_set< std::string > property_keys; // storing all the 'key' values from 'properties' - std::unordered_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_sf( - geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, - doc_properties, property_types, expand_geometries, nempty - ); - } - - return construct_sfc(sfg_objects, sfc, bbox, geometry_types, nempty); -} // [[Rcpp::export]] Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& expand_geometries) { - return create_sfc(geojson, expand_geometries); -} - -Rcpp::List construct_sf( Rcpp::List& lst, std::unordered_set< std::string >& property_keys, - std::unordered_map< std::string, std::string>& property_types, - Document& doc_properties, - int& sfg_objects, - int& row_index ) { - - int n_cols = property_keys.size(); - if ( sfg_objects > 0 ) { - property_keys.insert("geometry"); - n_cols++; // expand to include geometry - } - - Rcpp::List properties( n_cols ); - - sort_property_names(properties, property_keys); - - properties["geometry"] = lst; - - setup_property_vectors( property_types, properties, sfg_objects ); - fill_property_vectors( doc_properties, property_types, properties, row_index ); - - if (sfg_objects > 0 ) { - Rcpp::IntegerVector nv = seq( 1, sfg_objects ); - properties.attr("row.names") = nv; - } else { - properties.attr("row.names") = Rcpp::IntegerVector(0); - } - properties.attr("class") = Rcpp::CharacterVector::create("sf", "data.frame"); - properties.attr("sf_column") = "geometry"; - - return properties; -} - -Rcpp::List generic_geojson_to_sf(Rcpp::StringVector geojson, bool& expand_geometries) { - // iterate over the geojson - int n = geojson.size(); - int sfg_objects = 0; // keep track of number of objects - int row_index = 0; - int nempty = 0; - - // Attributes to keep track of along the way - Rcpp::NumericVector bbox = start_bbox(); - std::unordered_set< std::string > geometry_types; - std::unordered_set< std::string > property_keys; // storing all the 'key' values from 'properties' - std::unordered_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_sf( - geojson[geo_ele], bbox, geometry_types, sfg_objects, property_keys, - doc_properties, property_types, expand_geometries, nempty - ); - } - - Rcpp::List res = construct_sfc( sfg_objects, sfc, bbox, geometry_types, nempty ); - return construct_sf( res, property_keys, property_types, doc_properties, sfg_objects, row_index ); + return geojsonsf::sf::create_sfc(geojson, expand_geometries); } // [[Rcpp::export]] Rcpp::List rcpp_geojson_to_sf( Rcpp::StringVector geojson, bool expand_geometries ) { - return generic_geojson_to_sf( geojson, expand_geometries ); + return geojsonsf::sf::generic_geojson_to_sf( geojson, expand_geometries ); } diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index 1f651e3..6252e69 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -2,11 +2,11 @@ #include "rapidjson/document.h" #include #include "geojsonsf/geojsonsf.h" -#include "geojsonsf/geojson_sfc.h" -#include "geojsonsf/geojson_sfg.h" +#include "geojsonsf/sf/sfc/geojson_sfc.h" +#include "geojsonsf/sf/sfg/geojson_sfg.h" #include "geojsonsf/geojson_to_sf.h" -#include "geojsonsf/geojson_validate.h" -#include "geojsonsf/geojson_properties.h" +#include "geojsonsf/geojson/geojson_validate.hpp" +#include "geojsonsf/geojson/geojson_properties.hpp" #include "geojsonsf/geojson_wkt.h" using namespace rapidjson; @@ -18,9 +18,9 @@ void parse_geometry_object_wkt(Rcpp::List& sfc, std::unordered_set< std::string >& geometry_types, int& wkt_objects) { - validate_type(geometry, wkt_objects); - validate_coordinates(geometry, wkt_objects); - validate_array(geometry["coordinates"], wkt_objects); + geojsonsf::validate::validate_type(geometry, wkt_objects); + geojsonsf::validate::validate_coordinates(geometry, wkt_objects); + geojsonsf::validate::validate_array(geometry["coordinates"], wkt_objects); std::string geom_type = geometry["type"].GetString(); const Value& coord_array = geometry["coordinates"]; @@ -60,7 +60,7 @@ void parse_geometry_object_wkt(Rcpp::List& sfc, // TODO( dimension ) std::string dim = "XY"; - wkt.attr("class") = sfg_attributes(dim, geom_type); + wkt.attr("class") = geojsonsf::sfg::sfg_attributes(dim, geom_type); sfc[i] = wkt; } @@ -71,7 +71,7 @@ Rcpp::List parse_geometry_collection_object_wkt(const Value& val, std::string geom_type; - validate_geometries(val, wkt_objects); + geojsonsf::validate::validate_geometries(val, wkt_objects); auto geometries = val["geometries"].GetArray(); unsigned int n = geometries.Size(); unsigned int i; @@ -81,7 +81,7 @@ Rcpp::List parse_geometry_collection_object_wkt(const Value& val, for (i = 0; i < n; i++) { const Value& gcval = geometries[i]; - validate_type(gcval, wkt_objects); + geojsonsf::validate::validate_type(gcval, wkt_objects); geom_type = gcval["type"].GetString(); parse_geometry_object_wkt(geom_collection, i, gcval, geometry_types, wkt_objects); } @@ -100,7 +100,7 @@ Rcpp::List parse_geometry_collection_object_wkt(const Value& val, // TODO( dimension ); std::string dim = "XY"; std::string attribute = "GEOMETRYCOLLECTION"; - geom_collection_wkt.attr("class") = sfg_attributes( dim, attribute ); + geom_collection_wkt.attr("class") = geojsonsf::sfg::sfg_attributes( dim, attribute ); return geom_collection_wkt; } @@ -112,15 +112,15 @@ Rcpp::List parse_feature_object_wkt(const Value& feature, Document& doc_properties, std::unordered_map< std::string, std::string>& property_types) { - validate_geometry(feature, wkt_objects); - validate_properties(feature, wkt_objects); + geojsonsf::validate::validate_geometry(feature, wkt_objects); + geojsonsf::validate::validate_properties(feature, wkt_objects); const Value& geometry = feature["geometry"]; Rcpp::List sfc(1); if (geometry.Size() > 0) { - validate_type(geometry, wkt_objects); + geojsonsf::validate::validate_type(geometry, wkt_objects); std::string geom_type = geometry["type"].GetString(); if (geom_type == "GeometryCollection") { @@ -134,15 +134,15 @@ Rcpp::List parse_feature_object_wkt(const Value& feature, Rcpp::StringVector wkt = "POINT EMPTY"; std::string dim = "XY"; std::string attribute = "POINT"; - wkt.attr("class") = sfg_attributes( dim, attribute ); + wkt.attr("class") = geojsonsf::sfg::sfg_attributes( dim, attribute ); sfc[0] = wkt; } wkt_objects++; // get property keys const Value& p = feature["properties"]; - get_property_keys(p, property_keys); - get_property_types(p, property_types); + geojsonsf::geojson_properties::get_property_keys(p, property_keys); + geojsonsf::geojson_properties::get_property_types(p, property_types); //https://stackoverflow.com/a/33473321/5977215 std::string s = std::to_string(wkt_objects); @@ -163,7 +163,7 @@ Rcpp::List parse_feature_collection_object_wkt(const Value& fc, Document& doc_properties, std::unordered_map< std::string, std::string>& property_types) { // a FeatureCollection MUST have members (arrays) called features, - validate_features(fc, wkt_objects); + geojsonsf::validate::validate_features(fc, wkt_objects); auto features = fc["features"].GetArray(); @@ -190,7 +190,7 @@ void parse_geojson_wkt(const Value& v, std::unordered_map< std::string, std::string >& property_types ) { Rcpp::List res(1); - validate_type(v, wkt_objects); + geojsonsf::validate::validate_type(v, wkt_objects); std::string geom_type = v["type"].GetString(); @@ -253,7 +253,7 @@ Rcpp::List geojson_to_wkt(const char* geojson, ) { Document d; - safe_parse(d, geojson); + geojsonsf::validate::safe_parse(d, geojson); Rcpp::List sf(1); Rcpp::List sfc(1); Rcpp::List properties(1); @@ -287,7 +287,7 @@ Rcpp::List construct_wkt(int& sfg_objects, std::string geom_attr; int sfg_counter = 0; - fetch_geometries(sf, sfc_output, sfg_counter); + geojsonsf::sfc::utils::fetch_geometries(sf, sfc_output, sfg_counter); return sfc_output; } @@ -303,12 +303,12 @@ Rcpp::List construct_wkt_df(Rcpp::List& lst, std::unordered_set< std::string >& property_keys.insert("geometry"); properties.names() = property_keys; - sort_property_names(properties, property_keys); + geojsonsf::geojson_properties::sort_property_names(properties, property_keys); properties["geometry"] = lst; - setup_property_vectors(property_types, properties, wkt_objects); - fill_property_vectors(doc_properties, property_types, properties, row_index); + geojsonsf::geojson_properties::setup_property_vectors(property_types, properties, wkt_objects); + geojsonsf::geojson_properties::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"); diff --git a/src/geojson_validate.cpp b/src/geojson_validate.cpp deleted file mode 100644 index 734eb65..0000000 --- a/src/geojson_validate.cpp +++ /dev/null @@ -1,104 +0,0 @@ - -#include "rapidjson/document.h" -#include - -#include -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); -} - -void safe_parse(Document& d, const char* geojson) { - d.Parse(geojson); - if( d.Parse(geojson).HasParseError() ) { - Rcpp::stop("Invalid JSON"); - } -} - -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); - } - if (v["type"].IsNull()) { - geojson_object_error("type", sfg_objects); - } -} - -void validate_features(const Value& v, int& sfg_objects) { - if (v.HasMember("features") == false) { - geojson_object_error("features", sfg_objects); - } -} - -void validate_feature(const Value& v, int& sfg_objects) { - if (v.HasMember("feature") == false) { - geojson_object_error("feature", sfg_objects); - } -} - -void validate_properties(const Value& v, int& sfg_objects) { - if (v.HasMember("properties") == false) { - geojson_object_error("properties", sfg_objects); - } -} - -void validate_geometry(const Value& v, int& sfg_objects) { - if (v.HasMember("geometry") == false) { - geojson_object_error("geometry", sfg_objects); - } -} - -void validate_geometries(const Value& v, int& sfg_objects) { - if (v.HasMember("geometries") == false) { - geojson_object_error("geometries", sfg_objects); - } -} - -void validate_coordinates(const Value& v, int& sfg_objects) { - if (v.HasMember("coordinates") == false) { - geojson_object_error("coordinates", sfg_objects); - } -} - -void validate_points(const Value& v) { - if ( v.Size() < 2 || v.Size() > 4 ) { - geojson_object_error("lon/lat"); - } -} - -void validate_point(const Value& v) { - // TODO(move to header): - // Rcpp::Rcout << "v.size: " << v.Size() << std::endl; - // Rcpp::Rcout << "v.type: " << v.GetType() << std::endl; - // if ( v.Size() < 2 ) { - // Rcpp::stop("mis-specified geometry"); - // } - - static const char* ARRAY_TYPES[] = - { "Null", "false", "True", "Object", "Array", "String", "Number" }; - if( strncmp(ARRAY_TYPES[v.GetType()], "Num", 3) != 0) { - geojson_object_error("lon/lat"); - } -} - diff --git a/src/geojson_wkt.cpp b/src/geojson_wkt.cpp index caad8f1..b58728a 100644 --- a/src/geojson_wkt.cpp +++ b/src/geojson_wkt.cpp @@ -2,8 +2,9 @@ #include "rapidjson/document.h" #include #include "geojsonsf/geojsonsf.h" -#include "geojsonsf/geojson_sfg.h" -#include "geojsonsf/geojson_validate.h" +#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/geojson/geojson_validate.hpp" + using namespace rapidjson; using namespace Rcpp; @@ -74,8 +75,8 @@ void add_lonlat_to_wkt_stream(std::ostringstream& os, float lon, float lat ) { 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); + point[0] = geojsonsf::sfg::get_lon(coord_array); + point[1] = geojsonsf::sfg::get_lat(coord_array); add_lonlat_to_wkt_stream(os, point[0], point[1]); } @@ -84,7 +85,7 @@ void multi_point_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); unsigned int i; for (i = 0; i < n; i++) { - validate_array(coord_array[i]); + geojsonsf::validate::validate_array(coord_array[i]); point_to_wkt(os, coord_array[i]); coord_separator(os, i, n); } @@ -94,7 +95,7 @@ void line_string_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); unsigned int i; for (i = 0; i < n; i++) { - validate_array(coord_array[i]); + geojsonsf::validate::validate_array(coord_array[i]); point_to_wkt(os, coord_array[i]); coord_separator(os, i, n); } @@ -104,7 +105,7 @@ void multi_line_string_to_wkt(std::ostringstream& os, const Value& coord_array) size_t n = coord_array.Size(); unsigned int i; for (i = 0; i < n; i++) { - validate_array(coord_array[i]); + geojsonsf::validate::validate_array(coord_array[i]); line_string_to_wkt(os, coord_array[i]); line_separator_wkt(os, i, n); } @@ -114,7 +115,7 @@ void polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); unsigned int i; for (i = 0; i < n; i++) { - validate_array(coord_array[i]); + geojsonsf::validate::validate_array(coord_array[i]); line_string_to_wkt(os, coord_array[i]); line_separator_wkt(os, i, n); } @@ -124,7 +125,7 @@ void multi_polygon_to_wkt(std::ostringstream& os, const Value& coord_array) { size_t n = coord_array.Size(); unsigned int i; for (i = 0; i < n; i++) { - validate_array(coord_array[i]); + geojsonsf::validate::validate_array(coord_array[i]); polygon_to_wkt(os, coord_array[i]); polygon_separate_wkt(os, i, n); } diff --git a/src/read_geojson.cpp b/src/read_geojson.cpp index a9b307e..d68a0d0 100644 --- a/src/read_geojson.cpp +++ b/src/read_geojson.cpp @@ -4,9 +4,9 @@ #include "geojsonsf/geojsonsf.h" #include "geojsonsf/geojson_to_sf.h" -#include "geojsonsf/geojson_sfc.h" -#include "geojsonsf/geojson_sfg.h" -#include "geojsonsf/geojson_properties.h" +#include "geojsonsf/sf/sfc/geojson_sfc.h" +#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/geojson/geojson_properties.hpp" #include using namespace Rcpp; @@ -21,12 +21,12 @@ Rcpp::StringVector buffer_string(std::string file) { // [[Rcpp::export]] Rcpp::List rcpp_read_sfc_file(std::string file, bool flatten_geometries) { - return create_sfc(buffer_string(file), flatten_geometries); + return geojsonsf::sf::create_sfc(buffer_string(file), flatten_geometries); } // [[Rcpp::export]] Rcpp::List rcpp_read_sf_file(std::string file, bool flatten_geometries) { - return generic_geojson_to_sf(buffer_string(file), flatten_geometries); + return geojsonsf::sf::generic_geojson_to_sf(buffer_string(file), flatten_geometries); } diff --git a/src/sf_geojson.cpp b/src/sf_geojson.cpp index d3cff6c..f07a27d 100644 --- a/src/sf_geojson.cpp +++ b/src/sf_geojson.cpp @@ -16,88 +16,3 @@ Rcpp::StringVector rcpp_sf_to_geojson_atomise( Rcpp::DataFrame& sf, int& digits Rcpp::StringVector rcpp_sf_to_geojson( Rcpp::DataFrame& sf, int& digits ) { return geojsonsf::api::sf_to_geojson( sf, digits ); } - - - - -// // [[Rcpp::export]] -// Rcpp::StringVector rcpp_sf_to_geojson_downcast( Rcpp::DataFrame& sf ) { -// rapidjson::StringBuffer sb; -// rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); -// -// std::string geom_column = sf.attr("sf_column"); -// -// size_t n_cols = sf.ncol(); -// size_t n_properties = n_cols - 1; -// size_t n_rows = sf.nrows(); -// size_t i, j; -// Rcpp::StringVector column_names = sf.names(); -// Rcpp::StringVector property_names(sf.size() - 1); -// -// size_t property_multiplier = 0; -// std::string geom_type; -// Rcpp::CharacterVector cls; -// -// int property_counter = 0; -// for (int i = 0; i < sf.length(); i++) { -// if (column_names[i] != geom_column) { -// property_names[property_counter] = column_names[i]; -// property_counter++; -// } -// } -// -// writer.StartObject(); -// geojsonsf::writers::start_feature_collection( writer ); -// -// writer.StartArray(); -// -// for( i = 0; i < n_rows; i++ ) { -// -// Rcpp::List sfc = sf[ geom_column ]; -// SEXP sfg = sfc[ i ]; -// -// cls = geojsonsf::getSfClass(sfg); -// geom_type = cls[1]; -// -// if ( geom_type == "GEOMETRYCOLLECTION" ) { -// Rcpp::stop("GEOMETRYCOLLECTION not supported for down-casting"); -// } -// -// property_multiplier = geojsonsf::sizes::geometry_size( sfg, geom_type, cls ); -// // Rcpp::Rcout << "property_multiplier: " << property_multiplier << std::endl; -// -// for( int geometry = 0; geometry < property_multiplier; geometry++ ) { -// -// writer.StartObject(); -// -// geojsonsf::writers::start_features( writer ); -// geojsonsf::writers::start_properties( writer ); -// writer.StartObject(); -// -// // properties first, then sfc -// for( j = 0; j < n_properties; j++ ) { -// const char *h = property_names[ j ]; -// -// SEXP this_vec = sf[ h ]; -// -// jsonify::writers::write_value( writer, h ); -// jsonify::dataframe::dataframe_cell( writer, this_vec, i ); -// } -// writer.EndObject(); -// -// writer.String("geometry"); -// write_geometry( writer, sfc, i, geometry, geom_type, cls ); -// -// writer.EndObject(); -// } -// } -// -// writer.EndArray(); -// writer.EndObject(); -// -// Rcpp::StringVector geojson = sb.GetString(); -// geojson.attr("class") = Rcpp::CharacterVector::create("geojson","json"); -// return geojson; -// } - - From 9e4bfaf692218e453b8abe2f5434670fbb4c35f1 Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Sun, 30 Dec 2018 13:16:39 +1100 Subject: [PATCH 3/4] restructuring --- .../geojsonsf/geojson/geojson_properties.hpp | 1 + .../geojson_to_sf.hpp} | 6 +- inst/include/geojsonsf/geojson/parse.hpp | 2 +- .../geojson/writers/write_geojson.hpp | 111 ++++++++++++++++++ .../sf/sfc/{geojson_sfc.h => geojson_sfc.hpp} | 0 .../sf/sfg/{geojson_sfg.h => geojson_sfg.hpp} | 2 +- .../{geojson_wkt.h => wkt/geojson_wkt.hpp} | 0 inst/include/geojsonsf/write_geojson.hpp | 2 + src/geojson_properties.cpp | 1 - src/geojson_to_sf.cpp | 2 +- src/geojson_to_wkt.cpp | 11 +- src/geojson_wkt.cpp | 3 +- src/read_geojson.cpp | 7 +- 13 files changed, 129 insertions(+), 19 deletions(-) rename inst/include/geojsonsf/{geojson_to_sf.h => geojson/geojson_to_sf.hpp} (96%) create mode 100644 inst/include/geojsonsf/geojson/writers/write_geojson.hpp rename inst/include/geojsonsf/sf/sfc/{geojson_sfc.h => geojson_sfc.hpp} (100%) rename inst/include/geojsonsf/sf/sfg/{geojson_sfg.h => geojson_sfg.hpp} (99%) rename inst/include/geojsonsf/{geojson_wkt.h => wkt/geojson_wkt.hpp} (100%) diff --git a/inst/include/geojsonsf/geojson/geojson_properties.hpp b/inst/include/geojsonsf/geojson/geojson_properties.hpp index 8a58b07..e4fbd02 100644 --- a/inst/include/geojsonsf/geojson/geojson_properties.hpp +++ b/inst/include/geojsonsf/geojson/geojson_properties.hpp @@ -3,6 +3,7 @@ #include "rapidjson/document.h" #include + using namespace rapidjson; namespace geojsonsf { diff --git a/inst/include/geojsonsf/geojson_to_sf.h b/inst/include/geojsonsf/geojson/geojson_to_sf.hpp similarity index 96% rename from inst/include/geojsonsf/geojson_to_sf.h rename to inst/include/geojsonsf/geojson/geojson_to_sf.hpp index 85f22ff..d85ef7d 100644 --- a/inst/include/geojsonsf/geojson_to_sf.h +++ b/inst/include/geojsonsf/geojson/geojson_to_sf.hpp @@ -9,10 +9,10 @@ #include "rapidjson/writer.h" #include "geojsonsf/geojsonsf.h" -#include "geojsonsf/geojson_to_sf.h" +#include "geojsonsf/geojson/geojson_to_sf.hpp" #include "geojsonsf/sf/sf/construct.hpp" -#include "geojsonsf/sf/sfc/geojson_sfc.h" -#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/sf/sfc/geojson_sfc.hpp" +#include "geojsonsf/sf/sfg/geojson_sfg.hpp" #include "geojsonsf/geojson/geojson_validate.hpp" #include "geojsonsf/geojson/geojson_properties.hpp" #include "geojsonsf/geojson/parse.hpp" diff --git a/inst/include/geojsonsf/geojson/parse.hpp b/inst/include/geojsonsf/geojson/parse.hpp index 1ac8a64..8a22bc2 100644 --- a/inst/include/geojsonsf/geojson/parse.hpp +++ b/inst/include/geojsonsf/geojson/parse.hpp @@ -2,7 +2,7 @@ #define GEOJSONSF_GEOJSON_PARSE_H #include -#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/sf/sfg/geojson_sfg.hpp" #include "geojsonsf/geojson/geojson_validate.hpp" namespace geojsonsf { diff --git a/inst/include/geojsonsf/geojson/writers/write_geojson.hpp b/inst/include/geojsonsf/geojson/writers/write_geojson.hpp new file mode 100644 index 0000000..8db0067 --- /dev/null +++ b/inst/include/geojsonsf/geojson/writers/write_geojson.hpp @@ -0,0 +1,111 @@ +#ifndef GEOJSONSF_GEOJSON_WRITE_GEOJSON_H +#define GEOJSONSF_GEOJSON_WRITE_GEOJSON_H + +#include +#include "geojsonsf/geojson/writers/writers.hpp" +#include "geojsonsf/geometrycollection/geometrycollection.hpp" + +namespace geojsonsf { +namespace geojson { +namespace write_geojson { + + template< typename Writer > + inline void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, Rcpp::CharacterVector& cls, int& digits ) { + + if (geom_type == "POINT") { + geojsonsf::writers::points_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTIPOINT") { + geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "LINESTRING") { + geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTILINESTRING") { + Rcpp::List multiline = Rcpp::as< Rcpp::List >( sfg ); + geojsonsf::writers::polygon_to_geojson( writer, multiline, digits ); + + } else if (geom_type == "POLYGON") { + Rcpp::List polygon = Rcpp::as< Rcpp::List >(sfg); + geojsonsf::writers::polygon_to_geojson( writer, polygon, digits ); + + } else if (geom_type == "MULTIPOLYGON") { + Rcpp::List multipolygon = Rcpp::as< Rcpp::List >( sfg ); + geojsonsf::writers::multi_polygon_to_geojson( writer, multipolygon, digits ); + + } else if (geom_type == "GEOMETRYCOLLECTION") { + Rcpp::List gc = Rcpp::as< Rcpp::List >( sfg ); + Rcpp::List sfgi(1); + for (int i = 0; i < gc.size(); i++) { + sfgi[0] = gc[i]; + std::string gc_geom_type; + bool isnull = false; + geojsonsf::geometrycollection::gc_type( sfgi, gc_geom_type, isnull, cls ); + if( !isnull ) { + SEXP sfg_gc = gc[i]; + geojsonsf::writers::begin_geojson_geometry(writer, gc_geom_type); + write_geojson( writer, sfg_gc, gc_geom_type, cls, digits ); + geojsonsf::writers::end_geojson_geometry(writer, gc_geom_type); + } + } + } + } + + + /* + * used for down-casting MULTI objects + */ + template< typename Writer > + inline void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, + Rcpp::CharacterVector& cls, int geometry_index, int& digits ) { + + if (geom_type == "POINT") { + geojsonsf::writers::points_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTIPOINT") { + Rcpp::NumericMatrix mls = Rcpp::as< Rcpp::NumericMatrix >( sfg ); + Rcpp::NumericVector pts = mls(geometry_index, Rcpp::_ ); + geojsonsf::writers::points_to_geojson( writer, pts, digits ); + + } else if (geom_type == "LINESTRING") { + geojsonsf::writers::linestring_to_geojson( writer, sfg, digits ); + + } else if (geom_type == "MULTILINESTRING") { + Rcpp::List multiline = Rcpp::as< Rcpp::List >( sfg ); + SEXP ml = multiline[ geometry_index ]; + geojsonsf::writers::linestring_to_geojson( writer, ml, digits ); + + } else if (geom_type == "POLYGON") { + Rcpp::List polygon = Rcpp::as< Rcpp::List >(sfg); + geojsonsf::writers::polygon_to_geojson( writer, polygon, digits ); + + } else if (geom_type == "MULTIPOLYGON") { + Rcpp::List multipolygon = Rcpp::as< Rcpp::List >( sfg ); + Rcpp::List mlp = multipolygon[ geometry_index ]; + geojsonsf::writers::polygon_to_geojson( writer, mlp, digits ); + + } else if (geom_type == "GEOMETRYCOLLECTION") { + Rcpp::List gc = Rcpp::as< Rcpp::List >( sfg ); + Rcpp::List sfgi(1); + for (int i = 0; i < gc.size(); i++) { + sfgi[0] = gc[i]; + + std::string gc_geom_type; + bool isnull = false; + geojsonsf::geometrycollection::gc_type( sfgi, gc_geom_type, isnull, cls ); + if( !isnull ) { + SEXP sfg_gc = gc[i]; + geojsonsf::writers::begin_geojson_geometry(writer, gc_geom_type); + write_geojson( writer, sfg_gc, gc_geom_type, cls, digits ); + geojsonsf::writers::end_geojson_geometry(writer, gc_geom_type); + } + + } + } + } + +} // namespace geojsonsf +} // namespace geojson +} // namespace write_geojson + +#endif diff --git a/inst/include/geojsonsf/sf/sfc/geojson_sfc.h b/inst/include/geojsonsf/sf/sfc/geojson_sfc.hpp similarity index 100% rename from inst/include/geojsonsf/sf/sfc/geojson_sfc.h rename to inst/include/geojsonsf/sf/sfc/geojson_sfc.hpp diff --git a/inst/include/geojsonsf/sf/sfg/geojson_sfg.h b/inst/include/geojsonsf/sf/sfg/geojson_sfg.hpp similarity index 99% rename from inst/include/geojsonsf/sf/sfg/geojson_sfg.h rename to inst/include/geojsonsf/sf/sfg/geojson_sfg.hpp index f0289b3..9703000 100644 --- a/inst/include/geojsonsf/sf/sfg/geojson_sfg.h +++ b/inst/include/geojsonsf/sf/sfg/geojson_sfg.hpp @@ -4,7 +4,7 @@ #include #include "rapidjson/document.h" -#include "geojsonsf/sf/sfc/geojson_sfc.h" +#include "geojsonsf/sf/sfc/geojson_sfc.hpp" #include "geojsonsf/geojson/geojson_validate.hpp" #include "geojsonsf/utils/utils.hpp" #include "geojsonsf/utils/where/where.hpp" diff --git a/inst/include/geojsonsf/geojson_wkt.h b/inst/include/geojsonsf/wkt/geojson_wkt.hpp similarity index 100% rename from inst/include/geojsonsf/geojson_wkt.h rename to inst/include/geojsonsf/wkt/geojson_wkt.hpp diff --git a/inst/include/geojsonsf/write_geojson.hpp b/inst/include/geojsonsf/write_geojson.hpp index d05db63..87d5878 100644 --- a/inst/include/geojsonsf/write_geojson.hpp +++ b/inst/include/geojsonsf/write_geojson.hpp @@ -5,6 +5,8 @@ #include "geojsonsf/geojson/writers/writers.hpp" #include "geojsonsf/geometrycollection/geometrycollection.hpp" +// TODO( deprecate this in favour of geojsonsf/geojson/writers/write_geojson) + namespace geojsonsf { namespace write_geojson { diff --git a/src/geojson_properties.cpp b/src/geojson_properties.cpp index 5443614..a62f4c6 100644 --- a/src/geojson_properties.cpp +++ b/src/geojson_properties.cpp @@ -4,7 +4,6 @@ // #include // #include "geojsonsf/geojsonsf.h" // using namespace rapidjson; -// using namespace Rcpp; // // // diff --git a/src/geojson_to_sf.cpp b/src/geojson_to_sf.cpp index ca98abf..83625dd 100644 --- a/src/geojson_to_sf.cpp +++ b/src/geojson_to_sf.cpp @@ -1,5 +1,5 @@ -#include "geojsonsf/geojson_to_sf.h" +#include "geojsonsf/geojson/geojson_to_sf.hpp" // [[Rcpp::export]] Rcpp::List rcpp_geojson_to_sfc(Rcpp::StringVector geojson, bool& expand_geometries) { diff --git a/src/geojson_to_wkt.cpp b/src/geojson_to_wkt.cpp index 6252e69..3aaab7a 100644 --- a/src/geojson_to_wkt.cpp +++ b/src/geojson_to_wkt.cpp @@ -2,15 +2,14 @@ #include "rapidjson/document.h" #include #include "geojsonsf/geojsonsf.h" -#include "geojsonsf/sf/sfc/geojson_sfc.h" -#include "geojsonsf/sf/sfg/geojson_sfg.h" -#include "geojsonsf/geojson_to_sf.h" +#include "geojsonsf/sf/sfc/geojson_sfc.hpp" +#include "geojsonsf/sf/sfg/geojson_sfg.hpp" +#include "geojsonsf/geojson/geojson_to_sf.hpp" #include "geojsonsf/geojson/geojson_validate.hpp" #include "geojsonsf/geojson/geojson_properties.hpp" -#include "geojsonsf/geojson_wkt.h" +#include "geojsonsf/wkt/geojson_wkt.hpp" using namespace rapidjson; -using namespace Rcpp; void parse_geometry_object_wkt(Rcpp::List& sfc, int i, @@ -310,7 +309,7 @@ Rcpp::List construct_wkt_df(Rcpp::List& lst, std::unordered_set< std::string >& geojsonsf::geojson_properties::setup_property_vectors(property_types, properties, wkt_objects); geojsonsf::geojson_properties::fill_property_vectors(doc_properties, property_types, properties, row_index); - Rcpp::IntegerVector nv = seq(1, wkt_objects); + Rcpp::IntegerVector nv = Rcpp::seq(1, wkt_objects); properties.attr("class") = Rcpp::CharacterVector::create("data.frame"); properties.attr("wkt_column") = "geometry"; properties.attr("row.names") = nv; diff --git a/src/geojson_wkt.cpp b/src/geojson_wkt.cpp index b58728a..756f952 100644 --- a/src/geojson_wkt.cpp +++ b/src/geojson_wkt.cpp @@ -2,11 +2,10 @@ #include "rapidjson/document.h" #include #include "geojsonsf/geojsonsf.h" -#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/sf/sfg/geojson_sfg.hpp" #include "geojsonsf/geojson/geojson_validate.hpp" using namespace rapidjson; -using namespace Rcpp; void begin_wkt(std::ostringstream& os, std::string& geom_type) { diff --git a/src/read_geojson.cpp b/src/read_geojson.cpp index d68a0d0..c4867a9 100644 --- a/src/read_geojson.cpp +++ b/src/read_geojson.cpp @@ -3,13 +3,12 @@ #include #include "geojsonsf/geojsonsf.h" -#include "geojsonsf/geojson_to_sf.h" -#include "geojsonsf/sf/sfc/geojson_sfc.h" -#include "geojsonsf/sf/sfg/geojson_sfg.h" +#include "geojsonsf/geojson/geojson_to_sf.hpp" +#include "geojsonsf/sf/sfc/geojson_sfc.hpp" +#include "geojsonsf/sf/sfg/geojson_sfg.hpp" #include "geojsonsf/geojson/geojson_properties.hpp" #include -using namespace Rcpp; using namespace rapidjson; Rcpp::StringVector buffer_string(std::string file) { From 65e2f89e9db3245f38bc472be99eb4bef8219bea Mon Sep 17 00:00:00 2001 From: SymbolixAU Date: Sun, 30 Dec 2018 13:18:42 +1100 Subject: [PATCH 4/4] bump version - close #48 --- DESCRIPTION | 2 +- NEWS.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6bbdc39..f7f981f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: geojsonsf Type: Package Title: GeoJSON to Simple Feature Converter -Version: 1.2.2001 +Version: 1.3.0 Date: 2019-01-02 Authors@R: c( person("David", "Cooley", ,"dcooley@symbolix.com.au", role = c("aut", "cre")) diff --git a/NEWS.md b/NEWS.md index aebef93..d05e54d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,9 @@ +## v1.3 + +* restructured C++ src code for easier navigation and linking + + ## v1.2.2 * `digits` argument for rounding coordinates