diff --git a/DESCRIPTION b/DESCRIPTION index 8d85fc9..5c05c8a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,12 +15,15 @@ URL: https://github.com/ropensci/geojsonio BugReports: http://www.github.com/ropensci/geojsonio/issues LazyData: true VignetteBuilder: knitr +LinkingTo: + Rcpp (>= 0.12.0) Depends: - R (>= 2.10) + R (>= 3.02) Imports: methods, stats, utils, + Rcpp (>= 0.12.3), sp, rgdal, rgeos, diff --git a/NAMESPACE b/NAMESPACE index ecc674e..1fe2821 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -165,6 +165,7 @@ export(validate) import(methods) import(rgeos) import(sp) +importFrom(Rcpp,evalCpp) importFrom(V8,new_context) importFrom(httr,GET) importFrom(httr,POST) @@ -182,3 +183,4 @@ importFrom(rgdal,readOGR) importFrom(rgdal,writeOGR) importFrom(stats,setNames) importFrom(utils,download.file) +useDynLib(geojsonio) diff --git a/R/RcppExports.R b/R/RcppExports.R new file mode 100644 index 0000000..4eee30b --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,20 @@ +# This file was generated by Rcpp::compileAttributes +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#' An \code{writeOGR} shim to capture stdout +#' +#' @param obj the \code{Spatial} object to use +#' @param obj_size pls pass in \code{object.size(obj)} +#' @param layer spatial layer to use +#' @param writeOGR pls pass in \code{writeOGR} (no quotes) +#' @return character vector of GeoJSON if all goes well +#' @examples \dontrun{ +#' capturedWriteOGR(cities[1:10,], +#' object.size(cities[1:10,]), +#' "cities", +#' writeOGR)) +#' } +capturedWriteOGR <- function(obj, obj_size, layer, writeOGR) { + .Call('geojsonio_capturedWriteOGR', PACKAGE = 'geojsonio', obj, obj_size, layer, writeOGR) +} + diff --git a/R/geojsonio-package.r b/R/geojsonio-package.r index a1380c8..b2d6eb9 100644 --- a/R/geojsonio-package.r +++ b/R/geojsonio-package.r @@ -42,6 +42,8 @@ #' @importFrom maptools readShapeSpatial #' @importFrom rgdal readOGR writeOGR ogrListLayers #' @importFrom magrittr %>% +#' @importFrom Rcpp evalCpp +#' @useDynLib geojsonio #' @name geojsonio #' @author Scott Chamberlain \email{myrmecocystus@@gmail.com} #' @author Andy Teucher \email{andy.teucher@@gmail.com} diff --git a/man/capturedWriteOGR.Rd b/man/capturedWriteOGR.Rd new file mode 100644 index 0000000..dc45984 --- /dev/null +++ b/man/capturedWriteOGR.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{capturedWriteOGR} +\alias{capturedWriteOGR} +\title{An \code{writeOGR} shim to capture stdout} +\usage{ +capturedWriteOGR(obj, obj_size, layer, writeOGR) +} +\arguments{ +\item{obj}{the \code{Spatial} object to use} + +\item{obj_size}{pls pass in \code{object.size(obj)}} + +\item{layer}{spatial layer to use} + +\item{writeOGR}{pls pass in \code{writeOGR} (no quotes)} +} +\value{ +character vector of GeoJSON if all goes well +} +\description{ +An \code{writeOGR} shim to capture stdout +} +\examples{ +\dontrun{ +capturedWriteOGR(cities[1:10,], + object.size(cities[1:10,]), + "cities", + writeOGR)) +} +} + diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp new file mode 100644 index 0000000..1661ffa --- /dev/null +++ b/src/RcppExports.cpp @@ -0,0 +1,21 @@ +// This file was generated by Rcpp::compileAttributes +// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#include + +using namespace Rcpp; + +// capturedWriteOGR +CharacterVector capturedWriteOGR(SEXP obj, int obj_size, SEXP layer, Function writeOGR); +RcppExport SEXP geojsonio_capturedWriteOGR(SEXP objSEXP, SEXP obj_sizeSEXP, SEXP layerSEXP, SEXP writeOGRSEXP) { +BEGIN_RCPP + Rcpp::RObject __result; + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< SEXP >::type obj(objSEXP); + Rcpp::traits::input_parameter< int >::type obj_size(obj_sizeSEXP); + Rcpp::traits::input_parameter< SEXP >::type layer(layerSEXP); + Rcpp::traits::input_parameter< Function >::type writeOGR(writeOGRSEXP); + __result = Rcpp::wrap(capturedWriteOGR(obj, obj_size, layer, writeOGR)); + return __result; +END_RCPP +} diff --git a/src/RcppExports.o b/src/RcppExports.o new file mode 100644 index 0000000..ba04bb2 Binary files /dev/null and b/src/RcppExports.o differ diff --git a/src/captured_write_ogr.cpp b/src/captured_write_ogr.cpp new file mode 100644 index 0000000..02fe223 --- /dev/null +++ b/src/captured_write_ogr.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +using namespace Rcpp; + +//' An \code{writeOGR} shim to capture stdout +//' +//' @param obj the \code{Spatial} object to use +//' @param obj_size pls pass in \code{object.size(obj)} +//' @param layer spatial layer to use +//' @param writeOGR pls pass in \code{writeOGR} (no quotes) +//' @return character vector of GeoJSON if all goes well +//' @examples \dontrun{ +//' capturedWriteOGR(cities[1:10,], +//' object.size(cities[1:10,]), +//' "cities", +//' writeOGR)) +//' } +// [[Rcpp::export]] +CharacterVector capturedWriteOGR(SEXP obj, + int obj_size, + SEXP layer, + Function writeOGR) { + + // we don't know how big the output is going to be. + // this is the main problem with this approach. + // there is no error checking yet + // i have no idea if this works on Windows + // this was a hack on a commuter train ;-) + + // this could cause us to run out of memory + // we need checking for sizes, mem avail and errors + + // allocate a buffer to hold the output geojson + // and initialize it so we's sure to have a null term'd string + int MAX_LEN = obj_size * 10 ; + char buffer[MAX_LEN+1]; + memset(buffer, 0, sizeof (char) * (MAX_LEN+1)); + + // now we're going to mess with pipes. the way the VSI stuff + // mangles stdout in gdal means that we can't use nice C++ + // redirects and have to resort to file descriptor hacking + + int out_pipe[2]; + int saved_stdout; + + saved_stdout = dup(STDOUT_FILENO); + + // ok, there's some error checking if we couldn't even + // get the hack started + + if (pipe(out_pipe) != 0 ) { return(NA_STRING); } + + dup2(out_pipe[1], STDOUT_FILENO); + close(out_pipe[1]); + + // we've setup the capture, so let writeOGR do it's thing + // we are calling R from C since the rgdal folks have not + // exposed anything we can use AFAI can tell + + writeOGR(obj, "/vsistdout/", layer, "GeoJSON"); + + // ok, we let it do it's thing, now make sure we've + // cleatned up after it + + fflush(stdout); + + // now, get that mess into somet hign we can use + + read(out_pipe[0], buffer, MAX_LEN); + + // restore order to the universe + + dup2(saved_stdout, STDOUT_FILENO); + + // say a little prayer + + return(wrap(buffer)); + +} diff --git a/src/captured_write_ogr.o b/src/captured_write_ogr.o new file mode 100644 index 0000000..abc3a24 Binary files /dev/null and b/src/captured_write_ogr.o differ diff --git a/src/geojsonio.so b/src/geojsonio.so new file mode 100755 index 0000000..6dd9de1 Binary files /dev/null and b/src/geojsonio.so differ