Skip to content

Commit

Permalink
z, m and tests, closes #49
Browse files Browse the repository at this point in the history
  • Loading branch information
SymbolixAU committed Nov 13, 2018
1 parent 61038c7 commit 4d4b4b6
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 58 deletions.
8 changes: 4 additions & 4 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

rcpp_df_to_geojson_atomise <- function(df, lon, lat) {
.Call(`_geojsonsf_rcpp_df_to_geojson_atomise`, df, lon, lat)
rcpp_df_to_geojson_atomise <- function(df, geometry_columns) {
.Call(`_geojsonsf_rcpp_df_to_geojson_atomise`, df, geometry_columns)
}

rcpp_df_to_geojson <- function(sf, geometry_columns) {
.Call(`_geojsonsf_rcpp_df_to_geojson`, sf, geometry_columns)
rcpp_df_to_geojson <- function(df, geometry_columns) {
.Call(`_geojsonsf_rcpp_df_to_geojson`, df, geometry_columns)
}

rcpp_geojson_to_sfc <- function(geojson, expand_geometries) {
Expand Down
31 changes: 25 additions & 6 deletions R/sf_geojson.R
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ sfc_geojson.default <- function(sfc) stop("Expected an sfc object")
#' @param df data.frame
#' @param lon column of \code{df} containing the longitude data
#' @param lat column of \code{df} containing the latitude data
#' @param z column of \code{df} containing the Z attribute of the GeoJSON
#' @param m column of \code{df} containing the M attribute of the GeoJSON.
#' If supplied, you must also supply \code{z}
#' @param atomise logical indicating if the data.frame should be converted into a vector
#' of GeoJSON objects
#' @param simplify logical indicating if data.frame without property columns should simplify
Expand All @@ -85,21 +88,37 @@ sfc_geojson.default <- function(sfc) stop("Expected an sfc object")
#'
#' df <- data.frame(lon = c(1:5, NA), lat = c(1:5, NA), id = 1:6, val = letters[1:6])
#' df_geojson( df, lon = "lon", lat = "lat")
#' df_geojson( df, lon = "lon", lat = "lat", atomise = T)
#' df_geojson( df, lon = "lon", lat = "lat", atomise = TRUE)
#'
#' df <- data.frame(lon = c(1:5, NA), lat = c(1:5, NA) )
#' df_geojson( df, lon = "lon", lat = "lat")
#' df_geojson( df, lon = "lon", lat = "lat", simplify = T)
#' df_geojson( df, lon = "lon", lat = "lat", simplify = FALSE)
#'
#' df <- data.frame(lon = c(1:5), lat = c(1:5), elevation = c(1:5) )
#' df_geojson( df, lon = "lon", lat = "lat", z = "elevation")
#' df_geojson( df, lon = "lon", lat = "lat", z = "elevation", simplify = FALSE)
#'
#' df <- data.frame(lon = c(1:5), lat = c(1:5), elevation = c(1:5), id = 1:5 )
#' df_geojson( df, lon = "lon", lat = "lat", z = "elevation")
#' df_geojson( df, lon = "lon", lat = "lat", z = "elevation", atomise = TRUE)
#'
#'
#' ## to sf objects
#' geo <- df_geojson( df, lon = "lon", lat = "lat", z = "elevation")
#' sf <- geojson_sf( geo )
#'
#' @export
df_geojson <- function(df, lon, lat, atomise = FALSE, simplify = TRUE) UseMethod("df_geojson")
df_geojson <- function(df, lon, lat, z = NULL, m = NULL, atomise = FALSE, simplify = TRUE) UseMethod("df_geojson")

#' @export
df_geojson.data.frame <- function(df, lon, lat, atomise = FALSE, simplify = TRUE) {
df_geojson.data.frame <- function(df, lon, lat, z = NULL, m = NULL, atomise = FALSE, simplify = TRUE) {
df <- handle_dates( df )
lon <- force( lon )
lat <- force( lat )
if( atomise | ( ncol( df ) == 2 & simplify ) ) return( rcpp_df_to_geojson_atomise( df, lon, lat ) )
return( rcpp_df_to_geojson( df, c(lon, lat) ) )
z <- force( z )
m <- force( m )
if( is.null(z) && !is.null(m)) stop("z must be supplied when using m")
geometries <- c(lon, lat, z, m)
if( atomise | ( ncol( df ) == length( geometries ) & simplify ) ) return( rcpp_df_to_geojson_atomise( df, geometries ) )
return( rcpp_df_to_geojson( df, geometries ) )
}
24 changes: 21 additions & 3 deletions man/df_geojson.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 9 additions & 10 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,26 @@
using namespace Rcpp;

// rcpp_df_to_geojson_atomise
Rcpp::StringVector rcpp_df_to_geojson_atomise(Rcpp::DataFrame& df, const char* lon, const char* lat);
RcppExport SEXP _geojsonsf_rcpp_df_to_geojson_atomise(SEXP dfSEXP, SEXP lonSEXP, SEXP latSEXP) {
Rcpp::StringVector rcpp_df_to_geojson_atomise(Rcpp::DataFrame& df, Rcpp::StringVector& geometry_columns);
RcppExport SEXP _geojsonsf_rcpp_df_to_geojson_atomise(SEXP dfSEXP, SEXP geometry_columnsSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::DataFrame& >::type df(dfSEXP);
Rcpp::traits::input_parameter< const char* >::type lon(lonSEXP);
Rcpp::traits::input_parameter< const char* >::type lat(latSEXP);
rcpp_result_gen = Rcpp::wrap(rcpp_df_to_geojson_atomise(df, lon, lat));
Rcpp::traits::input_parameter< Rcpp::StringVector& >::type geometry_columns(geometry_columnsSEXP);
rcpp_result_gen = Rcpp::wrap(rcpp_df_to_geojson_atomise(df, geometry_columns));
return rcpp_result_gen;
END_RCPP
}
// rcpp_df_to_geojson
Rcpp::StringVector rcpp_df_to_geojson(Rcpp::DataFrame& sf, Rcpp::StringVector& geometry_columns);
RcppExport SEXP _geojsonsf_rcpp_df_to_geojson(SEXP sfSEXP, SEXP geometry_columnsSEXP) {
Rcpp::StringVector rcpp_df_to_geojson(Rcpp::DataFrame& df, Rcpp::StringVector& geometry_columns);
RcppExport SEXP _geojsonsf_rcpp_df_to_geojson(SEXP dfSEXP, SEXP geometry_columnsSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::DataFrame& >::type sf(sfSEXP);
Rcpp::traits::input_parameter< Rcpp::DataFrame& >::type df(dfSEXP);
Rcpp::traits::input_parameter< Rcpp::StringVector& >::type geometry_columns(geometry_columnsSEXP);
rcpp_result_gen = Rcpp::wrap(rcpp_df_to_geojson(sf, geometry_columns));
rcpp_result_gen = Rcpp::wrap(rcpp_df_to_geojson(df, geometry_columns));
return rcpp_result_gen;
END_RCPP
}
Expand Down Expand Up @@ -124,7 +123,7 @@ END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
{"_geojsonsf_rcpp_df_to_geojson_atomise", (DL_FUNC) &_geojsonsf_rcpp_df_to_geojson_atomise, 3},
{"_geojsonsf_rcpp_df_to_geojson_atomise", (DL_FUNC) &_geojsonsf_rcpp_df_to_geojson_atomise, 2},
{"_geojsonsf_rcpp_df_to_geojson", (DL_FUNC) &_geojsonsf_rcpp_df_to_geojson, 2},
{"_geojsonsf_rcpp_geojson_to_sfc", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sfc, 2},
{"_geojsonsf_rcpp_geojson_to_sf", (DL_FUNC) &_geojsonsf_rcpp_geojson_to_sf, 2},
Expand Down
68 changes: 33 additions & 35 deletions src/df_geojson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,38 @@ void write_geojson(Writer& writer, SEXP sfg, std::string& geom_type, Rcpp::Chara
// [[Rcpp::export]]
Rcpp::StringVector rcpp_df_to_geojson_atomise(
Rcpp::DataFrame& df,
const char* lon,
const char* lat ) {
Rcpp::StringVector& geometry_columns ) {

size_t n_cols = df.ncol();
size_t n_properties = n_cols - 2; // LON & LAT columns
size_t n_rows = df.nrows();
size_t i, j;
Rcpp::StringVector column_names = df.names();
Rcpp::StringVector property_names(df.size() - 1);

Rcpp::StringVector geojson( n_rows );

// the sfc_POINT
Rcpp::NumericVector nv_lon = df[lon];
Rcpp::NumericVector nv_lat = df[lat];

Rcpp::CharacterVector cls = Rcpp::CharacterVector::create("XY", "POINT", "sfg");
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++) {
if ( column_names[i] != lon && column_names[i] != lat ) {

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++;
}
Expand Down Expand Up @@ -103,7 +114,12 @@ Rcpp::StringVector rcpp_df_to_geojson_atomise(
writer.String("geometry");
}

SEXP sfg = Rcpp::NumericVector::create(nv_lon[i], nv_lat[i]);
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 );

if( n_properties > 0 ) {
Expand All @@ -118,23 +134,16 @@ Rcpp::StringVector rcpp_df_to_geojson_atomise(

// [[Rcpp::export]]
Rcpp::StringVector rcpp_df_to_geojson(
Rcpp::DataFrame& sf,
Rcpp::DataFrame& df,
Rcpp::StringVector& geometry_columns ) {

// TODO change the lon & lat to be character / string vector specifying the columns to use
// so we can dynmically use Z&M attributes where appropriate.
// and change the cls accordingly

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 - 2; // LON & LAT columns
size_t n_rows = sf.nrows();
size_t n_cols = df.ncol();
size_t n_rows = df.nrows();
size_t i, j;
Rcpp::StringVector column_names = sf.names();
Rcpp::StringVector column_names = df.names();

// the sfc_POINT
size_t n_geometry_columns = geometry_columns.size();
Expand All @@ -143,30 +152,21 @@ Rcpp::StringVector rcpp_df_to_geojson(
size_t n_properties = n_cols - n_geometry_columns;
Rcpp::StringVector property_names( n_properties );

// Rcpp::Rcout << "n_geometry_columns: " << n_geometry_columns << std::endl;

for ( i = 0; i < n_geometry_columns; i++ ) {
Rcpp::String this_geometry = geometry_columns[i];
geometry_vectors[i] = sf[ this_geometry ];
geometry_vectors[i] = df[ this_geometry ];
}
// Rcpp::NumericVector nv_lon = sf[lon];
// Rcpp::NumericVector nv_lat = sf[lat];

std::string dim = geojsonsf::utils::make_dimension( n_geometry_columns );
// Rcpp::Rcout << "dim: " << dim << std::endl;
Rcpp::CharacterVector cls = Rcpp::CharacterVector::create( dim , "POINT", "sfg");

int property_counter = 0;

for ( int i = 0; i < sf.length(); i++ ) {
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 );

// Rcpp::Rcout << "this_column: " << this_column.get_cstring() << std::endl;
// Rcpp::Rcout << "idx: " << idx << std::endl;

//if ( column_names[i] != lon && column_names[i] != lat ) {
if ( idx == -1 ) { // i.e. it's not in the vector
property_names[property_counter] = column_names[i];
property_counter++;
Expand All @@ -189,7 +189,7 @@ Rcpp::StringVector rcpp_df_to_geojson(

for( j = 0; j < n_properties; j++ ) {
const char *h = property_names[ j ];
SEXP this_vec = sf[ h ];
SEXP this_vec = df[ h ];

jsonify::writers::write_value( writer, h );
jsonify::dataframe::dataframe_cell( writer, this_vec, i );
Expand All @@ -198,8 +198,6 @@ Rcpp::StringVector rcpp_df_to_geojson(

writer.String("geometry");

//SEXP sfg = Rcpp::NumericVector::create(nv_lon[i], nv_lat[i]);

Rcpp::NumericVector geom( n_geometry_columns );
for ( j = 0; j < n_geometry_columns; j++ ) {
Rcpp::NumericVector this_geometry_vector = geometry_vectors[j];
Expand Down
23 changes: 23 additions & 0 deletions tests/testthat/test-df_geojson.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,26 @@ test_that("data.frame converted to GeoJSON", {
expect_true(all(sapply(geo, jsonify::validate_json)))

})

test_that("z and m handled", {

n <- 1
df <- data.frame(lon = c(1:n, NA), lat = c(1:n, NA), z = c(1:n, NA) )
geo <- df_geojson( df, lon = "lon", lat = "lat", z = "z")
expect_true(length(geo) == 2)
expect_true(geo[1] == '{"type":"Point","coordinates":[1.0,1.0,1.0]}')
expect_true(geo[2] == 'null')
expect_true(all(sapply(geo, jsonify::validate_json)))

df <- data.frame(lon = c(1:n, NA), lat = c(1:n, NA), z = c(1:n, NA), m = c(1:n, NA), id = 1:(n+1), val = letters[1:(n+1)])
geo <- df_geojson( df, lon = "lon", lat = "lat", z = "z", m = "m")
expect_true(length(geo) == 1)
expect_true(jsonify::validate_json( geo ) )
geo <- df_geojson( df, lon = "lon", lat = "lat", atomise = T)
expect_true(all(sapply(geo, jsonify::validate_json)))

expect_error(
df_geojson( df, lon = "lon", lat = "lat", m = "m")
, "z must be supplied when using m"
)
})

0 comments on commit 4d4b4b6

Please sign in to comment.