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