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