PostGIS  3.3.9dev-r@@SVN_REVISION@@
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 
47 Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS);
48 Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS);
49 
50 struct 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
59 struct dumpstate {
61  int stacklen; /* collections/geoms on stack */
62  int pathlen; /* polygon rings and such need extra path info */
63  struct dumpnode stack[MAXDEPTH];
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 
76 Datum 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, "Unabled 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 
301 Datum 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 pt_start, pt_end;
384  POINTARRAY *segment_pa;
385  LWLINE *segment;
386 
387  node = &state->stack[state->stacklen - 1];
388  lwgeom = node->geom;
389 
390  if ( !lwgeom_is_empty(lwgeom) && (lwgeom->type == LINETYPE || lwgeom->type == TRIANGLETYPE || lwgeom->type == POLYGONTYPE) )
391  {
392  if (lwgeom->type == LINETYPE)
393  {
394  line = (LWLINE *)lwgeom;
395 
396  if (state->pt < line->points->npoints - 1)
397  {
398  points = line->points;
399  }
400  }
401  if (lwgeom->type == TRIANGLETYPE)
402  {
403  tri = (LWTRIANGLE *)lwgeom;
404 
405  if (state->pt == 0)
406  {
407  state->path[state->pathlen++] = Int32GetDatum(state->ring + 1);
408  }
409 
410  if (state->pt < 3)
411  {
412  points = tri->points;
413  }
414  else
415  {
416  state->pathlen--;
417  }
418  }
419  if (lwgeom->type == POLYGONTYPE)
420  {
421  poly = (LWPOLY *)lwgeom;
422 
423  if (state->pt == poly->rings[state->ring]->npoints)
424  {
425  state->pt = 0;
426  state->ring++;
427  state->pathlen--;
428  }
429 
430  if (state->ring < poly->nrings)
431  {
432  if (state->pt == 0)
433  {
434  state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
435  state->pathlen++;
436  }
437 
438  if (state->pt < poly->rings[state->ring]->npoints - 1)
439  {
440  points = poly->rings[state->ring];
441  }
442  else
443  {
444  state->pt++;
445  continue;
446  }
447  }
448  }
449 
450  if (points)
451  {
452  getPoint4d_p(points, state->pt, &pt_start);
453  getPoint4d_p(points, state->pt + 1, &pt_end);
454 
455  segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
456  ptarray_set_point4d(segment_pa, 0, &pt_start);
457  ptarray_set_point4d(segment_pa, 1, &pt_end);
458 
459  segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
460 
461  state->pt++;
462 
463  state->path[state->pathlen] = Int32GetDatum(state->pt);
464  pathpt[0] = PointerGetDatum(construct_array(state->path,
465  state->pathlen + 1,
466  INT4OID,
467  state->typlen,
468  state->byval,
469  state->align));
470  pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
471 
472  tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
473  result = HeapTupleGetDatum(tuple);
474  SRF_RETURN_NEXT(funcctx, result);
475  }
476  else
477  {
478  if (--state->stacklen == 0)
479  SRF_RETURN_DONE(funcctx);
480  state->pathlen--;
481  continue;
482  }
483  }
484 
485  if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE ||
486  lwgeom->type == MULTIPOLYGONTYPE || lwgeom->type == TINTYPE)
487  {
488  lwcoll = (LWCOLLECTION *)node->geom;
489 
490  /* if a collection and we have more geoms */
491  if (node->idx < lwcoll->ngeoms)
492  {
493  if (state->stacklen > MAXDEPTH)
494  elog(ERROR, "Unable to dump overly nested collection");
495 
496  /* push the next geom on the path and the stack */
497  lwgeom = lwcoll->geoms[node->idx++];
498  state->path[state->pathlen++] = Int32GetDatum(node->idx);
499 
500  node = &state->stack[state->stacklen++];
501  node->idx = 0;
502  node->geom = lwgeom;
503 
504  state->pt = 0;
505  state->ring = 0;
506 
507  /* loop back to beginning, which will then check whatever node we just pushed */
508  continue;
509  }
510  }
511 
512  /* no more geometries in the current collection */
513  if (--state->stacklen == 0)
514  SRF_RETURN_DONE(funcctx);
515  state->pathlen--;
516  }
517 }
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.
Definition: gserialized.c:239
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWPOINT * lwcircstring_get_lwpoint(const LWCIRCSTRING *circ, uint32_t where)
Definition: lwcircstring.c:286
#define COLLECTIONTYPE
Definition: liblwgeom.h:123
#define MULTILINETYPE
Definition: liblwgeom.h:121
#define LINETYPE
Definition: liblwgeom.h:118
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
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:117
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
LWTRIANGLE * lwgeom_as_lwtriangle(const LWGEOM *lwgeom)
Definition: lwgeom.c:224
#define TINTYPE
Definition: liblwgeom.h:131
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:122
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:1097
#define POLYGONTYPE
Definition: liblwgeom.h:119
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:124
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:126
#define TRIANGLETYPE
Definition: liblwgeom.h:130
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCIRCSTRING * lwgeom_as_lwcircstring(const LWGEOM *lwgeom)
Definition: lwgeom.c:188
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:370
LWPOINT * lwpoint_make(int32_t srid, int hasz, int hasm, const POINT4D *p)
Definition: lwpoint.c:206
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:309
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)
if(!(yy_init))
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:203
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:131
POINTARRAY * points
Definition: liblwgeom.h:522
uint32_t ngeoms
Definition: liblwgeom.h:595
LWGEOM ** geoms
Definition: liblwgeom.h:590
uint8_t type
Definition: liblwgeom.h:477
int32_t srid
Definition: liblwgeom.h:475
POINTARRAY * points
Definition: liblwgeom.h:498
POINTARRAY ** rings
Definition: liblwgeom.h:534
uint32_t nrings
Definition: liblwgeom.h:539
int32_t srid
Definition: liblwgeom.h:535
int32_t srid
Definition: liblwgeom.h:511
POINTARRAY * points
Definition: liblwgeom.h:510
uint32_t npoints
Definition: liblwgeom.h:442
uint32_t idx
LWGEOM * geom
Datum path[34]
LWGEOM * root
struct dumpnode stack[MAXDEPTH]
uint32_t ring