PostGIS  3.3.9dev-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  AttInMetadata *attinmeta;
81  MemoryContext oldcontext, newcontext;
82  Datum result;
83  char address[256];
84  char *ptr;
85  int i;
86  char *values[2];
87 
88  if (SRF_IS_FIRSTCALL())
89  {
90  funcctx = SRF_FIRSTCALL_INIT();
91  newcontext = funcctx->multi_call_memory_ctx;
92 
93  oldcontext = MemoryContextSwitchTo(newcontext);
94 
95  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
96  lwgeom = lwgeom_from_gserialized(pglwgeom);
97 
98  /* Create function state */
99  state = lwalloc(sizeof(GEOMDUMPSTATE));
100  state->root = lwgeom;
101  state->stacklen=0;
102 
103  if ( lwgeom_is_collection(lwgeom) )
104  {
105  /*
106  * Push a GEOMDUMPNODE on the state stack
107  */
108  node = lwalloc(sizeof(GEOMDUMPNODE));
109  node->idx=0;
110  node->geom = lwgeom;
111  PUSH(state, node);
112  }
113 
114  funcctx->user_fctx = state;
115 
116  /*
117  * Build a tuple description for an
118  * geometry_dump tuple
119  */
120  get_call_result_type(fcinfo, 0, &tupdesc);
121  BlessTupleDesc(tupdesc);
122 
123  /*
124  * generate attribute metadata needed later to produce
125  * tuples from raw C strings
126  */
127  attinmeta = TupleDescGetAttInMetadata(tupdesc);
128  funcctx->attinmeta = attinmeta;
129 
130  MemoryContextSwitchTo(oldcontext);
131  }
132 
133  /* stuff done on every call of the function */
134  funcctx = SRF_PERCALL_SETUP();
135  newcontext = funcctx->multi_call_memory_ctx;
136 
137  /* get state */
138  state = funcctx->user_fctx;
139 
140  /* Handled simple geometries */
141  if ( ! state->root ) SRF_RETURN_DONE(funcctx);
142  /* Return nothing for empties */
143  if ( lwgeom_is_empty(state->root) ) SRF_RETURN_DONE(funcctx);
144  if ( ! lwgeom_is_collection(state->root) )
145  {
146  values[0] = "{}";
147  values[1] = lwgeom_to_hexwkb_buffer(state->root, WKB_EXTENDED);
148  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
149  result = HeapTupleGetDatum(tuple);
150 
151  state->root = NULL;
152  SRF_RETURN_NEXT(funcctx, result);
153  }
154 
155  while (1)
156  {
157  node = LAST(state);
158  lwcoll = (LWCOLLECTION*)node->geom;
159 
160  if ( node->idx < lwcoll->ngeoms )
161  {
162  lwgeom = lwcoll->geoms[node->idx];
163  if ( ! lwgeom_is_collection(lwgeom) )
164  {
165  /* write address of current geom */
166  ptr=address;
167  *ptr++='{';
168  for (i=0; i<state->stacklen; i++)
169  {
170  if ( i ) ptr += sprintf(ptr, ",");
171  ptr += sprintf(ptr, "%d", state->stack[i]->idx+1);
172  }
173  *ptr++='}';
174  *ptr='\0';
175 
176  break;
177  }
178 
179  /*
180  * It's a collection, increment index
181  * of current node, push a new one on the
182  * stack
183  */
184 
185  if (state->stacklen > MAXDEPTH)
186  elog(ERROR, "Unable to dump overly nested collection.");
187 
188  oldcontext = MemoryContextSwitchTo(newcontext);
189 
190  node = lwalloc(sizeof(GEOMDUMPNODE));
191  node->idx=0;
192  node->geom = lwgeom;
193  PUSH(state, node);
194 
195  MemoryContextSwitchTo(oldcontext);
196 
197  continue;
198  }
199 
200  if ( ! POP(state) ) SRF_RETURN_DONE(funcctx);
201  LAST(state)->idx++;
202  }
203 
204  lwgeom->srid = state->root->srid;
205 
206  values[0] = address;
207  values[1] = lwgeom_to_hexwkb_buffer(lwgeom, WKB_EXTENDED);
208  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
209  result = TupleGetDatum(funcctx->slot, tuple);
210  node->idx++;
211  SRF_RETURN_NEXT(funcctx, result);
212 }
213 
215 {
216  uint32_t ringnum;
218 };
219 
221 Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
222 {
223  GSERIALIZED *pglwgeom;
224  LWGEOM *lwgeom;
225  FuncCallContext *funcctx;
226  struct POLYDUMPSTATE *state;
227  TupleDesc tupdesc;
228  HeapTuple tuple;
229  AttInMetadata *attinmeta;
230  MemoryContext oldcontext, newcontext;
231  Datum result;
232  char address[256];
233  char *values[2];
234 
235  if (SRF_IS_FIRSTCALL())
236  {
237  funcctx = SRF_FIRSTCALL_INIT();
238  newcontext = funcctx->multi_call_memory_ctx;
239 
240  oldcontext = MemoryContextSwitchTo(newcontext);
241 
242  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
243  if ( gserialized_get_type(pglwgeom) != POLYGONTYPE )
244  {
245  elog(ERROR, "Input is not a polygon");
246  }
247 
248  lwgeom = lwgeom_from_gserialized(pglwgeom);
249 
250  /* Create function state */
251  state = lwalloc(sizeof(struct POLYDUMPSTATE));
252  state->poly = lwgeom_as_lwpoly(lwgeom);
253  assert (state->poly);
254  state->ringnum=0;
255 
256  funcctx->user_fctx = state;
257 
258  /*
259  * Build a tuple description for an
260  * geometry_dump tuple
261  */
262  get_call_result_type(fcinfo, 0, &tupdesc);
263  BlessTupleDesc(tupdesc);
264 
265  /*
266  * generate attribute metadata needed later to produce
267  * tuples from raw C strings
268  */
269  attinmeta = TupleDescGetAttInMetadata(tupdesc);
270  funcctx->attinmeta = attinmeta;
271 
272  MemoryContextSwitchTo(oldcontext);
273  }
274 
275  /* stuff done on every call of the function */
276  funcctx = SRF_PERCALL_SETUP();
277  newcontext = funcctx->multi_call_memory_ctx;
278 
279  /* get state */
280  state = funcctx->user_fctx;
281 
282  /* Loop trough polygon rings */
283  while (state->ringnum < state->poly->nrings )
284  {
285  LWPOLY* poly = state->poly;
286  POINTARRAY *ring;
287  LWGEOM* ringgeom;
288 
289  /* Switch to an appropriate memory context for POINTARRAY
290  * cloning and hexwkb allocation */
291  oldcontext = MemoryContextSwitchTo(newcontext);
292 
293  /* We need a copy of input ring here */
294  ring = ptarray_clone_deep(poly->rings[state->ringnum]);
295 
296  /* Construct another polygon with shell only */
297  ringgeom = (LWGEOM*)lwpoly_construct(
298  poly->srid,
299  NULL, /* TODO: could use input bounding box here */
300  1, /* one ring */
301  &ring);
302 
303  /* Write path as ``{ <ringnum> }'' */
304  sprintf(address, "{%d}", state->ringnum);
305 
306  values[0] = address;
307  values[1] = lwgeom_to_hexwkb_buffer(ringgeom, WKB_EXTENDED);
308 
309  MemoryContextSwitchTo(oldcontext);
310 
311  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
312  result = HeapTupleGetDatum(tuple);
313  ++state->ringnum;
314  SRF_RETURN_NEXT(funcctx, result);
315  }
316 
317  SRF_RETURN_DONE(funcctx);
318 
319 }
320 
321 
323 {
324  int geomnum;
326 };
327 
328 /*
329 * Break an object up into smaller objects of no more than N vertices
330 */
332 Datum ST_Subdivide(PG_FUNCTION_ARGS)
333 {
334  typedef struct
335  {
336  int nextgeom;
337  int numgeoms;
338  LWCOLLECTION *col;
339  } collection_fctx;
340 
341  FuncCallContext *funcctx;
342  collection_fctx *fctx;
343  MemoryContext oldcontext;
344 
345  /* stuff done only on the first call of the function */
346  if (SRF_IS_FIRSTCALL())
347  {
348  GSERIALIZED *gser;
349  LWGEOM *geom;
350  LWCOLLECTION *col;
351  /* default to maxvertices < page size */
352  int maxvertices = 128;
353  double gridSize = -1;
354 
355  /* create a function context for cross-call persistence */
356  funcctx = SRF_FIRSTCALL_INIT();
357 
358  /*
359  * switch to memory context appropriate for multiple function calls
360  */
361  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
362 
363  /*
364  * Get the geometry value
365  */
366  gser = PG_GETARG_GSERIALIZED_P(0);
367  geom = lwgeom_from_gserialized(gser);
368 
369  /*
370  * Get the max vertices value
371  */
372  if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
373  maxvertices = PG_GETARG_INT32(1);
374 
375  /*
376  * Get the gridSize value
377  */
378  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
379  gridSize = PG_GETARG_FLOAT8(2);
380 
381  /*
382  * Compute the subdivision of the geometry
383  */
384  col = lwgeom_subdivide_prec(geom, maxvertices, gridSize);
385 
386  if ( ! col )
387  SRF_RETURN_DONE(funcctx);
388 
389  /* allocate memory for user context */
390  fctx = (collection_fctx *) palloc(sizeof(collection_fctx));
391 
392  /* initialize state */
393  fctx->nextgeom = 0;
394  fctx->numgeoms = col->ngeoms;
395  fctx->col = col;
396 
397  /* save user context, switch back to function context */
398  funcctx->user_fctx = fctx;
399  MemoryContextSwitchTo(oldcontext);
400  }
401 
402  /* stuff done on every call of the function */
403  funcctx = SRF_PERCALL_SETUP();
404  fctx = funcctx->user_fctx;
405 
406  if (fctx->nextgeom < fctx->numgeoms)
407  {
408  GSERIALIZED *gpart = geometry_serialize(fctx->col->geoms[fctx->nextgeom]);
409  fctx->nextgeom++;
410  SRF_RETURN_NEXT(funcctx, PointerGetDatum(gpart));
411  }
412  else
413  {
414  /* do when there is no more left */
415  SRF_RETURN_DONE(funcctx);
416  }
417 }
418 
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:239
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:89
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:634
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 can contain sub-geometries or not.
Definition: lwgeom.c:1097
#define POLYGONTYPE
Definition: liblwgeom.h:119
#define WKB_EXTENDED
Definition: liblwgeom.h:2159
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:2488
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:332
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:221
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:203
LWCOLLECTION * col
Definition: lwgeom_dump.c:325
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:595
LWGEOM ** geoms
Definition: liblwgeom.h:590
int32_t srid
Definition: liblwgeom.h:475
POINTARRAY ** rings
Definition: liblwgeom.h:534
uint32_t nrings
Definition: liblwgeom.h:539
int32_t srid
Definition: liblwgeom.h:535
uint32_t ringnum
Definition: lwgeom_dump.c:216
LWPOLY * poly
Definition: lwgeom_dump.c:217