PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeom_window.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 2016 Paul Ramsey <pramsey@cleverelephant.ca>
22 * Copyright 2016 Daniel Baston <dbaston@gmail.com>
23 *
24 **********************************************************************/
25
26#include "../postgis_config.h"
27
28/* PostgreSQL */
29#include "postgres.h"
30#include "funcapi.h"
31#include "windowapi.h"
32#include "utils/builtins.h"
33
34/* PostGIS */
35#include "liblwgeom.h"
36#include "lwunionfind.h" /* TODO move into liblwgeom.h ? */
37#include "lwgeom_geos.h"
38#include "lwgeom_log.h"
39#include "lwgeom_pg.h"
40
41
42typedef struct {
43 bool isdone;
44 bool isnull;
45 int result[1];
46 /* variable length */
48
49typedef struct
50{
51 uint32_t cluster_id;
52 char is_null; /* NULL may result from a NULL geometry input, or it may be used by
53 algorithms such as DBSCAN that do not assign all inputs to a
54 cluster. */
56
57typedef struct
58{
60 cluster_entry clusters[1];
62
63static cluster_context*
64fetch_cluster_context(WindowObject win_obj, uint32_t ngeoms)
65{
66 size_t context_sz = sizeof(cluster_context) + (ngeoms * sizeof(cluster_entry));
67 cluster_context* context = WinGetPartitionLocalMemory(win_obj, context_sz);
68 return context;
69}
70
71static LWGEOM*
72read_lwgeom_from_partition(WindowObject win_obj, uint32_t i, bool* is_null)
73{
74 GSERIALIZED* g;
75 Datum arg = WinGetFuncArgInPartition(win_obj, 0, i, WINDOW_SEEK_HEAD, false, is_null, NULL);
76
77 if (*is_null) {
78 /* So that the indexes in our clustering input array can match our partition positions,
79 * toss an empty point into the clustering inputs, as a pass-through.
80 * NOTE: this will cause gaps in the output cluster id sequence.
81 * */
83 }
84
85 g = (GSERIALIZED*) PG_DETOAST_DATUM_COPY(arg);
87}
88
89static GEOSGeometry*
90read_geos_from_partition(WindowObject win_obj, uint32_t i, bool* is_null)
91{
92 GSERIALIZED* g;
93 LWGEOM* lwg;
94 GEOSGeometry* gg;
95 Datum arg = WinGetFuncArgInPartition(win_obj, 0, i, WINDOW_SEEK_HEAD, false, is_null, NULL);
96
97 if (*is_null) {
98 /* So that the indexes in our clustering input array can match our partition positions,
99 * toss an empty point into the clustering inputs, as a pass-through.
100 * NOTE: this will cause gaps in the output cluster id sequence.
101 * */
103 gg = LWGEOM2GEOS(lwg, LW_FALSE);
104 lwgeom_free(lwg);
105 return gg;
106 }
107
108 g = (GSERIALIZED*) PG_DETOAST_DATUM_COPY(arg);
110 gg = LWGEOM2GEOS(lwg, LW_TRUE);
111 lwgeom_free(lwg);
112 if (!gg) {
113 *is_null = LW_TRUE;
114 }
115 return gg;
116}
117
118extern Datum ST_ClusterDBSCAN(PG_FUNCTION_ARGS);
120Datum ST_ClusterDBSCAN(PG_FUNCTION_ARGS)
121{
122 WindowObject win_obj = PG_WINDOW_OBJECT();
123 uint32_t row = WinGetCurrentPosition(win_obj);
124 uint32_t ngeoms = WinGetPartitionRowCount(win_obj);
125 cluster_context* context = fetch_cluster_context(win_obj, ngeoms);
126
127 if (row == 0) /* beginning of the partition; do all of the work now */
128 {
129 uint32_t i;
130 uint32_t* result_ids;
131 LWGEOM** geoms;
132 char* is_in_cluster = NULL;
133 UNIONFIND* uf;
134 bool tolerance_is_null;
135 bool minpoints_is_null;
136 Datum tolerance_datum = WinGetFuncArgCurrent(win_obj, 1, &tolerance_is_null);
137 Datum minpoints_datum = WinGetFuncArgCurrent(win_obj, 2, &minpoints_is_null);
138 double tolerance = DatumGetFloat8(tolerance_datum);
139 int minpoints = DatumGetInt32(minpoints_datum);
140
141 context->is_error = LW_TRUE; /* until proven otherwise */
142
143 /* Validate input parameters */
144 if (tolerance_is_null || tolerance < 0)
145 {
146 lwpgerror("Tolerance must be a positive number, got %g", tolerance);
147 PG_RETURN_NULL();
148 }
149 if (minpoints_is_null || minpoints < 0)
150 {
151 lwpgerror("Minpoints must be a positive number, got %d", minpoints);
152 }
153
154 initGEOS(lwpgnotice, lwgeom_geos_error);
155 geoms = lwalloc(ngeoms * sizeof(LWGEOM*));
156 uf = UF_create(ngeoms);
157 for (i = 0; i < ngeoms; i++)
158 {
159 bool geom_is_null;
160 geoms[i] = read_lwgeom_from_partition(win_obj, i, &geom_is_null);
161 context->clusters[i].is_null = geom_is_null;
162
163 if (!geoms[i]) {
164 /* TODO release memory ? */
165 lwpgerror("Error reading geometry.");
166 PG_RETURN_NULL();
167 }
168 }
169
170 if (union_dbscan(geoms, ngeoms, uf, tolerance, minpoints, minpoints > 1 ? &is_in_cluster : NULL) == LW_SUCCESS)
171 context->is_error = LW_FALSE;
172
173 for (i = 0; i < ngeoms; i++)
174 {
175 lwgeom_free(geoms[i]);
176 }
177 lwfree(geoms);
178
179 if (context->is_error)
180 {
181 UF_destroy(uf);
182 if (is_in_cluster)
183 lwfree(is_in_cluster);
184 lwpgerror("Error during clustering");
185 PG_RETURN_NULL();
186 }
187
188 result_ids = UF_get_collapsed_cluster_ids(uf, is_in_cluster);
189 for (i = 0; i < ngeoms; i++)
190 {
191 if (minpoints > 1 && !is_in_cluster[i])
192 {
193 context->clusters[i].is_null = LW_TRUE;
194 }
195 else
196 {
197 context->clusters[i].cluster_id = result_ids[i];
198 }
199 }
200
201 lwfree(result_ids);
202 UF_destroy(uf);
203 }
204
205 if (context->clusters[row].is_null)
206 PG_RETURN_NULL();
207
208 PG_RETURN_INT32(context->clusters[row].cluster_id);
209}
210
211extern Datum ST_ClusterWithinWin(PG_FUNCTION_ARGS);
213Datum ST_ClusterWithinWin(PG_FUNCTION_ARGS)
214{
215 WindowObject win_obj = PG_WINDOW_OBJECT();
216 uint32_t row = WinGetCurrentPosition(win_obj);
217 uint32_t ngeoms = WinGetPartitionRowCount(win_obj);
218 cluster_context* context = fetch_cluster_context(win_obj, ngeoms);
219
220 if (row == 0) /* beginning of the partition; do all of the work now */
221 {
222 uint32_t i;
223 uint32_t* result_ids;
224 LWGEOM** geoms;
225 UNIONFIND* uf;
226 bool tolerance_is_null;
227 double tolerance = DatumGetFloat8(WinGetFuncArgCurrent(win_obj, 1, &tolerance_is_null));
228
229 /* Validate input parameters */
230 if (tolerance_is_null || tolerance < 0)
231 {
232 lwpgerror("Tolerance must be a positive number, got %g", tolerance);
233 PG_RETURN_NULL();
234 }
235
236 context->is_error = LW_TRUE; /* until proven otherwise */
237
238 geoms = lwalloc(ngeoms * sizeof(LWGEOM*));
239 uf = UF_create(ngeoms);
240 for (i = 0; i < ngeoms; i++)
241 {
242 bool geom_is_null;
243 geoms[i] = read_lwgeom_from_partition(win_obj, i, &geom_is_null);
244 context->clusters[i].is_null = geom_is_null;
245
246 if (!geoms[i])
247 {
248 lwpgerror("Error reading geometry.");
249 PG_RETURN_NULL();
250 }
251 }
252
253 initGEOS(lwpgnotice, lwgeom_geos_error);
254
255 if (union_dbscan(geoms, ngeoms, uf, tolerance, 1, NULL) == LW_SUCCESS)
256 context->is_error = LW_FALSE;
257
258 for (i = 0; i < ngeoms; i++)
259 {
260 lwgeom_free(geoms[i]);
261 }
262 lwfree(geoms);
263
264 if (context->is_error)
265 {
266 UF_destroy(uf);
267 lwpgerror("Error during clustering");
268 PG_RETURN_NULL();
269 }
270
271 result_ids = UF_get_collapsed_cluster_ids(uf, NULL);
272 for (i = 0; i < ngeoms; i++)
273 {
274 context->clusters[i].cluster_id = result_ids[i];
275 }
276
277 lwfree(result_ids);
278 UF_destroy(uf);
279 }
280
281 if (context->clusters[row].is_null)
282 PG_RETURN_NULL();
283
284 PG_RETURN_INT32(context->clusters[row].cluster_id);
285}
286
287extern Datum ST_ClusterIntersectingWin(PG_FUNCTION_ARGS);
289Datum ST_ClusterIntersectingWin(PG_FUNCTION_ARGS)
290{
291 WindowObject win_obj = PG_WINDOW_OBJECT();
292 uint32_t row = WinGetCurrentPosition(win_obj);
293 uint32_t ngeoms = WinGetPartitionRowCount(win_obj);
294 cluster_context* context = fetch_cluster_context(win_obj, ngeoms);
295
296 if (row == 0) /* beginning of the partition; do all of the work now */
297 {
298 uint32_t i;
299 uint32_t* result_ids;
300 GEOSGeometry** geoms = lwalloc(ngeoms * sizeof(GEOSGeometry*));
301 UNIONFIND* uf = UF_create(ngeoms);
302
303 context->is_error = LW_TRUE; /* until proven otherwise */
304
305 initGEOS(lwpgnotice, lwgeom_geos_error);
306
307 for (i = 0; i < ngeoms; i++)
308 {
309 bool geom_is_null;
310 geoms[i] = read_geos_from_partition(win_obj, i, &geom_is_null);
311 context->clusters[i].is_null = geom_is_null;
312
313 if (!geoms[i])
314 {
315 lwpgerror("Error reading geometry.");
316 PG_RETURN_NULL();
317 }
318 }
319
320 if (union_intersecting_pairs(geoms, ngeoms, uf) == LW_SUCCESS)
321 context->is_error = LW_FALSE;
322
323 for (i = 0; i < ngeoms; i++)
324 {
325 GEOSGeom_destroy(geoms[i]);
326 }
327 lwfree(geoms);
328
329 if (context->is_error)
330 {
331 UF_destroy(uf);
332 lwpgerror("Error during clustering");
333 PG_RETURN_NULL();
334 }
335
336 result_ids = UF_get_collapsed_cluster_ids(uf, NULL);
337 for (i = 0; i < ngeoms; i++)
338 {
339 context->clusters[i].cluster_id = result_ids[i];
340 }
341
342 lwfree(result_ids);
343 UF_destroy(uf);
344 }
345
346 if (context->clusters[row].is_null)
347 PG_RETURN_NULL();
348
349 PG_RETURN_INT32(context->clusters[row].cluster_id);
350}
351
352
353extern Datum ST_ClusterKMeans(PG_FUNCTION_ARGS);
355Datum ST_ClusterKMeans(PG_FUNCTION_ARGS)
356{
357 WindowObject winobj = PG_WINDOW_OBJECT();
358 kmeans_context *context;
359 int64 curpos, rowcount;
360
361 rowcount = WinGetPartitionRowCount(winobj);
362 context = (kmeans_context *)
363 WinGetPartitionLocalMemory(winobj,
364 sizeof(kmeans_context) + sizeof(int) * rowcount);
365
366 if (!context->isdone)
367 {
368 int i, k, N;
369 bool isnull, isout;
370 double max_radius = 0.0;
371 LWGEOM **geoms;
372 int *r;
373 Datum argdatum;
374
375 /* What is K? If it's NULL or invalid, we can't proceed */
376 argdatum = WinGetFuncArgCurrent(winobj, 1, &isnull);
377 k = DatumGetInt32(argdatum);
378 if (isnull || k <= 0)
379 {
380 context->isdone = true;
381 context->isnull = true;
382 PG_RETURN_NULL();
383 }
384
385 /* We also need a non-zero N */
386 N = (int) WinGetPartitionRowCount(winobj);
387 if (N <= 0)
388 {
389 context->isdone = true;
390 context->isnull = true;
391 PG_RETURN_NULL();
392 }
393
394 /* Maximum cluster radius. 0 if not set*/
395 argdatum = WinGetFuncArgCurrent(winobj, 2, &isnull);
396 if (!isnull)
397 {
398 max_radius = DatumGetFloat8(argdatum);
399 if (max_radius < 0)
400 max_radius = 0.0;
401 }
402
403 /* Error out if N < K */
404 if (N<k)
405 lwpgerror("K (%d) must be smaller than the number of rows in the group (%d)", k, N);
406
407 /* Read all the geometries from the partition window into a list */
408 geoms = palloc(sizeof(LWGEOM*) * N);
409 for (i = 0; i < N; i++)
410 {
411 GSERIALIZED *g;
412 Datum arg = WinGetFuncArgInPartition(winobj, 0, i,
413 WINDOW_SEEK_HEAD, false, &isnull, &isout);
414
415 /* Null geometries are entered as NULL pointers */
416 if (isnull)
417 {
418 geoms[i] = NULL;
419 continue;
420 }
421
422 g = (GSERIALIZED*)PG_DETOAST_DATUM_COPY(arg);
423 geoms[i] = lwgeom_from_gserialized(g);
424 }
425
426 /* Calculate k-means on the list! */
427 r = lwgeom_cluster_kmeans((const LWGEOM **)geoms, N, k, max_radius);
428
429 /* Clean up */
430 for (i = 0; i < N; i++)
431 if (geoms[i])
432 lwgeom_free(geoms[i]);
433
434 pfree(geoms);
435
436 if (!r)
437 {
438 context->isdone = true;
439 context->isnull = true;
440 PG_RETURN_NULL();
441 }
442
443 /* Safe the result */
444 memcpy(context->result, r, sizeof(int) * N);
445 lwfree(r);
446 context->isdone = true;
447 }
448
449 if (context->isnull)
450 PG_RETURN_NULL();
451
452 curpos = WinGetCurrentPosition(winobj);
453 PG_RETURN_INT32(context->result[curpos]);
454}
455
456
457/********************************************************************************
458 * GEOS Coverage Functions
459 *
460 * The GEOS "coverage" support is a set of functions for working with
461 * "implicit coverages". That is, collections of polygonal geometry
462 * that have perfectly shared edges. Since perfectly matched edges are
463 * fast to detect, "building a coverage" on these inputs is fast, so
464 * operations like edge simplification and so on can include the "build"
465 * step with relatively low cost.
466 *
467 */
468
469/*
470 * For CoverageUnion and CoverageSimplify, clean up
471 * the start of a collection if things fail in mid-stream.
472 */
473static void
474coverage_destroy_geoms(GEOSGeometry **geoms, uint32 ngeoms)
475{
476 if (!geoms) return;
477 if (!ngeoms) return;
478 for (uint32 i = 0; i < ngeoms; i++)
479 {
480 if(geoms[i])
481 GEOSGeom_destroy(geoms[i]);
482 }
483}
484
485#if POSTGIS_GEOS_VERSION >= 31200
486
487/*
488 * For CoverageIsValid and CoverageSimplify, maintain context
489 * of unioned output and the position of the resultant in that
490 * output relative to the index of the input geometries.
491 */
492typedef struct {
493 bool isdone;
494 bool isnull;
496 int64 idx[0];
497 /* variable length */
499
500/*
501 * For CoverageIsValid and CoverageSimplify, read the context
502 * out of the WindowObject.
503 */
504static coverage_context *
505coverage_context_fetch(WindowObject winobj, int64 rowcount)
506{
507 size_t ctx_size = sizeof(coverage_context) + rowcount * sizeof(int64);
508 coverage_context *ctx = (coverage_context*) WinGetPartitionLocalMemory(
509 winobj, ctx_size);
510 return ctx;
511}
512
513/*
514 * For CoverageIsValid and CoverageSimplify, read all the
515 * geometries in this partition and form a GEOS geometry
516 * collection out of them.
517 */
518static GEOSGeometry*
520 WindowObject winobj,
521 coverage_context* context)
522{
523 int64 rowcount = WinGetPartitionRowCount(winobj);
524 GEOSGeometry* geos;
525 GEOSGeometry** geoms;
526 uint32 i, ngeoms = 0, gtype;
527
528 /* Read in all the geometries in this partition */
529 geoms = palloc(rowcount * sizeof(GEOSGeometry*));
530 for (i = 0; i < rowcount; i++)
531 {
532 GSERIALIZED* gser;
533 bool isnull, isout;
534 bool isempty, ispolygonal;
535 Datum d;
536
537 /* Read geometry in first argument */
538 d = WinGetFuncArgInPartition(winobj, 0, i,
539 WINDOW_SEEK_HEAD, false, &isnull, &isout);
540
541 /*
542 * The input to the GEOS function will be smaller than
543 * the input to this window, since we will not feed
544 * GEOS nulls or empties. So we need to maintain a
545 * map (context->idx) from the window position of the
546 * input to the GEOS position, so we can put the
547 * right result in the output stream. Things we want to
548 * skip get an index of -1.
549 */
550
551 /* Skip NULL inputs and move on */
552 if (isnull)
553 {
554 context->idx[i] = -1;
555 continue;
556 }
557
558 gser = (GSERIALIZED*)PG_DETOAST_DATUM(d);
559 gtype = gserialized_get_type(gser);
560 isempty = gserialized_is_empty(gser);
561 ispolygonal = (gtype == MULTIPOLYGONTYPE) || (gtype == POLYGONTYPE);
562
563 /* Skip empty or non-polygonal inputs */
564 if (isempty || !ispolygonal)
565 {
566 context->idx[i] = -1;
567 continue;
568 }
569
570 /* Skip failed inputs */
571 geos = POSTGIS2GEOS(gser);
572 if (!geos)
573 {
574 context->idx[i] = -1;
575 continue;
576 }
577
578 context->idx[i] = ngeoms;
579 geoms[ngeoms] = geos;
580 ngeoms = ngeoms + 1;
581 }
582
583 /*
584 * Create the GEOS input collection! The new
585 * collection takes ownership of the input GEOSGeometry
586 * objects, leaving just the geoms array, which
587 * will be cleaned up on function exit.
588 */
589 geos = GEOSGeom_createCollection(
590 GEOS_GEOMETRYCOLLECTION,
591 geoms, ngeoms);
592
593 /*
594 * If the creation failed, the objects will still be
595 * hanging around, so clean them up first. Should
596 * never happen, really.
597 */
598 if (!geos)
599 {
600 coverage_destroy_geoms(geoms, ngeoms);
601 return NULL;
602 }
603
604 pfree(geoms);
605 return geos;
606}
607
608
609/*
610 * The coverage_window_calculation function is just
611 * the shared machinery of the two window functions
612 * CoverageIsValid and CoverageSimplify which are almost
613 * the same except the GEOS function they call. That
614 * operating mode is controlled with this enumeration.
615 */
616enum {
619#if POSTGIS_GEOS_VERSION >= 31400
620 ,COVERAGE_CLEAN = 2
621#endif
623
624#if POSTGIS_GEOS_VERSION >= 31400
625
626static char * overlapMergeStrategies[] = {
627 /* Merge strategy that chooses polygon with longest common border */
628 "MERGE_LONGEST_BORDER",
629 /* Merge strategy that chooses polygon with maximum area */
630 "MERGE_MAX_AREA",
631 /* Merge strategy that chooses polygon with minimum area */
632 "MERGE_MIN_AREA",
633 /* Merge strategy that chooses polygon with smallest input index */
634 "MERGE_MIN_INDEX"
635};
636
637static int
638coverage_merge_strategy(const char *strategy)
639{
640 size_t stratLen = sizeof(overlapMergeStrategies) / sizeof(overlapMergeStrategies[0]);
641 for (size_t i = 0; i < stratLen; i++)
642 {
643 if (strcasecmp(strategy, overlapMergeStrategies[i]) == 0)
644 {
645 return i;
646 }
647 }
648 return -1;
649}
650
651#endif
652
653/*
654 * This calculation is shared by both coverage operations
655 * since they have the same pattern of "consume collection,
656 * return collection", and are both window functions.
657 */
658static Datum
659coverage_window_calculation(PG_FUNCTION_ARGS, int mode)
660{
661 WindowObject winobj = PG_WINDOW_OBJECT();
662 int64 curpos = WinGetCurrentPosition(winobj);
663 int64 rowcount = WinGetPartitionRowCount(winobj);
664 coverage_context *context = coverage_context_fetch(winobj, rowcount);
666 MemoryContext uppercontext = fcinfo->flinfo->fn_mcxt;
667 MemoryContext oldcontext;
668 const LWGEOM* subgeom;
669
670 if (!context->isdone)
671 {
672 bool isnull;
673 GEOSGeometry *output = NULL;
674 GEOSGeometry *input = NULL;
675 Datum d;
676
677 if (!fcinfo->flinfo)
678 elog(ERROR, "%s: Could not find upper context", __func__);
679
680 if (rowcount == 0)
681 {
682 context->isdone = true;
683 context->isnull = true;
684 PG_RETURN_NULL();
685 }
686
687 initGEOS(lwpgnotice, lwgeom_geos_error);
688
689 input = coverage_read_partition_into_collection(winobj, context);
690 if (!input)
691 HANDLE_GEOS_ERROR("Failed to create collection");
692
693 /* Run the correct GEOS function for the calling mode */
694 if (mode == COVERAGE_SIMPLIFY)
695 {
696 bool simplifyBoundary = true;
697 double tolerance = 0.0;
698
699 d = WinGetFuncArgCurrent(winobj, 1, &isnull);
700 if (!isnull) tolerance = DatumGetFloat8(d);
701
702 d = WinGetFuncArgCurrent(winobj, 2, &isnull);
703 if (!isnull) simplifyBoundary = DatumGetFloat8(d);
704
705 /* GEOSCoverageSimplifyVW is "preserveBoundary" so we invert simplifyBoundary */
706 output = GEOSCoverageSimplifyVW(input, tolerance, !simplifyBoundary);
707 }
708 else if (mode == COVERAGE_ISVALID)
709 {
710 double tolerance = 0.0;
711 d = WinGetFuncArgCurrent(winobj, 1, &isnull);
712 if (!isnull) tolerance = DatumGetFloat8(d);
713 GEOSCoverageIsValid(input, tolerance, &output);
714 }
715
716#if POSTGIS_GEOS_VERSION >= 31400
717
718 else if (mode == COVERAGE_CLEAN)
719 {
720 double snappingDistance = 0.0;
721 double gapMaximumWidth = 0.0;
722 text *overlapMergeStrategyText;
723 int overlapMergeStrategy;
724 GEOSCoverageCleanParams *params = NULL;
725
726 d = WinGetFuncArgCurrent(winobj, 1, &isnull);
727 if (!isnull) gapMaximumWidth = DatumGetFloat8(d);
728
729 d = WinGetFuncArgCurrent(winobj, 2, &isnull);
730 if (!isnull) snappingDistance = DatumGetFloat8(d);
731
732 d = WinGetFuncArgCurrent(winobj, 3, &isnull);
733 if (!isnull)
734 {
735 overlapMergeStrategyText = DatumGetTextP(d);
736 overlapMergeStrategy = coverage_merge_strategy(text_to_cstring(overlapMergeStrategyText));
737 }
738 else
739 {
740 overlapMergeStrategy = 0; /* Default to MERGE_LONGEST_BORDER */
741 }
742 if (overlapMergeStrategy < 0)
743 {
744 HANDLE_GEOS_ERROR("Invalid OverlapMergeStrategy");
745 }
746
747 params = GEOSCoverageCleanParams_create();
748 GEOSCoverageCleanParams_setGapMaximumWidth(params, gapMaximumWidth);
749 GEOSCoverageCleanParams_setSnappingDistance(params, snappingDistance);
750 if (!GEOSCoverageCleanParams_setOverlapMergeStrategy(params, overlapMergeStrategy))
751 {
752 GEOSCoverageCleanParams_destroy(params);
753 HANDLE_GEOS_ERROR("Invalid OverlapMergeStrategy");
754 }
755
756 output = GEOSCoverageCleanWithParams(input, params);
757 GEOSCoverageCleanParams_destroy(params);
758 }
759#endif
760 else
761 {
762 elog(ERROR, "Unknown mode, never get here");
763 }
764
765 GEOSGeom_destroy(input);
766
767 if (!output)
768 {
769 HANDLE_GEOS_ERROR("Failed to process collection");
770 }
771
772 oldcontext = MemoryContextSwitchTo(uppercontext);
773 context->geom = (LWCOLLECTION *) GEOS2LWGEOM(output, GEOSHasZ(output));
774 MemoryContextSwitchTo(oldcontext);
775 GEOSGeom_destroy(output);
776
777 context->isdone = true;
778 }
779
780 /* Bomb out of any errors */
781 if (context->isnull)
782 PG_RETURN_NULL();
783
784 /* Propagate the null entries */
785 if (context->idx[curpos] < 0)
786 PG_RETURN_NULL();
787
788 /*
789 * Geometry serialization is not const-safe! (we
790 * calculate bounding boxes on demand) so we need
791 * to make sure we have a persistent context when
792 * we call the serialization, lest we create dangling
793 * pointers in the object. It's possible we could
794 * ignore them, skip the manual lwcollection_free
795 * and let the aggcontext deletion take
796 * care of the memory.
797 */
798 oldcontext = MemoryContextSwitchTo(uppercontext);
799 subgeom = lwcollection_getsubgeom(context->geom, context->idx[curpos]);
800 if (mode == COVERAGE_ISVALID && lwgeom_is_empty(subgeom))
801 {
802 result = NULL;
803 }
804 else
805 {
806 result = geometry_serialize((LWGEOM*)subgeom);
807 }
808 MemoryContextSwitchTo(oldcontext);
809
810 /* When at the end of the partition, release the */
811 /* simplified collection we have been reading. */
812 if (curpos == rowcount - 1)
813 lwcollection_free(context->geom);
814
815 if (!result) PG_RETURN_NULL();
816
817 PG_RETURN_POINTER(result);
818
819}
820#endif /* POSTGIS_GEOS_VERSION >= 31200 */
821
822
823extern Datum ST_CoverageSimplify(PG_FUNCTION_ARGS);
825Datum ST_CoverageSimplify(PG_FUNCTION_ARGS)
826{
827#if POSTGIS_GEOS_VERSION < 31200
828
829 lwpgerror("The GEOS version this PostGIS binary "
830 "was compiled against (%d) not include "
831 "'GEOSCoverageSimplifyVW' function (3.12 or greater required)",
833 PG_RETURN_NULL();
834
835#else /* POSTGIS_GEOS_VERSION >= 31200 */
836
838
839#endif
840}
841
842
843extern Datum ST_CoverageInvalidEdges(PG_FUNCTION_ARGS);
845Datum ST_CoverageInvalidEdges(PG_FUNCTION_ARGS)
846{
847#if POSTGIS_GEOS_VERSION < 31200
848
849 lwpgerror("The GEOS version this PostGIS binary "
850 "was compiled against (%d) does not include "
851 "'GEOSCoverageIsValid' function (3.12 or greater required)",
853 PG_RETURN_NULL();
854
855#else /* POSTGIS_GEOS_VERSION >= 31200 */
856
858
859#endif
860}
861
862extern Datum ST_CoverageClean(PG_FUNCTION_ARGS);
864Datum ST_CoverageClean(PG_FUNCTION_ARGS)
865{
866#if POSTGIS_GEOS_VERSION < 31400
867
868 lwpgerror("The GEOS version this PostGIS binary "
869 "was compiled against (%d) not include "
870 "'GEOSCoverageClean' function (3.14 or greater required)",
872 PG_RETURN_NULL();
873
874#else /* POSTGIS_GEOS_VERSION >= 31400 */
875
876 return coverage_window_calculation(fcinfo, COVERAGE_CLEAN);
877
878#endif
879}
880
881/**********************************************************************
882 * ST_CoverageUnion(geometry[])
883 *
884 */
885
886Datum ST_CoverageUnion(PG_FUNCTION_ARGS);
888Datum ST_CoverageUnion(PG_FUNCTION_ARGS)
889{
890 GSERIALIZED *result = NULL;
891
892 Datum value;
893 bool isnull;
894
895 GEOSGeometry **geoms = NULL;
896 GEOSGeometry *geos = NULL;
897 GEOSGeometry *geos_result = NULL;
898 uint32 ngeoms = 0;
899
900 ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
901 uint32 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
902 ArrayIterator iterator = array_create_iterator(array, 0, NULL);
903
904 /* Return null on 0-elements input array */
905 if (nelems == 0)
906 PG_RETURN_NULL();
907
908 /* Convert all geometries into GEOSGeometry array */
909 geoms = palloc(sizeof(GEOSGeometry *) * nelems);
910
911 initGEOS(lwpgnotice, lwgeom_geos_error);
912
913 while (array_iterate(iterator, &value, &isnull))
914 {
915 GSERIALIZED *gser;
916 /* Omit nulls */
917 if (isnull) continue;
918
919 /* Omit empty */
920 gser = (GSERIALIZED *)DatumGetPointer(value);
921 if (gserialized_is_empty(gser)) continue;
922
923 /* Omit unconvertible */
924 geos = POSTGIS2GEOS(gser);
925 if (!geos) continue;
926
927 geoms[ngeoms++] = geos;
928 }
929 array_free_iterator(iterator);
930
931 if (ngeoms == 0)
932 PG_RETURN_NULL();
933
934 geos = GEOSGeom_createCollection(
935 GEOS_GEOMETRYCOLLECTION,
936 geoms, ngeoms);
937
938 if (!geos)
939 {
940 coverage_destroy_geoms(geoms, ngeoms);
941 HANDLE_GEOS_ERROR("Geometry could not be converted");
942 }
943
944 geos_result = GEOSCoverageUnion(geos);
945 GEOSGeom_destroy(geos);
946 if (!geos_result)
947 HANDLE_GEOS_ERROR("Error computing coverage union");
948
949 result = GEOS2POSTGIS(geos_result, LW_FALSE);
950 GEOSGeom_destroy(geos_result);
951
952 PG_RETURN_POINTER(result);
953}
954
955
char * r
Definition cu_in_wkt.c:24
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.
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
void lwgeom_geos_error(const char *fmt,...)
void(*) LWGEOM GEOS2LWGEOM)(const GEOSGeometry *geom, uint8_t want3d)
int union_intersecting_pairs(GEOSGeometry **geoms, uint32_t num_geoms, UNIONFIND *uf)
int union_dbscan(LWGEOM **geoms, uint32_t num_geoms, UNIONFIND *uf, double eps, uint32_t min_points, char **is_in_cluster_ret)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:372
#define LW_FALSE
Definition liblwgeom.h:94
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define LW_SUCCESS
Definition liblwgeom.h:97
void * lwalloc(size_t size)
Definition lwutil.c:227
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
int * lwgeom_cluster_kmeans(const LWGEOM **geoms, uint32_t n, uint32_t k, double max_radius)
Take a list of LWGEOMs and a number of clusters and return an integer array indicating which cluster ...
Definition lwkmeans.c:321
void lwfree(void *mem)
Definition lwutil.c:248
#define POLYGONTYPE
Definition liblwgeom.h:104
const LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, uint32_t gnum)
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwpoint.c:151
void lwcollection_free(LWCOLLECTION *col)
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
This library is the generic geometry handling section of PostGIS.
Datum ST_CoverageClean(PG_FUNCTION_ARGS)
@ COVERAGE_ISVALID
@ COVERAGE_SIMPLIFY
Datum ST_CoverageInvalidEdges(PG_FUNCTION_ARGS)
Datum ST_ClusterWithinWin(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(ST_ClusterDBSCAN)
Datum ST_ClusterIntersectingWin(PG_FUNCTION_ARGS)
Datum ST_CoverageUnion(PG_FUNCTION_ARGS)
static GEOSGeometry * read_geos_from_partition(WindowObject win_obj, uint32_t i, bool *is_null)
static coverage_context * coverage_context_fetch(WindowObject winobj, int64 rowcount)
static GEOSGeometry * coverage_read_partition_into_collection(WindowObject winobj, coverage_context *context)
static LWGEOM * read_lwgeom_from_partition(WindowObject win_obj, uint32_t i, bool *is_null)
Datum ST_CoverageSimplify(PG_FUNCTION_ARGS)
static Datum coverage_window_calculation(PG_FUNCTION_ARGS, int mode)
Datum ST_ClusterKMeans(PG_FUNCTION_ARGS)
Datum ST_ClusterDBSCAN(PG_FUNCTION_ARGS)
static cluster_context * fetch_cluster_context(WindowObject win_obj, uint32_t ngeoms)
static void coverage_destroy_geoms(GEOSGeometry **geoms, uint32 ngeoms)
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:199
void UF_destroy(UNIONFIND *uf)
Definition lwunionfind.c:54
UNIONFIND * UF_create(uint32_t N)
Definition lwunionfind.c:35
uint32_t * UF_get_collapsed_cluster_ids(UNIONFIND *uf, const char *is_in_cluster)
GSERIALIZED * GEOS2POSTGIS(GEOSGeom geom, char want3d)
GEOSGeometry * POSTGIS2GEOS(const GSERIALIZED *pglwgeom)
#define HANDLE_GEOS_ERROR(label)
#define POSTGIS_GEOS_VERSION
Definition sqldefines.h:11
cluster_entry clusters[1]
uint32_t cluster_id
char is_null
LWCOLLECTION * geom