PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeom_dump.c
Go to the documentation of this file.
1/**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * PostGIS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright 2001-2009 Refractions Research Inc.
22 *
23 **********************************************************************/
24
25
26#include <math.h>
27#include <float.h>
28#include <string.h>
29#include <stdio.h>
30#include <assert.h>
31
32#include "postgres.h"
33#include "fmgr.h"
34#include "utils/elog.h"
35#include "utils/array.h"
36#include "utils/geo_decls.h"
37#include "funcapi.h"
38
39#include "../postgis_config.h"
40#include "liblwgeom.h"
41#include "lwgeom_pg.h"
42
43
44Datum LWGEOM_dump(PG_FUNCTION_ARGS);
45Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS);
46Datum ST_Subdivide(PG_FUNCTION_ARGS);
47
48typedef struct GEOMDUMPNODE_T
49{
50 uint32_t idx;
52}
54
55#define MAXDEPTH 32
63
64#define PUSH(x,y) ((x)->stack[(x)->stacklen++]=(y))
65#define LAST(x) ((x)->stack[(x)->stacklen-1])
66#define POP(x) (--((x)->stacklen))
67
68
70Datum LWGEOM_dump(PG_FUNCTION_ARGS)
71{
72 GSERIALIZED *pglwgeom;
73 LWCOLLECTION *lwcoll;
74 LWGEOM *lwgeom;
75 FuncCallContext *funcctx;
76 GEOMDUMPSTATE *state;
77 GEOMDUMPNODE *node;
78 TupleDesc tupdesc;
79 HeapTuple tuple;
80 AttInMetadata *attinmeta;
81 MemoryContext oldcontext, newcontext;
82 Datum result;
83 char address[256];
84 char *ptr;
85 int i;
86 char *values[2];
87
88 if (SRF_IS_FIRSTCALL())
89 {
90 funcctx = SRF_FIRSTCALL_INIT();
91 newcontext = funcctx->multi_call_memory_ctx;
92
93 oldcontext = MemoryContextSwitchTo(newcontext);
94
95 pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
96 lwgeom = lwgeom_from_gserialized(pglwgeom);
97
98 /* Create function state */
99 state = lwalloc(sizeof(GEOMDUMPSTATE));
100 state->root = lwgeom;
101 state->stacklen=0;
102
103 if ( lwgeom_is_collection(lwgeom) )
104 {
105 /*
106 * Push a GEOMDUMPNODE on the state stack
107 */
108 node = lwalloc(sizeof(GEOMDUMPNODE));
109 node->idx=0;
110 node->geom = lwgeom;
111 PUSH(state, node);
112 }
113
114 funcctx->user_fctx = state;
115
116 /*
117 * Build a tuple description for an
118 * geometry_dump tuple
119 */
120 get_call_result_type(fcinfo, 0, &tupdesc);
121 BlessTupleDesc(tupdesc);
122
123 /*
124 * generate attribute metadata needed later to produce
125 * tuples from raw C strings
126 */
127 attinmeta = TupleDescGetAttInMetadata(tupdesc);
128 funcctx->attinmeta = attinmeta;
129
130 MemoryContextSwitchTo(oldcontext);
131 }
132
133 /* stuff done on every call of the function */
134 funcctx = SRF_PERCALL_SETUP();
135 newcontext = funcctx->multi_call_memory_ctx;
136
137 /* get state */
138 state = funcctx->user_fctx;
139
140 /* Handled simple geometries */
141 if ( ! state->root ) SRF_RETURN_DONE(funcctx);
142 /* Return nothing for empties */
143 if ( lwgeom_is_empty(state->root) ) SRF_RETURN_DONE(funcctx);
144 if ( ! lwgeom_is_collection(state->root) )
145 {
146 values[0] = "{}";
147 values[1] = lwgeom_to_hexwkb(state->root, WKB_EXTENDED, 0);
148 tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
149 result = HeapTupleGetDatum(tuple);
150
151 state->root = NULL;
152 SRF_RETURN_NEXT(funcctx, result);
153 }
154
155 while (1)
156 {
157 node = LAST(state);
158 lwcoll = (LWCOLLECTION*)node->geom;
159
160 if ( node->idx < lwcoll->ngeoms )
161 {
162 lwgeom = lwcoll->geoms[node->idx];
163 if ( ! lwgeom_is_collection(lwgeom) )
164 {
165 /* write address of current geom */
166 ptr=address;
167 *ptr++='{';
168 for (i=0; i<state->stacklen; i++)
169 {
170 if ( i ) ptr += sprintf(ptr, ",");
171 ptr += sprintf(ptr, "%d", state->stack[i]->idx+1);
172 }
173 *ptr++='}';
174 *ptr='\0';
175
176 break;
177 }
178
179 /*
180 * It's a collection, increment index
181 * of current node, push a new one on the
182 * stack
183 */
184
185 oldcontext = MemoryContextSwitchTo(newcontext);
186
187 node = lwalloc(sizeof(GEOMDUMPNODE));
188 node->idx=0;
189 node->geom = lwgeom;
190 PUSH(state, node);
191
192 MemoryContextSwitchTo(oldcontext);
193
194 continue;
195 }
196
197 if ( ! POP(state) ) SRF_RETURN_DONE(funcctx);
198 LAST(state)->idx++;
199 }
200
201 lwgeom->srid = state->root->srid;
202
203 values[0] = address;
204 values[1] = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, 0);
205 tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
206 result = TupleGetDatum(funcctx->slot, tuple);
207 node->idx++;
208 SRF_RETURN_NEXT(funcctx, result);
209}
210
212{
213 uint32_t ringnum;
215};
216
218Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
219{
220 GSERIALIZED *pglwgeom;
221 LWGEOM *lwgeom;
222 FuncCallContext *funcctx;
223 struct POLYDUMPSTATE *state;
224 TupleDesc tupdesc;
225 HeapTuple tuple;
226 AttInMetadata *attinmeta;
227 MemoryContext oldcontext, newcontext;
228 Datum result;
229 char address[256];
230 char *values[2];
231
232 if (SRF_IS_FIRSTCALL())
233 {
234 funcctx = SRF_FIRSTCALL_INIT();
235 newcontext = funcctx->multi_call_memory_ctx;
236
237 oldcontext = MemoryContextSwitchTo(newcontext);
238
239 pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
240 if ( gserialized_get_type(pglwgeom) != POLYGONTYPE )
241 {
242 elog(ERROR, "Input is not a polygon");
243 }
244
245 lwgeom = lwgeom_from_gserialized(pglwgeom);
246
247 /* Create function state */
248 state = lwalloc(sizeof(struct POLYDUMPSTATE));
249 state->poly = lwgeom_as_lwpoly(lwgeom);
250 assert (state->poly);
251 state->ringnum=0;
252
253 funcctx->user_fctx = state;
254
255 /*
256 * Build a tuple description for an
257 * geometry_dump tuple
258 */
259 get_call_result_type(fcinfo, 0, &tupdesc);
260 BlessTupleDesc(tupdesc);
261
262 /*
263 * generate attribute metadata needed later to produce
264 * tuples from raw C strings
265 */
266 attinmeta = TupleDescGetAttInMetadata(tupdesc);
267 funcctx->attinmeta = attinmeta;
268
269 MemoryContextSwitchTo(oldcontext);
270 }
271
272 /* stuff done on every call of the function */
273 funcctx = SRF_PERCALL_SETUP();
274 newcontext = funcctx->multi_call_memory_ctx;
275
276 /* get state */
277 state = funcctx->user_fctx;
278
279 /* Loop trough polygon rings */
280 while (state->ringnum < state->poly->nrings )
281 {
282 LWPOLY* poly = state->poly;
283 POINTARRAY *ring;
284 LWGEOM* ringgeom;
285
286 /* Switch to an appropriate memory context for POINTARRAY
287 * cloning and hexwkb allocation */
288 oldcontext = MemoryContextSwitchTo(newcontext);
289
290 /* We need a copy of input ring here */
291 ring = ptarray_clone_deep(poly->rings[state->ringnum]);
292
293 /* Construct another polygon with shell only */
294 ringgeom = (LWGEOM*)lwpoly_construct(
295 poly->srid,
296 NULL, /* TODO: could use input bounding box here */
297 1, /* one ring */
298 &ring);
299
300 /* Write path as ``{ <ringnum> }'' */
301 sprintf(address, "{%d}", state->ringnum);
302
303 values[0] = address;
304 values[1] = lwgeom_to_hexwkb(ringgeom, WKB_EXTENDED, 0);
305
306 MemoryContextSwitchTo(oldcontext);
307
308 tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
309 result = HeapTupleGetDatum(tuple);
310 ++state->ringnum;
311 SRF_RETURN_NEXT(funcctx, result);
312 }
313
314 SRF_RETURN_DONE(funcctx);
315
316}
317
318
324
325/*
326* Break an object up into smaller objects of no more than N vertices
327*/
329Datum ST_Subdivide(PG_FUNCTION_ARGS)
330{
331 typedef struct
332 {
333 int nextgeom;
334 int numgeoms;
335 LWCOLLECTION *col;
336 } collection_fctx;
337
338 FuncCallContext *funcctx;
339 collection_fctx *fctx;
340 MemoryContext oldcontext;
341
342 /* stuff done only on the first call of the function */
343 if (SRF_IS_FIRSTCALL())
344 {
345 GSERIALIZED *gser;
346 LWGEOM *geom;
347 LWCOLLECTION *col;
348 /* default to maxvertices < page size */
349 int maxvertices = 128;
350
351 /* create a function context for cross-call persistence */
352 funcctx = SRF_FIRSTCALL_INIT();
353
354 /*
355 * switch to memory context appropriate for multiple function calls
356 */
357 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
358
359 /*
360 * Get the geometry value
361 */
362 gser = PG_GETARG_GSERIALIZED_P(0);
363 geom = lwgeom_from_gserialized(gser);
364
365 /*
366 * Get the max vertices value
367 */
368 if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
369 maxvertices = PG_GETARG_INT32(1);
370
371 /*
372 * Compute the subdivision of the geometry
373 */
374 col = lwgeom_subdivide(geom, maxvertices);
375
376 if ( ! col )
377 SRF_RETURN_DONE(funcctx);
378
379 /* allocate memory for user context */
380 fctx = (collection_fctx *) palloc(sizeof(collection_fctx));
381
382 /* initialize state */
383 fctx->nextgeom = 0;
384 fctx->numgeoms = col->ngeoms;
385 fctx->col = col;
386
387 /* save user context, switch back to function context */
388 funcctx->user_fctx = fctx;
389 MemoryContextSwitchTo(oldcontext);
390 }
391
392 /* stuff done on every call of the function */
393 funcctx = SRF_PERCALL_SETUP();
394 fctx = funcctx->user_fctx;
395
396 if (fctx->nextgeom < fctx->numgeoms)
397 {
398 GSERIALIZED *gpart = geometry_serialize(fctx->col->geoms[fctx->nextgeom]);
399 fctx->nextgeom++;
400 SRF_RETURN_NEXT(funcctx, PointerGetDatum(gpart));
401 }
402 else
403 {
404 /* do when there is no more left */
405 SRF_RETURN_DONE(funcctx);
406 }
407}
408
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition gserialized.c:89
char * lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out)
Definition lwout_wkb.c:874
void * lwalloc(size_t size)
Definition lwutil.c:227
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition lwgeom.c:197
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition lwgeom.c:1079
#define POLYGONTYPE
Definition liblwgeom.h:118
#define WKB_EXTENDED
Definition liblwgeom.h:2123
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition lwpoly.c:43
LWCOLLECTION * lwgeom_subdivide(const LWGEOM *geom, uint32_t maxvertices)
Definition lwgeom.c:2439
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition ptarray.c:634
This library is the generic geometry handling section of PostGIS.
#define MAXDEPTH
Definition lwgeom_dump.c:55
Datum ST_Subdivide(PG_FUNCTION_ARGS)
struct GEOMDUMPNODE_T GEOMDUMPNODE
#define PUSH(x, y)
Definition lwgeom_dump.c:64
#define LAST(x)
Definition lwgeom_dump.c:65
Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(LWGEOM_dump)
Datum LWGEOM_dump(PG_FUNCTION_ARGS)
Definition lwgeom_dump.c:70
#define POP(x)
Definition lwgeom_dump.c:66
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:193
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
LWGEOM * geom
Definition lwgeom_dump.c:51
LWGEOM * root
Definition lwgeom_dump.c:60
GEOMDUMPNODE * stack[MAXDEPTH]
Definition lwgeom_dump.c:59
uint32_t ngeoms
Definition liblwgeom.h:566
LWGEOM ** geoms
Definition liblwgeom.h:561
int32_t srid
Definition liblwgeom.h:446
POINTARRAY ** rings
Definition liblwgeom.h:505
uint32_t nrings
Definition liblwgeom.h:510
int32_t srid
Definition liblwgeom.h:506
uint32_t ringnum
LWPOLY * poly