]> pd.if.org Git - dumppoints/blob - dp.c
added email address to README
[dumppoints] / dp.c
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 "liblwgeom.h"
11
12 /* ST_DumpPoints for postgis.
13  * By Nathan Wagner, copyright disclaimed,
14  * this entire file is in the public domain
15  */
16
17 PG_MODULE_MAGIC;
18
19 struct dumpnode {
20         LWGEOM *geom;
21         int idx; /* which member geom we're working on */
22 } ;
23
24 /* 32 is the max depth for st_dump, so it seems reasonable
25  * to use the same here
26  */
27 #define MAXDEPTH 32
28 struct dumpstate {
29         LWGEOM  *root;
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 */
34
35         /* used to cache the type attributes for integer arrays */
36         int16   typlen;
37         bool    byval;
38         char    align;
39
40         int ring; /* ring of top polygon */
41         int pt; /* point of top geom or current ring */
42 };
43
44 PG_FUNCTION_INFO_V1(LWGEOM_dumppoints);
45
46 Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS) {
47         FuncCallContext *funcctx;
48         MemoryContext oldcontext, newcontext;
49
50         GSERIALIZED *pglwgeom;
51         LWCOLLECTION *lwcoll;
52         LWGEOM *lwgeom;
53         struct dumpstate *state;
54         struct dumpnode *node;
55
56         HeapTuple tuple;
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 */
60
61         if (SRF_IS_FIRSTCALL()) {
62                 funcctx = SRF_FIRSTCALL_INIT();
63
64                 newcontext = funcctx->multi_call_memory_ctx;
65                 oldcontext = MemoryContextSwitchTo(newcontext);
66
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);
70
71                 /* return early if nothing to do */
72                 if (!lwgeom || lwgeom_is_empty(lwgeom)) {
73                         funcctx = SRF_PERCALL_SETUP();
74                         SRF_RETURN_DONE(funcctx);
75                 }
76
77                 /* Create function state */
78                 state = lwalloc(sizeof *state);
79                 state->root = lwgeom;
80                 state->stacklen = 0;
81                 state->pathlen = 0;
82                 state->pt = 0;
83                 state->ring = 0;
84
85                 funcctx->user_fctx = state;
86
87                 /*
88                  * Push a struct dumpnode on the state stack
89                  */
90
91                 state->stack[0].idx = 0;
92                 state->stack[0].geom = lwgeom;
93                 state->stacklen++;
94
95                 /*
96                  * get tuple description for return type
97                  */
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")));
101                 }
102
103                 BlessTupleDesc(funcctx->tuple_desc);
104
105                 /* get and cache data for constructing int4 arrays */
106                 get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
107
108                 MemoryContextSwitchTo(oldcontext);
109         }
110
111         /* stuff done on every call of the function */
112         funcctx = SRF_PERCALL_SETUP();
113         newcontext = funcctx->multi_call_memory_ctx;
114
115         /* get state */
116         state = funcctx->user_fctx;
117
118         while (1) {
119                 node = &state->stack[state->stacklen-1];
120                 lwgeom = node->geom;
121
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
127                          */
128                         LWLINE  *line;
129                         LWPOLY  *poly;
130                         LWTRIANGLE      *tri;
131                         LWPOINT *lwpoint = NULL;
132                         POINT4D pt;
133
134                         /*
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
138                          */
139                         switch(lwgeom->type) {
140                                 case TRIANGLETYPE:
141                                         tri = lwgeom_as_lwtriangle(lwgeom);
142                                         if (state->pt == 0) {
143                                                 state->path[state->pathlen++] = Int32GetDatum(state->ring+1);
144                                         }
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),
150                                                                 &pt);
151                                         }
152                                         if (state->pt > 3) {
153                                                 state->pathlen--;
154                                         }
155                                         break;
156                                 case POLYGONTYPE:
157                                         poly = lwgeom_as_lwpoly(lwgeom);
158                                         if (state->pt == poly->rings[state->ring]->npoints) {
159                                                 state->pt = 0;
160                                                 state->ring++;
161                                                 state->pathlen--;
162                                         }
163                                         if (state->pt == 0 && state->ring < poly->nrings) {
164                                                 /* handle new ring */
165                                                 state->path[state->pathlen] = Int32GetDatum(state->ring+1);
166                                                 state->pathlen++;
167                                         }
168                                         if (state->ring == poly->nrings) {
169                                         } else {
170                                         /* TODO should be able to directly get the point
171                                          * into the point array of a fixed lwpoint
172                                          */
173                                         /* can't get the point directly from the ptarray because
174                                          * it might be aligned wrong, so at least one memcpy
175                                          * seems unavoidable
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
179                                          */
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),
184                                                                 &pt);
185                                         }
186                                         break;
187                                 case POINTTYPE:
188                                         if (state->pt == 0) lwpoint = lwgeom_as_lwpoint(lwgeom);
189                                         break;
190                                 case LINETYPE:
191                                 case CIRCSTRINGTYPE:
192                                         line = lwgeom_as_lwline(lwgeom);
193                                         if (line->points && state->pt <= line->points->npoints) {
194                                                 lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, state->pt);
195                                         }
196                                         break;
197                                 case CURVEPOLYTYPE:
198                                 default:
199                                         /* TODO error? */
200                                         if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
201                                         state->pathlen--;
202                                         continue;
203                         }
204
205                         /*
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
211                          * geometry itself
212                          */
213
214                         if (!lwpoint) {
215                                 /* no point, so pop the geom and look for more */
216                                 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
217                                 state->pathlen--;
218                                 continue;
219                         } else {
220                                 /* write address of current geom/pt */
221                                 state->pt++;
222
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));
226                                 
227                                 pathpt[1] = PointerGetDatum(gserialized_from_lwgeom((LWGEOM*)lwpoint,0,0));
228                                 
229                                 tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
230                                 result = HeapTupleGetDatum(tuple);
231                                 SRF_RETURN_NEXT(funcctx, result);
232                         }
233                 }
234
235                 lwcoll = (LWCOLLECTION*)node->geom;
236
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);
242
243                         node = &state->stack[state->stacklen++];
244                         node->idx = 0;
245                         node->geom = lwgeom;
246
247                         state->pt = 0;
248                         state->ring = 0;
249
250                         /* loop back to beginning, which will then check whatever node we just pushed */
251                         continue;
252                 }
253
254                 /* no more geometries in the current collection */
255                 if (--state->stacklen == 0) SRF_RETURN_DONE(funcctx);
256                 state->pathlen--;
257                 state->stack[state->stacklen-1].idx++;
258         }
259 }
260
261 /*
262  * Geometry types of collection types for reference
263  */
264
265 #if 0
266         case MULTIPOINTTYPE:
267         case MULTILINETYPE:
268         case MULTIPOLYGONTYPE:
269         case COLLECTIONTYPE:
270         case CURVEPOLYTYPE:
271         case COMPOUNDTYPE:
272         case MULTICURVETYPE:
273         case MULTISURFACETYPE:
274         case POLYHEDRALSURFACETYPE:
275         case TINTYPE:
276 #endif
277