1
+ #include " duckdb/common/vector_operations/generic_executor.hpp"
2
+ #include " duckdb/common/vector_operations/unary_executor.hpp"
3
+ #include " duckdb/parser/parsed_data/create_scalar_function_info.hpp"
4
+ #include " duckdb/planner/expression/bound_function_expression.hpp"
5
+ #include " duckdb/common/types/cast_helpers.hpp"
6
+
7
+ #include " spatial/common.hpp"
8
+ #include " spatial/core/functions/scalar.hpp"
9
+ #include " spatial/core/functions/common.hpp"
10
+ #include " spatial/core/geometry/geometry_factory.hpp"
11
+ #include " spatial/core/types.hpp"
12
+
13
+ #include " yyjson.h"
14
+
15
+ namespace spatial {
16
+
17
+ namespace core {
18
+
19
+ // ------------------------------------------------------------------------------
20
+ // Operations
21
+ // ------------------------------------------------------------------------------
22
+ static void VerticesToGeoJSON (const VertexVector &vertices, yyjson_mut_doc* doc, yyjson_mut_val* arr) {
23
+ // TODO: If the vertexvector is empty, do we null, add an empty array or a pair of NaN?
24
+ for (uint32_t i = 0 ; i < vertices.count ; i++) {
25
+ auto &vertex = vertices[i];
26
+ auto coord = yyjson_mut_arr (doc);
27
+ yyjson_mut_arr_add_real (doc, coord, vertex.x );
28
+ yyjson_mut_arr_add_real (doc, coord, vertex.y );
29
+ yyjson_mut_arr_append (arr, coord);
30
+ }
31
+ }
32
+
33
+ static void ToGeoJSON (const Point &point, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
34
+ yyjson_mut_obj_add_str (doc, obj, " type" , " Point" );
35
+
36
+ auto coords = yyjson_mut_arr (doc);
37
+ yyjson_mut_obj_add_val (doc, obj, " coordinates" , coords);
38
+
39
+ if (!point.IsEmpty ()) {
40
+ auto &vertex = point.GetVertex ();
41
+ yyjson_mut_arr_add_real (doc, coords, vertex.x );
42
+ yyjson_mut_arr_add_real (doc, coords, vertex.y );
43
+ }
44
+ }
45
+
46
+ static void ToGeoJSON (const LineString &line, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
47
+ yyjson_mut_obj_add_str (doc, obj, " type" , " LineString" );
48
+
49
+ auto coords = yyjson_mut_arr (doc);
50
+ yyjson_mut_obj_add_val (doc, obj, " coordinates" , coords);
51
+ VerticesToGeoJSON (line.points , doc, coords);
52
+ }
53
+
54
+ static void ToGeoJSON (const Polygon &poly, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
55
+ yyjson_mut_obj_add_str (doc, obj, " type" , " Polygon" );
56
+
57
+ auto coords = yyjson_mut_arr (doc);
58
+ yyjson_mut_obj_add_val (doc, obj, " coordinates" , coords);
59
+ for (uint32_t i = 0 ; i < poly.num_rings ; i++) {
60
+ auto &ring = poly.rings [i];
61
+ auto ring_coords = yyjson_mut_arr (doc);
62
+ VerticesToGeoJSON (ring, doc, ring_coords);
63
+ yyjson_mut_arr_append (coords, ring_coords);
64
+ }
65
+ }
66
+
67
+ static void ToGeoJSON (const MultiPoint &mpoint, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
68
+ yyjson_mut_obj_add_str (doc, obj, " type" , " MultiPoint" );
69
+
70
+ auto coords = yyjson_mut_arr (doc);
71
+ yyjson_mut_obj_add_val (doc, obj, " coordinates" , coords);
72
+ for (uint32_t i = 0 ; i < mpoint.Count (); i++) {
73
+ auto &point = mpoint.points [i];
74
+ VerticesToGeoJSON (point.data , doc, coords);
75
+ }
76
+ }
77
+
78
+ static void ToGeoJSON (const MultiLineString &mline, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
79
+ yyjson_mut_obj_add_str (doc, obj, " type" , " MultiLineString" );
80
+
81
+ auto coords = yyjson_mut_arr (doc);
82
+ yyjson_mut_obj_add_val (doc, obj, " coordinates" , coords);
83
+
84
+ for (uint32_t i = 0 ; i < mline.Count (); i++) {
85
+ auto &line = mline.linestrings [i];
86
+ auto line_coords = yyjson_mut_arr (doc);
87
+ VerticesToGeoJSON (line.points , doc, line_coords);
88
+ yyjson_mut_arr_append (coords, line_coords);
89
+ }
90
+ }
91
+
92
+ static void ToGeoJSON (const MultiPolygon &mpoly, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
93
+ yyjson_mut_obj_add_str (doc, obj, " type" , " MultiPolygon" );
94
+
95
+ auto coords = yyjson_mut_arr (doc);
96
+ yyjson_mut_obj_add_val (doc, obj, " coordinates" , coords);
97
+
98
+ for (uint32_t i = 0 ; i < mpoly.Count (); i++) {
99
+ auto &poly = mpoly.polygons [i];
100
+ auto poly_coords = yyjson_mut_arr (doc);
101
+ for (uint32_t j = 0 ; j < poly.num_rings ; j++) {
102
+ auto &ring = poly.rings [j];
103
+ auto ring_coords = yyjson_mut_arr (doc);
104
+ VerticesToGeoJSON (ring, doc, ring_coords);
105
+ yyjson_mut_arr_append (poly_coords, ring_coords);
106
+ }
107
+ yyjson_mut_arr_append (coords, poly_coords);
108
+ }
109
+ }
110
+
111
+ static void ToGeoJSON (const Geometry &geom, yyjson_mut_doc* doc, yyjson_mut_val* obj);
112
+ static void ToGeoJSON (const GeometryCollection &collection, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
113
+ yyjson_mut_obj_add_str (doc, obj, " type" , " GeometryCollection" );
114
+ auto arr = yyjson_mut_arr (doc);
115
+ yyjson_mut_obj_add_val (doc, obj, " geometries" , arr);
116
+
117
+ for (idx_t i = 0 ; i < collection.num_geometries ; i++) {
118
+ auto &geom = collection.geometries [i];
119
+ auto geom_obj = yyjson_mut_obj (doc);
120
+ ToGeoJSON (geom, doc, geom_obj);
121
+ yyjson_mut_arr_append (arr, geom_obj);
122
+ }
123
+ }
124
+
125
+ static void ToGeoJSON (const Geometry &geom, yyjson_mut_doc* doc, yyjson_mut_val* obj) {
126
+ switch (geom.Type ()) {
127
+ case GeometryType::POINT: ToGeoJSON (geom.GetPoint (), doc, obj); break ;
128
+ case GeometryType::LINESTRING: ToGeoJSON (geom.GetLineString (), doc, obj); break ;
129
+ case GeometryType::POLYGON: ToGeoJSON (geom.GetPolygon (), doc, obj); break ;
130
+ case GeometryType::MULTIPOINT: ToGeoJSON (geom.GetMultiPoint (), doc, obj); break ;
131
+ case GeometryType::MULTILINESTRING: ToGeoJSON (geom.GetMultiLineString (), doc, obj); break ;
132
+ case GeometryType::MULTIPOLYGON: ToGeoJSON (geom.GetMultiPolygon (), doc, obj); break ;
133
+ case GeometryType::GEOMETRYCOLLECTION: ToGeoJSON (geom.GetGeometryCollection (), doc, obj); break ;
134
+ default : {
135
+ throw NotImplementedException (" Geometry type not supported" );
136
+ }
137
+ }
138
+ }
139
+
140
+ // ------------------------------------------------------------------------------
141
+ // GEOMETRY -> GEOJSON Fragment
142
+ // ------------------------------------------------------------------------------
143
+ class JSONAllocator {
144
+ // Stolen from the JSON extension :)
145
+ public:
146
+ explicit JSONAllocator (ArenaAllocator &allocator)
147
+ : allocator(allocator), yyjson_allocator({Allocate, Reallocate, Free, &allocator}) {
148
+ }
149
+
150
+ inline yyjson_alc *GetYYJSONAllocator () {
151
+ return &yyjson_allocator;
152
+ }
153
+
154
+ void Reset () {
155
+ allocator.Reset ();
156
+ }
157
+
158
+ private:
159
+ static inline void *Allocate (void *ctx, size_t size) {
160
+ auto alloc = (ArenaAllocator *)ctx;
161
+ return alloc->AllocateAligned (size);
162
+ }
163
+
164
+ static inline void *Reallocate (void *ctx, void *ptr, size_t old_size, size_t size) {
165
+ auto alloc = (ArenaAllocator *)ctx;
166
+ return alloc->ReallocateAligned ((data_ptr_t )ptr, old_size, size);
167
+ }
168
+
169
+ static inline void Free (void *ctx, void *ptr) {
170
+ // NOP because ArenaAllocator can't free
171
+ }
172
+
173
+ private:
174
+ ArenaAllocator &allocator;
175
+ yyjson_alc yyjson_allocator;
176
+ };
177
+
178
+ static void GeometryToGeoJSONFragmentFunction (DataChunk &args, ExpressionState &state, Vector &result) {
179
+ D_ASSERT (args.data .size () == 1 );
180
+ auto &input = args.data [0 ];
181
+ auto count = args.size ();
182
+
183
+ auto &lstate = GeometryFunctionLocalState::ResetAndGet (state);
184
+
185
+ JSONAllocator json_allocator (lstate.factory .allocator );
186
+
187
+ UnaryExecutor::Execute<string_t , string_t >(input, result, count, [&](string_t input) {
188
+ auto geometry = lstate.factory .Deserialize (input);
189
+
190
+ auto doc = yyjson_mut_doc_new (json_allocator.GetYYJSONAllocator ());
191
+ auto obj = yyjson_mut_obj (doc);
192
+ yyjson_mut_doc_set_root (doc, obj);
193
+
194
+ ToGeoJSON (geometry, doc, obj);
195
+
196
+ size_t json_size = 0 ;
197
+ // TODO: YYJSON_WRITE_PRETTY
198
+ auto json_data = yyjson_mut_write (doc, 0 , &json_size);
199
+ auto json_str = StringVector::AddString (result, json_data, json_size);
200
+ return json_str;
201
+ });
202
+ }
203
+
204
+ // ------------------------------------------------------------------------------
205
+ // Register functions
206
+ // ------------------------------------------------------------------------------
207
+ void CoreScalarFunctions::RegisterStAsGeoJSON (ClientContext &context) {
208
+ auto &catalog = Catalog::GetSystemCatalog (context);
209
+
210
+ ScalarFunctionSet geojson (" ST_AsGeoJSON" );
211
+ geojson.AddFunction (ScalarFunction ({GeoTypes::GEOMETRY ()}, LogicalType::VARCHAR, GeometryToGeoJSONFragmentFunction,
212
+ nullptr , nullptr , nullptr , GeometryFunctionLocalState::Init));
213
+
214
+ CreateScalarFunctionInfo info (geojson);
215
+ info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
216
+ catalog.CreateFunction (context, &info);
217
+ }
218
+
219
+ } // namespace core
220
+
221
+ } // namespace spatial
0 commit comments