PostGIS  3.0.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 
76  if (argType == InvalidOid)
77  ereport(ERROR,
78  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79  errmsg("could not determine input data type")));
80 
81  if ( ! AggCheckCallContext(fcinfo, &aggcontext) )
82  {
83  /* cannot be called directly because of dummy-type argument */
84  elog(ERROR, "%s called in non-aggregate context", __func__);
85  aggcontext = NULL; /* keep compiler quiet */
86  }
87 
88  if ( PG_ARGISNULL(0) )
89  {
90  int n = ((PG_NARGS()-2) <= CollectionBuildStateDataSize) ? (PG_NARGS()-2) : CollectionBuildStateDataSize;
91 
92  state = MemoryContextAlloc(aggcontext, sizeof(CollectionBuildState));
93  state->geoms = NULL;
94  state->geomOid = argType;
95 
96  for (int i = 0; i < n; i++)
97  {
98  Datum argument = PG_GETARG_DATUM(i+2);
99  Oid dataOid = get_fn_expr_argtype(fcinfo->flinfo, i+2);
100  old = MemoryContextSwitchTo(aggcontext);
101  state->data[i] = datumCopy(argument, get_typbyval(dataOid), get_typlen(dataOid));
102  MemoryContextSwitchTo(old);
103  }
104  }
105  else
106  {
107  state = (CollectionBuildState*) PG_GETARG_POINTER(0);
108  }
109 
110  if (!PG_ARGISNULL(1))
111  gser = PG_GETARG_GSERIALIZED_P(1);
112 
113  /* Take a copy of the geometry into the aggregate context */
114  old = MemoryContextSwitchTo(aggcontext);
115  if (gser)
117 
118  /* Initialize or append to list as necessary */
119  if (state->geoms)
120  state->geoms = lappend(state->geoms, geom);
121  else
122  state->geoms = list_make1(geom);
123 
124  MemoryContextSwitchTo(old);
125 
126  PG_RETURN_POINTER(state);
127 }
128 
129 
130 Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo);
131 
136 Datum
137 pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, __attribute__((__unused__)) FunctionCallInfo fcinfo)
138 {
139  ListCell *l;
140  size_t nelems = 0;
141  Datum *elems;
142  bool *nulls;
143  int16 elmlen;
144  bool elmbyval;
145  char elmalign;
146  size_t i = 0;
147  ArrayType *arr;
148  int dims[1];
149  int lbs[1] = {1};
150 
151  /* cannot be called directly because of internal-type argument */
152  Assert(fcinfo->context &&
153  (IsA(fcinfo->context, AggState) ||
154  IsA(fcinfo->context, WindowAggState))
155  );
156 
157  /* Retrieve geometry type metadata */
158  get_typlenbyvalalign(state->geomOid, &elmlen, &elmbyval, &elmalign);
159  nelems = list_length(state->geoms);
160 
161  /* Build up an array, because that's what we pass to all the */
162  /* specific final functions */
163  elems = palloc(nelems * sizeof(Datum));
164  nulls = palloc(nelems * sizeof(bool));
165 
166  foreach (l, state->geoms)
167  {
168  LWGEOM *geom = (LWGEOM*)(lfirst(l));
169  Datum elem = (Datum)0;
170  bool isNull = true;
171  if (geom)
172  {
173  GSERIALIZED *gser = geometry_serialize(geom);
174  elem = PointerGetDatum(gser);
175  isNull = false;
176  }
177  elems[i] = elem;
178  nulls[i] = isNull;
179  i++;
180 
181  if (i >= nelems)
182  break;
183  }
184 
185  /* Turn element array into PgSQL array */
186  dims[0] = nelems;
187  arr = construct_md_array(elems, nulls, 1, dims, lbs, state->geomOid,
188  elmlen, elmbyval, elmalign);
189 
190  return PointerGetDatum(arr);
191 }
192 
198 Datum
200 {
202  Datum result = 0;
203  Datum geometry_array = 0;
204 
205  if (PG_ARGISNULL(0))
206  PG_RETURN_NULL(); /* returns null iff no input values */
207 
208  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
209 
210  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
211  result = PGISDirectFunctionCall1( LWGEOM_collect_garray, geometry_array );
212  if (!result)
213  PG_RETURN_NULL();
214 
215  PG_RETURN_DATUM(result);
216 }
217 
218 
224 Datum
226 {
228  Datum result = 0;
229  Datum geometry_array = 0;
230 
231  if (PG_ARGISNULL(0))
232  PG_RETURN_NULL(); /* returns null iff no input values */
233 
234  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
235 
236  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
237  result = PGISDirectFunctionCall1( polygonize_garray, geometry_array );
238  if (!result)
239  PG_RETURN_NULL();
240 
241  PG_RETURN_DATUM(result);
242 }
243 
249 Datum
251 {
253  Datum result = 0;
254  Datum geometry_array = 0;
255 
256  if (PG_ARGISNULL(0))
257  PG_RETURN_NULL(); /* returns null iff no input values */
258 
259  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
260 
261  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
262  result = PGISDirectFunctionCall1( LWGEOM_makeline_garray, geometry_array );
263  if (!result)
264  PG_RETURN_NULL();
265 
266  PG_RETURN_DATUM(result);
267 }
268 
274 Datum
276 {
278  Datum result = 0;
279  Datum geometry_array = 0;
280 
281  if (PG_ARGISNULL(0))
282  PG_RETURN_NULL();
283 
284  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
285  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
286  result = PGISDirectFunctionCall1( clusterintersecting_garray, geometry_array );
287  if (!result)
288  PG_RETURN_NULL();
289 
290  PG_RETURN_DATUM(result);
291 }
292 
298 Datum
300 {
302  Datum result = 0;
303  Datum geometry_array = 0;
304 
305  if (PG_ARGISNULL(0))
306  PG_RETURN_NULL();
307 
308  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
309 
310  if (!p->data[0])
311  {
312  elog(ERROR, "Tolerance not defined");
313  PG_RETURN_NULL();
314  }
315 
316  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
317  result = PGISDirectFunctionCall2( cluster_within_distance_garray, geometry_array, p->data[0]);
318  if (!result)
319  PG_RETURN_NULL();
320 
321  PG_RETURN_DATUM(result);
322 }
323 
328 Datum
329 PGISDirectFunctionCall1(PGFunction func, Datum arg1)
330 {
331 #if POSTGIS_PGSQL_VERSION < 120
332  FunctionCallInfoData fcinfo;
333  Datum result;
334 
335 
336  InitFunctionCallInfoData(fcinfo, NULL, 1, InvalidOid, NULL, NULL);
337 
338 
339  fcinfo.arg[0] = arg1;
340  fcinfo.argnull[0] = false;
341 
342  result = (*func) (&fcinfo);
343 
344  /* Check for null result, returning a "NULL" Datum if indicated */
345  if (fcinfo.isnull)
346  return (Datum) 0;
347 
348  return result;
349 #else
350  LOCAL_FCINFO(fcinfo, 1);
351  Datum result;
352 
353  InitFunctionCallInfoData(*fcinfo, NULL, 1, InvalidOid, NULL, NULL);
354 
355  fcinfo->args[0].value = arg1;
356  fcinfo->args[0].isnull = false;
357 
358  result = (*func)(fcinfo);
359 
360  /* Check for null result, returning a "NULL" Datum if indicated */
361  if (fcinfo->isnull)
362  return (Datum)0;
363 
364  return result;
365 #endif /* POSTGIS_PGSQL_VERSION < 120 */
366 }
367 
372 Datum
373 PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2)
374 {
375 #if POSTGIS_PGSQL_VERSION < 120
376  FunctionCallInfoData fcinfo;
377  Datum result;
378 
379  InitFunctionCallInfoData(fcinfo, NULL, 2, InvalidOid, NULL, NULL);
380 
381  fcinfo.arg[0] = arg1;
382  fcinfo.arg[1] = arg2;
383  fcinfo.argnull[0] = false;
384  fcinfo.argnull[1] = false;
385 
386  result = (*func) (&fcinfo);
387 
388  /* Check for null result, returning a "NULL" Datum if indicated */
389  if (fcinfo.isnull)
390  return (Datum) 0;
391 
392  return result;
393 #else
394  LOCAL_FCINFO(fcinfo, 2);
395  Datum result;
396 
397  InitFunctionCallInfoData(*fcinfo, NULL, 2, InvalidOid, NULL, NULL);
398 
399  fcinfo->args[0].value = arg1;
400  fcinfo->args[1].value = arg2;
401  fcinfo->args[0].isnull = false;
402  fcinfo->args[1].isnull = false;
403 
404  result = (*func)(fcinfo);
405 
406  /* Check for null result, returning a "NULL" Datum if indicated */
407  if (fcinfo->isnull)
408  return (Datum)0;
409 
410  return result;
411 #endif /* POSTGIS_PGSQL_VERSION < 120 */
412 }
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:511
#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:250
Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:225
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:199
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:329
Datum pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:275
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:373
Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:299
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
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
Datum data[CollectionBuildStateDataSize]
Definition: lwgeom_accum.h:39