PostGIS  3.4.0dev-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 Datum ST_CoverageUnion(PG_FUNCTION_ARGS);
60 
61 
68 Datum
69 pgis_geometry_accum_transfn(PG_FUNCTION_ARGS)
70 {
71  MemoryContext aggcontext, old;
72  CollectionBuildState *state;
73  LWGEOM *geom = NULL;
74  GSERIALIZED *gser = NULL;
75  Datum argType = get_fn_expr_argtype(fcinfo->flinfo, 1);
76  double gridSize = -1.0;
77 
78  if (argType == InvalidOid)
79  ereport(ERROR,
80  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
81  errmsg("could not determine input data type")));
82 
83  if ( ! AggCheckCallContext(fcinfo, &aggcontext) )
84  {
85  /* cannot be called directly because of dummy-type argument */
86  elog(ERROR, "%s called in non-aggregate context", __func__);
87  aggcontext = NULL; /* keep compiler quiet */
88  }
89 
90  if ( PG_ARGISNULL(0) )
91  {
92  int n = ((PG_NARGS()-2) <= CollectionBuildStateDataSize) ? (PG_NARGS()-2) : CollectionBuildStateDataSize;
93 
94  state = MemoryContextAlloc(aggcontext, sizeof(CollectionBuildState));
95  state->geoms = NULL;
96  state->geomOid = argType;
97  state->gridSize = gridSize;
98 
99  for (int i = 0; i < n; i++)
100  {
101  Datum argument = PG_GETARG_DATUM(i+2);
102  Oid dataOid = get_fn_expr_argtype(fcinfo->flinfo, i+2);
103  old = MemoryContextSwitchTo(aggcontext);
104  state->data[i] = datumCopy(argument, get_typbyval(dataOid), get_typlen(dataOid));
105  MemoryContextSwitchTo(old);
106  }
107  }
108  else
109  {
110  state = (CollectionBuildState*) PG_GETARG_POINTER(0);
111  }
112 
113  if (!PG_ARGISNULL(1))
114  gser = PG_GETARG_GSERIALIZED_P(1);
115 
116  if (PG_NARGS()>2 && !PG_ARGISNULL(2))
117  {
118  gridSize = PG_GETARG_FLOAT8(2);
119  /*lwnotice("Passed gridSize %g", gridSize);*/
120  if ( gridSize > state->gridSize ) state->gridSize = gridSize;
121  }
122 
123  /* Take a copy of the geometry into the aggregate context */
124  old = MemoryContextSwitchTo(aggcontext);
125  if (gser)
127 
128  /* Initialize or append to list as necessary */
129  if (state->geoms)
130  state->geoms = lappend(state->geoms, geom);
131  else
132  state->geoms = list_make1(geom);
133 
134  MemoryContextSwitchTo(old);
135 
136  PG_RETURN_POINTER(state);
137 }
138 
139 
140 Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo);
141 
146 Datum
147 pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, __attribute__((__unused__)) FunctionCallInfo fcinfo)
148 {
149  ListCell *l;
150  size_t nelems = 0;
151  Datum *elems;
152  bool *nulls;
153  int16 elmlen;
154  bool elmbyval;
155  char elmalign;
156  size_t i = 0;
157  ArrayType *arr;
158  int dims[1];
159  int lbs[1] = {1};
160 
161  /* cannot be called directly because of internal-type argument */
162  Assert(fcinfo->context &&
163  (IsA(fcinfo->context, AggState) ||
164  IsA(fcinfo->context, WindowAggState))
165  );
166 
167  /* Retrieve geometry type metadata */
168  get_typlenbyvalalign(state->geomOid, &elmlen, &elmbyval, &elmalign);
169  nelems = list_length(state->geoms);
170 
171  /* Build up an array, because that's what we pass to all the */
172  /* specific final functions */
173  elems = palloc(nelems * sizeof(Datum));
174  nulls = palloc(nelems * sizeof(bool));
175 
176  foreach (l, state->geoms)
177  {
178  LWGEOM *geom = (LWGEOM*)(lfirst(l));
179  Datum elem = (Datum)0;
180  bool isNull = true;
181  if (geom)
182  {
183  GSERIALIZED *gser = geometry_serialize(geom);
184  elem = PointerGetDatum(gser);
185  isNull = false;
186  }
187  elems[i] = elem;
188  nulls[i] = isNull;
189  i++;
190 
191  if (i >= nelems)
192  break;
193  }
194 
195  /* Turn element array into PgSQL array */
196  dims[0] = nelems;
197  arr = construct_md_array(elems, nulls, 1, dims, lbs, state->geomOid,
198  elmlen, elmbyval, elmalign);
199 
200  return PointerGetDatum(arr);
201 }
202 
208 Datum
210 {
212  Datum result = 0;
213  Datum geometry_array = 0;
214 
215  if (PG_ARGISNULL(0))
216  PG_RETURN_NULL(); /* returns null iff no input values */
217 
218  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
219 
220  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
222  if (!result)
223  PG_RETURN_NULL();
224 
225  PG_RETURN_DATUM(result);
226 }
227 
228 
234 Datum
236 {
238  Datum result = 0;
239  Datum geometry_array = 0;
240 
241  if (PG_ARGISNULL(0))
242  PG_RETURN_NULL(); /* returns null iff no input values */
243 
244  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
245 
246  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
247  result = PGISDirectFunctionCall1( polygonize_garray, geometry_array );
248  if (!result)
249  PG_RETURN_NULL();
250 
251  PG_RETURN_DATUM(result);
252 }
253 
259 Datum
261 {
263  Datum result = 0;
264  Datum geometry_array = 0;
265 
266  if (PG_ARGISNULL(0))
267  PG_RETURN_NULL(); /* returns null iff no input values */
268 
269  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
270 
271  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
273  if (!result)
274  PG_RETURN_NULL();
275 
276  PG_RETURN_DATUM(result);
277 }
278 
284 Datum
286 {
288  Datum result = 0;
289  Datum geometry_array = 0;
290 
291  if (PG_ARGISNULL(0))
292  PG_RETURN_NULL();
293 
294  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
295  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
297  if (!result)
298  PG_RETURN_NULL();
299 
300  PG_RETURN_DATUM(result);
301 }
302 
308 Datum
310 {
312  Datum result = 0;
313  Datum geometry_array = 0;
314 
315  if (PG_ARGISNULL(0))
316  PG_RETURN_NULL();
317 
318  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
319 
320  if (!p->data[0])
321  {
322  elog(ERROR, "Tolerance not defined");
323  PG_RETURN_NULL();
324  }
325 
326  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
328  if (!result)
329  PG_RETURN_NULL();
330 
331  PG_RETURN_DATUM(result);
332 }
333 
334 
335 
341 Datum
343 {
345  Datum result = 0;
346  Datum geometry_array = 0;
347 
348  if (PG_ARGISNULL(0))
349  PG_RETURN_NULL(); /* returns null iff no input values */
350 
351  p = (CollectionBuildState*) PG_GETARG_POINTER(0);
352 
353  geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
355  if (!result)
356  PG_RETURN_NULL();
357 
358  PG_RETURN_DATUM(result);
359 }
360 
361 
366 Datum
367 PGISDirectFunctionCall1(PGFunction func, Datum arg1)
368 {
369  LOCAL_FCINFO(fcinfo, 1);
370  Datum result;
371 
372  InitFunctionCallInfoData(*fcinfo, NULL, 1, InvalidOid, NULL, NULL);
373 
374  fcinfo->args[0].value = arg1;
375  fcinfo->args[0].isnull = false;
376 
377  result = (*func)(fcinfo);
378 
379  /* Check for null result, returning a "NULL" Datum if indicated */
380  if (fcinfo->isnull)
381  return (Datum)0;
382 
383  return result;
384 }
385 
390 Datum
391 PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2)
392 {
393  LOCAL_FCINFO(fcinfo, 2);
394  Datum result;
395 
396  InitFunctionCallInfoData(*fcinfo, NULL, 2, InvalidOid, NULL, NULL);
397 
398  fcinfo->args[0].value = arg1;
399  fcinfo->args[1].value = arg2;
400  fcinfo->args[0].isnull = false;
401  fcinfo->args[1].isnull = false;
402 
403  result = (*func)(fcinfo);
404 
405  /* Check for null result, returning a "NULL" Datum if indicated */
406  if (fcinfo->isnull)
407  return (Datum)0;
408 
409  return result;
410 }
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
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:529
#define __attribute__(x)
Definition: liblwgeom.h:228
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:260
Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:235
Datum cluster_within_distance_garray(PG_FUNCTION_ARGS)
Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo)
Datum ST_CoverageUnion(PG_FUNCTION_ARGS)
Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:209
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:367
Datum pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:285
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:391
Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:309
Datum pgis_geometry_coverageunion_finalfn(PG_FUNCTION_ARGS)
Definition: lwgeom_accum.c:342
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:69
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