[SCM] PostGIS branch master updated. 3.4.0rc1-1138-g4903c6f76

git at osgeo.org git at osgeo.org
Sun Jun 2 01:12:22 PDT 2024


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  4903c6f76a29750e53d0ad923f9f67a6f4029c02 (commit)
       via  eeb490ce1d4a38f0f19da88aa0569692c8a31b34 (commit)
      from  2773d7419c4ada598847e1b5ac9b292cbd409846 (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 4903c6f76a29750e53d0ad923f9f67a6f4029c02
Author: Regina Obe <lr at pcorp.us>
Date:   Sun Jun 2 03:50:18 2024 -0400

    CHANGES to: ST_RemoveIrrelevantPointsForView
    
     - Mark as IMMUTABLE per coderabbitai suggestion
     - Add PARALLEL SAFE and costing
     - Add to NEWS and Sam Peters to credits

diff --git a/NEWS b/NEWS
index 55cab590e..da043ec6a 100644
--- a/NEWS
+++ b/NEWS
@@ -66,6 +66,7 @@ Andreas Schild (German Team)
             CG_3DDistance, CG_Distance (Loïc Bartoletti)
   - #5687, Don't rely on search_path to determine postgis schema
            Fix for PG17 security change (Regina Obe)
+  - #5705, GH-767, Remove irrelevant points for view (Sam Peters)
 
 * Enhancements *
 
diff --git a/doc/introduction.xml b/doc/introduction.xml
index 30d677767..b0df61672 100644
--- a/doc/introduction.xml
+++ b/doc/introduction.xml
@@ -319,6 +319,7 @@
 					<member>Robert Coup</member>
 					<member>Roger Crew</member>
 					<member>Ron Mayer</member>
+					<member>Sam Peters</member>
 					<member>Sebastiaan Couwenberg</member>
 					<member>Sergei Shoulbakov</member>
 					<member>Sergey Fedoseev</member>
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 1bca2d37c..67f63c98b 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -6975,6 +6975,7 @@ CREATE OR REPLACE FUNCTION ST_3DLineInterpolatePoint(geometry, float8)
 CREATE OR REPLACE FUNCTION ST_RemoveIrrelevantPointsForView(geometry, box2d)
 RETURNS geometry
 AS 'MODULE_PATHNAME','ST_RemoveIrrelevantPointsForView'
-LANGUAGE 'c' VOLATILE STRICT; 
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_MEDIUM;
 
 COMMIT;

commit eeb490ce1d4a38f0f19da88aa0569692c8a31b34
Author: Regina Obe <lr at pcorp.us>
Date:   Sun Jun 2 03:45:06 2024 -0400

    Remove irrelevant points for view
    Closes https://github.com/postgis/postgis/pull/767
    Closes #5705
    Author: Sam Peters

diff --git a/doc/html/images/static/st_removeirrelevantpointsforview.png b/doc/html/images/static/st_removeirrelevantpointsforview.png
new file mode 100644
index 000000000..c3ff3f5a0
Binary files /dev/null and b/doc/html/images/static/st_removeirrelevantpointsforview.png differ
diff --git a/doc/html/images/static/st_removeirrelevantpointsforview_crossing.png b/doc/html/images/static/st_removeirrelevantpointsforview_crossing.png
new file mode 100644
index 000000000..26557604d
Binary files /dev/null and b/doc/html/images/static/st_removeirrelevantpointsforview_crossing.png differ
diff --git a/doc/reference_editor.xml b/doc/reference_editor.xml
index 7ad891404..b19080547 100644
--- a/doc/reference_editor.xml
+++ b/doc/reference_editor.xml
@@ -1745,6 +1745,117 @@ SELECT ST_AsText( ST_RemoveRepeatedPoints( 'LINESTRING (0 0, 0 0, 1 1, 5 5, 1 1,
           </refsection>
     </refentry>
 
+	<refentry xml:id="ST_RemoveIrrelevantPointsForView">
+	  <refnamediv>
+		<refname>ST_RemoveIrrelevantPointsForView</refname>
+		<refpurpose>Removes points that are irrelevant for rendering a specific rectangluar view of a geometry.</refpurpose>
+	  </refnamediv>
+
+	  <refsynopsisdiv>
+		<funcsynopsis>
+		  <funcprototype>
+			<funcdef>geometry <function>ST_RemoveIrrelevantPointsForView</function></funcdef>
+			<paramdef><type>geometry </type> <parameter>geom</parameter></paramdef>
+			<paramdef><type>box2d </type> <parameter>bounds</parameter></paramdef>
+		  </funcprototype>
+		</funcsynopsis>
+	  </refsynopsisdiv>
+
+	  <refsection>
+		<title>Description</title>
+
+		<para>Returns a <xref linkend="geometry"/> without points being irrelevant for rendering the geometry within a given rectangluar view.</para>
+		<para>This function can be used to quickly preprocess geometries that should be rendered only within certain bounds.</para>
+		<para>Only geometries of type (MULTI)POLYGON and (MULTI)LINESTRING are evaluated. Other geometries keep unchanged.</para>
+		<para>In contrast to <code>ST_ClipByBox2D()</code> this function
+			<itemizedlist>
+				<listitem><para>sorts out points without computing new intersection points which avoids rounding errors and usually increases performance,</para></listitem>
+				<listitem><para>returns a geometry with equal or similar point number,</para></listitem>
+				<listitem><para>leads to the same rendering result within the specified view, and</para></listitem>
+				<listitem><para>may introduce self-intersections which would make the resulting geometry invalid (see example below).</para></listitem>
+			</itemizedlist>
+		</para>
+		
+		<warning><para>For polygons, this function does currently not ensure that the result is valid.
+		This situation can be checked with <xref linkend="ST_IsValid"/> and repaired with <xref linkend="ST_MakeValid"/>.
+		</para></warning>
+
+		<para>
+			<informalfigure>
+				<mediaobject>
+					<imageobject>
+						<imagedata fileref="images/st_removeirrelevantpointsforview.png"/>
+					</imageobject>
+					<caption>
+						<para>Example: ST_RemoveIrrelevantPointsForView() applied to a polygon. Blue points remain, the rendering result (light-blue area) within the grey view box remains as well.</para>
+					</caption>
+				</mediaobject>
+			</informalfigure>
+		</para>
+		
+		<para>
+			<informalfigure>
+				<mediaobject>
+					<imageobject>
+						<imagedata fileref="images/st_removeirrelevantpointsforview_crossing.png"/>
+					</imageobject>
+					<caption>
+						<para>Example: Due to the fact that points are just sorted out and no new points are computed, the result of ST_RemoveIrrelevantPointsForView() may contain self-intersections.</para>
+					</caption>
+				</mediaobject>
+			</informalfigure>
+		</para>
+		
+		<para role="availability" conformance="3.5.0">Availability: 3.5.0</para>
+		
+		
+	  </refsection>
+
+	  <refsection>
+		<title>Examples</title>
+
+		<programlisting>
+			SELECT ST_AsText(
+			ST_RemoveIrrelevantPointsForView(
+			ST_GeomFromText('MULTIPOLYGON(((10 10, 20 10, 30 10, 40 10, 20 20, 10 20, 10 10)),((10 10, 20 10, 20 20, 10 20, 10 10)))'),
+			ST_MakeEnvelope(12,12,18,18)));
+		
+		st_astext
+		---------
+		    MULTIPOLYGON(((10 10,40 10,20 20,10 20,10 10)),((10 10,20 10,20 20,10 20,10 10)))
+		</programlisting>
+		
+		<programlisting>
+			SELECT ST_AsText(
+			ST_RemoveIrrelevantPointsForView(
+			ST_GeomFromText('MULTILINESTRING((0 0, 10 0,20 0,30 0), (0 15, 5 15, 10 15, 15 15, 20 15, 25 15, 30 15, 40 15), (13 13,15 15,17 17))'),
+			ST_MakeEnvelope(12,12,18,18)));
+		
+		st_astext
+		---------
+			MULTILINESTRING((10 15,15 15,20 15),(13 13,15 15,17 17))
+		</programlisting>
+		
+		<programlisting>
+			SELECT ST_AsText(
+			ST_RemoveIrrelevantPointsForView(
+			ST_GeomFromText('LINESTRING(0 0, 10 0,20 0,30 0)'),
+			ST_MakeEnvelope(12,12,18,18)));
+		
+		st_astext
+		---------
+		    LINESTRING EMPTY
+		</programlisting>
+
+	  </refsection>
+
+      <refsection>
+        <title>See Also</title>
+        <para><xref linkend="ST_ClipByBox2D"/>, <xref linkend="ST_Intersection"/></para>
+      </refsection>
+
+	</refentry>
+
 	<refentry xml:id="ST_Reverse">
 	  <refnamediv>
 		<refname>ST_Reverse</refname>
diff --git a/postgis/Makefile.in b/postgis/Makefile.in
index d9361ff1e..780287b1b 100644
--- a/postgis/Makefile.in
+++ b/postgis/Makefile.in
@@ -130,6 +130,7 @@ PG_OBJS= \
 	flatgeobuf.o \
 	lwgeom_in_flatgeobuf.o \
 	lwgeom_out_flatgeobuf.o \
+	lwgeom_remove_irrelevant_points_for_view.o \
 	postgis_legacy.o
 
 # Objects to build using PGXS
diff --git a/postgis/lwgeom_remove_irrelevant_points_for_view.c b/postgis/lwgeom_remove_irrelevant_points_for_view.c
new file mode 100644
index 000000000..3f2e09654
--- /dev/null
+++ b/postgis/lwgeom_remove_irrelevant_points_for_view.c
@@ -0,0 +1,489 @@
+/**********************************************************************
+ *
+ * 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 (C) 2024 Sam Peters <gluser1357 at gmx.de>
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+#include "funcapi.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/numeric.h"
+#include "access/htup_details.h"
+
+#include "liblwgeom.h"
+#include "liblwgeom_internal.h"
+
+// ===============================================================================
+// Encodes the location of a value related to min and max.
+
+// Returns
+// - 0x1 for value < min
+// - 0x2 for min <= value <= max
+// - 0x4 for value > max
+// ===============================================================================
+static int encodeToBits(double value, double min, double max) {
+	return value < min ? 0x1 : value <= max ? 0x2 : 0x4;
+}
+
+// ===============================================================================
+// Encodes the location where a line segment S given by (xa, ya) and (xb, yb)
+// cuts a straight L (either xmin, ymin, xmax or ymax) of a bounding box
+// defined by (xmin, ymin) and (xmax, ymax) without actually computing
+// the cutting point (xs, ys) for performance reasons.
+//
+// Allowed values for the straightPosition are
+// - 1 for top (ymin)
+// - 2 for bottom (ymax)
+// - 3 for left (xmin)
+// - 4 for right (xmax)
+//
+// Returns
+// - 0x1 if xs < xmin (top or bottom), or ys < ymin (left or right)
+// - 0x2 if xmin <= xs < xmax (top or bottom), or ymin <= ys < ymax (left or right)
+// - 0x4 if xs >= xmax (top or buttom), or ys >= ymax (left or right)
+// - 0x0 if no cutting point can be determined (if S and L are parallel or straightPosition is not valid)
+// ===============================================================================
+static int encodeToBitsStraight(double xa, double ya, double xb, double yb, double xmin, double ymin, double xmax, double ymax, int straightPosition) {
+
+	double x, y, dx, dy, d, c;
+
+	if (straightPosition == 1 || straightPosition == 2) {
+
+		// top and bottom
+		if (ya == yb) return 0;
+
+		y = straightPosition == 2 ? ymax : ymin;
+		if (ya < y && yb < y) return 0;
+		if (ya > y && yb > y) return 0;
+
+		dx = xb - xa;
+		dy = yb - ya;
+		d = dy;
+		c = dx * (y - ya);
+		if (dy < 0) {
+			d = -d;
+			c = -c;
+		}
+		return c < d * (xmin - xa) ? 0x1 : c < d * (xmax - xa) ? 0x2 : 0x4;
+	}
+
+	if (straightPosition == 3 || straightPosition == 4) {
+
+		// left and right
+		if (xa == xb) return 0;
+
+		x = straightPosition == 4 ? xmax : xmin;
+		if (xa < x && xb < x) return 0;
+		if (xa > x && xb > x) return 0;
+
+		dx = xb - xa;
+		dy = yb - ya;
+		d = dx;
+		c = dy * (x - xa);
+		if (dx < 0) {
+			d = -d;
+			c = -c;
+		}
+		return c < d * (ymin - ya) ? 0x1 : c < d * (ymax - ya) ? 0x2 : 0x4;
+	}
+
+	return 0;
+}
+
+// ===============================================================================
+// Helper function for polygon and polyline POINTARRAY's.
+// Removes points being irrelevant for rendering the geometry
+// within a view specified by rectangular bounds without introducing
+// new points. The main idea is to sequentially evaluate a group of
+// three consecutive points and decide if the second point has impact
+// on the rendering result within the given bounds. If it doesn't
+// have impact it will be skipped.
+//
+// Note on the algorithm:
+// The algorithm tries to remove points outside the given bounds
+// on a best-effort basis, optimized for speed. It doesn't use allocs,
+// instead it reuses the given point array.
+// There are some known cases where a minor improvement (slightly less points
+// in the result) could be achieved by checking which point(s) of a sequence of
+// outside points would be optimal to keep. Since this would introduce a lot
+// more code complexity and a backing array and would likely have
+// no real practical impact this step is skipped.
+// ===============================================================================
+static void removePoints(POINTARRAY *points, GBOX *bounds, bool closed) {
+
+	int npoints, minpoints;
+	double xmin, ymin, xmax, ymax;
+
+	int i, j, next, w;
+	int vx, vy, vx0, vy0, vx1, vy1, vxall, vyall;
+	double xx, yy, xx0, yy0, xx1, yy1;
+	bool sameX, sameY, same, insideX, insideY, inside, insideAll, skip, clear;
+	int vvx, vvy;
+	POINT4D p, p0, p1;  // current, previous, next;
+
+	double xa, ya, xb, yb;
+	bool cutting;
+	int crossingN;
+
+	bool optimize;
+	optimize = true;
+
+	// point number check
+	npoints = points->npoints;
+	minpoints = closed ? 4 : 2; // min points for each polygon ring or linestring
+	if (npoints < minpoints) {
+		// clear if not expected minimum number of points
+		points->npoints = 0;
+		return;
+	}
+
+	xmin = bounds->xmin;
+	ymin = bounds->ymin;
+	xmax = bounds->xmax;
+	ymax = bounds->ymax;
+
+	// get previous point [i-1]
+	if (closed) {
+		getPoint4d_p(points, 0, &p);
+		getPoint4d_p(points, npoints - 1, &p0);
+		if (p.x != p0.x || p.y != p0.y) return; // requirement for polygons: startpoint equals endpoint. Leave untouched of not met.
+		npoints--; // remove double here, and re-add at the end
+		getPoint4d_p(points, npoints - 1, &p0);
+	}
+	else {
+		getPoint4d_p(points, 0, &p0);  // for linestrings reuse start point
+	}
+	xx0 = p0.x;
+	yy0 = p0.y;
+	vx0 = encodeToBits(xx0, xmin, xmax);
+	vy0 = encodeToBits(yy0, ymin, ymax);
+
+	// for all points
+	w = 0;
+	vxall = 0;
+	vyall = 0;
+	insideAll = false;
+	for (i = 0; i < npoints; i++) {
+
+		// get current point [i]
+		getPoint4d_p(points, i, &p);
+		xx = p.x;
+		yy = p.y;
+		vx = encodeToBits(xx, xmin, xmax);
+		vy = encodeToBits(yy, ymin, ymax);
+
+		// get subsequent point [i+1]
+		next = i + 1;
+		if (next == npoints) {
+			if (closed) next = 0; // for polygons, use (new) start point as end point
+			else next = i;  // for linestrings reuse last point as end point
+		}
+		getPoint4d_p(points, next, &p1);
+		xx1 = p1.x;
+		yy1 = p1.y;
+		vx1 = encodeToBits(xx1, xmin, xmax);
+		vy1 = encodeToBits(yy1, ymin, ymax);
+
+		sameX = vx == vx1 && vx == vx0;
+		sameY = vy == vy1 && vy == vy0;
+		same = sameX && sameY;
+		insideX = vx == 0x02;
+		insideY = vy == 0x02;
+		inside = insideX && insideY;
+
+		skip = sameX && sameY && !inside;	// three consecutive points in same outside quarter, leave out central one
+		skip |= sameX && !insideX;			// three consecutive points in same outside area (left or right), leave out central one
+		skip |= sameY && !insideY;			// three consecutive points in same outside area (top or buttom), leave out central one
+
+		// check for irrelevant points that would introduce "diagonal"
+		// lines between different outside quadrants which may cross the bounds
+		if (optimize && !skip && !same && !inside && (vx0 | vy0) != 0x02 && (vx1 | vy1) != 0x02) {
+
+			vvx = 0;
+			vvy = 0;
+			for (j = 0; j < 2; j++) {
+				// left, right
+				vvx |= encodeToBitsStraight(xx0, yy0, xx, yy, xmin, ymin, xmax, ymax, j + 1);
+				vvx |= encodeToBitsStraight(xx, yy, xx1, yy1, xmin, ymin, xmax, ymax, j + 1);
+				vvx |= encodeToBitsStraight(xx0, yy0, xx1, yy1, xmin, ymin, xmax, ymax, j + 1);
+				if ((vvx & 0x2) != 0) break;
+
+				// top, bottom
+				vvy |= encodeToBitsStraight(xx0, yy0, xx, yy, xmin, ymin, xmax, ymax, j + 3);
+				vvy |= encodeToBitsStraight(xx, yy, xx1, yy1, xmin, ymin, xmax, ymax, j + 3);
+				vvy |= encodeToBitsStraight(xx0, yy0, xx1, yy1, xmin, ymin, xmax, ymax, j + 3);
+				if ((vvy & 0x2) != 0) break;
+			}
+
+			if (((vvx | vvy) & 0x2) == 0) {
+				// if no bbox bounds crossed:
+				skip |= vvx == 0x1;		// three cutting points are left outside
+				skip |= vvx == 0x4;		// three cutting points are right outside
+				skip |= vvy == 0x1;		// three cutting points are top outside
+				skip |= vvy == 0x4;		// three cutting points are bottom outside
+			}
+		}
+
+		if (skip) continue;
+
+		// save current point at [w <= i]
+		ptarray_set_point4d(points, w++, &p);
+		vx0 = vx;
+		vy0 = vy;
+		xx0 = xx;
+		yy0 = yy;
+		vxall |= vx;
+		vyall |= vy;
+		insideAll |= inside;
+	}
+
+	if (closed && w > 0) {
+		// re-add first new point at the end if closed
+		getPoint4d_p(points, 0, &p);
+		ptarray_set_point4d(points, w++, &p);
+	}
+
+	// eval empty cases
+	clear = w < minpoints; 		// too less points
+	clear |= vxall == 0x01;		// completely left outside
+	clear |= vxall == 0x04;		// completely right outside
+	clear |= vyall == 0x01;		// completely top outside
+	clear |= vyall == 0x04;		// completely bottom outside
+
+	// clear if everything is outside and not enclosing
+	if (optimize && !clear && !insideAll) { // not required if points inside bbox
+		cutting = false;
+		for (int r = 0; r < w - 1; r++) {
+
+			getPoint4d_p(points, r, &p);
+			getPoint4d_p(points, r + 1, &p1);
+
+			xa = p.x;
+			ya = p.y;
+			xb = p1.x;
+			yb = p1.y;
+
+			for (j = 0; j < 4 && !cutting; j++) {
+				cutting |= encodeToBitsStraight(xa, ya, xb, yb, xmin, ymin, xmax, ymax, j + 1) == 0x2;
+			}
+		}
+
+		if (!cutting && closed) {
+			// test if polygon surrounds bbox completely or is fully contained within bbox
+			// using even-odd rule algorithm
+			crossingN = 0;
+			for (int r = 0; r < w - 1; r++) {
+
+				getPoint4d_p(points, r, &p);
+				getPoint4d_p(points, r + 1, &p1);
+
+				xa = p.x;
+				ya = p.y;
+				xb = p1.x;
+				yb = p1.y;
+
+				if (encodeToBitsStraight(xa, ya, xb, yb, xmin, ymin, xmax, ymax, 1) == 0x1) crossingN++;
+			}
+			clear |= crossingN % 2 == 0; // not surrounding, we can clear
+		}
+	}
+	if (clear) w = 0;
+
+	points->npoints = w;
+}
+
+// ===============================================================================
+// remove points that are irrelevant for rendering the geometry within
+// a view specified by rectangular bounds.
+// 2D-(MULTI)POLYGONs and (MULTI)LINESTRINGs are evaluated, others keep untouched.
+// ===============================================================================
+PG_FUNCTION_INFO_V1(ST_RemoveIrrelevantPointsForView);
+Datum ST_RemoveIrrelevantPointsForView(PG_FUNCTION_ARGS) {
+
+	unsigned int i, j, iw, jw;
+
+	// gserialized logic see for example in /postgis/lwgeom_functions_basic.c,
+	// type definitions see /liblwgeom/liblwgeom.h(.in)
+
+	GSERIALIZED *serialized_in;
+	GSERIALIZED *serialized_out;
+
+	LWGEOM *geom;
+	GBOX *bbox;
+
+	// geom input check
+	if (PG_GETARG_POINTER(0) == NULL) {
+		PG_RETURN_NULL();
+	}
+
+	serialized_in = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+
+	// box input check
+	if (PG_GETARG_POINTER(1) == NULL) {
+		// no BBOX given, leave untouched
+		PG_RETURN_POINTER(serialized_in);
+	}
+
+	// type check (only polygon and line types are supported yet)
+	if (gserialized_get_type(serialized_in) != POLYGONTYPE &&
+		gserialized_get_type(serialized_in) != MULTIPOLYGONTYPE &&
+		gserialized_get_type(serialized_in) != LINETYPE &&
+		gserialized_get_type(serialized_in) != MULTILINETYPE) {
+
+		// no (multi)polygon or (multi)linetype, leave untouched
+		PG_RETURN_POINTER(serialized_in);
+	}
+
+	// deserialize geom and copy coordinates (no clone_deep)
+	geom = lwgeom_from_gserialized(serialized_in);
+
+	// bbox checks
+	bbox = (GBOX*)PG_GETARG_DATUM(1);
+	if (!geom->bbox) {
+		lwgeom_add_bbox(geom);
+	}
+
+	if (!geom->bbox) {
+		// no bbox determinable, leave untouched
+		lwgeom_free(geom);
+		PG_RETURN_POINTER(serialized_in);
+	}
+
+	if (bbox->xmin <= geom->bbox->xmin &&
+		bbox->ymin <= geom->bbox->ymin &&
+		bbox->xmax >= geom->bbox->xmax &&
+		bbox->ymax >= geom->bbox->ymax) {
+
+		// trivial case: geometry is fully covered by requested bbox
+		lwgeom_free(geom);
+		PG_RETURN_POINTER(serialized_in);
+	}
+
+	if (geom->type == LINETYPE) {
+
+		LWLINE* line = (LWLINE*)geom;
+		removePoints(line->points, bbox, false);
+	}
+
+	if (geom->type == MULTILINETYPE) {
+
+		LWMLINE* mline = (LWMLINE*)geom;
+		iw = 0;
+		for (i=0; i<mline->ngeoms; i++) {
+			LWLINE* line = mline->geoms[i];
+			removePoints(line->points, bbox, false);
+
+			if (line->points->npoints) {
+				// keep (reduced) line
+				mline->geoms[iw++] = line;
+			}
+			else {
+				// discard current line
+				lwfree(line);
+			}
+		}
+		mline->ngeoms = iw;
+	}
+
+	if (geom->type == POLYGONTYPE) {
+
+		LWPOLY* polygon = (LWPOLY*)geom;
+		iw = 0;
+		for (i=0; i<polygon->nrings; i++) {
+			removePoints(polygon->rings[i], bbox, true);
+
+			if (polygon->rings[i]->npoints) {
+				// keep (reduced) ring
+				polygon->rings[iw++] = polygon->rings[i];
+			}
+			else {
+				if (!i) {
+					// exterior ring outside, free and skip all rings
+					unsigned int k;
+					for (k=0; k<polygon->nrings; k++) {
+						lwfree(polygon->rings[k]);
+					}
+					break;
+				}
+				else {
+					// free and remove current interior ring
+					lwfree(polygon->rings[i]);
+				}
+			}
+		}
+		polygon->nrings = iw;
+	}
+
+	if (geom->type == MULTIPOLYGONTYPE) {
+
+		LWMPOLY* mpolygon = (LWMPOLY*)geom;
+		jw = 0;
+		for (j=0; j<mpolygon->ngeoms; j++) {
+
+			LWPOLY* polygon = mpolygon->geoms[j];
+			iw = 0;
+			for (i=0; i<polygon->nrings; i++) {
+				removePoints(polygon->rings[i], bbox, true);
+
+				if (polygon->rings[i]->npoints) {
+					// keep (reduced) ring
+					polygon->rings[iw++] = polygon->rings[i];
+				}
+				else {
+					if (!i) {
+						// exterior ring outside, free and skip all rings
+						unsigned int k;
+						for (k=0; k<polygon->nrings; k++) {
+							lwfree(polygon->rings[k]);
+						}
+						break;
+					}
+					else {
+						// free and remove current interior ring
+						lwfree(polygon->rings[i]);
+					}
+				}
+			}
+			polygon->nrings = iw;
+
+			if (iw) {
+				mpolygon->geoms[jw++] = polygon;
+			}
+			else {
+				// free and remove polygon from multipolygon
+				lwfree(polygon);
+			}
+		}
+		mpolygon->ngeoms = jw;
+	}
+
+	// recompute bbox if computed previously (may result in NULL)
+	lwgeom_drop_bbox(geom);
+	lwgeom_add_bbox(geom);
+
+	serialized_out = gserialized_from_lwgeom(geom, 0);
+	lwgeom_free(geom);
+
+	PG_FREE_IF_COPY(serialized_in, 0); // see postgis/lwgeom_functions_basic.c, force_2d
+	PG_RETURN_POINTER(serialized_out);
+}
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 89626c9dd..1bca2d37c 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -6967,4 +6967,14 @@ CREATE OR REPLACE FUNCTION ST_3DLineInterpolatePoint(geometry, float8)
 #include "postgis_spgist.sql.in"
 #include "postgis_letters.sql"
 
+
+-----------------------------------------------------------------------
+-- ST_RemoveIrrelevantPointsForView
+-----------------------------------------------------------------------
+-- Availability: 3.5.0
+CREATE OR REPLACE FUNCTION ST_RemoveIrrelevantPointsForView(geometry, box2d)
+RETURNS geometry
+AS 'MODULE_PATHNAME','ST_RemoveIrrelevantPointsForView'
+LANGUAGE 'c' VOLATILE STRICT; 
+
 COMMIT;
diff --git a/regress/core/remove_irrelevant_points_for_view.sql b/regress/core/remove_irrelevant_points_for_view.sql
new file mode 100644
index 000000000..3777c5f02
--- /dev/null
+++ b/regress/core/remove_irrelevant_points_for_view.sql
@@ -0,0 +1,24 @@
+SELECT 0, ST_AsText(
+    ST_RemoveIrrelevantPointsForView(
+    ST_GeomFromText('MULTIPOLYGON(((10 10, 20 10, 30 10, 40 10, 20 20, 10 20, 10 10)),((10 10, 20 10, 20 20, 10 20, 10 10)))'),
+	ST_MakeEnvelope(12,12,18,18)));
+
+SELECT 1, ST_AsText(
+    ST_RemoveIrrelevantPointsForView(
+    ST_GeomFromText('MULTILINESTRING((0 0, 10 0,20 0,30 0), (0 15, 5 15, 10 15, 15 15, 20 15, 25 15, 30 15, 40 15), (13 13,15 15,17 17))'),
+	ST_MakeEnvelope(12,12,18,18)));
+
+SELECT 2, ST_AsText(
+    ST_RemoveIrrelevantPointsForView(
+    ST_GeomFromText('LINESTRING(0 0, 10 0,20 0,30 0)'),
+	ST_MakeEnvelope(12,12,18,18)));
+
+SELECT 3, ST_AsText(
+    ST_RemoveIrrelevantPointsForView(
+    ST_GeomFromText('MULTIPOLYGON Z(((10 10 1, 20 10 2, 30 10 3, 40 10 4, 20 20 5, 10 20 6, 10 10 1)),((10 10 1, 20 10 2, 20 20 3, 10 20 4, 10 10 5)))'),
+	ST_MakeEnvelope(12,12,18,18)));
+
+SELECT 4, ST_AsText(
+    ST_RemoveIrrelevantPointsForView(
+    ST_GeomFromText('LINESTRING Z(0 0 1, 10 0 2,20 0 3,30 0 1)'),
+	ST_MakeEnvelope(12,12,18,18)));
diff --git a/regress/core/remove_irrelevant_points_for_view_expected b/regress/core/remove_irrelevant_points_for_view_expected
new file mode 100644
index 000000000..80c617523
--- /dev/null
+++ b/regress/core/remove_irrelevant_points_for_view_expected
@@ -0,0 +1,5 @@
+0|MULTIPOLYGON(((10 10,40 10,20 20,10 20,10 10)),((10 10,20 10,20 20,10 20,10 10)))
+1|MULTILINESTRING((10 15,15 15,20 15),(13 13,15 15,17 17))
+2|LINESTRING EMPTY
+3|MULTIPOLYGON Z (((10 10 1,40 10 4,20 20 5,10 20 6,10 10 1)),((10 10 1,20 10 2,20 20 3,10 20 4,10 10 1)))
+4|LINESTRING Z EMPTY
diff --git a/regress/core/tests.mk.in b/regress/core/tests.mk.in
index 6d8b4ed22..7182db0c0 100644
--- a/regress/core/tests.mk.in
+++ b/regress/core/tests.mk.in
@@ -94,6 +94,7 @@ TESTS += \
 	$(top_srcdir)/regress/core/regress_proj_4890 \
 	$(top_srcdir)/regress/core/regress_proj_pipeline \
 	$(top_srcdir)/regress/core/relate \
+	$(top_srcdir)/regress/core/remove_irrelevant_points_for_view \
 	$(top_srcdir)/regress/core/remove_repeated_points \
 	$(top_srcdir)/regress/core/removepoint \
 	$(top_srcdir)/regress/core/reverse \

-----------------------------------------------------------------------

Summary of changes:
 NEWS                                               |   1 +
 .../static/st_removeirrelevantpointsforview.png    | Bin 0 -> 25389 bytes
 .../st_removeirrelevantpointsforview_crossing.png  | Bin 0 -> 12137 bytes
 doc/introduction.xml                               |   1 +
 doc/reference_editor.xml                           | 111 +++++
 postgis/Makefile.in                                |   1 +
 postgis/lwgeom_remove_irrelevant_points_for_view.c | 489 +++++++++++++++++++++
 postgis/postgis.sql.in                             |  11 +
 regress/core/remove_irrelevant_points_for_view.sql |  24 +
 .../remove_irrelevant_points_for_view_expected     |   5 +
 regress/core/tests.mk.in                           |   1 +
 11 files changed, 644 insertions(+)
 create mode 100644 doc/html/images/static/st_removeirrelevantpointsforview.png
 create mode 100644 doc/html/images/static/st_removeirrelevantpointsforview_crossing.png
 create mode 100644 postgis/lwgeom_remove_irrelevant_points_for_view.c
 create mode 100644 regress/core/remove_irrelevant_points_for_view.sql
 create mode 100644 regress/core/remove_irrelevant_points_for_view_expected


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list