26 #include "../postgis_config.h"
31 #include "windowapi.h"
32 #include "utils/builtins.h"
39 #include "lwgeom_pg.h"
67 cluster_context* context = WinGetPartitionLocalMemory(win_obj, context_sz);
75 Datum arg = WinGetFuncArgInPartition(win_obj, 0, i, WINDOW_SEEK_HEAD,
false, is_null, NULL);
95 Datum arg = WinGetFuncArgInPartition(win_obj, 0, i, WINDOW_SEEK_HEAD,
false, is_null, NULL);
122 WindowObject win_obj = PG_WINDOW_OBJECT();
123 uint32_t row = WinGetCurrentPosition(win_obj);
124 uint32_t ngeoms = WinGetPartitionRowCount(win_obj);
130 uint32_t* result_ids;
132 char* is_in_cluster = NULL;
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);
144 if (tolerance_is_null || tolerance < 0)
146 lwpgerror(
"Tolerance must be a positive number, got %g", tolerance);
149 if (minpoints_is_null || minpoints < 0)
151 lwpgerror(
"Minpoints must be a positive number, got %d", minpoints);
157 for (i = 0; i < ngeoms; i++)
165 lwpgerror(
"Error reading geometry.");
170 if (
union_dbscan(geoms, ngeoms, uf, tolerance, minpoints, minpoints > 1 ? &is_in_cluster : NULL) ==
LW_SUCCESS)
173 for (i = 0; i < ngeoms; i++)
184 lwpgerror(
"Error during clustering");
189 for (i = 0; i < ngeoms; i++)
191 if (minpoints > 1 && !is_in_cluster[i])
215 WindowObject win_obj = PG_WINDOW_OBJECT();
216 uint32_t row = WinGetCurrentPosition(win_obj);
217 uint32_t ngeoms = WinGetPartitionRowCount(win_obj);
223 uint32_t* result_ids;
226 bool tolerance_is_null;
227 double tolerance = DatumGetFloat8(WinGetFuncArgCurrent(win_obj, 1, &tolerance_is_null));
230 if (tolerance_is_null || tolerance < 0)
232 lwpgerror(
"Tolerance must be a positive number, got %g", tolerance);
240 for (i = 0; i < ngeoms; i++)
248 lwpgerror(
"Error reading geometry.");
258 for (i = 0; i < ngeoms; i++)
267 lwpgerror(
"Error during clustering");
272 for (i = 0; i < ngeoms; i++)
291 WindowObject win_obj = PG_WINDOW_OBJECT();
292 uint32_t row = WinGetCurrentPosition(win_obj);
293 uint32_t ngeoms = WinGetPartitionRowCount(win_obj);
299 uint32_t* result_ids;
300 GEOSGeometry** geoms =
lwalloc(ngeoms *
sizeof(GEOSGeometry*));
307 for (i = 0; i < ngeoms; i++)
315 lwpgerror(
"Error reading geometry.");
323 for (i = 0; i < ngeoms; i++)
325 GEOSGeom_destroy(geoms[i]);
332 lwpgerror(
"Error during clustering");
337 for (i = 0; i < ngeoms; i++)
357 WindowObject winobj = PG_WINDOW_OBJECT();
359 int64 curpos, rowcount;
361 rowcount = WinGetPartitionRowCount(winobj);
363 WinGetPartitionLocalMemory(winobj,
370 double max_radius = 0.0;
376 argdatum = WinGetFuncArgCurrent(winobj, 1, &isnull);
377 k = DatumGetInt32(argdatum);
378 if (isnull || k <= 0)
386 N = (int) WinGetPartitionRowCount(winobj);
395 argdatum = WinGetFuncArgCurrent(winobj, 2, &isnull);
398 max_radius = DatumGetFloat8(argdatum);
405 lwpgerror(
"K (%d) must be smaller than the number of rows in the group (%d)", k, N);
408 geoms = palloc(
sizeof(
LWGEOM*) * N);
409 for (i = 0; i < N; i++)
412 Datum arg = WinGetFuncArgInPartition(winobj, 0, i,
413 WINDOW_SEEK_HEAD,
false, &isnull, &isout);
430 for (i = 0; i < N; i++)
444 memcpy(context->
result,
r,
sizeof(
int) * N);
452 curpos = WinGetCurrentPosition(winobj);
453 PG_RETURN_INT32(context->
result[curpos]);
478 for (uint32 i = 0; i < ngeoms; i++)
481 GEOSGeom_destroy(geoms[i]);
485 #if POSTGIS_GEOS_VERSION >= 31200
523 int64 rowcount = WinGetPartitionRowCount(winobj);
525 GEOSGeometry** geoms;
526 uint32 i, ngeoms = 0, gtype;
529 geoms = palloc(rowcount *
sizeof(GEOSGeometry*));
530 for (i = 0; i < rowcount; i++)
534 bool isempty, ispolygonal;
538 d = WinGetFuncArgInPartition(winobj, 0, i,
539 WINDOW_SEEK_HEAD,
false, &isnull, &isout);
554 context->
idx[i] = -1;
564 if (isempty || !ispolygonal)
566 context->
idx[i] = -1;
574 context->
idx[i] = -1;
578 context->
idx[i] = ngeoms;
579 geoms[ngeoms] = geos;
589 geos = GEOSGeom_createCollection(
590 GEOS_GEOMETRYCOLLECTION,
619 #if POSTGIS_GEOS_VERSION >= 31400
624 #if POSTGIS_GEOS_VERSION >= 31400
626 static char * overlapMergeStrategies[] = {
628 "MERGE_LONGEST_BORDER",
638 coverage_merge_strategy(
const char *strategy)
640 size_t stratLen =
sizeof(overlapMergeStrategies) /
sizeof(overlapMergeStrategies[0]);
641 for (
size_t i = 0; i < stratLen; i++)
643 if (strcasecmp(strategy, overlapMergeStrategies[i]) == 0)
661 WindowObject winobj = PG_WINDOW_OBJECT();
662 int64 curpos = WinGetCurrentPosition(winobj);
663 int64 rowcount = WinGetPartitionRowCount(winobj);
666 MemoryContext uppercontext = fcinfo->flinfo->fn_mcxt;
667 MemoryContext oldcontext;
673 GEOSGeometry *output = NULL;
674 GEOSGeometry *input = NULL;
678 elog(ERROR,
"%s: Could not find upper context", __func__);
696 bool simplifyBoundary =
true;
697 double tolerance = 0.0;
699 d = WinGetFuncArgCurrent(winobj, 1, &isnull);
700 if (!isnull) tolerance = DatumGetFloat8(d);
702 d = WinGetFuncArgCurrent(winobj, 2, &isnull);
703 if (!isnull) simplifyBoundary = DatumGetFloat8(d);
706 output = GEOSCoverageSimplifyVW(input, tolerance, !simplifyBoundary);
710 double tolerance = 0.0;
711 d = WinGetFuncArgCurrent(winobj, 1, &isnull);
712 if (!isnull) tolerance = DatumGetFloat8(d);
713 GEOSCoverageIsValid(input, tolerance, &output);
716 #if POSTGIS_GEOS_VERSION >= 31400
718 else if (mode == COVERAGE_CLEAN)
720 double snappingDistance = 0.0;
721 double gapMaximumWidth = 0.0;
722 text *overlapMergeStrategyText;
723 int overlapMergeStrategy;
724 GEOSCoverageCleanParams *params = NULL;
726 d = WinGetFuncArgCurrent(winobj, 1, &isnull);
727 if (!isnull) gapMaximumWidth = DatumGetFloat8(d);
729 d = WinGetFuncArgCurrent(winobj, 2, &isnull);
730 if (!isnull) snappingDistance = DatumGetFloat8(d);
732 d = WinGetFuncArgCurrent(winobj, 3, &isnull);
735 overlapMergeStrategyText = DatumGetTextP(d);
736 overlapMergeStrategy = coverage_merge_strategy(text_to_cstring(overlapMergeStrategyText));
740 overlapMergeStrategy = 0;
742 if (overlapMergeStrategy < 0)
747 params = GEOSCoverageCleanParams_create();
748 GEOSCoverageCleanParams_setGapMaximumWidth(params, gapMaximumWidth);
749 GEOSCoverageCleanParams_setSnappingDistance(params, snappingDistance);
750 if (!GEOSCoverageCleanParams_setOverlapMergeStrategy(params, overlapMergeStrategy))
752 GEOSCoverageCleanParams_destroy(params);
756 output = GEOSCoverageCleanWithParams(input, params);
757 GEOSCoverageCleanParams_destroy(params);
762 elog(ERROR,
"Unknown mode, never get here");
765 GEOSGeom_destroy(input);
772 oldcontext = MemoryContextSwitchTo(uppercontext);
774 MemoryContextSwitchTo(oldcontext);
775 GEOSGeom_destroy(output);
785 if (context->
idx[curpos] < 0)
798 oldcontext = MemoryContextSwitchTo(uppercontext);
808 MemoryContextSwitchTo(oldcontext);
812 if (curpos == rowcount - 1)
815 if (!
result) PG_RETURN_NULL();
817 PG_RETURN_POINTER(
result);
827 #if POSTGIS_GEOS_VERSION < 31200
829 lwpgerror(
"The GEOS version this PostGIS binary "
830 "was compiled against (%d) not include "
831 "'GEOSCoverageSimplifyVW' function (3.12 or greater required)",
847 #if POSTGIS_GEOS_VERSION < 31200
849 lwpgerror(
"The GEOS version this PostGIS binary "
850 "was compiled against (%d) does not include "
851 "'GEOSCoverageIsValid' function (3.12 or greater required)",
866 #if POSTGIS_GEOS_VERSION < 31400
868 lwpgerror(
"The GEOS version this PostGIS binary "
869 "was compiled against (%d) not include "
870 "'GEOSCoverageClean' function (3.14 or greater required)",
895 GEOSGeometry **geoms = NULL;
896 GEOSGeometry *geos = NULL;
897 GEOSGeometry *geos_result = NULL;
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);
909 geoms = palloc(
sizeof(GEOSGeometry *) * nelems);
913 while (array_iterate(iterator, &
value, &isnull))
917 if (isnull)
continue;
927 geoms[ngeoms++] = geos;
929 array_free_iterator(iterator);
934 geos = GEOSGeom_createCollection(
935 GEOS_GEOMETRYCOLLECTION,
944 geos_result = GEOSCoverageUnion(geos);
945 GEOSGeom_destroy(geos);
950 GEOSGeom_destroy(geos_result);
952 PG_RETURN_POINTER(
result);
char result[OUT_DOUBLE_BUFFER_SIZE]
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)
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
void lwgeom_free(LWGEOM *geom)
const LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, uint32_t gnum)
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 ...
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
void lwcollection_free(LWCOLLECTION *col)
void * lwalloc(size_t size)
#define LW_TRUE
Return types for functions with status returns.
This library is the generic geometry handling section of PostGIS.
Datum ST_CoverageClean(PG_FUNCTION_ARGS)
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)
Datum ST_CoverageSimplify(PG_FUNCTION_ARGS)
static Datum coverage_window_calculation(PG_FUNCTION_ARGS, int mode)
Datum ST_ClusterKMeans(PG_FUNCTION_ARGS)
static cluster_context * fetch_cluster_context(WindowObject win_obj, uint32_t ngeoms)
static LWGEOM * read_lwgeom_from_partition(WindowObject win_obj, uint32_t i, bool *is_null)
Datum ST_ClusterDBSCAN(PG_FUNCTION_ARGS)
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)
uint32_t * UF_get_collapsed_cluster_ids(UNIONFIND *uf, const char *is_in_cluster)
void UF_destroy(UNIONFIND *uf)
UNIONFIND * UF_create(uint32_t N)
GSERIALIZED * GEOS2POSTGIS(GEOSGeom geom, char want3d)
GEOSGeometry * POSTGIS2GEOS(const GSERIALIZED *pglwgeom)
#define HANDLE_GEOS_ERROR(label)
#define POSTGIS_GEOS_VERSION
cluster_entry clusters[1]