From 88f3341b6b3fb6a5f64107d0e1d6dfe11444fb6d Mon Sep 17 00:00:00 2001 From: tospig Date: Wed, 22 May 2019 21:38:29 +1000 Subject: [PATCH] valid json for #33 --- R/RcppExports.R | 4 + .../include/spatialwidget/geojson/geojson.hpp | 907 ++++++++++-------- src/RcppExports.cpp | 12 + src/geojson.cpp | 5 + 4 files changed, 519 insertions(+), 409 deletions(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index 59333a5..99c6e92 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -5,6 +5,10 @@ rcpp_construct_data <- function(param_names, params, data_names, lst_defaults, d .Call(`_spatialwidget_rcpp_construct_data`, param_names, params, data_names, lst_defaults, data, data_rows) } +rcpp_geojson_mesh <- function(mesh) { + .Call(`_spatialwidget_rcpp_geojson_mesh`, mesh) +} + rcpp_geojson <- function(sf, geometry) { .Call(`_spatialwidget_rcpp_geojson`, sf, geometry) } diff --git a/inst/include/spatialwidget/geojson/geojson.hpp b/inst/include/spatialwidget/geojson/geojson.hpp index def8dad..f95368c 100644 --- a/inst/include/spatialwidget/geojson/geojson.hpp +++ b/inst/include/spatialwidget/geojson/geojson.hpp @@ -14,589 +14,678 @@ namespace spatialwidget { namespace geojson { - /* - * a variation on the atomise function to return an array of atomised features - */ - inline Rcpp::StringVector to_geojson_atomise( - Rcpp::DataFrame& sf, - Rcpp::StringVector& geometries, - int digits) { - - int n_geometries = geometries.size(); - int geom; - - int n_cols = sf.ncol(); - int n_properties = n_cols - n_geometries; - int n_rows = sf.nrows(); - int i, j; - Rcpp::StringVector column_names = sf.names(); - Rcpp::StringVector property_names( n_properties ); - - int property_counter = 0; - - for ( i = 0; i < sf.length(); i++) { - Rcpp::String this_column = column_names[i]; - int idx = spatialwidget::utils::where::where_is( this_column, geometries ); - - if ( idx == -1 ) { // i.e., it's not in the vector of geometries - property_names[ property_counter ] = column_names[i]; - property_counter++; - } - } +inline Rcpp::StringVector to_geojson_mesh( + Rcpp::List& mesh +) { - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - writer.StartArray(); + Rcpp::NumericMatrix ib = mesh["ib"]; + Rcpp::NumericMatrix vb = mesh["vb"]; - for( i = 0; i < n_rows; i++ ) { + //Rcpp::Rcout << "ib nrow: " << ib.nrow() << std::endl; + //Rcpp::Rcout << "ib ncol: " << ib.ncol() << std::endl; + //Rcpp::Rcout << "vb nrow: " << vb.nrow() << std::endl; + //Rcpp::Rcout << "vb ncol: " << vb.ncol() << std::endl; - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); + int n_cols = ib.ncol(); // number of geometries + int i, j; - writer.StartObject(); + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + writer.StartArray(); - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; - SEXP this_vec = sf[ h ]; + for( i = 0; i < n_cols; i++ ) { - writer.String( h ); - jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); - } - writer.EndObject(); + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); - writer.String("geometry"); - writer.StartObject(); + writer.StartObject(); + writer.EndObject(); - for ( geom = 0; geom < n_geometries; geom++ ) { - const char* geom_column = geometries[ geom ]; + // iterate over each column if 'ib'; this tells the columns of 'vb' to use to make the polygon + + + // writer.StartObject(); + // + // // properties first, then sfc + // for( j = 0; j < n_properties; j++ ) { + // const char *h = property_names[ j ]; + // + // SEXP this_vec = sf[ h ]; + // + // writer.String( h ); + // jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); + // } + // writer.EndObject(); + // + writer.String("geometry"); + writer.StartObject(); - writer.String( geom_column ); - Rcpp::List sfc = sf[ geom_column ]; - geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); - } + Rcpp::NumericVector these_cols = ib( _, i ); + //Rcpp::Rcout << "these_cols: " << these_cols << std::endl; + writer.String("type"); + writer.String("Polygon"); + writer.String("coordinates"); + writer.StartArray(); + writer.StartArray(); - writer.EndObject(); - writer.EndObject(); + for( j = 0; j < 4; j ++ ) { + int this_idx = these_cols[ j ]; + this_idx -= 1; + + //writer.StartArray(); + Rcpp::NumericVector coords = vb(_, this_idx ); + jsonify::writers::simple::write_value(writer, coords, false, -1, false); + //writer.EndArray(); } + writer.EndArray(); + writer.EndArray(); + + // + // for ( geom = 0; geom < n_geometries; geom++ ) { + // const char* geom_column = geometries[ geom ]; + // + // writer.String( geom_column ); + // Rcpp::List sfc = sf[ geom_column ]; + // geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); + // } - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("json"); - return geojson; + writer.EndObject(); + writer.EndObject(); } - // down-casts MULTIGEOMETRIES to their simpler geometry - // only for one-column sfc objects - inline Rcpp::StringVector to_geojson_downcast_atomise( - Rcpp::DataFrame& sf, - std::string geometry, - int digits ) { + writer.EndArray(); + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} - const char* geom_column = geometry.c_str(); +/* + * a variation on the atomise function to return an array of atomised features + */ +inline Rcpp::StringVector to_geojson_atomise( + Rcpp::DataFrame& sf, + Rcpp::StringVector& geometries, + int digits) { - int n_cols = sf.ncol(); - int n_properties = n_cols - 1; // single geometry column - int n_rows = sf.nrows(); - int i, j, i_geometry; - Rcpp::StringVector column_names = sf.names(); - Rcpp::StringVector property_names(sf.size() - 1); + int n_geometries = geometries.size(); + int geom; - int property_multiplier = 0; - std::string geom_type; - Rcpp::CharacterVector cls; + int n_cols = sf.ncol(); + int n_properties = n_cols - n_geometries; + int n_rows = sf.nrows(); + int i, j; + Rcpp::StringVector column_names = sf.names(); + Rcpp::StringVector property_names( n_properties ); - int property_counter = 0; - for ( i = 0; i < n_cols; i++ ) { + int property_counter = 0; - Rcpp::String this_column = column_names[i]; + for ( i = 0; i < sf.length(); i++) { + Rcpp::String this_column = column_names[i]; + int idx = spatialwidget::utils::where::where_is( this_column, geometries ); - if ( this_column.get_cstring() != geometry ) { // i.e., it's not the geometry - property_names[ property_counter ] = column_names[i]; - property_counter++; - } + if ( idx == -1 ) { // i.e., it's not in the vector of geometries + property_names[ property_counter ] = column_names[i]; + property_counter++; } + } - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - writer.StartArray(); + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + writer.StartArray(); - for( i = 0; i < n_rows; i++ ) { + for( i = 0; i < n_rows; i++ ) { - Rcpp::List sfc = sf[ geom_column ]; - SEXP sfg = sfc[ i ]; - - cls = geojsonsf::getSfClass(sfg); - geojsonsf::write_geometry::cls_check( cls ); - geom_type = cls[1]; + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); - if ( geom_type == "GEOMETRYCOLLECTION" ) { - Rcpp::stop("GEOMETRYCOLLECTION not supported for down-casting"); - } + writer.StartObject(); - property_multiplier = geojsonsf::sizes::geometry_size( sfg, geom_type, cls ); + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; - for( i_geometry = 0; i_geometry < property_multiplier; i_geometry++ ) { + SEXP this_vec = sf[ h ]; - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); + writer.String( h ); + jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); + } + writer.EndObject(); - writer.StartObject(); + writer.String("geometry"); + writer.StartObject(); - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; + for ( geom = 0; geom < n_geometries; geom++ ) { + const char* geom_column = geometries[ geom ]; - SEXP this_vec = sf[ h ]; + writer.String( geom_column ); + Rcpp::List sfc = sf[ geom_column ]; + geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); + } - writer.String( h ); - jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); - } - writer.EndObject(); + writer.EndObject(); + writer.EndObject(); + } + writer.EndArray(); - writer.String("geometry"); - writer.StartObject(); + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} - writer.String( geom_column ); - geojsonsf::write_geometry::write_geometry( writer, sfc, i, i_geometry, geom_type, cls, digits ); +// down-casts MULTIGEOMETRIES to their simpler geometry +// only for one-column sfc objects +inline Rcpp::StringVector to_geojson_downcast_atomise( + Rcpp::DataFrame& sf, + std::string geometry, + int digits ) { - writer.EndObject(); - writer.EndObject(); - } - } - writer.EndArray(); + const char* geom_column = geometry.c_str(); - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("json"); - return geojson; - } + int n_cols = sf.ncol(); + int n_properties = n_cols - 1; // single geometry column + int n_rows = sf.nrows(); + int i, j, i_geometry; + Rcpp::StringVector column_names = sf.names(); + Rcpp::StringVector property_names(sf.size() - 1); - // down-casts MULTIGEOMETRIES to their simpler geometry - // for two-sfc-columned sf objects - inline Rcpp::StringVector to_geojson_downcast_atomise( - Rcpp::DataFrame& sf, - Rcpp::StringVector geometries, - int digits) - { - - int n_geometries = geometries.size(); - if ( n_geometries != 2 ) { - Rcpp::stop("Only supports 2-column sf objects"); - } + int property_multiplier = 0; + std::string geom_type; + Rcpp::CharacterVector cls; - int n_cols = sf.ncol(); - int n_rows = sf.nrows(); - int i, j; - int geometry, geometry_column; - Rcpp::StringVector column_names = sf.names(); - Rcpp::StringVector property_names(sf.size() - 1); + int property_counter = 0; + for ( i = 0; i < n_cols; i++ ) { - std::string geom_type; - Rcpp::CharacterVector cls; + Rcpp::String this_column = column_names[i]; - int property_counter = 0; - for ( i = 0; i < n_cols; i++) { + if ( this_column.get_cstring() != geometry ) { // i.e., it's not the geometry + property_names[ property_counter ] = column_names[i]; + property_counter++; + } + } - Rcpp::String this_column = column_names[i]; - int idx = spatialwidget::utils::where::where_is( this_column, geometries ); + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + writer.StartArray(); - if ( idx == -1 ) { // i.e., it's not in the vector of geometries - 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 ); - writer.StartArray(); + Rcpp::List sfc = sf[ geom_column ]; + SEXP sfg = sfc[ i ]; - for( i = 0; i < n_rows; i++ ) { + cls = geojsonsf::getSfClass(sfg); + geojsonsf::write_geometry::cls_check( cls ); + geom_type = cls[1]; - int geometry_size = 0; - int row_multiplier = 0; - Rcpp::IntegerVector geometry_sizes( n_geometries ); + if ( geom_type == "GEOMETRYCOLLECTION" ) { + Rcpp::stop("GEOMETRYCOLLECTION not supported for down-casting"); + } - for ( geometry = 0; geometry < n_geometries; geometry++ ) { + property_multiplier = geojsonsf::sizes::geometry_size( sfg, geom_type, cls ); - const char* geom_column = geometries[ geometry ]; + for( i_geometry = 0; i_geometry < property_multiplier; i_geometry++ ) { - Rcpp::List sfc = sf[ geom_column ]; - SEXP sfg = sfc[ i ]; + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); - cls = geojsonsf::getSfClass(sfg); - geojsonsf::write_geometry::cls_check( cls ); - geom_type = cls[1]; + writer.StartObject(); - if ( geom_type == "GEOMETRYCOLLECTION" ) { - Rcpp::stop("GEOMETRYCOLLECTION not supported for down-casting"); - } + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; - geometry_size = geojsonsf::sizes::geometry_size( sfg, geom_type, cls ); - geometry_sizes[ geometry ] = geometry_size; // keeping track of the size of each geometry + SEXP this_vec = sf[ h ]; - if ( geometry == 0 ) { - row_multiplier = geometry_size; - } else { - row_multiplier = row_multiplier * geometry_size; - } + writer.String( h ); + jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); } - // - // we now have stored a list of each geometry - downcast_geometries - // And we can create a matrix to store the indexes of the geometries - // we need to access in each iteration. + writer.EndObject(); - Rcpp::IntegerMatrix geometry_indeces( row_multiplier, n_geometries ); // for OD, n_geometries should be 2 - Rcpp::IntegerVector xx = Rcpp::seq_len( geometry_sizes[0] ); - Rcpp::IntegerVector yy = Rcpp::seq_len( geometry_sizes[1] ); - Rcpp::IntegerVector one = Rcpp::rep( xx, yy.size() ); - Rcpp::IntegerVector two = Rcpp::rep_each( yy, xx.size() ); + writer.String("geometry"); + writer.StartObject(); - geometry_indeces( Rcpp::_, 0 ) = one; - geometry_indeces( Rcpp::_, 1 ) = two; + writer.String( geom_column ); + geojsonsf::write_geometry::write_geometry( writer, sfc, i, i_geometry, geom_type, cls, digits ); - for( geometry = 0; geometry < row_multiplier; geometry++ ) { - // loop over all down-casted geometries for this row + writer.EndObject(); + writer.EndObject(); + } + } - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); + writer.EndArray(); - writer.StartObject(); + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} - // properties first, then sfc - for( j = 0; j < property_counter; j++ ) { - const char *h = property_names[ j ]; +// down-casts MULTIGEOMETRIES to their simpler geometry +// for two-sfc-columned sf objects +inline Rcpp::StringVector to_geojson_downcast_atomise( + Rcpp::DataFrame& sf, + Rcpp::StringVector geometries, + int digits) +{ - SEXP this_vec = sf[ h ]; + int n_geometries = geometries.size(); + if ( n_geometries != 2 ) { + Rcpp::stop("Only supports 2-column sf objects"); + } - writer.String( h ); - jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); - } - writer.EndObject(); + int n_cols = sf.ncol(); + int n_rows = sf.nrows(); + int i, j; + int geometry, geometry_column; + Rcpp::StringVector column_names = sf.names(); + Rcpp::StringVector property_names(sf.size() - 1); + std::string geom_type; + Rcpp::CharacterVector cls; - // loop over geometries, and replicate the geometry 'row_multiplier' number of times? - writer.String("geometry"); - writer.StartObject(); + int property_counter = 0; + for ( i = 0; i < n_cols; i++) { - for ( geometry_column = 0; geometry_column < n_geometries; geometry_column++ ) { + Rcpp::String this_column = column_names[i]; + int idx = spatialwidget::utils::where::where_is( this_column, geometries ); - const char* geom_column = geometries[ geometry_column ]; + if ( idx == -1 ) { // i.e., it's not in the vector of geometries + property_names[ property_counter ] = column_names[i]; + property_counter++; + } + } - Rcpp::List sfc = sf[ geom_column ]; - SEXP sfg = sfc[ i ]; + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + writer.StartArray(); - cls = geojsonsf::getSfClass(sfg); - geojsonsf::write_geometry::cls_check( cls ); - geom_type = cls[1]; + for( i = 0; i < n_rows; i++ ) { - writer.String( geom_column ); - int geometry_index = geometry_indeces(geometry, geometry_column) - 1; - geojsonsf::write_geometry::write_geometry( writer, sfc, i, geometry_index, geom_type, cls, digits); - } + int geometry_size = 0; + int row_multiplier = 0; + Rcpp::IntegerVector geometry_sizes( n_geometries ); - writer.EndObject(); - writer.EndObject(); - } - } + for ( geometry = 0; geometry < n_geometries; geometry++ ) { - writer.EndArray(); - - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("json"); - return geojson; - } + const char* geom_column = geometries[ geometry ]; + Rcpp::List sfc = sf[ geom_column ]; + SEXP sfg = sfc[ i ]; - /* - * converts 'sf' object to standard GeoJSON - */ - inline Rcpp::StringVector to_geojson( Rcpp::DataFrame& sf, std::string geom_column, int digits ) { + cls = geojsonsf::getSfClass(sfg); + geojsonsf::write_geometry::cls_check( cls ); + geom_type = cls[1]; - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + if ( geom_type == "GEOMETRYCOLLECTION" ) { + Rcpp::stop("GEOMETRYCOLLECTION not supported for down-casting"); + } - int n_cols = sf.ncol(); - int n_properties = n_cols - 1; - int n_rows = sf.nrows(); - int i, j; - Rcpp::StringVector column_names = sf.names(); - Rcpp::StringVector property_names(sf.size() - 1); + geometry_size = geojsonsf::sizes::geometry_size( sfg, geom_type, cls ); + geometry_sizes[ geometry ] = geometry_size; // keeping track of the size of each geometry - int property_counter = 0; - for ( i = 0; i < n_cols; i++) { - if (column_names[i] != geom_column) { - property_names[property_counter] = column_names[i]; - property_counter++; + if ( geometry == 0 ) { + row_multiplier = geometry_size; + } else { + row_multiplier = row_multiplier * geometry_size; } } + // + // we now have stored a list of each geometry - downcast_geometries + // And we can create a matrix to store the indexes of the geometries + // we need to access in each iteration. - writer.StartObject(); - geojsonsf::writers::start_feature_collection( writer ); + Rcpp::IntegerMatrix geometry_indeces( row_multiplier, n_geometries ); // for OD, n_geometries should be 2 + Rcpp::IntegerVector xx = Rcpp::seq_len( geometry_sizes[0] ); + Rcpp::IntegerVector yy = Rcpp::seq_len( geometry_sizes[1] ); + Rcpp::IntegerVector one = Rcpp::rep( xx, yy.size() ); + Rcpp::IntegerVector two = Rcpp::rep_each( yy, xx.size() ); - writer.StartArray(); + geometry_indeces( Rcpp::_, 0 ) = one; + geometry_indeces( Rcpp::_, 1 ) = two; - for( i = 0; i < n_rows; i++ ) { + for( geometry = 0; geometry < row_multiplier; geometry++ ) { + // loop over all down-casted geometries for this row writer.StartObject(); - geojsonsf::writers::start_features( writer ); geojsonsf::writers::start_properties( writer ); + writer.StartObject(); - for( j = 0; j < n_properties; j++ ) { + // properties first, then sfc + for( j = 0; j < property_counter; j++ ) { const char *h = property_names[ j ]; SEXP this_vec = sf[ h ]; writer.String( h ); - jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); + jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); } writer.EndObject(); + + // loop over geometries, and replicate the geometry 'row_multiplier' number of times? writer.String("geometry"); - Rcpp::List sfc = sf[ geom_column ]; - geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); + writer.StartObject(); + + for ( geometry_column = 0; geometry_column < n_geometries; geometry_column++ ) { + + const char* geom_column = geometries[ geometry_column ]; + + Rcpp::List sfc = sf[ geom_column ]; + SEXP sfg = sfc[ i ]; + + cls = geojsonsf::getSfClass(sfg); + geojsonsf::write_geometry::cls_check( cls ); + geom_type = cls[1]; + writer.String( geom_column ); + int geometry_index = geometry_indeces(geometry, geometry_column) - 1; + geojsonsf::write_geometry::write_geometry( writer, sfc, i, geometry_index, geom_type, cls, digits); + } + + writer.EndObject(); writer.EndObject(); } + } - writer.EndArray(); - writer.EndObject(); + writer.EndArray(); + + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} + + +/* + * converts 'sf' object to standard GeoJSON + */ +inline Rcpp::StringVector to_geojson( Rcpp::DataFrame& sf, std::string geom_column, int digits ) { + + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("json"); - return geojson; + int n_cols = sf.ncol(); + int n_properties = n_cols - 1; + int n_rows = sf.nrows(); + int i, j; + Rcpp::StringVector column_names = sf.names(); + Rcpp::StringVector property_names(sf.size() - 1); + + int property_counter = 0; + for ( i = 0; i < n_cols; i++) { + if (column_names[i] != geom_column) { + property_names[property_counter] = column_names[i]; + property_counter++; + } } - // list of geometries is designed for lon & lat columns of data - inline Rcpp::StringVector to_geojson_atomise( - Rcpp::DataFrame& df, - Rcpp::List& geometries, // i.e., list(origin = c("start_lon", "start_lat", destination = c("end_lon", "end_lat"))) - int digits ) - { + writer.StartObject(); + geojsonsf::writers::start_feature_collection( writer ); - int n_cols = df.ncol(); - int n_rows = df.nrows(); + writer.StartArray(); - int n_lons = geometries.size(); - int n_lats = geometries.size(); // it is expected the lon & lat data is the same size because + for( i = 0; i < n_rows; i++ ) { - int n_lonlat = n_lons + n_lats; - int n_properties = n_cols - n_lonlat; // LON & LAT columns + writer.StartObject(); - // it comes as columns on a data.frame - int i, j; - Rcpp::StringVector lons( n_lons ); // the first elements of each 'geometry' - Rcpp::StringVector lats( n_lats ); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); + writer.StartObject(); - if( Rf_isNull( geometries.names() ) ) { - Rcpp::stop("Expecting a list of geometries, each element is named and contains the lon and lat columns"); - } + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; - Rcpp::StringVector geometry_names = geometries.names(); + SEXP this_vec = sf[ h ]; - for ( i = 0; i < n_lons; i++ ) { - Rcpp::StringVector this_lonlat = geometries[i]; - lons[i] = this_lonlat[0]; - lats[i] = this_lonlat[1]; + writer.String( h ); + jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); } + writer.EndObject(); - Rcpp::StringVector column_names = df.names(); - Rcpp::StringVector property_names( n_properties ); + writer.String("geometry"); + Rcpp::List sfc = sf[ geom_column ]; + geojsonsf::write_geometry::write_geometry( writer, sfc, i, digits ); - Rcpp::CharacterVector cls = Rcpp::CharacterVector::create("XY", "POINT", "sfg"); + writer.EndObject(); + } - int property_counter = 0; + writer.EndArray(); + writer.EndObject(); - for (int i = 0; i < df.length(); i++) { + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} - Rcpp::String this_column = column_names[i]; +// list of geometries is designed for lon & lat columns of data +inline Rcpp::StringVector to_geojson_atomise( + Rcpp::DataFrame& df, + Rcpp::List& geometries, // i.e., list(origin = c("start_lon", "start_lat", destination = c("end_lon", "end_lat"))) + int digits ) +{ - int idx_lon = spatialwidget::utils::where::where_is( this_column, lons ); - int idx_lat = spatialwidget::utils::where::where_is( this_column, lats ); + int n_cols = df.ncol(); + int n_rows = df.nrows(); - if ( idx_lon == -1 && idx_lat == -1 ) { - property_names[property_counter] = column_names[i]; - property_counter++; - } - } + int n_lons = geometries.size(); + int n_lats = geometries.size(); // it is expected the lon & lat data is the same size because - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - writer.StartArray(); + int n_lonlat = n_lons + n_lats; + int n_properties = n_cols - n_lonlat; // LON & LAT columns - for( i = 0; i < n_rows; i++ ) { + // it comes as columns on a data.frame + int i, j; + Rcpp::StringVector lons( n_lons ); // the first elements of each 'geometry' + Rcpp::StringVector lats( n_lats ); - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); + if( Rf_isNull( geometries.names() ) ) { + Rcpp::stop("Expecting a list of geometries, each element is named and contains the lon and lat columns"); + } - writer.StartObject(); + Rcpp::StringVector geometry_names = geometries.names(); - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; - SEXP this_vec = df[ h ]; + for ( i = 0; i < n_lons; i++ ) { + Rcpp::StringVector this_lonlat = geometries[i]; + lons[i] = this_lonlat[0]; + lats[i] = this_lonlat[1]; + } - writer.String( h ); - jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); - } + Rcpp::StringVector column_names = df.names(); + Rcpp::StringVector property_names( n_properties ); - writer.EndObject(); + Rcpp::CharacterVector cls = Rcpp::CharacterVector::create("XY", "POINT", "sfg"); - writer.String("geometry"); + int property_counter = 0; - writer.StartObject(); - for ( j = 0; j < n_lons; j++ ) { - const char* this_lon = lons[j]; - const char* this_lat = lats[j]; - Rcpp::NumericVector nv_lon = df[this_lon]; - Rcpp::NumericVector nv_lat = df[this_lat]; - SEXP sfg = Rcpp::NumericVector::create( nv_lon[i], nv_lat[i] ); + for (int i = 0; i < df.length(); i++) { - writer.String( geometry_names[j] ); + Rcpp::String this_column = column_names[i]; - geojsonsf::write_geometry::write_geometry( writer, sfg, cls, digits ); - } - writer.EndObject(); - writer.EndObject(); - } - writer.EndArray(); + int idx_lon = spatialwidget::utils::where::where_is( this_column, lons ); + int idx_lat = spatialwidget::utils::where::where_is( this_column, lats ); - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("json"); - return geojson; + if ( idx_lon == -1 && idx_lat == -1 ) { + property_names[property_counter] = column_names[i]; + property_counter++; + } } + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + writer.StartArray(); - // list of geometries is designed for lon & lat & z columns of data - inline Rcpp::StringVector to_geojson_z_atomise( - Rcpp::DataFrame& df, - Rcpp::List& geometries, // i.e., list(origin = c("start_lon", "start_lat", destination = c("end_lon", "end_lat"))) - int digits ) - { - - int n_cols = df.ncol(); - int n_rows = df.nrows(); + for( i = 0; i < n_rows; i++ ) { - int n_lons = geometries.size(); - int n_lats = geometries.size(); // it is expected the lon & lat data is the same size because - int n_elevs = geometries.size(); + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); - int n_lonlat = n_lons + n_lats + n_elevs; - int n_properties = n_cols - n_lonlat; // LON & LAT & ELEV columns + writer.StartObject(); - // it comes as columns on a data.frame - int i, j; - Rcpp::StringVector lons( n_lons ); // the first elements of each 'geometry' - Rcpp::StringVector lats( n_lats ); - Rcpp::StringVector elevs( n_elevs ); + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; + SEXP this_vec = df[ h ]; - if( Rf_isNull( geometries.names() ) ) { - Rcpp::stop("Expecting a list of geometries, each element is named and contains the lon, lat and z columns"); + writer.String( h ); + jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); } - Rcpp::StringVector geometry_names = geometries.names(); + writer.EndObject(); - // check there are three entries in the list - Rcpp::StringVector this_lonlat; + writer.String("geometry"); - for ( i = 0; i < n_lons; i++ ) { - this_lonlat = geometries[i]; - if ( this_lonlat.size() != 3 ) { - Rcpp::stop("Expecting a list of geometries, each element is named and contains the lon, lat and z columns"); - } - lons[i] = this_lonlat[0]; - lats[i] = this_lonlat[1]; - elevs[i] = this_lonlat[2]; + writer.StartObject(); + for ( j = 0; j < n_lons; j++ ) { + const char* this_lon = lons[j]; + const char* this_lat = lats[j]; + Rcpp::NumericVector nv_lon = df[this_lon]; + Rcpp::NumericVector nv_lat = df[this_lat]; + SEXP sfg = Rcpp::NumericVector::create( nv_lon[i], nv_lat[i] ); + + writer.String( geometry_names[j] ); + + geojsonsf::write_geometry::write_geometry( writer, sfg, cls, digits ); } + writer.EndObject(); + writer.EndObject(); + } + writer.EndArray(); - Rcpp::StringVector column_names = df.names(); - Rcpp::StringVector property_names( n_properties ); + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} - Rcpp::CharacterVector cls = Rcpp::CharacterVector::create("XYZ", "POINT", "sfg"); - int property_counter = 0; +// list of geometries is designed for lon & lat & z columns of data +inline Rcpp::StringVector to_geojson_z_atomise( + Rcpp::DataFrame& df, + Rcpp::List& geometries, // i.e., list(origin = c("start_lon", "start_lat", destination = c("end_lon", "end_lat"))) + int digits ) +{ - for ( i = 0; i < df.length(); i++) { + int n_cols = df.ncol(); + int n_rows = df.nrows(); - Rcpp::String this_column = column_names[i]; + int n_lons = geometries.size(); + int n_lats = geometries.size(); // it is expected the lon & lat data is the same size because + int n_elevs = geometries.size(); - int idx_lon = spatialwidget::utils::where::where_is( this_column, lons ); - int idx_lat = spatialwidget::utils::where::where_is( this_column, lats ); - int idx_elev = spatialwidget::utils::where::where_is( this_column, elevs ); + int n_lonlat = n_lons + n_lats + n_elevs; + int n_properties = n_cols - n_lonlat; // LON & LAT & ELEV columns - if ( idx_lon == -1 && idx_lat == -1 && idx_elev == -1 ) { - property_names[property_counter] = column_names[i]; - property_counter++; - } + // it comes as columns on a data.frame + int i, j; + Rcpp::StringVector lons( n_lons ); // the first elements of each 'geometry' + Rcpp::StringVector lats( n_lats ); + Rcpp::StringVector elevs( n_elevs ); + + if( Rf_isNull( geometries.names() ) ) { + Rcpp::stop("Expecting a list of geometries, each element is named and contains the lon, lat and z columns"); + } + + Rcpp::StringVector geometry_names = geometries.names(); + + // check there are three entries in the list + Rcpp::StringVector this_lonlat; + + for ( i = 0; i < n_lons; i++ ) { + this_lonlat = geometries[i]; + if ( this_lonlat.size() != 3 ) { + Rcpp::stop("Expecting a list of geometries, each element is named and contains the lon, lat and z columns"); } + lons[i] = this_lonlat[0]; + lats[i] = this_lonlat[1]; + elevs[i] = this_lonlat[2]; + } - rapidjson::StringBuffer sb; - rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); - writer.StartArray(); + Rcpp::StringVector column_names = df.names(); + Rcpp::StringVector property_names( n_properties ); + Rcpp::CharacterVector cls = Rcpp::CharacterVector::create("XYZ", "POINT", "sfg"); - for( i = 0; i < n_rows; i++ ) { + int property_counter = 0; - if ( n_properties > 0 ) { + for ( i = 0; i < df.length(); i++) { - writer.StartObject(); - geojsonsf::writers::start_features( writer ); - geojsonsf::writers::start_properties( writer ); + Rcpp::String this_column = column_names[i]; - writer.StartObject(); + int idx_lon = spatialwidget::utils::where::where_is( this_column, lons ); + int idx_lat = spatialwidget::utils::where::where_is( this_column, lats ); + int idx_elev = spatialwidget::utils::where::where_is( this_column, elevs ); - // properties first, then sfc - for( j = 0; j < n_properties; j++ ) { - const char *h = property_names[ j ]; + if ( idx_lon == -1 && idx_lat == -1 && idx_elev == -1 ) { + property_names[property_counter] = column_names[i]; + property_counter++; + } + } - SEXP this_vec = df[ h ]; + rapidjson::StringBuffer sb; + rapidjson::Writer < rapidjson::StringBuffer > writer( sb ); + writer.StartArray(); - writer.String( h ); - jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); - } - writer.EndObject(); - } - // now geometries - if( n_properties > 0 ) { - writer.String("geometry"); - } + for( i = 0; i < n_rows; i++ ) { + + if ( n_properties > 0 ) { + + writer.StartObject(); + geojsonsf::writers::start_features( writer ); + geojsonsf::writers::start_properties( writer ); writer.StartObject(); - for ( j = 0; j < n_lons; j++ ) { - const char* this_lon = lons[j]; - const char* this_lat = lats[j]; - const char* this_elev = elevs[j]; - Rcpp::NumericVector nv_lon = df[this_lon]; - Rcpp::NumericVector nv_lat = df[this_lat]; - Rcpp::NumericVector nv_elev = df[this_elev]; - SEXP sfg = Rcpp::NumericVector::create( nv_lon[i], nv_lat[i], nv_elev[i] ); + // properties first, then sfc + for( j = 0; j < n_properties; j++ ) { + const char *h = property_names[ j ]; - writer.String( geometry_names[j] ); + SEXP this_vec = df[ h ]; - geojsonsf::write_geometry::write_geometry( writer, sfg, cls, digits ); + writer.String( h ); + jsonify::writers::simple::write_value( writer, this_vec, i, -1, false, true ); } writer.EndObject(); + } - if( n_properties > 0 ) { - writer.EndObject(); - } + // now geometries + if( n_properties > 0 ) { + writer.String("geometry"); } - writer.EndArray(); - Rcpp::StringVector geojson = sb.GetString(); - geojson.attr("class") = Rcpp::CharacterVector::create("json"); - return geojson; + writer.StartObject(); + for ( j = 0; j < n_lons; j++ ) { + const char* this_lon = lons[j]; + const char* this_lat = lats[j]; + const char* this_elev = elevs[j]; + + Rcpp::NumericVector nv_lon = df[this_lon]; + Rcpp::NumericVector nv_lat = df[this_lat]; + Rcpp::NumericVector nv_elev = df[this_elev]; + SEXP sfg = Rcpp::NumericVector::create( nv_lon[i], nv_lat[i], nv_elev[i] ); + + writer.String( geometry_names[j] ); + + geojsonsf::write_geometry::write_geometry( writer, sfg, cls, digits ); + } + writer.EndObject(); + + if( n_properties > 0 ) { + writer.EndObject(); + } } + writer.EndArray(); + + Rcpp::StringVector geojson = sb.GetString(); + geojson.attr("class") = Rcpp::CharacterVector::create("json"); + return geojson; +} } // namespace geojson } // namespace spatialwidget diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 0a6f3d9..d0936a1 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -21,6 +21,17 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// rcpp_geojson_mesh +Rcpp::StringVector rcpp_geojson_mesh(Rcpp::List mesh); +RcppExport SEXP _spatialwidget_rcpp_geojson_mesh(SEXP meshSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< Rcpp::List >::type mesh(meshSEXP); + rcpp_result_gen = Rcpp::wrap(rcpp_geojson_mesh(mesh)); + return rcpp_result_gen; +END_RCPP +} // rcpp_geojson Rcpp::StringVector rcpp_geojson(Rcpp::DataFrame sf, std::string geometry); RcppExport SEXP _spatialwidget_rcpp_geojson(SEXP sfSEXP, SEXP geometrySEXP) { @@ -245,6 +256,7 @@ END_RCPP static const R_CallMethodDef CallEntries[] = { {"_spatialwidget_rcpp_construct_data", (DL_FUNC) &_spatialwidget_rcpp_construct_data, 6}, + {"_spatialwidget_rcpp_geojson_mesh", (DL_FUNC) &_spatialwidget_rcpp_geojson_mesh, 1}, {"_spatialwidget_rcpp_geojson", (DL_FUNC) &_spatialwidget_rcpp_geojson, 2}, {"_spatialwidget_rcpp_geojson_sf", (DL_FUNC) &_spatialwidget_rcpp_geojson_sf, 2}, {"_spatialwidget_rcpp_geojson_df", (DL_FUNC) &_spatialwidget_rcpp_geojson_df, 2}, diff --git a/src/geojson.cpp b/src/geojson.cpp index a875acb..7b5b193 100644 --- a/src/geojson.cpp +++ b/src/geojson.cpp @@ -2,6 +2,11 @@ #include "spatialwidget/spatialwidget.hpp" +// [[Rcpp::export]] +Rcpp::StringVector rcpp_geojson_mesh( Rcpp::List mesh ) { + return spatialwidget::geojson::to_geojson_mesh( mesh ); +} + // [[Rcpp::export]] Rcpp::StringVector rcpp_geojson( Rcpp::DataFrame sf, std::string geometry ) { return spatialwidget::geojson::to_geojson( sf, geometry, -1 );