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"
10 #include "liblwgeom.h"
12 /* ST_DumpPoints for postgis.
13 * By Nathan Wagner, copyright disclaimed,
14 * this entire file is in the public domain
21 int idx; /* which member geom we're working on */
24 /* 32 is the max depth for st_dump, so it seems reasonable
25 * to use the same here
30 int stacklen; /* collections/geoms on stack */
31 int pathlen; /* polygon rings and such need extra path info */
32 struct dumpnode stack[MAXDEPTH];
33 Datum path[34]; /* two more than max depth, for ring and point */
35 /* used to cache the type attributes for integer arrays */
40 int ring; /* ring of top polygon */
41 int pt; /* point of top geom or current ring */
44 PG_FUNCTION_INFO_V1(LWGEOM_dumppoints);
46 Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS) {
47 FuncCallContext *funcctx;
48 MemoryContext oldcontext, newcontext;
50 GSERIALIZED *pglwgeom;
53 struct dumpstate *state;
54 struct dumpnode *node;
57 Datum pathpt[2]; /* used to construct the composite return value */
58 bool isnull[2] = {0,0}; /* needed to say neither value is null */
59 Datum result; /* the actual composite return value */
61 if (SRF_IS_FIRSTCALL()) {
62 funcctx = SRF_FIRSTCALL_INIT();
64 newcontext = funcctx->multi_call_memory_ctx;
65 oldcontext = MemoryContextSwitchTo(newcontext);
67 /* get a local copy of what we're doing a dump points on */
68 pglwgeom = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
69 lwgeom = lwgeom_from_gserialized(pglwgeom);
71 /* return early if nothing to do */
72 if (!lwgeom || lwgeom_is_empty(lwgeom)) {
73 funcctx = SRF_PERCALL_SETUP();
74 SRF_RETURN_DONE(funcctx);
77 /* Create function state */
78 state = lwalloc(sizeof *state);
85 funcctx->user_fctx = state;
88 * Push a struct dumpnode on the state stack
91 state->stack[0].idx = 0;
92 state->stack[0].geom = lwgeom;
96 * get tuple description for return type
98 if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE) {
99 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
100 errmsg("set-valued function called in context that cannot accept a set")));
103 BlessTupleDesc(funcctx->tuple_desc);
105 /* get and cache data for constructing int4 arrays */
106 get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
108 MemoryContextSwitchTo(oldcontext);
111 /* stuff done on every call of the function */
112 funcctx = SRF_PERCALL_SETUP();
113 newcontext = funcctx->multi_call_memory_ctx;
116 state = funcctx->user_fctx;
119 node = &state->stack[state->stacklen-1];
122 /* need to return a point from this geometry */
123 if (!lwgeom_is_collection(lwgeom)) {
124 /* either return a point, or pop the stack */
125 /* TODO use a union? would save a tiny amount of stack space.
126 * probably not worth the bother
131 LWPOINT *lwpoint = NULL;
135 * net result of switch should be to set lwpoint to the
136 * next point to return, or leave at NULL if there
137 * are no more points in the geometry
139 switch(lwgeom->type) {
141 tri = lwgeom_as_lwtriangle(lwgeom);
142 if (state->pt == 0) {
143 state->path[state->pathlen++] = Int32GetDatum(state->ring+1);
145 if (state->pt <= 3) {
146 getPoint4d_p(tri->points, state->pt, &pt);
147 lwpoint = lwpoint_make(tri->srid,
148 FLAGS_GET_Z(tri->points->flags),
149 FLAGS_GET_M(tri->points->flags),
157 poly = lwgeom_as_lwpoly(lwgeom);
158 if (state->pt == poly->rings[state->ring]->npoints) {
163 if (state->pt == 0 && state->ring < poly->nrings) {
164 /* handle new ring */
165 state->path[state->pathlen] = Int32GetDatum(state->ring+1);
168 if (state->ring == poly->nrings) {
170 /* TODO should be able to directly get the point
171 * into the point array of a fixed lwpoint
173 /* can't get the point directly from the ptarray because
174 * it might be aligned wrong, so at least one memcpy
176 * It might be possible to pass it directly to gserialized
177 * depending how that works, it might effectively be gserialized
178 * though a brief look at the code indicates not
180 getPoint4d_p(poly->rings[state->ring], state->pt, &pt);
181 lwpoint = lwpoint_make(poly->srid,
182 FLAGS_GET_Z(poly->rings[state->ring]->flags),
183 FLAGS_GET_M(poly->rings[state->ring]->flags),
188 if (state->pt == 0) lwpoint = lwgeom_as_lwpoint(lwgeom);
192 line = lwgeom_as_lwline(lwgeom);
193 if (line->points && state->pt <= line->points->npoints) {
194 lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, state->pt);
200 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
206 * At this point, lwpoint is either NULL, in which case
207 * we need to pop the geometry stack and get the next
208 * geometry, if amy, or lwpoint is set and we construct
209 * a record type with the integer array of geometry
210 * indexes and the point number, and the actual point
215 /* no point, so pop the geom and look for more */
216 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
220 /* write address of current geom/pt */
223 state->path[state->pathlen] = Int32GetDatum(state->pt);
224 pathpt[0] = PointerGetDatum(construct_array(state->path, state->pathlen+1,
225 INT4OID, state->typlen, state->byval, state->align));
227 pathpt[1] = PointerGetDatum(gserialized_from_lwgeom((LWGEOM*)lwpoint,0,0));
229 tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
230 result = HeapTupleGetDatum(tuple);
231 SRF_RETURN_NEXT(funcctx, result);
235 lwcoll = (LWCOLLECTION*)node->geom;
237 /* if a collection and we have more geoms */
238 if (node->idx < lwcoll->ngeoms) {
239 /* push the next geom on the path and the stack */
240 lwgeom = lwcoll->geoms[node->idx++];
241 state->path[state->pathlen++] = Int32GetDatum(node->idx);
243 node = &state->stack[state->stacklen++];
250 /* loop back to beginning, which will then check whatever node we just pushed */
254 /* no more geometries in the current collection */
255 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
257 state->stack[state->stacklen-1].idx++;
262 * Geometry types of collection types for reference
268 case MULTIPOLYGONTYPE:
273 case MULTISURFACETYPE:
274 case POLYHEDRALSURFACETYPE: