PostGIS  3.7.0dev-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  *
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 2001-2009 Refractions Research Inc.
22  *
23  **********************************************************************/
24 
25 
26 #include <math.h>
27 #include <float.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <assert.h>
31 
32 #include "postgres.h"
33 #include "fmgr.h"
34 #include "utils/elog.h"
35 #include "utils/array.h"
36 #include "utils/geo_decls.h"
37 #include "funcapi.h"
38 
39 #include "../postgis_config.h"
40 #include "liblwgeom.h"
41 #include "lwgeom_pg.h"
42 
43 
44 Datum LWGEOM_dump(PG_FUNCTION_ARGS);
45 Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS);
46 Datum ST_Subdivide(PG_FUNCTION_ARGS);
47 
48 typedef struct GEOMDUMPNODE_T
49 {
50  uint32_t idx;
52 }
54 
55 #define MAXDEPTH 32
56 typedef struct GEOMDUMPSTATE
57 {
58  int stacklen;
61 }
63 
64 #define PUSH(x,y) ((x)->stack[(x)->stacklen++]=(y))
65 #define LAST(x) ((x)->stack[(x)->stacklen-1])
66 #define POP(x) (--((x)->stacklen))
67 
68 
70 Datum LWGEOM_dump(PG_FUNCTION_ARGS)
71 {
72  GSERIALIZED *pglwgeom;
73  LWCOLLECTION *lwcoll;
74  LWGEOM *lwgeom;
75  FuncCallContext *funcctx;
76  GEOMDUMPSTATE *state;
77  GEOMDUMPNODE *node;
78  TupleDesc tupdesc;
79  HeapTuple tuple;
80  MemoryContext oldcontext, newcontext;
81  Datum result;
82  char address[256];
83  char *ptr;
84  int i;
85  char *values[2];
86 
87  if (SRF_IS_FIRSTCALL())
88  {
89  funcctx = SRF_FIRSTCALL_INIT();
90  newcontext = funcctx->multi_call_memory_ctx;
91 
92  oldcontext = MemoryContextSwitchTo(newcontext);
93 
94  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
95  lwgeom = lwgeom_from_gserialized(pglwgeom);
96 
97  /* Create function state */
98  state = lwalloc(sizeof(GEOMDUMPSTATE));
99  state->root = lwgeom;
100  state->stacklen=0;
101 
102  if ( lwgeom_is_collection(lwgeom) )
103  {
104  /*
105  * Push a GEOMDUMPNODE on the state stack
106  */
107  node = lwalloc(sizeof(GEOMDUMPNODE));
108  node->idx=0;
109  node->geom = lwgeom;
110  PUSH(state, node);
111  }
112 
113  funcctx->user_fctx = state;
114 
115  /*
116  * Build a tuple description for an
117  * geometry_dump tuple
118  */
119  get_call_result_type(fcinfo, 0, &tupdesc);
120  BlessTupleDesc(tupdesc);
121 
122  /*
123  * generate attribute metadata needed later to produce
124  * tuples from raw C strings
125  */
126  funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);;
127 
128  MemoryContextSwitchTo(oldcontext);
129  }
130 
131  /* stuff done on every call of the function */
132  funcctx = SRF_PERCALL_SETUP();
133  newcontext = funcctx->multi_call_memory_ctx;
134 
135  /* get state */
136  state = funcctx->user_fctx;
137 
138  /* Handled simple geometries */
139  if ( ! state->root ) SRF_RETURN_DONE(funcctx);
140  /* Return nothing for empties */
141  if ( lwgeom_is_empty(state->root) ) SRF_RETURN_DONE(funcctx);
142  if ( ! lwgeom_is_collection(state->root) )
143  {
144  values[0] = "{}";
145  values[1] = lwgeom_to_hexwkb_buffer(state->root, WKB_EXTENDED);
146  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
147  result = HeapTupleGetDatum(tuple);
148 
149  state->root = NULL;
150  SRF_RETURN_NEXT(funcctx, result);
151  }
152 
153  while (1)
154  {
155  node = LAST(state);
156  lwcoll = (LWCOLLECTION*)node->geom;
157 
158  if ( node->idx < lwcoll->ngeoms )
159  {
160  lwgeom = lwcoll->geoms[node->idx];
161  if ( ! lwgeom_is_collection(lwgeom) )
162  {
163  /* write address of current geom */
164  ptr=address;
165  *ptr++='{';
166  for (i=0; i<state->stacklen; i++)
167  {
168  if ( i ) ptr += sprintf(ptr, ",");
169  ptr += sprintf(ptr, "%d", state->stack[i]->idx+1);
170  }
171  *ptr++='}';
172  *ptr='\0';
173 
174  break;
175  }
176 
177  /*
178  * It's a collection, increment index
179  * of current node, push a new one on the
180  * stack
181  */
182 
183  if (state->stacklen > MAXDEPTH)
184  elog(ERROR, "Unable to dump overly nested collection.");
185 
186  oldcontext = MemoryContextSwitchTo(newcontext);
187 
188  node = lwalloc(sizeof(GEOMDUMPNODE));
189  node->idx=0;
190  node->geom = lwgeom;
191  PUSH(state, node);
192 
193  MemoryContextSwitchTo(oldcontext);
194 
195  continue;
196  }
197 
198  if ( ! POP(state) ) SRF_RETURN_DONE(funcctx);
199  LAST(state)->idx++;
200  }
201 
202  lwgeom->srid = state->root->srid;
203 
204  values[0] = address;
205  values[1] = lwgeom_to_hexwkb_buffer(lwgeom, WKB_EXTENDED);
206  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
207  result = TupleGetDatum(funcctx->slot, tuple);
208  node->idx++;
209  SRF_RETURN_NEXT(funcctx, result);
210 }
211 
213 {
214  uint32_t ringnum;
216 };
217 
219 Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
220 {
221  GSERIALIZED *pglwgeom;
222  LWGEOM *lwgeom;
223  FuncCallContext *funcctx;
224  struct POLYDUMPSTATE *state;
225  TupleDesc tupdesc;
226  HeapTuple tuple;
227  MemoryContext oldcontext, newcontext;
228  Datum result;
229  char address[256];
230  char *values[2];
231 
232  if (SRF_IS_FIRSTCALL())
233  {
234  funcctx = SRF_FIRSTCALL_INIT();
235  newcontext = funcctx->multi_call_memory_ctx;
236 
237  oldcontext = MemoryContextSwitchTo(newcontext);
238 
239  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
240  if ( gserialized_get_type(pglwgeom) != POLYGONTYPE )
241  {
242  elog(ERROR, "Input is not a polygon");
243  }
244 
245  lwgeom = lwgeom_from_gserialized(pglwgeom);
246 
247  /* Create function state */
248  state = lwalloc(sizeof(struct POLYDUMPSTATE));
249  state->poly = lwgeom_as_lwpoly(lwgeom);
250  assert (state->poly);
251  state->ringnum=0;
252 
253  funcctx->user_fctx = state;
254 
255  /*
256  * Build a tuple description for an
257  * geometry_dump tuple
258  */
259  get_call_result_type(fcinfo, 0, &tupdesc);
260  BlessTupleDesc(tupdesc);
261 
262  /*
263  * generate attribute metadata needed later to produce
264  * tuples from raw C strings
265  */
266  funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);;
267 
268  MemoryContextSwitchTo(oldcontext);
269  }
270 
271  /* stuff done on every call of the function */
272  funcctx = SRF_PERCALL_SETUP();
273  newcontext = funcctx->multi_call_memory_ctx;
274 
275  /* get state */
276  state = funcctx->user_fctx;
277 
278  /* Loop through polygon rings */
279  while (state->ringnum < state->poly->nrings )
280  {
281  LWPOLY* poly = state->poly;
282  POINTARRAY *ring;
283  LWGEOM* ringgeom;
284 
285  /* Switch to an appropriate memory context for POINTARRAY
286  * cloning and hexwkb allocation */
287  oldcontext = MemoryContextSwitchTo(newcontext);
288 
289  /* We need a copy of input ring here */
290  ring = ptarray_clone_deep(poly->rings[state->ringnum]);
291 
292  /* Construct another polygon with shell only */
293  ringgeom = (LWGEOM*)lwpoly_construct(
294  poly->srid,
295  NULL, /* TODO: could use input bounding box here */
296  1, /* one ring */
297  &ring);
298 
299  /* Write path as ``{ <ringnum> }'' */
300  sprintf(address, "{%d}", state->ringnum);
301 
302  values[0] = address;
303  values[1] = lwgeom_to_hexwkb_buffer(ringgeom, WKB_EXTENDED);
304 
305  MemoryContextSwitchTo(oldcontext);
306 
307  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
308  result = HeapTupleGetDatum(tuple);
309  ++state->ringnum;
310  SRF_RETURN_NEXT(funcctx, result);
311  }
312 
313  SRF_RETURN_DONE(funcctx);
314 
315 }
316 
317 
318 /*
319 * Break an object up into smaller objects of no more than N vertices
320 */
322 Datum ST_Subdivide(PG_FUNCTION_ARGS)
323 {
324  typedef struct
325  {
326  int nextgeom;
327  int numgeoms;
328  LWCOLLECTION *col;
329  } collection_fctx;
330 
331  FuncCallContext *funcctx;
332  collection_fctx *fctx;
333  MemoryContext oldcontext;
334 
335  /* stuff done only on the first call of the function */
336  if (SRF_IS_FIRSTCALL())
337  {
338  GSERIALIZED *gser;
339  LWGEOM *geom;
340  LWCOLLECTION *col;
341  /* default to maxvertices < page size */
342  int maxvertices = 128;
343  double gridSize = -1;
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  * Get the gridSize value
367  */
368  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
369  gridSize = PG_GETARG_FLOAT8(2);
370 
371  /*
372  * Compute the subdivision of the geometry
373  */
374  col = lwgeom_subdivide_prec(geom, maxvertices, gridSize);
375 
376  if ( ! col )
377  SRF_RETURN_DONE(funcctx);
378 
379  /* allocate memory for user context */
380  fctx = (collection_fctx *) palloc(sizeof(collection_fctx));
381 
382  /* initialize state */
383  fctx->nextgeom = 0;
384  fctx->numgeoms = col->ngeoms;
385  fctx->col = col;
386 
387  /* save user context, switch back to function context */
388  funcctx->user_fctx = fctx;
389  MemoryContextSwitchTo(oldcontext);
390  }
391 
392  /* stuff done on every call of the function */
393  funcctx = SRF_PERCALL_SETUP();
394  fctx = funcctx->user_fctx;
395 
396  if (fctx->nextgeom < fctx->numgeoms)
397  {
398  GSERIALIZED *gpart = geometry_serialize(fctx->col->geoms[fctx->nextgeom]);
399  fctx->nextgeom++;
400  SRF_RETURN_NEXT(funcctx, PointerGetDatum(gpart));
401  }
402  else
403  {
404  /* do when there is no more left */
405  SRF_RETURN_DONE(funcctx);
406  }
407 }
408 
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:268
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:118
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:643
char * lwgeom_to_hexwkb_buffer(const LWGEOM *geom, uint8_t variant)
Definition: lwout_wkb.c:845
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM contains sub-geometries or not This basically just checks that the struct ...
Definition: lwgeom.c:1097
#define POLYGONTYPE
Definition: liblwgeom.h:104
#define WKB_EXTENDED
Definition: liblwgeom.h:2209
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
LWCOLLECTION * lwgeom_subdivide_prec(const LWGEOM *geom, uint32_t maxvertices, double gridSize)
Definition: lwgeom.c:2591
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:43
This library is the generic geometry handling section of PostGIS.
#define MAXDEPTH
Definition: lwgeom_dump.c:55
Datum ST_Subdivide(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:322
struct GEOMDUMPNODE_T GEOMDUMPNODE
#define PUSH(x, y)
Definition: lwgeom_dump.c:64
#define LAST(x)
Definition: lwgeom_dump.c:65
Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:219
struct GEOMDUMPSTATE GEOMDUMPSTATE
PG_FUNCTION_INFO_V1(LWGEOM_dump)
Datum LWGEOM_dump(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:70
#define POP(x)
Definition: lwgeom_dump.c:66
if(!(yy_init))
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:199
uint32_t idx
Definition: lwgeom_dump.c:50
LWGEOM * geom
Definition: lwgeom_dump.c:51
LWGEOM * root
Definition: lwgeom_dump.c:60
GEOMDUMPNODE * stack[MAXDEPTH]
Definition: lwgeom_dump.c:59
uint32_t ngeoms
Definition: liblwgeom.h:580
LWGEOM ** geoms
Definition: liblwgeom.h:575
int32_t srid
Definition: liblwgeom.h:460
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
int32_t srid
Definition: liblwgeom.h:520
uint32_t ringnum
Definition: lwgeom_dump.c:214
LWPOLY * poly
Definition: lwgeom_dump.c:215