PostGIS  2.4.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 
49 struct dumpnode {
51  int idx; /* which member geom we're working on */
52 } ;
53 
54 /* 32 is the max depth for st_dump, so it seems reasonable
55  * to use the same here
56  */
57 #define MAXDEPTH 32
58 struct dumpstate {
60  int stacklen; /* collections/geoms on stack */
61  int pathlen; /* polygon rings and such need extra path info */
62  struct dumpnode stack[MAXDEPTH];
63  Datum path[34]; /* two more than max depth, for ring and point */
64 
65  /* used to cache the type attributes for integer arrays */
66  int16 typlen;
67  bool byval;
68  char align;
69 
70  int ring; /* ring of top polygon */
71  int pt; /* point of top geom or current ring */
72 };
73 
75 Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS) {
76  FuncCallContext *funcctx;
77  MemoryContext oldcontext, newcontext;
78 
79  GSERIALIZED *pglwgeom;
80  LWCOLLECTION *lwcoll;
81  LWGEOM *lwgeom;
82  struct dumpstate *state;
83  struct dumpnode *node;
84 
85  HeapTuple tuple;
86  Datum pathpt[2]; /* used to construct the composite return value */
87  bool isnull[2] = {0,0}; /* needed to say neither value is null */
88  Datum result; /* the actual composite return value */
89 
90  if (SRF_IS_FIRSTCALL()) {
91  funcctx = SRF_FIRSTCALL_INIT();
92 
93  newcontext = funcctx->multi_call_memory_ctx;
94  oldcontext = MemoryContextSwitchTo(newcontext);
95 
96  /* get a local copy of what we're doing a dump points on */
97  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
98  lwgeom = lwgeom_from_gserialized(pglwgeom);
99 
100  /* return early if nothing to do */
101  if (!lwgeom || lwgeom_is_empty(lwgeom)) {
102  MemoryContextSwitchTo(oldcontext);
103  funcctx = SRF_PERCALL_SETUP();
104  SRF_RETURN_DONE(funcctx);
105  }
106 
107  /* Create function state */
108  state = lwalloc(sizeof *state);
109  state->root = lwgeom;
110  state->stacklen = 0;
111  state->pathlen = 0;
112  state->pt = 0;
113  state->ring = 0;
114 
115  funcctx->user_fctx = state;
116 
117  /*
118  * Push a struct dumpnode on the state stack
119  */
120 
121  state->stack[0].idx = 0;
122  state->stack[0].geom = lwgeom;
123  state->stacklen++;
124 
125  /*
126  * get tuple description for return type
127  */
128  if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE) {
129  ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
130  errmsg("set-valued function called in context that cannot accept a set")));
131  }
132 
133  BlessTupleDesc(funcctx->tuple_desc);
134 
135  /* get and cache data for constructing int4 arrays */
136  get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
137 
138  MemoryContextSwitchTo(oldcontext);
139  }
140 
141  /* stuff done on every call of the function */
142  funcctx = SRF_PERCALL_SETUP();
143  newcontext = funcctx->multi_call_memory_ctx;
144 
145  /* get state */
146  state = funcctx->user_fctx;
147 
148  while (1) {
149  node = &state->stack[state->stacklen-1];
150  lwgeom = node->geom;
151 
152  /* need to return a point from this geometry */
153  if (!lwgeom_is_collection(lwgeom)) {
154  /* either return a point, or pop the stack */
155  /* TODO use a union? would save a tiny amount of stack space.
156  * probably not worth the bother
157  */
158  LWLINE *line;
159  LWCIRCSTRING *circ;
160  LWPOLY *poly;
161  LWTRIANGLE *tri;
162  LWPOINT *lwpoint = NULL;
163  POINT4D pt;
164 
165  /*
166  * net result of switch should be to set lwpoint to the
167  * next point to return, or leave at NULL if there
168  * are no more points in the geometry
169  */
170  switch(lwgeom->type) {
171  case TRIANGLETYPE:
172  tri = lwgeom_as_lwtriangle(lwgeom);
173  if (state->pt == 0) {
174  state->path[state->pathlen++] = Int32GetDatum(state->ring+1);
175  }
176  if (state->pt <= 3) {
177  getPoint4d_p(tri->points, state->pt, &pt);
178  lwpoint = lwpoint_make(tri->srid,
179  FLAGS_GET_Z(tri->points->flags),
180  FLAGS_GET_M(tri->points->flags),
181  &pt);
182  }
183  if (state->pt > 3) {
184  state->pathlen--;
185  }
186  break;
187  case POLYGONTYPE:
188  poly = lwgeom_as_lwpoly(lwgeom);
189  if (state->pt == poly->rings[state->ring]->npoints) {
190  state->pt = 0;
191  state->ring++;
192  state->pathlen--;
193  }
194  if (state->pt == 0 && state->ring < poly->nrings) {
195  /* handle new ring */
196  state->path[state->pathlen] = Int32GetDatum(state->ring+1);
197  state->pathlen++;
198  }
199  if (state->ring == poly->nrings) {
200  } else {
201  /* TODO should be able to directly get the point
202  * into the point array of a fixed lwpoint
203  */
204  /* can't get the point directly from the ptarray because
205  * it might be aligned wrong, so at least one memcpy
206  * seems unavoidable
207  * It might be possible to pass it directly to gserialized
208  * depending how that works, it might effectively be gserialized
209  * though a brief look at the code indicates not
210  */
211  getPoint4d_p(poly->rings[state->ring], state->pt, &pt);
212  lwpoint = lwpoint_make(poly->srid,
213  FLAGS_GET_Z(poly->rings[state->ring]->flags),
214  FLAGS_GET_M(poly->rings[state->ring]->flags),
215  &pt);
216  }
217  break;
218  case POINTTYPE:
219  if (state->pt == 0) lwpoint = lwgeom_as_lwpoint(lwgeom);
220  break;
221  case LINETYPE:
222  line = lwgeom_as_lwline(lwgeom);
223  if (line->points && state->pt <= line->points->npoints) {
224  lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, state->pt);
225  }
226  break;
227  case CIRCSTRINGTYPE:
228  circ = lwgeom_as_lwcircstring(lwgeom);
229  if (circ->points && state->pt <= circ->points->npoints) {
230  lwpoint = lwcircstring_get_lwpoint((LWCIRCSTRING*)lwgeom, state->pt);
231  }
232  break;
233  default:
234  ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
235  errmsg("Invalid Geometry type %d passed to ST_DumpPoints()", lwgeom->type)));
236  }
237 
238  /*
239  * At this point, lwpoint is either NULL, in which case
240  * we need to pop the geometry stack and get the next
241  * geometry, if amy, or lwpoint is set and we construct
242  * a record type with the integer array of geometry
243  * indexes and the point number, and the actual point
244  * geometry itself
245  */
246 
247  if (!lwpoint) {
248  /* no point, so pop the geom and look for more */
249  if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
250  state->pathlen--;
251  continue;
252  } else {
253  /* write address of current geom/pt */
254  state->pt++;
255 
256  state->path[state->pathlen] = Int32GetDatum(state->pt);
257  pathpt[0] = PointerGetDatum(construct_array(state->path, state->pathlen+1,
258  INT4OID, state->typlen, state->byval, state->align));
259 
260  pathpt[1] = PointerGetDatum(gserialized_from_lwgeom((LWGEOM*)lwpoint,0));
261 
262  tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
263  result = HeapTupleGetDatum(tuple);
264  SRF_RETURN_NEXT(funcctx, result);
265  }
266  }
267 
268  lwcoll = (LWCOLLECTION*)node->geom;
269 
270  /* if a collection and we have more geoms */
271  if (node->idx < lwcoll->ngeoms) {
272  /* push the next geom on the path and the stack */
273  lwgeom = lwcoll->geoms[node->idx++];
274  state->path[state->pathlen++] = Int32GetDatum(node->idx);
275 
276  node = &state->stack[state->stacklen++];
277  node->idx = 0;
278  node->geom = lwgeom;
279 
280  state->pt = 0;
281  state->ring = 0;
282 
283  /* loop back to beginning, which will then check whatever node we just pushed */
284  continue;
285  }
286 
287  /* no more geometries in the current collection */
288  if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
289  state->pathlen--;
290  state->stack[state->stacklen-1].idx++;
291  }
292 }
293 
294 /*
295  * Geometry types of collection types for reference
296  */
297 
298 #if 0
299  case MULTIPOINTTYPE:
300  case MULTILINETYPE:
301  case MULTIPOLYGONTYPE:
302  case COLLECTIONTYPE:
303  case CURVEPOLYTYPE:
304  case COMPOUNDTYPE:
305  case MULTICURVETYPE:
306  case MULTISURFACETYPE:
308  case TINTYPE:
309 
310 #endif
311 
#define LINETYPE
Definition: liblwgeom.h:86
POINTARRAY * points
Definition: liblwgeom.h:433
#define MAXDEPTH
#define MULTICURVETYPE
Definition: liblwgeom.h:95
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:1040
Datum path[34]
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
int npoints
Definition: liblwgeom.h:371
#define POLYGONTYPE
Definition: liblwgeom.h:87
#define CURVEPOLYTYPE
Definition: liblwgeom.h:94
#define COMPOUNDTYPE
Definition: liblwgeom.h:93
#define MULTIPOINTTYPE
Definition: liblwgeom.h:88
#define TRIANGLETYPE
Definition: liblwgeom.h:98
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:97
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:174
LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:129
LWGEOM * geom
uint8_t flags
Definition: liblwgeom.h:369
LWGEOM ** geoms
Definition: liblwgeom.h:509
#define TINTYPE
Definition: liblwgeom.h:99
LWPOINT * lwpoint_make(int srid, int hasz, int hasm, const POINT4D *p)
Definition: lwpoint.c:206
POINTARRAY ** rings
Definition: liblwgeom.h:457
int nrings
Definition: liblwgeom.h:455
#define FLAGS_GET_Z(flags)
Macros for manipulating the &#39;flags&#39; byte.
Definition: liblwgeom.h:140
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:138
LWCIRCSTRING * lwgeom_as_lwcircstring(const LWGEOM *lwgeom)
Definition: lwgeom.c:147
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:90
LWTRIANGLE * lwgeom_as_lwtriangle(const LWGEOM *lwgeom)
Definition: lwgeom.c:183
#define MULTISURFACETYPE
Definition: liblwgeom.h:96
int32_t srid
Definition: liblwgeom.h:454
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:85
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:141
Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS)
LWPOINT * lwline_get_lwpoint(const LWLINE *line, int where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:324
uint8_t type
Definition: liblwgeom.h:396
POINTARRAY * points
Definition: liblwgeom.h:444
struct dumpnode stack[MAXDEPTH]
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:92
void * lwalloc(size_t size)
Definition: lwutil.c:229
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
Definition: lwgeom.c:1346
GSERIALIZED * gserialized_from_lwgeom(LWGEOM *geom, size_t *size)
Allocate a new GSERIALIZED from an LWGEOM.
#define MULTILINETYPE
Definition: liblwgeom.h:89
LWPOINT * lwcircstring_get_lwpoint(const LWCIRCSTRING *circ, int where)
Definition: lwcircstring.c:300
if(!(yy_init))
int32_t srid
Definition: liblwgeom.h:432
LWGEOM * root
int getPoint4d_p(const POINTARRAY *pa, int n, POINT4D *point)
Definition: lwgeom_api.c:122
#define COLLECTIONTYPE
Definition: liblwgeom.h:91
This library is the generic geometry handling section of PostGIS.
PG_FUNCTION_INFO_V1(LWGEOM_dumppoints)
POINTARRAY * points
Definition: liblwgeom.h:422