PostGIS  3.1.6dev-r@@SVN_REVISION@@
lwgeom_accum.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 2009 Paul Ramsey <pramsey@opengeo.org>
22  *
23  **********************************************************************/
24 
25 
26 #include "postgres.h"
27 #include "fmgr.h"
28 #include "funcapi.h"
29 #include "access/tupmacs.h"
30 #include "utils/datum.h"
31 #include "utils/array.h"
32 #include "utils/lsyscache.h"
33 
34 #include "../postgis_config.h"
35 
36 #include "liblwgeom.h"
37 #include "lwgeom_geos.h"
38 #include "lwgeom_pg.h"
39 #include "lwgeom_transform.h"
40 #include "lwgeom_accum.h"
41 
42 /* Local prototypes */
43 Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1);
44 Datum PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2);
45 Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS);
46 Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS);
47 Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS);
48 Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS);
49 Datum pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS);
50 Datum pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS);
51 
52 /* External prototypes */
53 Datum pgis_union_geometry_array(PG_FUNCTION_ARGS);
54 Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS);
55 Datum polygonize_garray(PG_FUNCTION_ARGS);
56 Datum clusterintersecting_garray(PG_FUNCTION_ARGS);
57 Datum cluster_within_distance_garray(PG_FUNCTION_ARGS);
58 Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS);
59 
60 
67 Datum
68 pgis_geometry_accum_transfn(PG_FUNCTION_ARGS)
69 {
70  MemoryContext aggcontext, old;
71  CollectionBuildState *state;
72  LWGEOM *geom = NULL;
73  GSERIALIZED *gser = NULL;
74  Datum argType = get_fn_expr_argtype(fcinfo->flinfo, 1);
75  double gridSize = -1.0;
76 
77  if (argType == InvalidOid)
78  ereport(ERROR,
79  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
80  errmsg("could not determine input data type")));
81 
82  if ( ! AggCheckCallContext(fcinfo, &aggcontext) )
83  {
84  /* cannot be called directly because of dummy-type argument */
85  elog(ERROR, "%s called in non-aggregate context", __func__);
86  aggcontext = NULL; /* keep compiler quiet */
87  }
88 
89  if ( PG_ARGISNULL(0) )
90  {
91  int n = ((PG_NARGS()-2) <= CollectionBuildStateDataSize) ? (PG_NARGS()-2) : CollectionBuildStateDataSize;
92 
93  state = MemoryContextAlloc(aggcontext, sizeof(CollectionBuildState));
94  state->geoms = NULL;
95  state->geomOid = argType;
96  state->gridSize = gridSize;
97 
98  for (int i = 0; i < n; i++)
99  {
100  Datum argument = PG_GETARG_DATUM(i+2);
101  Oid dataOid = get_fn_expr_argtype(fcinfo->flinfo, i+2);
102  old = MemoryContextSwitchTo(aggcontext);
103  state->data[i] = datumCopy(argument, get_typbyval(dataOid), get_typlen(dataOid));
104  MemoryContextSwitchTo(old);
105  }
106  }
107  else
108  {
109  state = (CollectionBuildState*) PG_GETARG_POINTER(0);
110  }
111 
112  if (!PG_ARGISNULL(1))
113  gser = PG_GETARG_GSERIALIZED_P(1);
114 
115  if (PG_NARGS()>2 && !PG_ARGISNULL(2))
116  {
117  gridSize = PG_GETARG_FLOAT8(2);
118  /*lwnotice("Passed gridSize %g", gridSize);*/
119  if ( gridSize > state->gridSize ) state->gridSize = gridSize;
120  }
121 
122  /* Take a copy of the geometry into the aggregate context */
123  old = MemoryContextSwitchTo(aggcontext);
124  if (gser)
126 
127  /* Initialize or append to list as necessary */
128  if (state->geoms)
129  state->geoms = lappend(state->geoms, geom);
130  else
131  state->geoms = list_make1(geom);
132 
133  MemoryContextSwitchTo(old);
134 
135  PG_RETURN_POINTER(state);
136 }
137 
138 
139 Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo);
140 
145 Datum
146 pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, __attribute__((__unused__)) FunctionCallInfo fcinfo)
147 {
148  ListCell *l;
149  size_t nelems = 0;
150  Datum *elems;
151  bool *nulls;
152  int16 elmlen;
153  bool elmbyval;
154  char elmalign;
155  size_t i = 0;
156  ArrayType *arr;
157  int dims[1];
158  int lbs[1] = {1};
159 
160  /* cannot be called directly because of internal-type argument */
161  Assert(fcinfo->context &&
162  (IsA(fcinfo->context, AggState) ||
163  IsA(fcinfo->context, WindowAggState))
164  );
165 
166  /* Retrieve geometry type metadata */
167  get_typlenbyvalalign(state->geomOid, &elmlen, &elmbyval, &elmalign);
168  nelems = list_length(state->geoms);
169 
170  /* Build up an array, because that's what we pass to all the */
171  /* specific final functions */
172  elems = palloc(nelems * sizeof(Datum));
173  nulls = palloc(nelems * sizeof(bool));
174 
175  foreach (l, state->geoms)
176  {
177  LWGEOM *geom = (LWGEOM*)(lfirst(l));
178  Datum elem = (Datum)0;
179  bool isNull = true;
180  if (geom)
181  {
182  GSERIALIZED *gser = geometry_serialize(geom);
183  elem = PointerGetDatum(gser);
184  isNull = false;
185  }
186  elems[i] = elem;
187  nulls[i] = isNull;
188  i++;
189 
190  if (i >= nelems)
191  break;
192  }
193 
194  /* Turn element array into PgSQL array */
195  dims[0] = nelems;
196  arr = construct_md_array(elems, nulls, 1, dims, lbs, state->geomOid,
197  elmlen, elmbyval, elmalign);
198 
199  return PointerGetDatum(arr);
200 }
201 
207 Datum
209 {
211  Datum result = 0;
212  Datum geometry_array = 0;
213 
214  if (PG_ARGISNULL(0))
215  PG_RETURN_NULL(); /* returns null iff no input values */
216 
217  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
218 
219  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
221  if (!result)
222  PG_RETURN_NULL();
223 
224  PG_RETURN_DATUM(result);
225 }
226 
227 
233 Datum
235 {
237  Datum result = 0;
238  Datum geometry_array = 0;
239 
240  if (PG_ARGISNULL(0))
241  PG_RETURN_NULL(); /* returns null iff no input values */
242 
243  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
244 
245  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
246  result = PGISDirectFunctionCall1( polygonize_garray, geometry_array );
247  if (!result)
248  PG_RETURN_NULL();
249 
250  PG_RETURN_DATUM(result);
251 }
252 
258 Datum
260 {
262  Datum result = 0;
263  Datum geometry_array = 0;
264 
265  if (PG_ARGISNULL(0))
266  PG_RETURN_NULL(); /* returns null iff no input values */
267 
268  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
269 
270  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
272  if (!result)
273  PG_RETURN_NULL();
274 
275  PG_RETURN_DATUM(result);
276 }
277 
283 Datum
285 {
287  Datum result = 0;
288  Datum geometry_array = 0;
289 
290  if (PG_ARGISNULL(0))
291  PG_RETURN_NULL();
292 
293  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
294  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
296  if (!result)
297  PG_RETURN_NULL();
298 
299  PG_RETURN_DATUM(result);
300 }
301 
307 Datum
309 {
311  Datum result = 0;
312  Datum geometry_array = 0;
313 
314  if (PG_ARGISNULL(0))
315  PG_RETURN_NULL();
316 
317  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
318 
319  if (!p->data[0])
320  {
321  elog(ERROR, "Tolerance not defined");
322  PG_RETURN_NULL();
323  }
324 
325  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
327  if (!result)
328  PG_RETURN_NULL();
329 
330  PG_RETURN_DATUM(result);
331 }
332 
337 Datum
338 PGISDirectFunctionCall1(PGFunction func, Datum arg1)
339 {
340 #if POSTGIS_PGSQL_VERSION < 120
341  FunctionCallInfoData fcinfo;
342  Datum result;
343 
344 
345  InitFunctionCallInfoData(fcinfo, NULL, 1, InvalidOid, NULL, NULL);
346 
347 
348  fcinfo.arg[0] = arg1;
349  fcinfo.argnull[0] = false;
350 
351  result = (*func) (&fcinfo);
352 
353  /* Check for null result, returning a "NULL" Datum if indicated */
354  if (fcinfo.isnull)
355  return (Datum) 0;
356 
357  return result;
358 #else
359  LOCAL_FCINFO(fcinfo, 1);
360  Datum result;
361 
362  InitFunctionCallInfoData(*fcinfo, NULL, 1, InvalidOid, NULL, NULL);
363 
364  fcinfo->args[0].value = arg1;
365  fcinfo->args[0].isnull = false;
366 
367  result = (*func)(fcinfo);
368 
369  /* Check for null result, returning a "NULL" Datum if indicated */
370  if (fcinfo->isnull)
371  return (Datum)0;
372 
373  return result;
374 #endif /* POSTGIS_PGSQL_VERSION < 120 */
375 }
376 
381 Datum
382 PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2)
383 {
384 #if POSTGIS_PGSQL_VERSION < 120
385  FunctionCallInfoData fcinfo;
386  Datum result;
387 
388  InitFunctionCallInfoData(fcinfo, NULL, 2, InvalidOid, NULL, NULL);
389 
390  fcinfo.arg[0] = arg1;
391  fcinfo.arg[1] = arg2;
392  fcinfo.argnull[0] = false;
393  fcinfo.argnull[1] = false;
394 
395  result = (*func) (&fcinfo);
396 
397  /* Check for null result, returning a "NULL" Datum if indicated */
398  if (fcinfo.isnull)
399  return (Datum) 0;
400 
401  return result;
402 #else
403  LOCAL_FCINFO(fcinfo, 2);
404  Datum result;
405 
406  InitFunctionCallInfoData(*fcinfo, NULL, 2, InvalidOid, NULL, NULL);
407 
408  fcinfo->args[0].value = arg1;
409  fcinfo->args[1].value = arg2;
410  fcinfo->args[0].isnull = false;
411  fcinfo->args[1].isnull = false;
412 
413  result = (*func)(fcinfo);
414 
415  /* Check for null result, returning a "NULL" Datum if indicated */
416  if (fcinfo->isnull)
417  return (Datum)0;
418 
419  return result;
420 #endif /* POSTGIS_PGSQL_VERSION < 120 */
421 }
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
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:512
#define __attribute__(x)
Definition: liblwgeom.h:242
This library is the generic geometry handling section of PostGIS.
Datum polygonize_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:259
Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:234
Datum cluster_within_distance_garray(PG_FUNCTION_ARGS)
Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo)
Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:208
PG_FUNCTION_INFO_V1(pgis_geometry_accum_transfn)
The transfer function builds a List of LWGEOM* allocated in the aggregate memory context.
Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1)
A modified version of PostgreSQL's DirectFunctionCall1 which allows NULL results; this is required fo...
Definition: lwgeom_accum.c:338
Datum pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:284
Datum PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2)
A modified version of PostgreSQL's DirectFunctionCall2 which allows NULL results; this is required fo...
Definition: lwgeom_accum.c:382
Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:308
Datum clusterintersecting_garray(PG_FUNCTION_ARGS)
Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:68
Datum pgis_union_geometry_array(PG_FUNCTION_ARGS)
#define CollectionBuildStateDataSize
To pass the internal state of our collection between the transfn and finalfn we need to wrap it into ...
Definition: lwgeom_accum.h:35
Datum data[CollectionBuildStateDataSize]
Definition: lwgeom_accum.h:39