PostGIS  2.4.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 <errno.h>
31 #include <assert.h>
32 
33 #include "postgres.h"
34 #include "fmgr.h"
35 #include "utils/elog.h"
36 #include "utils/array.h"
37 #include "utils/geo_decls.h"
38 #include "funcapi.h"
39 
40 #include "../postgis_config.h"
41 #include "liblwgeom.h"
42 #include "lwgeom_pg.h"
43 
44 
45 Datum LWGEOM_dump(PG_FUNCTION_ARGS);
46 Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS);
47 Datum ST_Subdivide(PG_FUNCTION_ARGS);
48 
49 typedef struct GEOMDUMPNODE_T
50 {
51  int idx;
53 }
55 
56 #define MAXDEPTH 32
57 typedef struct GEOMDUMPSTATE
58 {
59  int stacklen;
62 }
64 
65 #define PUSH(x,y) ((x)->stack[(x)->stacklen++]=(y))
66 #define LAST(x) ((x)->stack[(x)->stacklen-1])
67 #define POP(x) (--((x)->stacklen))
68 
69 
71 Datum LWGEOM_dump(PG_FUNCTION_ARGS)
72 {
73  GSERIALIZED *pglwgeom;
74  LWCOLLECTION *lwcoll;
75  LWGEOM *lwgeom;
76  FuncCallContext *funcctx;
77  GEOMDUMPSTATE *state;
78  GEOMDUMPNODE *node;
79  TupleDesc tupdesc;
80  HeapTuple tuple;
81  AttInMetadata *attinmeta;
82  MemoryContext oldcontext, newcontext;
83  Datum result;
84  char address[256];
85  char *ptr;
86  uint32 i;
87  char *values[2];
88 
89  if (SRF_IS_FIRSTCALL())
90  {
91  funcctx = SRF_FIRSTCALL_INIT();
92  newcontext = funcctx->multi_call_memory_ctx;
93 
94  oldcontext = MemoryContextSwitchTo(newcontext);
95 
96  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
97  lwgeom = lwgeom_from_gserialized(pglwgeom);
98 
99  /* Create function state */
100  state = lwalloc(sizeof(GEOMDUMPSTATE));
101  state->root = lwgeom;
102  state->stacklen=0;
103 
104  if ( lwgeom_is_collection(lwgeom) )
105  {
106  /*
107  * Push a GEOMDUMPNODE on the state stack
108  */
109  node = lwalloc(sizeof(GEOMDUMPNODE));
110  node->idx=0;
111  node->geom = lwgeom;
112  PUSH(state, node);
113  }
114 
115  funcctx->user_fctx = state;
116 
117  /*
118  * Build a tuple description for an
119  * geometry_dump tuple
120  */
121  get_call_result_type(fcinfo, 0, &tupdesc);
122  BlessTupleDesc(tupdesc);
123 
124  /*
125  * generate attribute metadata needed later to produce
126  * tuples from raw C strings
127  */
128  attinmeta = TupleDescGetAttInMetadata(tupdesc);
129  funcctx->attinmeta = attinmeta;
130 
131  MemoryContextSwitchTo(oldcontext);
132  }
133 
134  /* stuff done on every call of the function */
135  funcctx = SRF_PERCALL_SETUP();
136  newcontext = funcctx->multi_call_memory_ctx;
137 
138  /* get state */
139  state = funcctx->user_fctx;
140 
141  /* Handled simple geometries */
142  if ( ! state->root ) SRF_RETURN_DONE(funcctx);
143  /* Return nothing for empties */
144  if ( lwgeom_is_empty(state->root) ) SRF_RETURN_DONE(funcctx);
145  if ( ! lwgeom_is_collection(state->root) )
146  {
147  values[0] = "{}";
148  values[1] = lwgeom_to_hexwkb(state->root, WKB_EXTENDED, 0);
149  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
150  result = HeapTupleGetDatum(tuple);
151 
152  state->root = NULL;
153  SRF_RETURN_NEXT(funcctx, result);
154  }
155 
156  while (1)
157  {
158  node = LAST(state);
159  lwcoll = (LWCOLLECTION*)node->geom;
160 
161  if ( node->idx < lwcoll->ngeoms )
162  {
163  lwgeom = lwcoll->geoms[node->idx];
164  if ( ! lwgeom_is_collection(lwgeom) )
165  {
166  /* write address of current geom */
167  ptr=address;
168  *ptr++='{';
169  for (i=0; i<state->stacklen; i++)
170  {
171  if ( i ) ptr += sprintf(ptr, ",");
172  ptr += sprintf(ptr, "%d", state->stack[i]->idx+1);
173  }
174  *ptr++='}';
175  *ptr='\0';
176 
177  break;
178  }
179 
180  /*
181  * It's a collection, increment index
182  * of current node, push a new one on the
183  * stack
184  */
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(lwgeom, WKB_EXTENDED, 0);
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  int 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  AttInMetadata *attinmeta;
228  MemoryContext oldcontext, newcontext;
229  Datum result;
230  char address[256];
231  char *values[2];
232 
233  if (SRF_IS_FIRSTCALL())
234  {
235  funcctx = SRF_FIRSTCALL_INIT();
236  newcontext = funcctx->multi_call_memory_ctx;
237 
238  oldcontext = MemoryContextSwitchTo(newcontext);
239 
240  pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
241  if ( gserialized_get_type(pglwgeom) != POLYGONTYPE )
242  {
243  elog(ERROR, "Input is not a polygon");
244  }
245 
246  lwgeom = lwgeom_from_gserialized(pglwgeom);
247 
248  /* Create function state */
249  state = lwalloc(sizeof(struct POLYDUMPSTATE));
250  state->poly = lwgeom_as_lwpoly(lwgeom);
251  assert (state->poly);
252  state->ringnum=0;
253 
254  funcctx->user_fctx = state;
255 
256  /*
257  * Build a tuple description for an
258  * geometry_dump tuple
259  */
260  get_call_result_type(fcinfo, 0, &tupdesc);
261  BlessTupleDesc(tupdesc);
262 
263  /*
264  * generate attribute metadata needed later to produce
265  * tuples from raw C strings
266  */
267  attinmeta = TupleDescGetAttInMetadata(tupdesc);
268  funcctx->attinmeta = attinmeta;
269 
270  MemoryContextSwitchTo(oldcontext);
271  }
272 
273  /* stuff done on every call of the function */
274  funcctx = SRF_PERCALL_SETUP();
275  newcontext = funcctx->multi_call_memory_ctx;
276 
277  /* get state */
278  state = funcctx->user_fctx;
279 
280  /* Loop trough polygon rings */
281  while (state->ringnum < state->poly->nrings )
282  {
283  LWPOLY* poly = state->poly;
284  POINTARRAY *ring;
285  LWGEOM* ringgeom;
286 
287  /* Switch to an appropriate memory context for POINTARRAY
288  * cloning and hexwkb allocation */
289  oldcontext = MemoryContextSwitchTo(newcontext);
290 
291  /* We need a copy of input ring here */
292  ring = ptarray_clone_deep(poly->rings[state->ringnum]);
293 
294  /* Construct another polygon with shell only */
295  ringgeom = (LWGEOM*)lwpoly_construct(
296  poly->srid,
297  NULL, /* TODO: could use input bounding box here */
298  1, /* one ring */
299  &ring);
300 
301  /* Write path as ``{ <ringnum> }'' */
302  sprintf(address, "{%d}", state->ringnum);
303 
304  values[0] = address;
305  values[1] = lwgeom_to_hexwkb(ringgeom, WKB_EXTENDED, 0);
306 
307  MemoryContextSwitchTo(oldcontext);
308 
309  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
310  result = HeapTupleGetDatum(tuple);
311  ++state->ringnum;
312  SRF_RETURN_NEXT(funcctx, result);
313  }
314 
315  SRF_RETURN_DONE(funcctx);
316 
317 }
318 
319 
321 {
322  int geomnum;
324 };
325 
326 /*
327 * Break an object up into smaller objects of no more than N vertices
328 */
330 Datum ST_Subdivide(PG_FUNCTION_ARGS)
331 {
332 #if POSTGIS_GEOS_VERSION < 35
333 
334  elog(ERROR, "The GEOS version this PostGIS binary "
335  "was compiled against (%d) doesn't support "
336  "'%s' function (3.5.0+ required)",
337  POSTGIS_GEOS_VERSION, __func__);
338  PG_RETURN_NULL();
339 
340 #else /* POSTGIS_GEOS_VERSION >= 35 */
341 
342  typedef struct
343  {
344  int nextgeom;
345  int numgeoms;
346  LWCOLLECTION *col;
347  } collection_fctx;
348 
349  FuncCallContext *funcctx;
350  collection_fctx *fctx;
351  MemoryContext oldcontext;
352 
353  /* stuff done only on the first call of the function */
354  if (SRF_IS_FIRSTCALL())
355  {
356  GSERIALIZED *gser;
357  LWGEOM *geom;
358  LWCOLLECTION *col;
359  int maxvertices = 256;
360 
361  /* create a function context for cross-call persistence */
362  funcctx = SRF_FIRSTCALL_INIT();
363 
364  /*
365  * switch to memory context appropriate for multiple function calls
366  */
367  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
368 
369  /*
370  * Get the geometry value
371  */
372  gser = PG_GETARG_GSERIALIZED_P(0);
373  geom = lwgeom_from_gserialized(gser);
374 
375  /*
376  * Get the max vertices value
377  */
378  if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
379  maxvertices = PG_GETARG_INT32(1);
380 
381  /*
382  * Compute the subdivision of the geometry
383  */
384  col = lwgeom_subdivide(geom, maxvertices);
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 #endif /* POSTGIS_GEOS_VERSION >= 35 */
419 }
420 
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:86
char * lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out)
Definition: lwout_wkb.c:848
Datum LWGEOM_dump(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:71
#define POSTGIS_GEOS_VERSION
Definition: sqldefines.h:10
#define MAXDEPTH
Definition: lwgeom_dump.c:56
#define POP(x)
Definition: lwgeom_dump.c:67
#define PUSH(x, y)
Definition: lwgeom_dump.c:65
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:1040
LWGEOM * root
Definition: lwgeom_dump.c:61
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
#define POLYGONTYPE
Definition: liblwgeom.h:87
LWCOLLECTION * col
Definition: lwgeom_dump.c:323
PG_FUNCTION_INFO_V1(LWGEOM_dump)
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:174
int32_t srid
Definition: liblwgeom.h:399
struct GEOMDUMPSTATE GEOMDUMPSTATE
Datum ST_Subdivide(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:330
LWPOLY * lwpoly_construct(int srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:43
LWGEOM ** geoms
Definition: liblwgeom.h:509
Datum LWGEOM_dump_rings(PG_FUNCTION_ARGS)
Definition: lwgeom_dump.c:219
POINTARRAY ** rings
Definition: liblwgeom.h:457
POINTARRAY * ptarray_clone_deep(const POINTARRAY *ptarray)
Deep clone a pointarray (also clones serialized pointlist)
Definition: ptarray.c:630
int nrings
Definition: liblwgeom.h:455
GEOMDUMPNODE * stack[MAXDEPTH]
Definition: lwgeom_dump.c:60
#define LAST(x)
Definition: lwgeom_dump.c:66
#define WKB_EXTENDED
Definition: liblwgeom.h:2076
LWGEOM * geom
Definition: lwgeom_dump.c:52
LWPOLY * poly
Definition: lwgeom_dump.c:215
LWCOLLECTION * lwgeom_subdivide(const LWGEOM *geom, int maxvertices)
Definition: lwgeom.c:2054
int32_t srid
Definition: liblwgeom.h:454
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
void * lwalloc(size_t size)
Definition: lwutil.c:229
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:1346
struct GEOMDUMPNODE_T GEOMDUMPNODE
if(!(yy_init))
This library is the generic geometry handling section of PostGIS.