1
2 #define R_NO_REMAP
3 #include <R.h>
4 #include <Rinternals.h>
5 #include "wk-v1.h"
6 #include <stdlib.h>
7
8 #define SFC_FLAGS_NOT_YET_DEFINED UINT32_MAX
9 #define SFC_GEOMETRY_TYPE_NOT_YET_DEFINED -1
10 #define SFC_MAX_RECURSION_DEPTH 32
11 #define SFC_WRITER_GEOM_LENGTH SFC_MAX_RECURSION_DEPTH + 2
12 #define SFC_INITIAL_SIZE_IF_UNKNOWN 32
13 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
14 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
15
16 typedef struct {
17 // output vector list()
18 SEXP sfc;
19 // container list() geometries
20 SEXP geom[SFC_WRITER_GEOM_LENGTH];
21 // keep track of recursion level and number of parts seen in a geometry
22 size_t recursion_level;
23 R_xlen_t part_id[SFC_WRITER_GEOM_LENGTH];
24
25 // the current coordinate sequence and information about
26 // where we are in the coordinate sequence
27 SEXP coord_seq;
28 int coord_size;
29 uint32_t coord_id;
30 int coord_seq_rows;
31
32 // attr(sfc, "bbox"): xmin, ymin, xmax, ymax
33 double bbox[4];
34 // attr(sfc, "z_range"): zmin, zmax
35 double z_range[2];
36 // attr(sfc, "m_range"): mmin, mmax
37 double m_range[2];
38 // attr(sfc, "precision")
39 double precision;
40 // used to tell if all items are the same type for output class
41 int geometry_type;
42 // when all elements are empty, sfc holds the classes of these objects
43 // so in addition to knowing the common geometry type, we need to know
44 // all types that were encountered in the off chance that they are all empty
45 // using a bitwise OR with (1 << (wk geometry type))
46 int all_geometry_types;
47 // used to enforce requirement that all sub geometries to have the same dimensions
48 uint32_t flags;
49 // attr(sfc, "n_empty")
50 R_xlen_t n_empty;
51 // sfc views NULL as equivalent to EMPTY, but we can skip this replacement if
52 // there were not any NULLs (almost 100% of the time)
53 int any_null;
54 // needed to access feat_id in geometry handlers
55 R_xlen_t feat_id;
56 } sfc_writer_t;
57
sfc_writer_new()58 sfc_writer_t* sfc_writer_new() {
59 sfc_writer_t* writer = (sfc_writer_t*) malloc(sizeof(sfc_writer_t));
60 if (writer == NULL) {
61 return NULL; // # nocov
62 }
63
64 writer->sfc = R_NilValue;
65 for (int i = 0; i < SFC_WRITER_GEOM_LENGTH; i++) {
66 writer->geom[i] = R_NilValue;
67 writer->part_id[i] = 0;
68 }
69 writer->recursion_level = 0;
70
71 writer->coord_seq = R_NilValue;
72 writer->coord_id = -1;
73 writer->coord_size = 2;
74 writer->coord_seq_rows = -1;
75
76 writer->bbox[0] = R_PosInf;
77 writer->bbox[1] = R_PosInf;
78 writer->bbox[2] = R_NegInf;
79 writer->bbox[3] = R_NegInf;
80
81 writer->z_range[0] = R_PosInf;
82 writer->z_range[1] = R_NegInf;
83
84 writer->m_range[0] = R_PosInf;
85 writer->m_range[1] = R_NegInf;
86
87 writer->precision = R_PosInf;
88
89 writer->geometry_type = SFC_GEOMETRY_TYPE_NOT_YET_DEFINED;
90 writer->all_geometry_types = 0;
91 writer->flags = SFC_FLAGS_NOT_YET_DEFINED;
92 writer->n_empty = 0;
93 writer->any_null = 0;
94 writer->feat_id = 0;
95
96 return writer;
97 }
98
sfc_writer_is_nesting_geometrycollection(sfc_writer_t * writer)99 int sfc_writer_is_nesting_geometrycollection(sfc_writer_t* writer) {
100 return (writer->recursion_level > 0) &&
101 Rf_inherits(writer->geom[writer->recursion_level - 1], "GEOMETRYCOLLECTION");
102 }
103
sfc_writer_is_nesting_multipoint(sfc_writer_t * writer)104 int sfc_writer_is_nesting_multipoint(sfc_writer_t* writer) {
105 return Rf_inherits(writer->coord_seq, "MULTIPOINT");
106 }
107
sfc_double_all_na_or_nan(int n_values,const double * values)108 static inline int sfc_double_all_na_or_nan(int n_values, const double* values) {
109 for (int i = 0; i < n_values; i++) {
110 if (!ISNA(values[i]) && !ISNAN(values[i])) {
111 return 0;
112 }
113 }
114
115 return 1;
116 }
117
118 // this is intended to replicate NA_crs_
sfc_na_crs()119 SEXP sfc_na_crs() {
120 const char* crs_names[] = {"input", "wkt", ""};
121 SEXP crs = PROTECT(Rf_mkNamed(VECSXP, crs_names));
122 SEXP crs_input = PROTECT(Rf_allocVector(STRSXP, 1));
123 SET_STRING_ELT(crs_input, 0, NA_STRING);
124 SET_VECTOR_ELT(crs, 0, crs_input);
125 UNPROTECT(1);
126 SEXP crs_wkt = PROTECT(Rf_allocVector(STRSXP, 1));
127 SET_STRING_ELT(crs_wkt, 0, NA_STRING);
128 SET_VECTOR_ELT(crs, 1, crs_wkt);
129 UNPROTECT(1);
130 Rf_setAttrib(crs, R_ClassSymbol, Rf_mkString("crs"));
131 UNPROTECT(1);
132 return crs;
133 }
134
sfc_writer_empty_sfg(int geometry_type,uint32_t flags)135 SEXP sfc_writer_empty_sfg(int geometry_type, uint32_t flags) {
136 SEXP result = R_NilValue;
137
138 int coord_size;
139 if ((flags & WK_FLAG_HAS_Z) && (flags & WK_FLAG_HAS_M)) {
140 coord_size = 4;
141 } else if ((flags & WK_FLAG_HAS_Z) || (flags & WK_FLAG_HAS_M)) {
142 coord_size = 3;
143 } else {
144 coord_size = 2;
145 }
146
147 switch (geometry_type) {
148 case WK_POINT:
149 result = PROTECT(Rf_allocVector(REALSXP, coord_size));
150 for (int i = 0; i < coord_size; i++) {
151 REAL(result)[i] = NA_REAL;
152 }
153 break;
154 case WK_LINESTRING:
155 result = PROTECT(Rf_allocMatrix(REALSXP, 0, coord_size));
156 break;
157 case WK_POLYGON:
158 result = PROTECT(Rf_allocVector(VECSXP, 0));
159 break;
160 case WK_MULTIPOINT:
161 result = PROTECT(Rf_allocMatrix(REALSXP, 0, coord_size));
162 break;
163 case WK_MULTILINESTRING:
164 result = PROTECT(Rf_allocVector(VECSXP, 0));
165 break;
166 case WK_MULTIPOLYGON:
167 result = PROTECT(Rf_allocVector(VECSXP, 0));
168 break;
169 case WK_GEOMETRYCOLLECTION:
170 result = PROTECT(Rf_allocVector(VECSXP, 0));
171 break;
172 default:
173 Rf_error("Can't generate empty 'sfg' for geometry type '%d'", geometry_type); // # nocov
174 }
175
176 UNPROTECT(1);
177 return result;
178 }
179
sfc_writer_maybe_add_class_to_sfg(sfc_writer_t * writer,SEXP item,const wk_meta_t * meta)180 void sfc_writer_maybe_add_class_to_sfg(sfc_writer_t* writer, SEXP item, const wk_meta_t* meta) {
181 if (writer->recursion_level == 0 || sfc_writer_is_nesting_geometrycollection(writer)) {
182 // in the form XY(ZM), GEOM_TYPE, sfg
183 SEXP class = PROTECT(Rf_allocVector(STRSXP, 3));
184 SET_STRING_ELT(class, 2, Rf_mkChar("sfg"));
185
186 if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) {
187 SET_STRING_ELT(class, 0, Rf_mkChar("XYZM"));
188 } else if (meta->flags & WK_FLAG_HAS_Z) {
189 SET_STRING_ELT(class, 0, Rf_mkChar("XYZ"));
190 } else if (meta->flags & WK_FLAG_HAS_M) {
191 SET_STRING_ELT(class, 0, Rf_mkChar("XYM"));
192 } else {
193 SET_STRING_ELT(class, 0, Rf_mkChar("XY"));
194 }
195
196 switch (meta->geometry_type) {
197 case WK_POINT:
198 SET_STRING_ELT(class, 1, Rf_mkChar("POINT"));
199 break;
200 case WK_LINESTRING:
201 SET_STRING_ELT(class, 1, Rf_mkChar("LINESTRING"));
202 break;
203 case WK_POLYGON:
204 SET_STRING_ELT(class, 1, Rf_mkChar("POLYGON"));
205 break;
206 case WK_MULTIPOINT:
207 SET_STRING_ELT(class, 1, Rf_mkChar("MULTIPOINT"));
208 break;
209 case WK_MULTILINESTRING:
210 SET_STRING_ELT(class, 1, Rf_mkChar("MULTILINESTRING"));
211 break;
212 case WK_MULTIPOLYGON:
213 SET_STRING_ELT(class, 1, Rf_mkChar("MULTIPOLYGON"));
214 break;
215 case WK_GEOMETRYCOLLECTION:
216 SET_STRING_ELT(class, 1, Rf_mkChar("GEOMETRYCOLLECTION"));
217 break;
218 default:
219 Rf_error("Can't generate class 'sfg' for geometry type '%d'", meta->geometry_type); // # nocov
220 }
221
222 Rf_setAttrib(item, R_ClassSymbol, class);
223 UNPROTECT(1);
224 }
225 }
226
sfc_writer_update_dimensions(sfc_writer_t * writer,const wk_meta_t * meta,uint32_t size)227 void sfc_writer_update_dimensions(sfc_writer_t* writer, const wk_meta_t* meta, uint32_t size) {
228 if (size > 0) {
229 if (writer->flags == SFC_FLAGS_NOT_YET_DEFINED) {
230 writer->flags = meta->flags;
231 } else if (writer->flags != meta->flags) {
232 Rf_error("Can't convert geometries with incompatible dimensions to 'sfc'");
233 }
234 }
235 }
236
sfc_writer_update_vector_attributes(sfc_writer_t * writer,const wk_meta_t * meta,uint32_t size)237 void sfc_writer_update_vector_attributes(sfc_writer_t* writer, const wk_meta_t* meta, uint32_t size) {
238 // all geometry types specifically matters for when everything is EMPTY
239 writer->all_geometry_types = writer->all_geometry_types | (1 << (meta->geometry_type - 1));
240
241 // these matter even for EMPTY
242 if (writer->geometry_type == SFC_GEOMETRY_TYPE_NOT_YET_DEFINED) {
243 writer->geometry_type = meta->geometry_type;
244 } else if (writer->geometry_type != meta->geometry_type) {
245 writer->geometry_type = WK_GEOMETRY;
246 }
247
248 // update empty count
249 writer->n_empty += size == 0;
250
251 // update dimensions
252 sfc_writer_update_dimensions(writer, meta, size);
253
254 // update precision
255 writer->precision = MIN(writer->precision, meta->precision);
256 }
257
sfc_writer_update_ranges(sfc_writer_t * writer,const wk_meta_t * meta,const double * coord)258 void sfc_writer_update_ranges(sfc_writer_t* writer, const wk_meta_t* meta, const double* coord) {
259 writer->bbox[0] = MIN(writer->bbox[0], coord[0]);
260 writer->bbox[1] = MIN(writer->bbox[1], coord[1]);
261 writer->bbox[2] = MAX(writer->bbox[2], coord[0]);
262 writer->bbox[3] = MAX(writer->bbox[3], coord[1]);
263
264 if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) {
265 writer->z_range[0] = MIN(writer->z_range[0], coord[2]);
266 writer->z_range[1] = MAX(writer->z_range[1], coord[2]);
267 writer->m_range[0] = MIN(writer->m_range[0], coord[3]);
268 writer->m_range[1] = MAX(writer->m_range[1], coord[3]);
269 } else if (meta->flags & WK_FLAG_HAS_Z) {
270 writer->z_range[0] = MIN(writer->z_range[0], coord[2]);
271 writer->z_range[1] = MAX(writer->z_range[1], coord[2]);
272 } else if (meta->flags & WK_FLAG_HAS_M) {
273 writer->m_range[0] = MIN(writer->m_range[0], coord[2]);
274 writer->m_range[1] = MAX(writer->m_range[1], coord[2]);
275 }
276 }
277
sfc_writer_alloc_coord_seq(uint32_t size_hint,int coord_size)278 SEXP sfc_writer_alloc_coord_seq(uint32_t size_hint, int coord_size) {
279 if (size_hint == WK_SIZE_UNKNOWN) {
280 size_hint = SFC_INITIAL_SIZE_IF_UNKNOWN;
281 }
282
283 return Rf_allocMatrix(REALSXP, size_hint, coord_size);
284 }
285
sfc_writer_realloc_coord_seq(SEXP coord_seq,uint32_t new_size)286 SEXP sfc_writer_realloc_coord_seq(SEXP coord_seq, uint32_t new_size) {
287 uint32_t current_size = Rf_nrows(coord_seq);
288 int coord_size = Rf_ncols(coord_seq);
289
290 SEXP new_coord_seq = PROTECT(Rf_allocMatrix(REALSXP, new_size, coord_size));
291
292 double* old_values = REAL(coord_seq);
293 double* new_values = REAL(new_coord_seq);
294
295 for (int j = 0; j < coord_size; j++) {
296 memcpy(
297 new_values + (j * new_size),
298 old_values + (j * current_size),
299 sizeof(double) * current_size
300 );
301 }
302
303 if (Rf_inherits(coord_seq, "sfg")) {
304 SEXP class = PROTECT(Rf_getAttrib(coord_seq, R_ClassSymbol));
305 Rf_setAttrib(new_coord_seq, R_ClassSymbol, class);
306 UNPROTECT(1);
307 }
308
309 UNPROTECT(1);
310 return new_coord_seq;
311 }
312
sfc_writer_finalize_coord_seq(SEXP coord_seq,uint32_t final_size)313 SEXP sfc_writer_finalize_coord_seq(SEXP coord_seq, uint32_t final_size) {
314 uint32_t current_size = Rf_nrows(coord_seq);
315 int coord_size = Rf_ncols(coord_seq);
316
317 SEXP new_coord_seq = PROTECT(Rf_allocMatrix(REALSXP, final_size, coord_size));
318
319 double* old_values = REAL(coord_seq);
320 double* new_values = REAL(new_coord_seq);
321
322 for (int j = 0; j < coord_size; j++) {
323 memcpy(
324 new_values + (j * final_size),
325 old_values + (j * current_size),
326 sizeof(double) * final_size
327 );
328 }
329
330 if (Rf_inherits(coord_seq, "sfg")) {
331 SEXP class = PROTECT(Rf_getAttrib(coord_seq, R_ClassSymbol));
332 Rf_setAttrib(new_coord_seq, R_ClassSymbol, class);
333 UNPROTECT(1);
334 }
335
336 UNPROTECT(1);
337 return new_coord_seq;
338 }
339
sfc_writer_alloc_geom(uint32_t size_hint)340 SEXP sfc_writer_alloc_geom(uint32_t size_hint) {
341 if (size_hint == WK_SIZE_UNKNOWN) {
342 size_hint = SFC_INITIAL_SIZE_IF_UNKNOWN;
343 }
344 return Rf_allocVector(VECSXP, size_hint);
345 }
346
sfc_writer_realloc_geom(SEXP geom,R_xlen_t new_size)347 SEXP sfc_writer_realloc_geom(SEXP geom, R_xlen_t new_size) {
348 R_xlen_t current_size = Rf_xlength(geom);
349
350 SEXP new_geom = PROTECT(Rf_allocVector(VECSXP, new_size));
351 for (R_xlen_t i = 0; i < current_size; i++) {
352 SET_VECTOR_ELT(new_geom, i, VECTOR_ELT(geom, i));
353 }
354
355 if (Rf_inherits(geom, "sfg")) {
356 SEXP class = PROTECT(Rf_getAttrib(geom, R_ClassSymbol));
357 Rf_setAttrib(new_geom, R_ClassSymbol, class);
358 UNPROTECT(1);
359 }
360
361 UNPROTECT(1);
362 return new_geom;
363 }
364
sfc_writer_finalize_geom(SEXP geom,R_xlen_t final_size)365 SEXP sfc_writer_finalize_geom(SEXP geom, R_xlen_t final_size) {
366 SEXP new_geom = PROTECT(Rf_allocVector(VECSXP, final_size));
367 for (R_xlen_t i = 0; i < final_size; i++) {
368 SET_VECTOR_ELT(new_geom, i, VECTOR_ELT(geom, i));
369 }
370
371 if (Rf_inherits(geom, "sfg")) {
372 SEXP class = PROTECT(Rf_getAttrib(geom, R_ClassSymbol));
373 Rf_setAttrib(new_geom, R_ClassSymbol, class);
374 UNPROTECT(1);
375 }
376
377 UNPROTECT(1);
378 return new_geom;
379 }
380
sfc_writer_sfc_append(sfc_writer_t * writer,SEXP value)381 static inline void sfc_writer_sfc_append(sfc_writer_t* writer, SEXP value) {
382 R_xlen_t current_size = Rf_xlength(writer->sfc);
383 if (writer->feat_id >= current_size) {
384 SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1));
385 for (R_xlen_t i = 0; i < current_size; i++) {
386 SET_VECTOR_ELT(new_result, i, VECTOR_ELT(writer->sfc, i));
387 }
388 R_ReleaseObject(writer->sfc);
389 writer->sfc = new_result;
390 R_PreserveObject(writer->sfc);
391 UNPROTECT(1);
392 }
393
394 SET_VECTOR_ELT(writer->sfc, writer->feat_id, value);
395 writer->feat_id++;
396 }
397
sfc_writer_sfc_finalize(sfc_writer_t * writer)398 static inline void sfc_writer_sfc_finalize(sfc_writer_t* writer) {
399 R_xlen_t current_size = Rf_xlength(writer->sfc);
400 if (writer->feat_id != current_size) {
401 SEXP new_result = PROTECT(Rf_allocVector(VECSXP, writer->feat_id));
402 for (R_xlen_t i = 0; i < writer->feat_id; i++) {
403 SET_VECTOR_ELT(new_result, i, VECTOR_ELT(writer->sfc, i));
404 }
405 R_ReleaseObject(writer->sfc);
406 writer->sfc = new_result;
407 R_PreserveObject(writer->sfc);
408 UNPROTECT(1);
409 }
410 }
411
sfc_writer_vector_start(const wk_vector_meta_t * vector_meta,void * handler_data)412 int sfc_writer_vector_start(const wk_vector_meta_t* vector_meta, void* handler_data) {
413 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
414
415 if (writer->sfc != R_NilValue) {
416 Rf_error("Destination vector was already allocated"); // # nocov
417 }
418
419 if (vector_meta->size == WK_VECTOR_SIZE_UNKNOWN) {
420 writer->sfc = PROTECT(Rf_allocVector(VECSXP, 1024));
421 } else {
422 writer->sfc = PROTECT(Rf_allocVector(VECSXP, vector_meta->size));
423 }
424
425 R_PreserveObject(writer->sfc);
426 UNPROTECT(1);
427
428 writer->feat_id = 0;
429
430 return WK_CONTINUE;
431 }
432
sfc_writer_feature_start(const wk_vector_meta_t * vector_meta,R_xlen_t feat_id,void * handler_data)433 int sfc_writer_feature_start(const wk_vector_meta_t* vector_meta, R_xlen_t feat_id, void* handler_data) {
434 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
435 writer->recursion_level = 0;
436 return WK_CONTINUE;
437 }
438
sfc_writer_null_feature(void * handler_data)439 int sfc_writer_null_feature(void* handler_data) {
440 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
441 // sfc doesn't do NULLs and replaces them with GEOMETRYCOLLECTION EMPTY
442 // however, as the dimensions have to align among features we asign a NULL here and fix
443 // in vector_end()
444 writer->any_null = 1;
445 sfc_writer_sfc_append(writer, R_NilValue);
446 return WK_ABORT_FEATURE;
447 }
448
sfc_writer_geometry_start(const wk_meta_t * meta,uint32_t part_id,void * handler_data)449 int sfc_writer_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) {
450 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
451
452 // ignore start of POINT nested in MULTIPOINT
453 if (sfc_writer_is_nesting_multipoint(writer)) {
454 return WK_CONTINUE;
455 }
456
457 if ((meta->flags & WK_FLAG_HAS_Z) && (meta->flags & WK_FLAG_HAS_M)) {
458 writer->coord_size = 4;
459 } else if ((meta->flags & WK_FLAG_HAS_Z) || (meta->flags & WK_FLAG_HAS_M)) {
460 writer->coord_size = 3;
461 } else {
462 writer->coord_size = 2;
463 }
464
465 // there isn't quite enough information here yet for points, which can
466 // be considered empty if coordinates are NA
467 if ((writer->recursion_level == 0) && (meta->geometry_type != WK_POINT)) {
468 sfc_writer_update_vector_attributes(writer, meta, meta->size);
469 } else if ((writer->recursion_level < 0) || (writer->recursion_level >= SFC_MAX_RECURSION_DEPTH)) {
470 Rf_error("Invalid recursion depth whilst parsing 'sfg': %d", writer->recursion_level);
471 }
472
473 // if POINT, LINESTRING, or MULTIPOINT
474 // replace coordinate sequence with a fresh one
475 // otherwise, create a list() container and push it to the writer->geom[] stack
476 switch (meta->geometry_type) {
477 case WK_POINT:
478 if (writer->coord_seq != R_NilValue) R_ReleaseObject(writer->coord_seq);
479 writer->coord_seq = PROTECT(Rf_allocVector(REALSXP, writer->coord_size));
480
481 // empty point is NA, NA ...
482 if (meta->size == 0) {
483 for (int i = 0; i < writer->coord_size; i++) {
484 REAL(writer->coord_seq)[i] = NA_REAL;
485 }
486 }
487
488 sfc_writer_maybe_add_class_to_sfg(writer, writer->coord_seq, meta);
489 R_PreserveObject(writer->coord_seq);
490 UNPROTECT(1);
491 writer->coord_id = 0;
492 writer->coord_seq_rows = 1;
493 break;
494 case WK_LINESTRING:
495 case WK_MULTIPOINT:
496 if (writer->coord_seq != R_NilValue) R_ReleaseObject(writer->coord_seq);
497 writer->coord_seq = PROTECT(sfc_writer_alloc_coord_seq(meta->size, writer->coord_size));
498
499 sfc_writer_maybe_add_class_to_sfg(writer, writer->coord_seq, meta);
500 R_PreserveObject(writer->coord_seq);
501 UNPROTECT(1);
502 writer->coord_id = 0;
503 writer->coord_seq_rows = Rf_nrows(writer->coord_seq);
504 break;
505 case WK_POLYGON:
506 case WK_MULTILINESTRING:
507 case WK_MULTIPOLYGON:
508 case WK_GEOMETRYCOLLECTION:
509 if (writer->geom[writer->recursion_level] != R_NilValue) {
510 R_ReleaseObject(writer->geom[writer->recursion_level]);
511 }
512
513 writer->geom[writer->recursion_level] = PROTECT(sfc_writer_alloc_geom(meta->size));
514 sfc_writer_maybe_add_class_to_sfg(writer, writer->geom[writer->recursion_level], meta);
515 R_PreserveObject(writer->geom[writer->recursion_level]);
516 UNPROTECT(1);
517 writer->part_id[writer->recursion_level] = 0;
518 break;
519 default:
520 Rf_error("Can't convert geometry type '%d' to sfg", meta->geometry_type); // # nocov
521 break;
522 }
523
524 writer->recursion_level++;
525 return WK_CONTINUE;
526 }
527
sfc_writer_ring_start(const wk_meta_t * meta,uint32_t size,uint32_t ring_id,void * handler_data)528 int sfc_writer_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) {
529 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
530
531 if (writer->coord_seq != NULL) {
532 R_ReleaseObject(writer->coord_seq);
533 }
534
535 writer->coord_seq = PROTECT(sfc_writer_alloc_coord_seq(size, writer->coord_size));
536 R_PreserveObject(writer->coord_seq);
537 UNPROTECT(1);
538 writer->coord_id = 0;
539 writer->coord_seq_rows = Rf_nrows(writer->coord_seq);
540
541 writer->recursion_level++;
542 return WK_CONTINUE;
543 }
544
sfc_writer_coord(const wk_meta_t * meta,const double * coord,uint32_t coord_id,void * handler_data)545 int sfc_writer_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) {
546 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
547
548 // This point might be EMPTY, in which case it will cause the ranges to be all NaN
549 if ((meta->geometry_type != WK_POINT) ||
550 (!sfc_double_all_na_or_nan(writer->coord_size, coord))) {
551 sfc_writer_update_ranges(writer, meta, coord);
552 }
553
554 // realloc the coordinate sequence if necessary
555 if (writer->coord_id >= writer->coord_seq_rows) {
556 SEXP new_coord_seq = PROTECT(sfc_writer_realloc_coord_seq(writer->coord_seq, writer->coord_id * 1.5 + 1));
557 R_ReleaseObject(writer->coord_seq);
558 writer->coord_seq = new_coord_seq;
559 R_PreserveObject(writer->coord_seq);
560 UNPROTECT(1);
561 writer->coord_seq_rows = Rf_nrows(writer->coord_seq);
562 }
563
564 double* current_values = REAL(writer->coord_seq);
565 for (int i = 0; i < writer->coord_size; i++) {
566 current_values[i * writer->coord_seq_rows + writer->coord_id] = coord[i];
567 }
568
569 writer->coord_id++;
570 return WK_CONTINUE;
571 }
572
sfc_writer_ring_end(const wk_meta_t * meta,uint32_t size,uint32_t ring_id,void * handler_data)573 int sfc_writer_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) {
574 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
575 writer->recursion_level--;
576
577 SEXP geom;
578 if (writer->coord_id < Rf_nrows(writer->coord_seq)) {
579 geom = PROTECT(sfc_writer_finalize_coord_seq(writer->coord_seq, writer->coord_id));
580 } else {
581 geom = PROTECT(writer->coord_seq);
582 }
583
584 R_ReleaseObject(writer->coord_seq);
585 writer->coord_seq = R_NilValue;
586
587 // may need to reallocate the container
588 R_xlen_t container_len = Rf_xlength(writer->geom[writer->recursion_level - 1]);
589 if (ring_id >= container_len) {
590 SEXP new_geom = PROTECT(
591 sfc_writer_realloc_geom(
592 writer->geom[writer->recursion_level - 1],
593 container_len * 1.5 + 1
594 )
595 );
596 R_ReleaseObject(writer->geom[writer->recursion_level - 1]);
597 writer->geom[writer->recursion_level - 1] = new_geom;
598 R_PreserveObject(writer->geom[writer->recursion_level - 1]);
599 UNPROTECT(1);
600 }
601
602 SET_VECTOR_ELT(writer->geom[writer->recursion_level - 1], ring_id, geom);
603 writer->part_id[writer->recursion_level - 1]++;
604 UNPROTECT(1);
605
606 return WK_CONTINUE;
607 }
608
sfc_writer_geometry_end(const wk_meta_t * meta,uint32_t part_id,void * handler_data)609 int sfc_writer_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) {
610 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
611
612 // ignore end of POINT nested in MULTIPOINT
613 if ((meta->geometry_type == WK_POINT) && sfc_writer_is_nesting_multipoint(writer)) {
614 return WK_CONTINUE;
615 }
616
617 writer->recursion_level--;
618
619 SEXP geom;
620 switch(meta->geometry_type) {
621 case WK_POINT:
622 geom = PROTECT(writer->coord_seq);
623 R_ReleaseObject(writer->coord_seq);
624 writer->coord_seq = R_NilValue;
625 break;
626 case WK_LINESTRING:
627 case WK_MULTIPOINT:
628 if (writer->coord_id < Rf_nrows(writer->coord_seq)) {
629 geom = PROTECT(sfc_writer_finalize_coord_seq(writer->coord_seq, writer->coord_id));
630 } else {
631 geom = PROTECT(writer->coord_seq);
632 }
633 R_ReleaseObject(writer->coord_seq);
634 writer->coord_seq = R_NilValue;
635 break;
636 case WK_POLYGON:
637 case WK_MULTILINESTRING:
638 case WK_MULTIPOLYGON:
639 case WK_GEOMETRYCOLLECTION:
640 if (writer->part_id[writer->recursion_level] < Rf_xlength(writer->geom[writer->recursion_level])) {
641 geom = PROTECT(
642 sfc_writer_finalize_geom(
643 writer->geom[writer->recursion_level],
644 writer->part_id[writer->recursion_level]
645 )
646 );
647 } else {
648 geom = PROTECT(writer->geom[writer->recursion_level]);
649 }
650
651 // R_ReleaseObject() is called on `geom` in finalize() or
652 // when it is replaced in geometry_start()
653 break;
654 default:
655 Rf_error("Can't convert geometry type '%d' to sfg", meta->geometry_type); // # nocov
656 break; // # nocov
657 }
658
659 // Top-level geometries have their dimensions checked but nested must be as well
660 if ((writer->recursion_level) > 0 && (meta->geometry_type == WK_POINT)) {
661 int all_na = sfc_double_all_na_or_nan(writer->coord_size, REAL(geom));
662 sfc_writer_update_dimensions(writer, meta, meta->size && !all_na);
663 } else if (writer->recursion_level > 0) {
664 sfc_writer_update_dimensions(writer, meta, meta->size);
665 }
666
667 // if we're above a top-level geometry, this geometry needs to be added to the parent
668 // otherwise, it needs to be added to sfc
669 if (writer->recursion_level > 0) {
670
671 // may need to reallocate the container
672 R_xlen_t container_len = Rf_xlength(writer->geom[writer->recursion_level - 1]);
673 if (part_id >= container_len) {
674 SEXP new_geom = PROTECT(
675 sfc_writer_realloc_geom(
676 writer->geom[writer->recursion_level - 1],
677 container_len * 1.5 + 1
678 )
679 );
680 R_ReleaseObject(writer->geom[writer->recursion_level - 1]);
681 writer->geom[writer->recursion_level - 1] = new_geom;
682 R_PreserveObject(writer->geom[writer->recursion_level - 1]);
683 UNPROTECT(1);
684 }
685
686 SET_VECTOR_ELT(writer->geom[writer->recursion_level - 1], part_id, geom);
687 writer->part_id[writer->recursion_level - 1]++;
688 } else if (meta->geometry_type == WK_POINT) {
689 // at the top level, we have to check again if all point coordinates are NA
690 // because this is 'empty' for the purposes of sfc
691 // We didn't update this earlier because we didn't know if the point was
692 // empty yet or not!
693 int all_na = sfc_double_all_na_or_nan(writer->coord_size, REAL(geom));
694 sfc_writer_update_vector_attributes(writer, meta, meta->size && !all_na);
695
696 sfc_writer_sfc_append(writer, geom);
697 } else {
698 sfc_writer_sfc_append(writer, geom);
699 }
700
701 UNPROTECT(1);
702 return WK_CONTINUE;
703 }
704
sfc_writer_vector_end(const wk_vector_meta_t * vector_meta,void * handler_data)705 SEXP sfc_writer_vector_end(const wk_vector_meta_t* vector_meta, void* handler_data) {
706 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
707
708 sfc_writer_sfc_finalize(writer);
709
710 // replace NULLs with EMPTY of an appropriate type
711 if (writer->any_null) {
712 wk_meta_t meta;
713
714 if (writer->geometry_type == WK_GEOMETRY || writer->geometry_type == SFC_GEOMETRY_TYPE_NOT_YET_DEFINED) {
715 WK_META_RESET(meta, WK_GEOMETRYCOLLECTION);
716 // also update the type list for attr(sfc, "classes")
717 writer->all_geometry_types = writer->all_geometry_types | (1 << (WK_GEOMETRYCOLLECTION - 1));
718 } else {
719 WK_META_RESET(meta, writer->geometry_type);
720 }
721
722 if (writer->flags != SFC_FLAGS_NOT_YET_DEFINED) {
723 meta.flags = writer->flags;
724 }
725
726 if (writer->geometry_type == SFC_GEOMETRY_TYPE_NOT_YET_DEFINED) {
727 writer->geometry_type = WK_GEOMETRYCOLLECTION;
728 }
729
730 meta.size = 0;
731 writer->recursion_level = 0;
732 SEXP empty = PROTECT(sfc_writer_empty_sfg(meta.geometry_type, meta.flags));
733 sfc_writer_maybe_add_class_to_sfg(writer, empty, &meta);
734
735 for (R_xlen_t i = 0; i < Rf_xlength(writer->sfc); i++) {
736 if (VECTOR_ELT(writer->sfc, i) == R_NilValue) {
737 writer->n_empty++;
738 SET_VECTOR_ELT(writer->sfc, i, empty);
739 }
740 }
741
742 UNPROTECT(1);
743 }
744
745 // attr(sfc, "precision")
746 SEXP precision;
747 if (writer->precision == R_PosInf) {
748 precision = PROTECT(Rf_ScalarReal(0.0));
749 } else {
750 precision = PROTECT(Rf_ScalarReal(writer->precision));
751 }
752 Rf_setAttrib(writer->sfc, Rf_install("precision"), precision);
753 UNPROTECT(1);
754
755 // attr(sfc, "bbox")
756 const char* bbox_names[] = {"xmin", "ymin", "xmax", "ymax", ""};
757 SEXP bbox = PROTECT(Rf_mkNamed(REALSXP, bbox_names));
758 Rf_setAttrib(bbox, R_ClassSymbol, Rf_mkString("bbox"));
759
760 // the bounding box may or may not have a crs attribute
761 // when all features are empty
762 if (Rf_xlength(writer->sfc) == writer->n_empty) {
763 SEXP na_crs = PROTECT(sfc_na_crs());
764 Rf_setAttrib(bbox, Rf_install("crs"), na_crs);
765 UNPROTECT(1);
766 }
767
768 // if the bounding box was never updated, set it to NAs
769 if (writer->bbox[0] == R_PosInf) {
770 writer->bbox[0] = NA_REAL;
771 writer->bbox[1] = NA_REAL;
772 writer->bbox[2] = NA_REAL;
773 writer->bbox[3] = NA_REAL;
774 }
775 memcpy(REAL(bbox), writer->bbox, sizeof(double) * 4);
776 Rf_setAttrib(writer->sfc, Rf_install("bbox"), bbox);
777 UNPROTECT(1);
778
779 // attr(sfc, "z_range"), attr(sfc, "m_range")
780 if (writer->flags == SFC_FLAGS_NOT_YET_DEFINED) {
781 writer->flags = 0;
782 }
783
784 if (writer->flags & WK_FLAG_HAS_Z) {
785 // if the z_range was never updated, set it to NAs
786 if (writer->z_range[0] == R_PosInf) {
787 writer->z_range[0] = NA_REAL;
788 writer->z_range[1] = NA_REAL;
789 }
790
791 const char* z_range_names[] = {"zmin", "zmax", ""};
792 SEXP z_range = PROTECT(Rf_mkNamed(REALSXP, z_range_names));
793 Rf_setAttrib(z_range, R_ClassSymbol, Rf_mkString("z_range"));
794 memcpy(REAL(z_range), writer->z_range, sizeof(double) * 2);
795 Rf_setAttrib(writer->sfc, Rf_install("z_range"), z_range);
796 UNPROTECT(1);
797 }
798
799 if (writer->flags & WK_FLAG_HAS_M) {
800 // if the m_range was never updated, set it to NAs
801 if (writer->m_range[0] == R_PosInf) {
802 writer->m_range[0] = NA_REAL;
803 writer->m_range[1] = NA_REAL;
804 }
805
806 const char* m_range_names[] = {"mmin", "mmax", ""};
807 SEXP m_range = PROTECT(Rf_mkNamed(REALSXP, m_range_names));
808 Rf_setAttrib(m_range, R_ClassSymbol, Rf_mkString("m_range"));
809 memcpy(REAL(m_range), writer->m_range, sizeof(double) * 2);
810 Rf_setAttrib(writer->sfc, Rf_install("m_range"), m_range);
811 UNPROTECT(1);
812 }
813
814 // attr(sfc, "crs")
815 // this should be handled in R; however, inserting a placeholder here
816 // because the print() method for sfc will error otherwise
817 SEXP na_crs = PROTECT(sfc_na_crs());
818 Rf_setAttrib(writer->sfc, Rf_install("crs"), na_crs);
819 UNPROTECT(1);
820
821 // attr(sfc, "n_empty")
822 SEXP n_empty = PROTECT(Rf_ScalarInteger(writer->n_empty));
823 Rf_setAttrib(writer->sfc, Rf_install("n_empty"), n_empty);
824 UNPROTECT(1);
825
826 // class(sfc)
827 SEXP class = PROTECT(Rf_allocVector(STRSXP, 2));
828 switch (writer->geometry_type) {
829 case WK_POINT:
830 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_POINT"));
831 break;
832 case WK_LINESTRING:
833 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_LINESTRING"));
834 break;
835 case WK_POLYGON:
836 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_POLYGON"));
837 break;
838 case WK_MULTIPOINT:
839 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_MULTIPOINT"));
840 break;
841 case WK_MULTILINESTRING:
842 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_MULTILINESTRING"));
843 break;
844 case WK_MULTIPOLYGON:
845 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_MULTIPOLYGON"));
846 break;
847 case WK_GEOMETRYCOLLECTION:
848 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_GEOMETRYCOLLECTION"));
849 break;
850 default:
851 SET_STRING_ELT(class, 0, Rf_mkChar("sfc_GEOMETRY"));
852 break;
853 }
854 SET_STRING_ELT(class, 1, Rf_mkChar("sfc"));
855 Rf_setAttrib(writer->sfc, R_ClassSymbol, class);
856 UNPROTECT(1);
857
858 // attr(sfc, "classes") (only for all empty)
859 if (Rf_xlength(writer->sfc) == writer->n_empty) {
860 int n_geometry_types = 0;
861 for (int i = 0; i < 7; i++) {
862 if (1 & (writer->all_geometry_types >> i)) n_geometry_types++;
863 }
864
865 const char* type_names[] = {
866 "POINT", "LINESTRING", "POLYGON",
867 "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
868 "GEOMETRYCOLLECTION"
869 };
870
871 SEXP classes = PROTECT(Rf_allocVector(STRSXP, n_geometry_types));
872 int classes_index = 0;
873 for (int i = 0; i < 7; i++) {
874 if (1 & (writer->all_geometry_types >> i)) {
875 SET_STRING_ELT(classes, classes_index, Rf_mkChar(type_names[i]));
876 classes_index++;
877 }
878 }
879 Rf_setAttrib(writer->sfc, Rf_install("classes"), classes);
880 UNPROTECT(1);
881 }
882
883 return writer->sfc;
884 }
885
sfc_writer_deinitialize(void * handler_data)886 void sfc_writer_deinitialize(void* handler_data) {
887 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
888
889 if (writer->sfc != R_NilValue) {
890 R_ReleaseObject(writer->sfc);
891 writer->sfc = R_NilValue;
892 }
893
894 for (int i = 0; i < (SFC_WRITER_GEOM_LENGTH); i++) {
895 if (writer->geom[i] != R_NilValue) {
896 R_ReleaseObject(writer->geom[i]);
897 writer->geom[i] = R_NilValue;
898 }
899 }
900
901 if (writer->coord_seq != R_NilValue) {
902 R_ReleaseObject(writer->coord_seq);
903 writer->coord_seq = R_NilValue;
904 }
905 }
906
sfc_writer_finalize(void * handler_data)907 void sfc_writer_finalize(void* handler_data) {
908 sfc_writer_t* writer = (sfc_writer_t*) handler_data;
909 if (writer != NULL) {
910 free(writer);
911 }
912 }
913
wk_c_sfc_writer_new()914 SEXP wk_c_sfc_writer_new() {
915 wk_handler_t* handler = wk_handler_create();
916
917 handler->finalizer = &sfc_writer_finalize;
918 handler->vector_start = &sfc_writer_vector_start;
919 handler->feature_start = &sfc_writer_feature_start;
920 handler->null_feature = &sfc_writer_null_feature;
921 handler->geometry_start = &sfc_writer_geometry_start;
922 handler->ring_start = &sfc_writer_ring_start;
923 handler->coord = &sfc_writer_coord;
924 handler->ring_end = &sfc_writer_ring_end;
925 handler->geometry_end = &sfc_writer_geometry_end;
926 handler->vector_end = &sfc_writer_vector_end;
927 handler->deinitialize = &sfc_writer_deinitialize;
928
929 handler->handler_data = sfc_writer_new();
930 if (handler->handler_data == NULL) {
931 wk_handler_destroy(handler); // # nocov
932 Rf_error("Failed to alloc handler data"); // # nocov
933 }
934
935 SEXP xptr = wk_handler_create_xptr(handler, R_NilValue, R_NilValue);
936 return xptr;
937 }
938