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