PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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 */
43Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1);
44Datum PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2);
45Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS);
46Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS);
47Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS);
48Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS);
49Datum pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS);
50Datum pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS);
51
52/* External prototypes */
53Datum pgis_union_geometry_array(PG_FUNCTION_ARGS);
54Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS);
55Datum polygonize_garray(PG_FUNCTION_ARGS);
56Datum clusterintersecting_garray(PG_FUNCTION_ARGS);
57Datum cluster_within_distance_garray(PG_FUNCTION_ARGS);
58Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS);
59
60
67Datum
69{
70 MemoryContext aggcontext, old;
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
130Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo);
131
136Datum
137pgis_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
198Datum
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
224Datum
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
249Datum
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
274Datum
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
298Datum
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
328Datum
329PGISDirectFunctionCall1(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
372Datum
373PGISDirectFunctionCall2(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.
#define __attribute__(x)
Definition liblwgeom.h:242
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition lwgeom.c:511
This library is the generic geometry handling section of PostGIS.
Datum polygonize_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS)
Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS)
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)
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...
Datum pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS)
Datum PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2)
A modified version of PostgreSQL's DirectFunctionCall2 which allows NULL results; this is required fo...
Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS)
Datum clusterintersecting_garray(PG_FUNCTION_ARGS)
Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS)
Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS)
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 ...
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
Datum data[CollectionBuildStateDataSize]