PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeom_dumppoints.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^
22 *
23 **********************************************************************/
24
25#include "postgres.h"
26#include "fmgr.h"
27#include "utils/elog.h"
28#include "utils/array.h"
29#include "utils/geo_decls.h"
30#include "utils/lsyscache.h"
31#include "catalog/pg_type.h"
32#include "funcapi.h"
33
34#include "../postgis_config.h"
35#include "lwgeom_pg.h"
36
37#include "access/htup_details.h"
38
39
40#include "liblwgeom.h"
41
42/* ST_DumpPoints for postgis.
43 * By Nathan Wagner, copyright disclaimed,
44 * this entire file is in the public domain
45 */
46
47Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS);
48Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS);
49
50struct dumpnode {
52 uint32_t idx; /* which member geom we're working on */
53} ;
54
55/* 32 is the max depth for st_dump, so it seems reasonable
56 * to use the same here
57 */
58#define MAXDEPTH 32
59struct dumpstate {
61 int stacklen; /* collections/geoms on stack */
62 int pathlen; /* polygon rings and such need extra path info */
64 Datum path[34]; /* two more than max depth, for ring and point */
65
66 /* used to cache the type attributes for integer arrays */
67 int16 typlen;
68 bool byval;
69 char align;
70
71 uint32_t ring; /* ring of top polygon */
72 uint32_t pt; /* point of top geom or current ring */
73};
74
76Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS) {
77 FuncCallContext *funcctx;
78 MemoryContext oldcontext, newcontext;
79
80 GSERIALIZED *pglwgeom;
81 LWCOLLECTION *lwcoll;
82 LWGEOM *lwgeom;
83 struct dumpstate *state;
84 struct dumpnode *node;
85
86 HeapTuple tuple;
87 Datum pathpt[2]; /* used to construct the composite return value */
88 bool isnull[2] = {0,0}; /* needed to say neither value is null */
89 Datum result; /* the actual composite return value */
90
91 if (SRF_IS_FIRSTCALL()) {
92 funcctx = SRF_FIRSTCALL_INIT();
93
94 newcontext = funcctx->multi_call_memory_ctx;
95 oldcontext = MemoryContextSwitchTo(newcontext);
96
97 /* get a local copy of what we're doing a dump points on */
98 pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
99 lwgeom = lwgeom_from_gserialized(pglwgeom);
100
101 /* return early if nothing to do */
102 if (!lwgeom || lwgeom_is_empty(lwgeom)) {
103 MemoryContextSwitchTo(oldcontext);
104 funcctx = SRF_PERCALL_SETUP();
105 SRF_RETURN_DONE(funcctx);
106 }
107
108 /* Create function state */
109 state = lwalloc(sizeof *state);
110 state->root = lwgeom;
111 state->stacklen = 0;
112 state->pathlen = 0;
113 state->pt = 0;
114 state->ring = 0;
115
116 funcctx->user_fctx = state;
117
118 /*
119 * Push a struct dumpnode on the state stack
120 */
121
122 state->stack[0].idx = 0;
123 state->stack[0].geom = lwgeom;
124 state->stacklen++;
125
126 /*
127 * get tuple description for return type
128 */
129 if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE) {
130 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
131 errmsg("set-valued function called in context that cannot accept a set")));
132 }
133
134 BlessTupleDesc(funcctx->tuple_desc);
135
136 /* get and cache data for constructing int4 arrays */
137 get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
138
139 MemoryContextSwitchTo(oldcontext);
140 }
141
142 /* stuff done on every call of the function */
143 funcctx = SRF_PERCALL_SETUP();
144 newcontext = funcctx->multi_call_memory_ctx;
145
146 /* get state */
147 state = funcctx->user_fctx;
148
149 while (1) {
150 node = &state->stack[state->stacklen-1];
151 lwgeom = node->geom;
152
153 /* need to return a point from this geometry */
154 if (!lwgeom_is_collection(lwgeom) ) {
155 /* either return a point, or pop the stack */
156 /* TODO use a union? would save a tiny amount of stack space.
157 * probably not worth the bother
158 */
159 LWLINE *line;
160 LWCIRCSTRING *circ;
161 LWPOLY *poly;
162 LWTRIANGLE *tri;
163 LWPOINT *lwpoint = NULL;
164 POINT4D pt;
165
166 if ( !lwgeom_is_empty(lwgeom) ) {
167 /*
168 * net result of switch should be to set lwpoint to the
169 * next point to return, or leave at NULL if there
170 * are no more points in the geometry
171 */
172 switch(lwgeom->type) {
173 case TRIANGLETYPE:
174 tri = lwgeom_as_lwtriangle(lwgeom);
175 if (state->pt == 0) {
176 state->path[state->pathlen++] = Int32GetDatum(state->ring+1);
177 }
178 if (state->pt <= 3) {
179 getPoint4d_p(tri->points, state->pt, &pt);
180 lwpoint = lwpoint_make(tri->srid,
181 lwgeom_has_z(lwgeom),
182 lwgeom_has_m(lwgeom),
183 &pt);
184 }
185 if (state->pt > 3) {
186 state->pathlen--;
187 }
188 break;
189 case POLYGONTYPE:
190 poly = lwgeom_as_lwpoly(lwgeom);
191 if (state->pt == poly->rings[state->ring]->npoints) {
192 state->pt = 0;
193 state->ring++;
194 state->pathlen--;
195 }
196 if (state->pt == 0 && state->ring < poly->nrings) {
197 /* handle new ring */
198 state->path[state->pathlen] = Int32GetDatum(state->ring+1);
199 state->pathlen++;
200 }
201 if (state->ring == poly->nrings) {
202 } else {
203 /* TODO should be able to directly get the point
204 * into the point array of a fixed lwpoint
205 */
206 /* can't get the point directly from the ptarray because
207 * it might be aligned wrong, so at least one memcpy
208 * seems unavoidable
209 * It might be possible to pass it directly to gserialized
210 * depending how that works, it might effectively be gserialized
211 * though a brief look at the code indicates not
212 */
213 getPoint4d_p(poly->rings[state->ring], state->pt, &pt);
214 lwpoint = lwpoint_make(poly->srid,
215 lwgeom_has_z(lwgeom),
216 lwgeom_has_m(lwgeom),
217 &pt);
218 }
219 break;
220 case POINTTYPE:
221 if (state->pt == 0) lwpoint = lwgeom_as_lwpoint(lwgeom);
222 break;
223 case LINETYPE:
224 line = lwgeom_as_lwline(lwgeom);
225 if (line->points && state->pt <= line->points->npoints) {
226 lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, state->pt);
227 }
228 break;
229 case CIRCSTRINGTYPE:
230 circ = lwgeom_as_lwcircstring(lwgeom);
231 if (circ->points && state->pt <= circ->points->npoints) {
232 lwpoint = lwcircstring_get_lwpoint((LWCIRCSTRING*)lwgeom, state->pt);
233 }
234 break;
235 default:
236 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
237 errmsg("Invalid Geometry type %d passed to ST_DumpPoints()", lwgeom->type)));
238 }
239 }
240
241 /*
242 * At this point, lwpoint is either NULL, in which case
243 * we need to pop the geometry stack and get the next
244 * geometry, if any, or lwpoint is set and we construct
245 * a record type with the integer array of geometry
246 * indexes and the point number, and the actual point
247 * geometry itself
248 */
249
250 if (!lwpoint) {
251 /* no point, so pop the geom and look for more */
252 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
253 state->pathlen--;
254 continue;
255 } else {
256 /* write address of current geom/pt */
257 state->pt++;
258
259 state->path[state->pathlen] = Int32GetDatum(state->pt);
260 pathpt[0] = PointerGetDatum(construct_array(state->path, state->pathlen+1,
261 INT4OID, state->typlen, state->byval, state->align));
262
263 pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM*)lwpoint));
264
265 tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
266 result = HeapTupleGetDatum(tuple);
267 SRF_RETURN_NEXT(funcctx, result);
268 }
269 }
270
271 lwcoll = (LWCOLLECTION*)node->geom;
272
273 /* if a collection and we have more geoms */
274 if (node->idx < lwcoll->ngeoms) {
275
276 if(state->stacklen >= MAXDEPTH)
277 elog(ERROR, "Unable to dump overly nested collection");
278
279 /* push the next geom on the path and the stack */
280 lwgeom = lwcoll->geoms[node->idx++];
281 state->path[state->pathlen++] = Int32GetDatum(node->idx);
282
283 node = &state->stack[state->stacklen++];
284 node->idx = 0;
285 node->geom = lwgeom;
286
287 state->pt = 0;
288 state->ring = 0;
289
290 /* loop back to beginning, which will then check whatever node we just pushed */
291 continue;
292 }
293
294 /* no more geometries in the current collection */
295 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
296 state->pathlen--;
297 }
298}
299
301Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
302{
303 FuncCallContext *funcctx;
304 MemoryContext oldcontext, newcontext;
305
306 GSERIALIZED *pglwgeom;
307 LWCOLLECTION *lwcoll;
308 LWGEOM *lwgeom;
309 struct dumpstate *state;
310 struct dumpnode *node;
311
312 HeapTuple tuple;
313 Datum pathpt[2]; /* used to construct the composite return value */
314 bool isnull[2] = {0, 0}; /* needed to say neither value is null */
315 Datum result; /* the actual composite return value */
316
317 if (SRF_IS_FIRSTCALL())
318 {
319 funcctx = SRF_FIRSTCALL_INIT();
320
321 newcontext = funcctx->multi_call_memory_ctx;
322 oldcontext = MemoryContextSwitchTo(newcontext);
323
324 pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
325 lwgeom = lwgeom_from_gserialized(pglwgeom);
326
327 /* return early if nothing to do */
328 if (!lwgeom || lwgeom_is_empty(lwgeom))
329 {
330 MemoryContextSwitchTo(oldcontext);
331 funcctx = SRF_PERCALL_SETUP();
332 SRF_RETURN_DONE(funcctx);
333 }
334
335 /* Create function state */
336 state = lwalloc(sizeof *state);
337 state->root = lwgeom;
338 state->stacklen = 0;
339 state->pathlen = 0;
340 state->pt = 0;
341 state->ring = 0;
342
343 funcctx->user_fctx = state;
344
345 /*
346 * Push a struct dumpnode on the state stack
347 */
348 state->stack[0].idx = 0;
349 state->stack[0].geom = lwgeom;
350 state->stacklen++;
351
352 /*
353 * get tuple description for return type
354 */
355 if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
356 {
357 ereport(ERROR,
358 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
359 errmsg("set-valued function called in context that cannot accept a set")));
360 }
361
362 BlessTupleDesc(funcctx->tuple_desc);
363
364 /* get and cache data for constructing int4 arrays */
365 get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
366
367 MemoryContextSwitchTo(oldcontext);
368 }
369
370 /* stuff done on every call of the function */
371 funcctx = SRF_PERCALL_SETUP();
372 newcontext = funcctx->multi_call_memory_ctx;
373
374 /* get state */
375 state = funcctx->user_fctx;
376
377 while (1)
378 {
379 POINTARRAY *points = NULL;
380 LWLINE *line;
381 LWTRIANGLE *tri;
382 LWPOLY *poly;
383 POINT4D pts_out[3];
384 POINTARRAY *segment_pa;
385 LWGEOM *segment;
386 uint8_t stride = 1;
387
388 node = &state->stack[state->stacklen - 1];
389 lwgeom = node->geom;
390
391 if (lwgeom->type == CIRCSTRINGTYPE)
392 stride =2 ;
393
394 if ( !lwgeom_is_empty(lwgeom) && (lwgeom->type == LINETYPE || lwgeom->type == TRIANGLETYPE || lwgeom->type == POLYGONTYPE || lwgeom->type == CIRCSTRINGTYPE) )
395 {
396 if (lwgeom->type == LINETYPE || lwgeom->type == CIRCSTRINGTYPE)
397 {
398 line = (LWLINE *)lwgeom;
399
400 if (state->pt < (line->points->npoints - stride))
401 {
402 points = line->points;
403 }
404 }
405 else if (lwgeom->type == TRIANGLETYPE)
406 {
407 tri = (LWTRIANGLE *)lwgeom;
408
409 if (state->pt == 0)
410 {
411 state->path[state->pathlen++] = Int32GetDatum(state->ring + 1);
412 }
413
414 if (state->pt < 3)
415 {
416 points = tri->points;
417 }
418 else
419 {
420 state->pathlen--;
421 }
422 }
423 else if (lwgeom->type == POLYGONTYPE)
424 {
425 poly = (LWPOLY *)lwgeom;
426
427 if (state->pt == poly->rings[state->ring]->npoints)
428 {
429 state->pt = 0;
430 state->ring++;
431 state->pathlen--;
432 }
433
434 if (state->ring < poly->nrings)
435 {
436 if (state->pt == 0)
437 {
438 state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
439 state->pathlen++;
440 }
441
442 if (state->pt < poly->rings[state->ring]->npoints - 1)
443 {
444 points = poly->rings[state->ring];
445 }
446 else
447 {
448 state->pt += stride;
449 continue;
450 }
451 }
452 }
453
454 if (points && ((state->pt % stride) == 0))
455 {
456 segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), stride+1);
457 for (uint8_t i = 0; i < stride+1; i++)
458 {
459 getPoint4d_p(points, state->pt+i, &pts_out[i]);
460 ptarray_set_point4d(segment_pa, i, &pts_out[i]);
461 }
462
463 if (stride == 1)
464 segment = lwline_as_lwgeom(lwline_construct(lwgeom->srid, NULL, segment_pa));
465 else if (stride == 2)
466 segment = lwcircstring_as_lwgeom(lwcircstring_construct(lwgeom->srid, NULL, segment_pa));
467 else
468 elog(ERROR, "%s got unexpected strid", __func__);
469
470 state->pt += stride;
471
472 state->path[state->pathlen] = Int32GetDatum(state->pt / stride);
473 pathpt[0] = PointerGetDatum(construct_array(state->path,
474 state->pathlen + 1,
475 INT4OID,
476 state->typlen,
477 state->byval,
478 state->align));
479 pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
480
481 tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
482 result = HeapTupleGetDatum(tuple);
483 SRF_RETURN_NEXT(funcctx, result);
484 }
485 else
486 {
487 if (--state->stacklen == 0)
488 SRF_RETURN_DONE(funcctx);
489 state->pathlen--;
490 continue;
491 }
492 }
493
494 if (lwgeom_is_collection(lwgeom))
495 {
496 lwcoll = (LWCOLLECTION *)node->geom;
497
498 /* if a collection and we have more geoms */
499 if (node->idx < lwcoll->ngeoms)
500 {
501 if (state->stacklen > MAXDEPTH)
502 elog(ERROR, "Unable to dump overly nested collection");
503
504 /* push the next geom on the path and the stack */
505 lwgeom = lwcoll->geoms[node->idx++];
506 state->path[state->pathlen++] = Int32GetDatum(node->idx);
507
508 node = &state->stack[state->stacklen++];
509 node->idx = 0;
510 node->geom = lwgeom;
511
512 state->pt = 0;
513 state->ring = 0;
514
515 /* loop back to beginning, which will then check whatever node we just pushed */
516 continue;
517 }
518 }
519
520 /* no more geometries in the current collection */
521 if (--state->stacklen == 0)
522 SRF_RETURN_DONE(funcctx);
523 state->pathlen--;
524 }
525}
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition lwline.c:319
#define LINETYPE
Definition liblwgeom.h:103
LWPOINT * lwcircstring_get_lwpoint(const LWCIRCSTRING *circ, uint32_t where)
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:962
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
void * lwalloc(size_t size)
Definition lwutil.c:227
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition lwgeom.c:243
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
LWTRIANGLE * lwgeom_as_lwtriangle(const LWGEOM *lwgeom)
Definition lwgeom.c:252
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM contains sub-geometries or not This basically just checks that the struct ...
Definition lwgeom.c:1125
#define POLYGONTYPE
Definition liblwgeom.h:104
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:367
LWCIRCSTRING * lwgeom_as_lwcircstring(const LWGEOM *lwgeom)
Definition lwgeom.c:216
#define CIRCSTRINGTYPE
Definition liblwgeom.h:109
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
LWGEOM * lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
Definition lwgeom.c:342
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:207
LWPOINT * lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p)
Definition lwpoint.c:206
#define TRIANGLETYPE
Definition liblwgeom.h:115
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition lwgeom.c:969
LWCIRCSTRING * lwcircstring_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:369
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition ptarray.c:51
This library is the generic geometry handling section of PostGIS.
#define MAXDEPTH
Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(LWGEOM_dumppoints)
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition lwinline.h:127
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:199
POINTARRAY * points
Definition liblwgeom.h:507
uint32_t ngeoms
Definition liblwgeom.h:580
LWGEOM ** geoms
Definition liblwgeom.h:575
uint8_t type
Definition liblwgeom.h:462
int32_t srid
Definition liblwgeom.h:460
POINTARRAY * points
Definition liblwgeom.h:483
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
int32_t srid
Definition liblwgeom.h:520
int32_t srid
Definition liblwgeom.h:496
POINTARRAY * points
Definition liblwgeom.h:495
uint32_t npoints
Definition liblwgeom.h:427
LWGEOM * geom
Datum path[34]
struct dumpnode stack[MAXDEPTH]