4
4
5
5
#include "geoarrow/geoarrow.h"
6
6
7
+ // From sf/src/wkb.cpp to apply the precision from attr(sfc, "precision")
8
+ static double make_precise (double d , double precision ) {
9
+ if (precision == 0.0 ) return d ;
10
+ if (precision < 0.0 ) { // round to float, 4-byte precision
11
+ float f = d ;
12
+ return (double )f ;
13
+ }
14
+ return round (d * precision ) / precision ;
15
+ }
16
+
17
+ static void make_buffer_precise (double * ptr , int size_elements , double precision ) {
18
+ if (precision == 0 ) {
19
+ return ;
20
+ }
21
+
22
+ for (int i = 0 ; i < size_elements ; i ++ ) {
23
+ ptr [i ] = make_precise (ptr [i ], precision );
24
+ }
25
+ }
26
+
7
27
static inline int builder_append_sfg (SEXP item , struct GeoArrowBuilder * builder ,
8
- int level , int32_t * current_offsets ) {
28
+ int level , int32_t * current_offsets ,
29
+ double precision ) {
9
30
switch (TYPEOF (item )) {
10
31
// Level of nesting
11
32
case VECSXP : {
@@ -18,7 +39,8 @@ static inline int builder_append_sfg(SEXP item, struct GeoArrowBuilder* builder,
18
39
GEOARROW_RETURN_NOT_OK (
19
40
GeoArrowBuilderOffsetAppend (builder , level , current_offsets + level , 1 ));
20
41
for (int32_t i = 0 ; i < n ; i ++ ) {
21
- builder_append_sfg (VECTOR_ELT (item , i ), builder , level + 1 , current_offsets );
42
+ builder_append_sfg (VECTOR_ELT (item , i ), builder , level + 1 , current_offsets ,
43
+ precision );
22
44
}
23
45
break ;
24
46
}
@@ -47,8 +69,17 @@ static inline int builder_append_sfg(SEXP item, struct GeoArrowBuilder* builder,
47
69
break ;
48
70
}
49
71
72
+ // Copy the buffer
50
73
GEOARROW_RETURN_NOT_OK (
51
74
GeoArrowBuilderAppendBuffer (builder , first_coord_buffer + i , view ));
75
+
76
+ // Apply precision from sfc
77
+ double * ordinates =
78
+ (double * )(builder -> view .buffers [first_coord_buffer + i ].data .as_uint8 +
79
+ builder -> view .buffers [first_coord_buffer + i ].size_bytes );
80
+ ordinates -= n_col ;
81
+ make_buffer_precise (ordinates , n_col , precision );
82
+
52
83
view .data += view .size_bytes ;
53
84
}
54
85
@@ -73,7 +104,8 @@ static inline int builder_append_sfg(SEXP item, struct GeoArrowBuilder* builder,
73
104
return GEOARROW_OK ;
74
105
}
75
106
76
- static inline int builder_append_sfc_point (SEXP sfc , struct GeoArrowBuilder * builder ) {
107
+ static inline int builder_append_sfc_point (SEXP sfc , struct GeoArrowBuilder * builder ,
108
+ double precision ) {
77
109
R_xlen_t n = Rf_xlength (sfc );
78
110
79
111
for (int i = 0 ; i < builder -> view .coords .n_values ; i ++ ) {
@@ -93,7 +125,7 @@ static inline int builder_append_sfc_point(SEXP sfc, struct GeoArrowBuilder* bui
93
125
break ;
94
126
}
95
127
96
- builder -> view .coords .values [j ][i ] = item [j ];
128
+ builder -> view .coords .values [j ][i ] = make_precise ( item [j ], precision ) ;
97
129
}
98
130
99
131
// Fill dimensions in builder but not in sfc with nan
@@ -107,9 +139,10 @@ static inline int builder_append_sfc_point(SEXP sfc, struct GeoArrowBuilder* bui
107
139
return GEOARROW_OK ;
108
140
}
109
141
110
- static int builder_append_sfc (SEXP sfc , struct GeoArrowBuilder * builder ) {
142
+ static int builder_append_sfc (SEXP sfc , struct GeoArrowBuilder * builder ,
143
+ double precision ) {
111
144
if (Rf_inherits (sfc , "sfc_POINT" )) {
112
- return builder_append_sfc_point (sfc , builder );
145
+ return builder_append_sfc_point (sfc , builder , precision );
113
146
}
114
147
115
148
R_xlen_t n = Rf_xlength (sfc );
@@ -131,7 +164,8 @@ static int builder_append_sfc(SEXP sfc, struct GeoArrowBuilder* builder) {
131
164
// Append elements
132
165
for (R_xlen_t i = 0 ; i < n ; i ++ ) {
133
166
SEXP item = VECTOR_ELT (sfc , i );
134
- GEOARROW_RETURN_NOT_OK (builder_append_sfg (item , builder , 0 , current_offsets ));
167
+ GEOARROW_RETURN_NOT_OK (
168
+ builder_append_sfg (item , builder , 0 , current_offsets , precision ));
135
169
}
136
170
137
171
builder -> view .length = n ;
@@ -164,6 +198,24 @@ SEXP geoarrow_c_as_nanoarrow_array_sfc(SEXP sfc, SEXP schema_xptr, SEXP array_xp
164
198
SEXP builder_xptr = PROTECT (R_MakeExternalPtr (builder , R_NilValue , R_NilValue ));
165
199
R_RegisterCFinalizer (builder_xptr , & finalize_builder_xptr );
166
200
201
+ // Get the precision from the sf object so we can apply it if needed
202
+ double precision = 0 ;
203
+ SEXP precision_sym = PROTECT (Rf_install ("precision" ));
204
+ SEXP precision_sexp = Rf_getAttrib (sfc , precision_sym );
205
+ UNPROTECT (1 );
206
+ if (precision_sexp != R_NilValue && Rf_length (precision_sexp ) == 1 ) {
207
+ switch (TYPEOF (precision_sexp )) {
208
+ case INTSXP :
209
+ precision = (double )INTEGER (precision_sexp )[0 ];
210
+ break ;
211
+ case REALSXP :
212
+ precision = REAL (precision_sexp )[0 ];
213
+ break ;
214
+ default :
215
+ break ;
216
+ }
217
+ }
218
+
167
219
struct GeoArrowError error ;
168
220
error .message [0 ] = '\0' ;
169
221
@@ -174,7 +226,7 @@ SEXP geoarrow_c_as_nanoarrow_array_sfc(SEXP sfc, SEXP schema_xptr, SEXP array_xp
174
226
}
175
227
176
228
// Build the offset buffers from the various layers of nesting
177
- result = builder_append_sfc (sfc , builder );
229
+ result = builder_append_sfc (sfc , builder , precision );
178
230
if (result != GEOARROW_OK ) {
179
231
Rf_error ("builder_append_sfc() failed to allocate memory for offset buffers" );
180
232
}
0 commit comments