Permalink
Fetching contributors…
Cannot retrieve contributors at this time
751 lines (670 sloc) 22.8 KB
/*
everything with "write" is meant as "write from R into foreign (wkb)",
"read" as "read from foreign (wkb) into R".
*/
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <math.h> // round()
#include <string.h> // memcpy()
#include <Rcpp.h>
#include "wkb.h"
#define EWKB_Z_BIT 0x80000000
#define EWKB_M_BIT 0x40000000
#define EWKB_SRID_BIT 0x20000000
// [[Rcpp::interfaces(r, cpp)]]
typedef struct {
const unsigned char *pt;
size_t size;
} wkb_buf;
void wkb_read(wkb_buf *wkb, void *dst, size_t n);
Rcpp::List read_data(wkb_buf *wkb, bool EWKB, bool spatialite, int endian,
bool addclass, int *type, uint32_t *srid);
void write_data(std::ostringstream& os, Rcpp::List sfc, int i, bool EWKB,
int endian, const char *cls, const char *dim, double prec, int srid);
void wkb_read(wkb_buf *wkb, void *dst, size_t n) {
if (n > wkb->size)
Rcpp::stop("range check error: WKB buffer too small. Input file corrupt?");
if (dst != NULL)
memcpy(dst, wkb->pt, n);
wkb->pt += n;
wkb->size -= n;
}
// https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c
template <typename T>
T swap_endian(T u) {
union {
T u;
unsigned char u8[sizeof(T)];
} source, dest;
source.u = u;
for (size_t k = 0; k < sizeof(T); k++)
dest.u8[k] = source.u8[sizeof(T) - k - 1];
return dest.u;
}
void read_spatialite_header(wkb_buf *wkb, uint32_t *srid, bool swap) {
// we're at byte 3 now:
wkb_read(wkb, srid, sizeof(uint32_t));
if (swap)
*srid = swap_endian<uint32_t>(*srid); // #nocov
wkb_read(wkb, NULL, 32); // skip header
// verify special marker; if not there, raise error:
unsigned char marker;
wkb_read(wkb, &marker, 1); // skip header
if (marker != 0x7c) {
Rcpp::Rcout << "byte 39 should be 0x7c, but is " << marker << std::endl; // #nocov
Rcpp::stop("invalid spatialite header"); // #nocov
}
}
void read_gpkg_header(wkb_buf *wkb, uint32_t *srid, int endian) {
// http://www.geopackage.org/spec/#gpb_format
wkb_read(wkb, NULL, 3); // 'G', 'P', version
// read flag:
unsigned char flag;
wkb_read(wkb, &flag, 1);
bool swap = ((flag & 0x01) != (int) endian); // endian check
// read srid, if needed, swap:
wkb_read(wkb, srid, 4);
if (swap)
*srid = swap_endian<uint32_t>(*srid); // #nocov
// how much header is there to skip? bbox: 4, 6, 6, or 8 doubles:
flag = (flag >> 1) & 0x07; // get bytes 3,2,1
int n = 0;
if (flag == 1) // [minx, maxx, miny, maxy]
n = 32;
else if (flag == 2 || flag == 3) // #nocov start
// [minx, maxx, miny, maxy, minz, maxz] or [minx, maxx, miny, maxy, minm, maxm]
n = 48;
else if (flag == 4) // [minx, maxx, miny, maxy, minz, maxz, minm, maxm]
n = 64; // #nocov end
wkb_read(wkb, NULL, n);
}
Rcpp::NumericMatrix read_multipoint(wkb_buf *wkb, int n_dims, bool swap,
bool EWKB = 0, bool spatialite = false, int endian = 0, Rcpp::CharacterVector cls = "",
bool *empty = NULL) {
uint32_t npts;
wkb_read(wkb, &npts, 4);
if (swap)
npts = swap_endian<uint32_t>(npts); // #nocov
Rcpp::NumericMatrix ret(npts, n_dims);
for (size_t i = 0; i < npts; i++) {
if (spatialite) {
// verify special marker; if not there, raise error:
unsigned char marker;
wkb_read(wkb, &marker, 1); // absorb the 0x69 #nocov start
if (marker != 0x69) {
Rcpp::Rcout << "0x69 marker missing before ring " << i+1 << std::endl;
Rcpp::stop("invalid spatialite header");
} // #nocov end
}
Rcpp::List lst = read_data(wkb, EWKB, spatialite, endian, false, NULL, NULL);
Rcpp::NumericVector vec = lst[0];
for (int j = 0; j < n_dims; j++)
ret(i,j) = vec(j);
}
if (cls.size() == 3)
ret.attr("class") = cls;
if (empty != NULL)
*empty = npts == 0;
return ret;
}
Rcpp::List read_geometrycollection(wkb_buf *wkb, int n_dims, bool swap, bool EWKB = 0,
bool spatialite = false, int endian = 0, Rcpp::CharacterVector cls = "", bool isGC = true,
bool *empty = NULL) {
uint32_t nlst;
wkb_read(wkb, &nlst, 4);
if (swap)
nlst = swap_endian<uint32_t>(nlst); // #nocov
Rcpp::List ret(nlst);
for (size_t i = 0; i < nlst; i++) {
if (spatialite) {
// verify special marker; if not there, raise error
unsigned char marker;
wkb_read(wkb, &marker, 1); // absorb the 0x69
if (marker != 0x69) { // #nocov start
Rcpp::Rcout << "0x69 marker missing before ring " << i+1 << std::endl;
Rcpp::stop("invalid spatialite header");
} // #nocov end
}
ret[i] = read_data(wkb, EWKB, spatialite, endian, isGC, NULL, NULL)[0];
}
if (cls.size() == 3)
ret.attr("class") = cls;
if (empty != NULL)
*empty = nlst == 0;
return ret;
}
Rcpp::NumericVector read_numeric_vector(wkb_buf *wkb, int n, bool swap,
Rcpp::CharacterVector cls = "", bool *empty = NULL) {
Rcpp::NumericVector ret(n);
for (int i = 0; i < n; i++) {
double d;
wkb_read(wkb, &d, 8);
if (swap)
ret(i) = swap_endian<double>(d); // #nocov
else
ret(i) = d;
if (i == 0 && empty != NULL && std::isnan(d))
*empty = true;
}
if (cls.size() == 3)
ret.attr("class") = cls;
return ret;
}
Rcpp::NumericMatrix read_numeric_matrix(wkb_buf *wkb, int n_dims, bool swap,
Rcpp::CharacterVector cls = "", bool *empty = NULL) {
uint32_t npts;
wkb_read(wkb, &npts, 4);
if (swap)
npts = swap_endian<uint32_t>(npts); // #nocov
Rcpp::NumericMatrix ret(npts, n_dims);
for (size_t i = 0; i < npts; i++)
for (int j = 0; j< n_dims; j++) {
double d;
wkb_read(wkb, &d, 8);
if (swap)
ret(i, j) = swap_endian<double>(d); // #nocov
else
ret(i, j) = d;
}
if (cls.size() == 3)
ret.attr("class") = cls;
if (empty != NULL)
*empty = npts == 0;
return ret;
}
Rcpp::List read_matrix_list(wkb_buf *wkb, int n_dims, bool swap,
Rcpp::CharacterVector cls = "", bool *empty = NULL) {
uint32_t nlst;
wkb_read(wkb, &nlst, 4);
if (swap)
nlst = swap_endian<uint32_t>(nlst); // #nocov
Rcpp::List ret(nlst);
for (size_t i = 0; i < nlst; i++)
ret[i] = read_numeric_matrix(wkb, n_dims, swap, "");
if (cls.size() == 3)
ret.attr("class") = cls;
if (empty != NULL)
*empty = nlst == 0;
return ret;
}
Rcpp::List read_data(wkb_buf *wkb, bool EWKB = false, bool spatialite = false,
int endian = 0, bool addclass = true, int *type = NULL, uint32_t *srid = NULL) {
/*
pt: handle to the memory buffer
EWKB: should we read EWKB, as opposed to ISO WKB?
endian: 0 or 1, indicating big (0) or little (1) endian of the buffer
addclass: write class information to object?
type: IF NOT NULL: output the geometry type of object read
srid: IF NOT NULL: output the srid read; NULL indicates a nested call
*/
Rcpp::List output(1); // to deal with varying result type
if (srid != NULL && wkb->size > 2 && wkb->pt[0] == 'G' && wkb->pt[1] == 'P') // GPKG header? skip:
read_gpkg_header(wkb, srid, endian);
if (spatialite && srid != NULL)
wkb_read(wkb, NULL, 1); // starting 0x00 contains no information
unsigned char swap_char;
bool swap;
if (spatialite && srid == NULL) // nested call: don't read swap:
swap = false;
else {
wkb_read(wkb, &swap_char, 1);
swap = ((int) swap_char != (int) endian); // endian check
}
if (spatialite) {
if (swap)
Rcpp::stop("reading non-native endian spatialite geometries not supported"); // #nocov
if (srid != NULL) // not nested:
read_spatialite_header(wkb, srid, swap);
}
// read type:
uint32_t wkbType;
wkb_read(wkb, &wkbType, 4);
if (swap)
wkbType = swap_endian<uint32_t>(wkbType); // #nocov
int sf_type = 0, n_dims = 0;
std::string dim_str = "";
if (EWKB) { // EWKB: PostGIS default
sf_type = wkbType & 0x000000ff; // mask the other bits
int wkbZ = wkbType & EWKB_Z_BIT;
int wkbM = wkbType & EWKB_M_BIT;
int wkbSRID = wkbType & EWKB_SRID_BIT;
n_dims = 2 + (int) (wkbZ != 0) + (int) (wkbM != 0);
if (wkbZ == 0 && wkbM == 0)
dim_str = "XY";
else if (wkbZ != 0 && wkbM == 0)
dim_str = "XYZ";
else if (wkbZ == 0 && wkbM != 1)
dim_str = "XYM";
else
dim_str = "XYZM";
if (wkbSRID != 0) {
if (srid != NULL) {
wkb_read(wkb, srid, 4);
if (swap)
*srid = swap_endian<uint32_t>(*srid); // #nocov
}
}
} else { // ISO
sf_type = wkbType % 1000;
switch (wkbType / 1000) { // 0: XY, 1: XYZ, 2: XYM, 3: XYZM
case 0: n_dims = 2; dim_str = "XY"; break;
case 1: n_dims = 3; dim_str = "XYZ"; break;
case 2: n_dims = 3; dim_str = "XYM"; break;
case 3: n_dims = 4; dim_str = "XYZM"; break;
default:
Rcpp::Rcout << "wkbType: " << wkbType << std::endl; // #nocov
Rcpp::stop("unsupported wkbType dim in switch"); // #nocov
}
}
bool empty = false;
switch(sf_type) {
case SF_Point:
output[0] = read_numeric_vector(wkb, n_dims, swap, addclass ?
Rcpp::CharacterVector::create(dim_str, "POINT", "sfg") : "", &empty);
break;
case SF_LineString:
output[0] = read_numeric_matrix(wkb, n_dims, swap, addclass ?
Rcpp::CharacterVector::create(dim_str, "LINESTRING", "sfg") : "", &empty);
break;
case SF_Polygon:
output[0] = read_matrix_list(wkb, n_dims, swap, addclass ?
Rcpp::CharacterVector::create(dim_str, "POLYGON", "sfg") : "", &empty);
break;
case SF_MultiPoint:
output[0] = read_multipoint(wkb, n_dims, swap, EWKB, spatialite, endian, addclass ?
Rcpp::CharacterVector::create(dim_str, "MULTIPOINT", "sfg") : "", &empty);
break;
case SF_MultiLineString:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "MULTILINESTRING", "sfg"), false, &empty);
break;
case SF_MultiPolygon:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "MULTIPOLYGON", "sfg"), false, &empty);
break;
case SF_GeometryCollection:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "GEOMETRYCOLLECTION", "sfg"), true,
&empty);
break;
case SF_CircularString:
output[0] = read_numeric_matrix(wkb, n_dims, swap, addclass ?
Rcpp::CharacterVector::create(dim_str, "CIRCULARSTRING", "sfg") : "", &empty);
break;
case SF_CompoundCurve:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "COMPOUNDCURVE", "sfg"), true, &empty);
break;
case SF_CurvePolygon:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "CURVEPOLYGON", "sfg"), true, &empty);
break;
case SF_MultiCurve:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "MULTICURVE", "sfg"), true, &empty);
break;
case SF_MultiSurface:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "MULTISURFACE", "sfg"), true, &empty);
break;
case SF_Curve: // #nocov start
output[0] = read_numeric_matrix(wkb, n_dims, swap, addclass ?
Rcpp::CharacterVector::create(dim_str, "CURVE", "sfg") : "", &empty);
break;
case SF_Surface:
output[0] = read_matrix_list(wkb, n_dims, swap, addclass ?
Rcpp::CharacterVector::create(dim_str, "SURFACE", "sfg") : "", &empty);
break; // #nocov end
case SF_PolyhedralSurface:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "POLYHEDRALSURFACE", "sfg"), false, &empty);
break;
case SF_TIN:
output[0] = read_geometrycollection(wkb, n_dims, swap, EWKB, spatialite, endian,
Rcpp::CharacterVector::create(dim_str, "TIN", "sfg"), false, &empty);
break;
case SF_Triangle:
output[0] = read_matrix_list(wkb, n_dims, swap,
Rcpp::CharacterVector::create(dim_str, "TRIANGLE", "sfg"), &empty);
break;
default: {
Rcpp::Rcout << "type is " << sf_type << std::endl; // #nocov
Rcpp::stop("reading this sf type is not supported, please file an issue"); // #nocov
}
}
if (type != NULL) {
if (empty)
*type = -sf_type;
else
*type = sf_type;
}
return output;
}
int native_endian(void) {
const int one = 1;
unsigned char *cp = (unsigned char *) &one;
return (int) *cp;
}
// [[Rcpp::export]]
Rcpp::List CPL_read_wkb(Rcpp::List wkb_list, bool EWKB = false, bool spatialite = false) {
Rcpp::List output(wkb_list.size());
int type = 0, last_type = 0, n_types = 0, n_empty = 0;
int endian = native_endian();
uint32_t srid = 0;
for (int i = 0; i < wkb_list.size(); i++) {
Rcpp::checkUserInterrupt();
Rcpp::RawVector raw = wkb_list[i];
wkb_buf wkb;
wkb.pt = &(raw[0]);
wkb.size = raw.size();
// const unsigned char *pt = &(raw[0]);
output[i] = read_data(&wkb, EWKB, spatialite, endian, true, &type, &srid)[0];
if (type <= 0) {
type = -type;
n_empty++;
}
// Rcpp::Rcout << "type is " << type << "\n";
if (n_types <= 1 && type != last_type) {
last_type = type;
n_types++; // check if there's more than 1 type:
}
}
output.attr("single_type") = n_types <= 1; // if 0, we have only empty geometrycollections
output.attr("n_empty") = (int) n_empty;
if ((EWKB || spatialite) && srid != 0)
output.attr("srid") = (int) srid;
return output;
}
//
// write wkb:
//
unsigned int make_type(const char *cls, const char *dim, bool EWKB = false, int *tp = NULL,
int srid = 0) {
int type = 0;
if (strstr(cls, "sfc_") == cls)
cls += 4;
if (strcmp(cls, "POINT") == 0)
type = SF_Point;
else if (strcmp(cls, "LINESTRING") == 0)
type = SF_LineString;
else if (strcmp(cls, "POLYGON") == 0)
type = SF_Polygon;
else if (strcmp(cls, "MULTIPOINT") == 0)
type = SF_MultiPoint;
else if (strcmp(cls, "MULTILINESTRING") == 0)
type = SF_MultiLineString;
else if (strcmp(cls, "MULTIPOLYGON") == 0)
type = SF_MultiPolygon;
else if (strcmp(cls, "GEOMETRYCOLLECTION") == 0)
type = SF_GeometryCollection;
else if (strcmp(cls, "CIRCULARSTRING") == 0)
type = SF_CircularString;
else if (strcmp(cls, "COMPOUNDCURVE") == 0)
type = SF_CompoundCurve;
else if (strcmp(cls, "CURVEPOLYGON") == 0)
type = SF_CurvePolygon;
else if (strcmp(cls, "MULTICURVE") == 0)
type = SF_MultiCurve;
else if (strcmp(cls, "MULTISURFACE") == 0)
type = SF_MultiSurface;
else if (strcmp(cls, "CURVE") == 0)
type = SF_Curve; // #nocov
else if (strcmp(cls, "SURFACE") == 0)
type = SF_Surface; // #nocov
else if (strcmp(cls, "POLYHEDRALSURFACE") == 0)
type = SF_PolyhedralSurface;
else if (strcmp(cls, "TIN") == 0)
type = SF_TIN;
else if (strcmp(cls, "TRIANGLE") == 0)
type = SF_Triangle;
else
type = SF_Unknown; // a mix: GEOMETRY
if (tp != NULL)
*tp = type;
if (EWKB) {
if (strcmp(dim, "XYZ") == 0)
type = type | EWKB_Z_BIT;
else if (strcmp(dim, "XYM") == 0)
type = type | EWKB_M_BIT;
else if (strcmp(dim, "XYZM") == 0)
type = type | EWKB_M_BIT | EWKB_Z_BIT;
if (srid != 0)
type = type | EWKB_SRID_BIT;
} else {
if (strcmp(dim, "XYZ") == 0)
type += 1000;
else if (strcmp(dim, "XYM") == 0)
type += 2000;
else if (strcmp(dim, "XYZM") == 0)
type += 3000;
}
return type;
}
void add_byte(std::ostringstream& os, char c) {
os.write((char*) &c, sizeof(char));
}
void add_int(std::ostringstream& os, unsigned int i) {
const char *cp = (char *)&i;
os.write((char*) cp, sizeof(int));
}
double make_precise(double d, double precision) {
if (precision == 0.0)
return d;
if (precision < 0.0) { // round to float, 4-byte precision
float f = d;
return (double) f;
}
return round(d * precision) / precision;
}
void add_double(std::ostringstream& os, double d, double prec = 0.0) {
d = make_precise(d, prec); // doubles are ALWAYS coordinates
const char *cp = (char *)&d;
os.write((char*) cp, sizeof(double));
}
void write_vector(std::ostringstream& os, Rcpp::NumericVector vec, double prec) {
for (int i = 0; i < vec.length(); i++)
add_double(os, vec(i), prec);
}
void write_matrix(std::ostringstream& os, Rcpp::NumericMatrix mat, double prec) {
add_int(os, mat.nrow());
for (int i = 0; i < mat.nrow(); i++)
for (int j = 0; j < mat.ncol(); j++)
add_double(os, mat(i,j), prec);
}
void write_matrix_list(std::ostringstream& os, Rcpp::List lst, double prec) {
size_t len = lst.length();
add_int(os, len);
for (size_t i = 0; i < len; i++)
write_matrix(os, lst[i], prec);
}
void write_multilinestring(std::ostringstream& os, Rcpp::List lst, bool EWKB = false,
int endian = 0, double prec = 0.0) {
Rcpp::CharacterVector cl_attr = lst.attr("class");
const char *dim = cl_attr[0];
add_int(os, lst.length());
for (int i = 0; i < lst.length(); i++)
write_data(os, lst, i, EWKB, endian, "LINESTRING", dim, prec, 0);
}
void write_multipolygon(std::ostringstream& os, Rcpp::List lst, bool EWKB = false,
int endian = 0, double prec = 0.0) {
Rcpp::CharacterVector cl_attr = lst.attr("class");
const char *dim = cl_attr[0];
add_int(os, lst.length());
for (int i = 0; i < lst.length(); i++)
write_data(os, lst, i, EWKB, endian, "POLYGON", dim, prec, 0);
}
void write_triangles(std::ostringstream& os, Rcpp::List lst, bool EWKB = false,
int endian = 0, double prec = 0.0) {
Rcpp::CharacterVector cl_attr = lst.attr("class");
const char *dim = cl_attr[0];
add_int(os, lst.length());
for (int i = 0; i < lst.length(); i++)
write_data(os, lst, i, EWKB, endian, "TRIANGLE", dim, prec, 0);
}
void write_geometrycollection(std::ostringstream& os, Rcpp::List lst, bool EWKB = false,
int endian = 0, double prec = 0.0) {
add_int(os, lst.length());
Rcpp::Function Rclass("class");
for (int i = 0; i < lst.length(); i++) {
Rcpp::CharacterVector cl_attr = Rclass(lst[i]);
const char *cls = cl_attr[1], *dim = cl_attr[0];
write_data(os, lst, i, EWKB, endian, cls, dim, prec, 0);
}
}
void write_multipoint(std::ostringstream& os, Rcpp::NumericMatrix mat,
bool EWKB = false, int endian = 0, double prec = 0.0) {
add_int(os, mat.nrow());
Rcpp::CharacterVector cl_attr = mat.attr("class");
const char *dim = cl_attr[0];
Rcpp::NumericVector v(mat.ncol()); // copy row i
for (int i = 0; i < mat.nrow(); i++) {
for (int j = 0; j < mat.ncol(); j++)
v(j) = mat(i,j);
write_data(os, Rcpp::List::create(v), 0, EWKB, endian, "POINT", dim, prec, 0);
}
}
// write single simple feature object as (E)WKB to stream os
void write_data(std::ostringstream& os, Rcpp::List sfc, int i = 0, bool EWKB = false,
int endian = 0, const char *cls = NULL, const char *dim = NULL, double prec = 0.0,
int srid = 0) {
add_byte(os, (char) endian);
int tp;
unsigned int sf_type = make_type(cls, dim, EWKB, &tp, srid);
add_int(os, sf_type);
if (EWKB && srid != 0)
add_int(os, srid);
switch(tp) {
case SF_Point: write_vector(os, sfc[i], prec);
break;
case SF_LineString: write_matrix(os, sfc[i], prec);
break;
case SF_Polygon: write_matrix_list(os, sfc[i], prec);
break;
case SF_MultiPoint: write_multipoint(os, sfc[i], EWKB, endian, prec);
break;
case SF_MultiLineString: write_multilinestring(os, sfc[i], EWKB, endian, prec);
break;
case SF_MultiPolygon: write_multipolygon(os, sfc[i], EWKB, endian, prec);
break;
case SF_GeometryCollection: write_geometrycollection(os, sfc[i], EWKB, endian, prec);
break;
case SF_CircularString: write_matrix(os, sfc[i], prec);
break;
case SF_CompoundCurve: write_geometrycollection(os, sfc[i], EWKB, endian, prec);
break;
case SF_CurvePolygon: write_geometrycollection(os, sfc[i], EWKB, endian, prec);
break;
case SF_MultiCurve: write_geometrycollection(os, sfc[i], EWKB, endian, prec);
break;
case SF_MultiSurface: write_geometrycollection(os, sfc[i], EWKB, endian, prec);
break;
case SF_Curve: write_matrix(os, sfc[i], prec); // #nocov start
break;
case SF_Surface: write_matrix_list(os, sfc[i], prec);
break; // #nocov end
case SF_PolyhedralSurface: write_multipolygon(os, sfc[i], EWKB, endian, prec);
break;
case SF_TIN: write_triangles(os, sfc[i], EWKB, endian, prec);
break;
case SF_Triangle: write_matrix_list(os, sfc[i], prec);
break;
default: {
Rcpp::Rcout << "type is " << sf_type << "\n"; // #nocov
Rcpp::stop("writing this sf type is not supported, please file an issue"); // #nocov
}
}
}
// [[Rcpp::export]]
Rcpp::List CPL_write_wkb(Rcpp::List sfc, bool EWKB = false) {
double precision = sfc.attr("precision");
Rcpp::CharacterVector cls_attr = sfc.attr("class");
Rcpp::List sfc_dim = get_dim_sfc(sfc);
Rcpp::CharacterVector dim = sfc_dim["_cls"];
const char *cls = cls_attr[0], *dm = dim[0];
Rcpp::List output(sfc.size()); // with raw vectors
int endian = native_endian();
// got the following from:
// http://stackoverflow.com/questions/24744802/rcpp-how-to-check-if-any-attribute-is-null
Rcpp::CharacterVector classes;
bool have_classes = false;
if (sfc.size() > 0 && strcmp(cls, "sfc_GEOMETRY") == 0) {
if (sfc.hasAttribute("classes")) { // only sfc_GEOMETRY, the mixed bag, sets the classes attr
classes = sfc.attr("classes");
if (classes.size() != sfc.size())
Rcpp::stop("attr classes has wrong size: please file an issue"); // #nocov
have_classes = true;
} else
Rcpp::stop("sfc_GEOMETRY has no classes attribute; please file an issue"); // #nocov
}
Rcpp::List crs = sfc.attr("crs");
int srid = crs(0);
if (srid == NA_INTEGER)
srid = 0; // non-zero now means: we have an srid
for (int i = 0; i < sfc.size(); i++) {
Rcpp::checkUserInterrupt();
std::ostringstream os;
if (have_classes)
cls = classes[i];
write_data(os, sfc, i, EWKB, endian, cls, dm, precision, srid);
Rcpp::RawVector raw(os.str().size()); // os -> raw:
std::string str = os.str();
const char *cp = str.c_str();
for (size_t j = 0; j < str.size(); j++)
raw[j] = cp[j];
output[i] = raw; // raw vector to list
}
return output;
}
// get dim, "XY", "XYZ", "XYZM" or "XYM" from an sfc object
Rcpp::List get_dim_sfc(Rcpp::List sfc) {
if (sfc.length() == 0)
return Rcpp::List::create(
Rcpp::Named("_cls") = Rcpp::CharacterVector::create("XY"),
Rcpp::Named("_dim") = Rcpp::IntegerVector::create(2)
);
// we have data:
Rcpp::CharacterVector cls = sfc.attr("class");
unsigned int tp = make_type(cls[0], "", false, NULL, 0);
if (tp == SF_Unknown) {
cls = sfc.attr("classes");
tp = make_type(cls[0], "", false, NULL, 0);
}
switch (tp) {
case SF_Unknown: { // further check:
Rcpp::stop("impossible classs in get_dim_sfc()"); // #nocov
} break;
case SF_Point: { // numeric:
Rcpp::NumericVector v = sfc[0];
cls = v.attr("class");
} break;
case SF_LineString: // matrix:
case SF_MultiPoint:
case SF_CircularString:
case SF_Curve: {
Rcpp::NumericMatrix m = sfc[0];
cls = m.attr("class");
} break;
case SF_Polygon: // list:
case SF_MultiLineString:
case SF_MultiPolygon:
case SF_GeometryCollection:
case SF_CompoundCurve:
case SF_CurvePolygon:
case SF_MultiCurve:
case SF_MultiSurface:
case SF_Surface:
case SF_PolyhedralSurface:
case SF_TIN:
case SF_Triangle: {
Rcpp::List l = sfc[0];
cls = l.attr("class");
} break;
}
return Rcpp::List::create(
Rcpp::Named("_cls") = cls,
Rcpp::Named("_dim") = strstr(cls[0], "Z") != NULL ?
Rcpp::IntegerVector::create(3) :
Rcpp::IntegerVector::create(2));
}