[postgis-tickets] [SCM] PostGIS branch master updated. 3.1.0rc1-162-g46efb9f
git at osgeo.org
git at osgeo.org
Tue Apr 27 12:06:32 PDT 2021
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".
The branch, master has been updated
via 46efb9f2d9f5a4bfa64764fc9d6ce5251d86825c (commit)
from 7e19e671ed87060f933bd2ca8b4233097ead5dc7 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 46efb9f2d9f5a4bfa64764fc9d6ce5251d86825c
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue Apr 27 12:04:43 2021 -0700
Add a new ST_MakeValid() signature that takes options to control
what validity building algorithm (and parameters) to use. Options are
valid for GEOS 3.10+.
diff --git a/NEWS b/NEWS
index 89ff3c8..54ca637 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,8 @@ PostGIS 3.2.0
* New features*
- #4841, FindTopology to quickly get a topology record (Sandro Santilli)
- #4851, TopoGeom_addTopoGeom function (Sandro Santilli)
+ - ST_MakeValid(geometry, options) allows alternative validity building
+ algorithms with GEOS 3.10 (Paul Ramsey)
- ST_InterpolateRaster() fills in raster cells between sample points
using one of a number of algorithms (inverse weighted distance, average, etc)
using algorithms from GDAL
diff --git a/doc/reference_validation.xml b/doc/reference_validation.xml
index 9299250..dd4b245 100644
--- a/doc/reference_validation.xml
+++ b/doc/reference_validation.xml
@@ -305,6 +305,11 @@ SELECT ST_IsValidReason('LINESTRING(220227 150406,2220227 150407,222020 150410)'
<funcdef>geometry <function>ST_MakeValid</function></funcdef>
<paramdef><type>geometry</type> <parameter>input</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>geometry <function>ST_MakeValid</function></funcdef>
+ <paramdef><type>geometry</type> <parameter>input</parameter></paramdef>
+ <paramdef choice="opt"><type>text</type> <parameter>params</parameter></paramdef>
+ </funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -331,12 +336,32 @@ SELECT ST_IsValidReason('LINESTRING(220227 150406,2220227 150407,222020 150410)'
<para>
Single polygons may become multi-geometries in case of self-intersections.
</para>
- <para>Performed by the GEOS module.</para>
+
+ <para>
+ The <varname>params</varname> argument can be used to supply an options
+ string to select the method to use for building valid geometry.
+ The options string is in the format "key1=value2 key2=value2".
+ </para>
+ <para>The "method" key has two values. "linework" is the original
+ algorithm, and builds valid geometries by first extracting all lines,
+ noding that linework together, then building a value output from the
+ linework. "structure" is an algorithm that distinguishes between
+ interior and exterior rings, building new geometry by unioning
+ exterior rings, and then differencing all interior rings.
+ </para>
+ <para>The "keepcollapsed" is only valid for the "structure" algorithm,
+ and takes a value of "true" or "false". When set to "false",
+ geometry components that collapse to a lower dimensionality,
+ for example a one-point linestring would be dropped.
+ </para>
+
+ <para>Performed by the GEOS module.</para>
<para>Availability: 2.0.0</para>
<para>Enhanced: 2.0.1, speed improvements</para>
<para>Enhanced: 2.1.0, added support for GEOMETRYCOLLECTION and MULTIPOINT.</para>
<para>Enhanced: 3.1.0, added removal of Coordinates with NaN values.</para>
+ <para>Enhanced: 3.2.0, added algorithm options, 'linework' and 'structure'.</para>
<para>&Z_support;</para>
@@ -403,6 +428,27 @@ FROM (SELECT 'MULTIPOLYGON(((186 194,187 194,188 195,189 195,190 195,
</tgroup>
</informaltable>
</refsection>
+<refsection>
+<title>Examples</title>
+<programlisting>SELECT ST_AsText(ST_MakeValid(
+ 'LINESTRING(0 0, 0 0)',
+ 'method=structure keepcollapsed=true'
+ ));
+
+ st_astext
+------------
+ POINT(0 0)
+
+
+SELECT ST_AsText(ST_MakeValid(
+ 'LINESTRING(0 0, 0 0)',
+ 'method=structure keepcollapsed=false'
+ ));
+
+ st_astext
+------------------
+ LINESTRING EMPTY</programlisting>
+</refsection>
<refsection>
<title>See Also</title>
<para>
diff --git a/liblwgeom/Makefile.in b/liblwgeom/Makefile.in
index 5470f9a..0d42eef 100644
--- a/liblwgeom/Makefile.in
+++ b/liblwgeom/Makefile.in
@@ -51,6 +51,7 @@ LEX=@LEX@
# Standalone LWGEOM objects
SA_OBJS = \
stringbuffer.o \
+ optionlist.o \
bytebuffer.o \
measures.o \
measures3d.o \
diff --git a/liblwgeom/cunit/cu_misc.c b/liblwgeom/cunit/cu_misc.c
index 8cb483e..37deb74 100644
--- a/liblwgeom/cunit/cu_misc.c
+++ b/liblwgeom/cunit/cu_misc.c
@@ -15,6 +15,7 @@
#include "CUnit/Basic.h"
#include "liblwgeom_internal.h"
+#include "optionlist.h"
#include "cu_tester.h"
@@ -233,7 +234,30 @@ static void test_gbox_serialized_size(void)
CU_ASSERT_EQUAL(gbox_serialized_size(flags),24);
}
-
+static void test_optionlist(void)
+{
+ const char* value;
+ // zero out all the pointers so our list ends up null-terminated
+ char *olist[OPTION_LIST_SIZE];
+ char input[128];
+ memset(olist, 0, sizeof(olist));
+ // input string needs to be writeable because we are inserting nulls
+ strcpy(input, "key1=value1 key2=value2");
+ option_list_parse(input, olist);
+
+ value = option_list_search(olist, "key1");
+ // printf("value: %s\n", value);
+ CU_ASSERT_STRING_EQUAL("value1", value);
+ value = option_list_search(olist, "key2");
+ CU_ASSERT_STRING_EQUAL("value2", value);
+ value = option_list_search(olist, "key3");
+ CU_ASSERT_EQUAL(NULL, value);
+
+ memset(olist, 0, sizeof(olist));
+ strcpy(input, " ");
+ option_list_parse(input, olist);
+ value = option_list_search(olist, "key1");
+}
/*
** Used by the test harness to register the tests in this file.
@@ -251,4 +275,5 @@ void misc_suite_setup(void)
PG_ADD_TEST(suite, test_clone);
PG_ADD_TEST(suite, test_lwmpoint_from_lwgeom);
PG_ADD_TEST(suite, test_gbox_serialized_size);
+ PG_ADD_TEST(suite, test_optionlist);
}
diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in
index 30d2646..bbacc19 100644
--- a/liblwgeom/liblwgeom.h.in
+++ b/liblwgeom/liblwgeom.h.in
@@ -2488,6 +2488,7 @@ LWGEOM* lwgeom_buildarea(const LWGEOM *geom) ;
* Attempts to make an invalid geometries valid w/out losing points.
*/
LWGEOM* lwgeom_make_valid(LWGEOM* geom);
+LWGEOM* lwgeom_make_valid_params(LWGEOM* geom, char *make_valid_params);
/*
* Split (multi)polygon by line; (multi)line by (multi)line,
diff --git a/liblwgeom/lwgeom_geos_clean.c b/liblwgeom/lwgeom_geos_clean.c
index 7b720a0..fdbe3e8 100644
--- a/liblwgeom/lwgeom_geos_clean.c
+++ b/liblwgeom/lwgeom_geos_clean.c
@@ -29,6 +29,7 @@
#include "lwgeom_geos.h"
#include "liblwgeom_internal.h"
#include "lwgeom_log.h"
+#include "optionlist.h"
#include <string.h>
#include <stdlib.h>
@@ -897,6 +898,13 @@ LWGEOM_GEOS_makeValid(const GEOSGeometry* gin)
LWGEOM*
lwgeom_make_valid(LWGEOM* lwgeom_in)
{
+ return lwgeom_make_valid_params(lwgeom_in, NULL);
+}
+
+/* Exported. Uses GEOS internally */
+LWGEOM*
+lwgeom_make_valid_params(LWGEOM* lwgeom_in, char* make_valid_params)
+{
int is3d;
GEOSGeom geosgeom;
GEOSGeometry* geosout;
@@ -934,8 +942,56 @@ lwgeom_make_valid(LWGEOM* lwgeom_in)
#if POSTGIS_GEOS_VERSION < 30800
geosout = LWGEOM_GEOS_makeValid(geosgeom);
-#else
+#elif POSTGIS_GEOS_VERSION < 31000
geosout = GEOSMakeValid(geosgeom);
+#else
+ if (!make_valid_params) {
+ geosout = GEOSMakeValid(geosgeom);
+ }
+ else {
+ /*
+ * Set up a parameters object for this
+ * make valid operation before calling
+ * it
+ */
+ const char *value;
+ char *param_list[OPTION_LIST_SIZE];
+ char param_list_text[OPTION_LIST_SIZE];
+ strncpy(param_list_text, make_valid_params, OPTION_LIST_SIZE);
+ memset(param_list, 0, sizeof(param_list));
+ option_list_parse(param_list_text, param_list);
+ GEOSMakeValidParams *params = GEOSMakeValidParams_create();
+ value = option_list_search(param_list, "method");
+ if (value) {
+ if (strcasecmp(value, "linework") == 0) {
+ GEOSMakeValidParams_setMethod(params, GEOS_MAKE_VALID_LINEWORK);
+ }
+ else if (strcasecmp(value, "structure") == 0) {
+ GEOSMakeValidParams_setMethod(params, GEOS_MAKE_VALID_STRUCTURE);
+ }
+ else
+ {
+ GEOSMakeValidParams_destroy(params);
+ lwerror("Unsupported value for 'method', '%s'. Use 'linework' or 'structure'.", value);
+ }
+ }
+ value = option_list_search(param_list, "keepcollapsed");
+ if (value) {
+ if (strcasecmp(value, "true") == 0) {
+ GEOSMakeValidParams_setKeepCollapsed(params, 1);
+ }
+ else if (strcasecmp(value, "false") == 0) {
+ GEOSMakeValidParams_setKeepCollapsed(params, 0);
+ }
+ else
+ {
+ GEOSMakeValidParams_destroy(params);
+ lwerror("Unsupported value for 'keepcollapsed', '%s'. Use 'true' or 'false'", value);
+ }
+ }
+ geosout = GEOSMakeValidWithParams(geosgeom, params);
+ GEOSMakeValidParams_destroy(params);
+ }
#endif
GEOSGeom_destroy(geosgeom);
if (!geosout) return NULL;
diff --git a/liblwgeom/optionlist.c b/liblwgeom/optionlist.c
new file mode 100644
index 0000000..bd25678
--- /dev/null
+++ b/liblwgeom/optionlist.c
@@ -0,0 +1,95 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * PostGIS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PostGIS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************
+ *
+ * Copyright 2021 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ **********************************************************************/
+
+#include "liblwgeom_internal.h"
+#include "optionlist.h"
+
+#include <ctype.h> // tolower
+#include <string.h> // strtok
+
+static void
+option_list_string_to_lower(char* key)
+{
+ if (!key) return;
+ while (*key) {
+ *key = tolower(*key);
+ key++;
+ }
+ return;
+}
+
+const char*
+option_list_search(char** olist, const char* key)
+{
+ size_t i = 0;
+ if (!olist) return NULL;
+ if (!key) return NULL;
+ while (olist[i]) {
+ // Even entries are keys
+ if (!(i % 2)) {
+ // Does this key match ours?
+ if (strcmp(olist[i], key) == 0) {
+ return olist[i+1];
+ }
+ }
+ i++;
+ }
+ return NULL;
+}
+
+void
+option_list_parse(char* input, char **olist)
+{
+ const char *toksep = " ";
+ const char kvsep = '=';
+ char *key, *val;
+ if (!input) return;
+ size_t i = 0, sz;
+
+ /* strtok nulls out the space between each token */
+ for (key = strtok(input, toksep); key; key = strtok(NULL, toksep)) {
+ if (i >= OPTION_LIST_SIZE) return;
+ olist[i] = key;
+ i += 2;
+ }
+
+ sz = i;
+ /* keys are every second entry in the olist */
+ for (i = 0; i < sz; i += 2) {
+ if (i >= OPTION_LIST_SIZE) return;
+ key = olist[i];
+ /* find the key/value separator */
+ val = strchr(key, kvsep);
+ if (!val) {
+ lwerror("Option string entry '%s' lacks separator '%c'", key, kvsep);
+ }
+ /* null out the separator */
+ *val = '\0';
+ /* point value entry to just after separator */
+ olist[i+1] = ++val;
+ /* all keys forced to lower case */
+ option_list_string_to_lower(key);
+ }
+}
+
diff --git a/liblwgeom/optionlist.h b/liblwgeom/optionlist.h
new file mode 100644
index 0000000..c9df6de
--- /dev/null
+++ b/liblwgeom/optionlist.h
@@ -0,0 +1,64 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * PostGIS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PostGIS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************
+ *
+ * Copyright 2021 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ **********************************************************************/
+
+
+#ifndef _OPTIONLIST_H
+#define _OPTIONLIST_H 1
+
+#include "liblwgeom_internal.h"
+
+#define OPTION_LIST_SIZE 128
+
+/**
+* option_list is a null-terminated list of strings, where every odd
+* string is a key and every even string is a value. To avoid lots of
+* memory allocations, we use the input string as the backing store
+* and fill in the olist with pointers to the start of the key and
+* value strings, with keys in the even slots and values in the
+* odd slots. The input needs to be writeable because we write a
+* '\0' into the string at the break between token separators.
+*
+* // array of char*
+* char *olist[OPTION_LIST_SIZE];
+* // zero out all the pointers so our list ends up null-terminated
+* memset(olist, 0, sizeof(olist));
+* char input[OPTION_LIST_SIZE];
+* strcpy(input, "key1=value1 key2=value2"); // writeable
+* option_list_parse(input, olist);
+* char* key = "key2";
+* const char* value = option_list_search(olist, key);
+* printf("value of '%s' is '%s'\n", key, value);
+*
+*/
+extern void option_list_parse(char* input, char **olist);
+
+/**
+* Returns null if the key cannot be found. Only
+* use fully lowercase keys, because we lowercase
+* keys when we parse the olist
+*/
+extern const char* option_list_search(char** olist, const char* key);
+
+#endif
+
diff --git a/postgis/lwgeom_geos_clean.c b/postgis/lwgeom_geos_clean.c
index db099d6..4f49616 100644
--- a/postgis/lwgeom_geos_clean.c
+++ b/postgis/lwgeom_geos_clean.c
@@ -26,6 +26,7 @@
#include "postgres.h"
#include "fmgr.h"
#include "funcapi.h"
+#include "utils/builtins.h"
#include "../postgis_config.h"
@@ -66,7 +67,14 @@ Datum ST_MakeValid(PG_FUNCTION_ARGS)
break;
}
- lwgeom_out = lwgeom_make_valid(lwgeom_in);
+ if(PG_NARGS() > 1 && ! PG_ARGISNULL(1)) {
+ char *make_valid_params_str = text_to_cstring(PG_GETARG_TEXT_P(1));
+ lwgeom_out = lwgeom_make_valid_params(lwgeom_in, make_valid_params_str);
+ }
+ else {
+ lwgeom_out = lwgeom_make_valid(lwgeom_in);
+ }
+
if ( ! lwgeom_out )
{
PG_FREE_IF_COPY(in, 0);
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 9e49473..1ef5d29 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -3691,6 +3691,12 @@ CREATE OR REPLACE FUNCTION ST_MakeValid(geometry)
LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
_COST_HIGH;
+CREATE OR REPLACE FUNCTION ST_MakeValid(geom geometry, params text)
+ RETURNS geometry
+ AS 'MODULE_PATHNAME', 'ST_MakeValid'
+ LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+ _COST_HIGH;
+
-- ST_CleanGeometry(in geometry)
--
-- Make input:
-----------------------------------------------------------------------
Summary of changes:
NEWS | 2 +
doc/reference_validation.xml | 48 +++++++++++++++++++++-
liblwgeom/Makefile.in | 1 +
liblwgeom/cunit/cu_misc.c | 27 +++++++++++-
liblwgeom/liblwgeom.h.in | 1 +
liblwgeom/lwgeom_geos_clean.c | 58 +++++++++++++++++++++++++-
liblwgeom/optionlist.c | 95 +++++++++++++++++++++++++++++++++++++++++++
liblwgeom/optionlist.h | 64 +++++++++++++++++++++++++++++
postgis/lwgeom_geos_clean.c | 10 ++++-
postgis/postgis.sql.in | 6 +++
10 files changed, 308 insertions(+), 4 deletions(-)
create mode 100644 liblwgeom/optionlist.c
create mode 100644 liblwgeom/optionlist.h
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list