PostGIS  2.2.7dev-r@@SVN_REVISION@@
lwgeom_dump.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  * Copyright 2001-2009 Refractions Research Inc.
6  *
7  * This is free software; you can redistribute and/or modify it under
8  * the terms of the GNU General Public Licence. See the COPYING file.
9  *
10  **********************************************************************/
11 
12 #include <math.h>
13 #include <float.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <errno.h>
17 #include <assert.h>
18 
19 #include "postgres.h"
20 #include "fmgr.h"
21 #include "utils/elog.h"
22 #include "utils/array.h"
23 #include "utils/geo_decls.h"
24 #include "funcapi.h"
25 
26 #include "../postgis_config.h"
27 #include "liblwgeom.h"
28 #include "lwgeom_pg.h"
29 
30 
31 Datum LWGEOM_dump(PG_FUNCTION_ARGS);
32 Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS);
33 Datum ST_Subdivide(PG_FUNCTION_ARGS);
34 
35 typedef struct GEOMDUMPNODE_T
36 {
37  int idx;
39 }
41 
42 #define MAXDEPTH 32
43 typedef struct GEOMDUMPSTATE
44 {
45  int stacklen;
48 }
50 
51 #define PUSH(x,y) ((x)->stack[(x)->stacklen++]=(y))
52 #define LAST(x) ((x)->stack[(x)->stacklen-1])
53 #define POP(x) (--((x)->stacklen))
54 
55 
57 Datum LWGEOM_dump(PG_FUNCTION_ARGS)
58 {
59  GSERIALIZED *pglwgeom;
60  LWCOLLECTION *lwcoll;
61  LWGEOM *lwgeom;
62  FuncCallContext *funcctx;
63  GEOMDUMPSTATE *state;
64  GEOMDUMPNODE *node;
65  TupleDesc tupdesc;
66  HeapTuple tuple;
67  AttInMetadata *attinmeta;
68  MemoryContext oldcontext, newcontext;
69  Datum result;
70  char address[256];
71  char *ptr;
72  uint32 i;
73  char *values[2];
74 
75  if (SRF_IS_FIRSTCALL())
76  {
77  funcctx = SRF_FIRSTCALL_INIT();
78  newcontext = funcctx->multi_call_memory_ctx;
79 
80  oldcontext = MemoryContextSwitchTo(newcontext);
81 
82  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
83  lwgeom = lwgeom_from_gserialized(pglwgeom);
84 
85  /* Create function state */
86  state = lwalloc(sizeof(GEOMDUMPSTATE));
87  state->root = lwgeom;
88  state->stacklen=0;
89 
90  if ( lwgeom_is_collection(lwgeom) )
91  {
92  /*
93  * Push a GEOMDUMPNODE on the state stack
94  */
95  node = lwalloc(sizeof(GEOMDUMPNODE));
96  node->idx=0;
97  node->geom = lwgeom;
98  PUSH(state, node);
99  }
100 
101  funcctx->user_fctx = state;
102 
103  /*
104  * Build a tuple description for an
105  * geometry_dump tuple
106  */
107  tupdesc = RelationNameGetTupleDesc("geometry_dump");
108 
109  /*
110  * generate attribute metadata needed later to produce
111  * tuples from raw C strings
112  */
113  attinmeta = TupleDescGetAttInMetadata(tupdesc);
114  funcctx->attinmeta = attinmeta;
115 
116  MemoryContextSwitchTo(oldcontext);
117  }
118 
119  /* stuff done on every call of the function */
120  funcctx = SRF_PERCALL_SETUP();
121  newcontext = funcctx->multi_call_memory_ctx;
122 
123  /* get state */
124  state = funcctx->user_fctx;
125 
126  /* Handled simple geometries */
127  if ( ! state->root ) SRF_RETURN_DONE(funcctx);
128  /* Return nothing for empties */
129  if ( lwgeom_is_empty(state->root) ) SRF_RETURN_DONE(funcctx);
130  if ( ! lwgeom_is_collection(state->root) )
131  {
132  values[0] = "{}";
133  values[1] = lwgeom_to_hexwkb(state->root, WKB_EXTENDED, 0);
134  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
135  result = HeapTupleGetDatum(tuple);
136 
137  state->root = NULL;
138  SRF_RETURN_NEXT(funcctx, result);
139  }
140 
141  while (1)
142  {
143  node = LAST(state);
144  lwcoll = (LWCOLLECTION*)node->geom;
145 
146  if ( node->idx < lwcoll->ngeoms )
147  {
148  lwgeom = lwcoll->geoms[node->idx];
149  if ( ! lwgeom_is_collection(lwgeom) )
150  {
151  /* write address of current geom */
152  ptr=address;
153  *ptr++='{';
154  for (i=0; i<state->stacklen; i++)
155  {
156  if ( i ) ptr += sprintf(ptr, ",");
157  ptr += sprintf(ptr, "%d", state->stack[i]->idx+1);
158  }
159  *ptr++='}';
160  *ptr='\0';
161 
162  break;
163  }
164 
165  /*
166  * It's a collection, increment index
167  * of current node, push a new one on the
168  * stack
169  */
170 
171  oldcontext = MemoryContextSwitchTo(newcontext);
172 
173  node = lwalloc(sizeof(GEOMDUMPNODE));
174  node->idx=0;
175  node->geom = lwgeom;
176  PUSH(state, node);
177 
178  MemoryContextSwitchTo(oldcontext);
179 
180  continue;
181  }
182 
183  if ( ! POP(state) ) SRF_RETURN_DONE(funcctx);
184  LAST(state)->idx++;
185  }
186 
187  lwgeom->srid = state->root->srid;
188 
189  values[0] = address;
190  values[1] = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, 0);
191  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
192  result = TupleGetDatum(funcctx->slot, tuple);
193  node->idx++;
194  SRF_RETURN_NEXT(funcctx, result);
195 }
196 
198 {
199  int ringnum;
201 };
202 
204 Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
205 {
206  GSERIALIZED *pglwgeom;
207  LWGEOM *lwgeom;
208  FuncCallContext *funcctx;
209  struct POLYDUMPSTATE *state;
210  TupleDesc tupdesc;
211  HeapTuple tuple;
212  AttInMetadata *attinmeta;
213  MemoryContext oldcontext, newcontext;
214  Datum result;
215  char address[256];
216  char *values[2];
217 
218  if (SRF_IS_FIRSTCALL())
219  {
220  funcctx = SRF_FIRSTCALL_INIT();
221  newcontext = funcctx->multi_call_memory_ctx;
222 
223  oldcontext = MemoryContextSwitchTo(newcontext);
224 
225  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
226  if ( gserialized_get_type(pglwgeom) != POLYGONTYPE )
227  {
228  elog(ERROR, "Input is not a polygon");
229  }
230 
231  lwgeom = lwgeom_from_gserialized(pglwgeom);
232 
233  /* Create function state */
234  state = lwalloc(sizeof(struct POLYDUMPSTATE));
235  state->poly = lwgeom_as_lwpoly(lwgeom);
236  assert (state->poly);
237  state->ringnum=0;
238 
239  funcctx->user_fctx = state;
240 
241  /*
242  * Build a tuple description for an
243  * geometry_dump tuple
244  */
245  tupdesc = RelationNameGetTupleDesc("geometry_dump");
246 
247  /*
248  * generate attribute metadata needed later to produce
249  * tuples from raw C strings
250  */
251  attinmeta = TupleDescGetAttInMetadata(tupdesc);
252  funcctx->attinmeta = attinmeta;
253 
254  MemoryContextSwitchTo(oldcontext);
255  }
256 
257  /* stuff done on every call of the function */
258  funcctx = SRF_PERCALL_SETUP();
259  newcontext = funcctx->multi_call_memory_ctx;
260 
261  /* get state */
262  state = funcctx->user_fctx;
263 
264  /* Loop trough polygon rings */
265  while (state->ringnum < state->poly->nrings )
266  {
267  LWPOLY* poly = state->poly;
268  POINTARRAY *ring;
269  LWGEOM* ringgeom;
270 
271  /* Switch to an appropriate memory context for POINTARRAY
272  * cloning and hexwkb allocation */
273  oldcontext = MemoryContextSwitchTo(newcontext);
274 
275  /* We need a copy of input ring here */
276  ring = ptarray_clone_deep(poly->rings[state->ringnum]);
277 
278  /* Construct another polygon with shell only */
279  ringgeom = (LWGEOM*)lwpoly_construct(
280  poly->srid,
281  NULL, /* TODO: could use input bounding box here */
282  1, /* one ring */
283  &ring);
284 
285  /* Write path as ``{ <ringnum> }'' */
286  sprintf(address, "{%d}", state->ringnum);
287 
288  values[0] = address;
289  values[1] = lwgeom_to_hexwkb(ringgeom, WKB_EXTENDED, 0);
290 
291  MemoryContextSwitchTo(oldcontext);
292 
293  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
294  result = HeapTupleGetDatum(tuple);
295  ++state->ringnum;
296  SRF_RETURN_NEXT(funcctx, result);
297  }
298 
299  SRF_RETURN_DONE(funcctx);
300 
301 }
302 
303 
305 {
306  int geomnum;
308 };
309 
310 /*
311 * Break an object up into smaller objects of no more than N vertices
312 */
314 Datum ST_Subdivide(PG_FUNCTION_ARGS)
315 {
316 #if POSTGIS_GEOS_VERSION < 35
317 
318  elog(ERROR, "The GEOS version this PostGIS binary "
319  "was compiled against (%d) doesn't support "
320  "'%s' function (3.5.0+ required)",
321  POSTGIS_GEOS_VERSION, __func__);
322  PG_RETURN_NULL();
323 
324 #else /* POSTGIS_GEOS_VERSION >= 35 */
325 
326  typedef struct
327  {
328  int nextgeom;
329  int numgeoms;
330  LWCOLLECTION *col;
331  } collection_fctx;
332 
333  FuncCallContext *funcctx;
334  collection_fctx *fctx;
335  MemoryContext oldcontext;
336 
337  /* stuff done only on the first call of the function */
338  if (SRF_IS_FIRSTCALL())
339  {
340  GSERIALIZED *gser;
341  LWGEOM *geom;
342  LWCOLLECTION *col;
343  int maxvertices = 256;
344 
345  /* create a function context for cross-call persistence */
346  funcctx = SRF_FIRSTCALL_INIT();
347 
348  /*
349  * switch to memory context appropriate for multiple function calls
350  */
351  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
352 
353  /*
354  * Get the geometry value
355  */
356  gser = PG_GETARG_GSERIALIZED_P(0);
357  geom = lwgeom_from_gserialized(gser);
358 
359  /*
360  * Get the max vertices value
361  */
362  if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
363  maxvertices = PG_GETARG_INT32(1);
364 
365  /*
366  * Compute the subdivision of the geometry
367  */
368  col = lwgeom_subdivide(geom, maxvertices);
369 
370  if ( ! col )
371  SRF_RETURN_DONE(funcctx);
372 
373  /* allocate memory for user context */
374  fctx = (collection_fctx *) palloc(sizeof(collection_fctx));
375 
376  /* initialize state */
377  fctx->nextgeom = 0;
378  fctx->numgeoms = col->ngeoms;
379  fctx->col = col;
380 
381  /* save user context, switch back to function context */
382  funcctx->user_fctx = fctx;
383  MemoryContextSwitchTo(oldcontext);
384  }
385 
386  /* stuff done on every call of the function */
387  funcctx = SRF_PERCALL_SETUP();
388  fctx = funcctx->user_fctx;
389 
390  if (fctx->nextgeom < fctx->numgeoms)
391  {
392  GSERIALIZED *gpart = geometry_serialize(fctx->col->geoms[fctx->nextgeom]);
393  fctx->nextgeom++;
394  SRF_RETURN_NEXT(funcctx, PointerGetDatum(gpart));
395  }
396  else
397  {
398  /* do when there is no more left */
399  SRF_RETURN_DONE(funcctx);
400  }
401 
402 #endif /* POSTGIS_GEOS_VERSION >= 35 */
403 }
404 
uint32_t gserialized_get_type(const GSERIALIZED *s)
Extract the geometry type from the serialized form (it hides in the anonymous data area...
Definition: g_serialized.c:55
char * lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out)
Definition: lwout_wkb.c:834
Datum LWGEOM_dump(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:57
#define POSTGIS_GEOS_VERSION
Definition: sqldefines.h:10
#define MAXDEPTH
Definition: lwgeom_dump.c:42
#define POP(x)
Definition: lwgeom_dump.c:53
#define PUSH(x, y)
Definition: lwgeom_dump.c:51
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:991
LWGEOM * root
Definition: lwgeom_dump.c:47
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
#define POLYGONTYPE
Definition: liblwgeom.h:72
LWCOLLECTION * col
Definition: lwgeom_dump.c:307
PG_FUNCTION_INFO_V1(LWGEOM_dump)
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:125
int32_t srid
Definition: liblwgeom.h:383
struct GEOMDUMPSTATE GEOMDUMPSTATE
Datum ST_Subdivide(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:314
LWPOLY * lwpoly_construct(int srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:29
LWGEOM ** geoms
Definition: liblwgeom.h:493
Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:204
POINTARRAY ** rings
Definition: liblwgeom.h:441
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:634
int nrings
Definition: liblwgeom.h:439
GEOMDUMPNODE * stack[MAXDEPTH]
Definition: lwgeom_dump.c:46
#define LAST(x)
Definition: lwgeom_dump.c:52
#define WKB_EXTENDED
Definition: liblwgeom.h:1932
LWGEOM * geom
Definition: lwgeom_dump.c:38
LWPOLY * poly
Definition: lwgeom_dump.c:200
LWCOLLECTION * lwgeom_subdivide(const LWGEOM *geom, int maxvertices)
Definition: lwgeom.c:2008
int32_t srid
Definition: liblwgeom.h:438
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
void * lwalloc(size_t size)
Definition: lwutil.c:199
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:1297
struct GEOMDUMPNODE_T GEOMDUMPNODE
if(!(yy_init))
Definition: lwin_wkt_lex.c:863
This library is the generic geometry handling section of PostGIS.