[postgis-tickets] r16998 - Impose min number of segments per arc during linearization

Daniel Baston dbaston at gmail.com
Thu Nov 8 04:14:54 PST 2018


Author: dbaston
Date: 2018-11-08 04:14:54 -0800 (Thu, 08 Nov 2018)
New Revision: 16998

Modified:
   trunk/doc/introduction.xml
   trunk/doc/reference_processing.xml
   trunk/liblwgeom/cunit/cu_lwstroke.c
   trunk/liblwgeom/lwstroke.c
   trunk/regress/core/sql-mm-circularstring_expected
   trunk/regress/core/sql-mm-compoundcurve_expected
   trunk/regress/core/sql-mm-multicurve_expected
   trunk/regress/core/tickets.sql
   trunk/regress/core/tickets_expected
Log:
Impose min number of segments per arc during linearization

This commit sets a fixed minimum of two segments per non-circular arc and three
segments per circular arc.

Funded by City of Helsinki.

Fixes #3719
Closes https://github.com/postgis/postgis/pull/320




Modified: trunk/doc/introduction.xml
===================================================================
--- trunk/doc/introduction.xml	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/doc/introduction.xml	2018-11-08 12:14:54 UTC (rev 16998)
@@ -317,6 +317,7 @@
 				<listitem><simpara><ulink url="https://www.camptocamp.com">Camptocamp</ulink></simpara></listitem>
 				<listitem><simpara><ulink url="https://carto.com">Carto</ulink></simpara></listitem>
 				<listitem><simpara><ulink url="https://www.boston.gov">City of Boston (DND)</ulink></simpara></listitem>
+				<listitem><simpara><ulink url="https://www.hel.fi">City of Helsinki</ulink></simpara></listitem>
 				<listitem><simpara><ulink url="https://blog.cleverelephant.ca">Clever Elephant Solutions</ulink></simpara></listitem>
 				<listitem><simpara><ulink url="https://www.alveo.coop">Cooperativa Alveo</ulink></simpara></listitem>
 				<listitem><simpara><ulink url="http://www.elecnor-deimos.com">Deimos Space</ulink></simpara></listitem>

Modified: trunk/doc/reference_processing.xml
===================================================================
--- trunk/doc/reference_processing.xml	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/doc/reference_processing.xml	2018-11-08 12:14:54 UTC (rev 16998)
@@ -976,6 +976,7 @@
 
 		<para>Availability: 1.3.0</para>
     <para>Enhanced: 2.4.0 added support for max-deviation and max-angle tolerance, and for symmetric output.</para>
+    <para>Enhanced: 3.0.0 implemented a minimum number of segments per linearized arc to prevent topological collapse.</para>
 
 		<para>&sfs_compliant;</para>
 		<para>&sqlmm_compliant; SQL-MM 3: 7.1.7</para>
@@ -3027,13 +3028,13 @@
 		  <refsection>
 			<title>Examples</title>
 			<para>A circle simplified too much becomes a triangle, medium an octagon, </para>
-				<programlisting>SELECT ST_Npoints(the_geom) AS np_before, 
-       ST_NPoints(ST_Simplify(the_geom,0.1)) AS np01_notbadcircle, 
+				<programlisting>SELECT ST_Npoints(the_geom) AS np_before,
+       ST_NPoints(ST_Simplify(the_geom,0.1)) AS np01_notbadcircle,
        ST_NPoints(ST_Simplify(the_geom,0.5)) AS np05_notquitecircle,
-       ST_NPoints(ST_Simplify(the_geom,1)) AS np1_octagon, 
+       ST_NPoints(ST_Simplify(the_geom,1)) AS np1_octagon,
        ST_NPoints(ST_Simplify(the_geom,10)) AS np10_triangle,
        (ST_Simplify(the_geom,100) is null) AS  np100_geometrygoesaway
-  FROM 
+  FROM
     (SELECT ST_Buffer('POINT(1 3)', 10,12) As the_geom) AS foo;
 
  np_before | np01_notbadcircle | np05_notquitecircle | np1_octagon | np10_triangle | np100_geometrygoesaway

Modified: trunk/liblwgeom/cunit/cu_lwstroke.c
===================================================================
--- trunk/liblwgeom/cunit/cu_lwstroke.c	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/liblwgeom/cunit/cu_lwstroke.c	2018-11-08 12:14:54 UTC (rev 16998)
@@ -231,7 +231,7 @@
 	 */
 	out = lwcurve_linearize(in, 500, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
 	str = lwgeom_to_text(out, 2);
-	ASSERT_STRING_EQUAL(str, "LINESTRING(20 50,72 -66)");
+	ASSERT_STRING_EQUAL(str, "LINESTRING(20 50,22 -18,72 -66)");
 	lwfree(str);
 	lwgeom_free(out);
 
@@ -240,7 +240,7 @@
 	/*
 	 * ROBUSTNESS: big radius, small tolerance
 	 * See https://trac.osgeo.org/postgis/ticket/4058
-	 * NOTE: we are really only interested in not enterying
+	 * NOTE: we are really only interested in not entering
 	 *       an infinite loop here
 	 */
 	toltype = LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION;
@@ -250,12 +250,12 @@
 			"2695865.195999999996275 1125835.189000)");
 	out = lwcurve_linearize(in, 0.0001, toltype, 0);
 	str = lwgeom_to_text(out, 2);
-	ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695866 1125836)");
+	ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695932 1125768,2695866 1125836)");
 	lwfree(str);
 	lwgeom_free(out);
 	out = lwcurve_linearize(in, 0.0001, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
 	str = lwgeom_to_text(out, 2);
-	ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695866 1125836)");
+	ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695932 1125768,2695866 1125836)");
 	lwfree(str);
 	lwgeom_free(out);
 #ifndef SKIP_TEST_RETAIN_ANGLE
@@ -280,36 +280,36 @@
 
 	in = lwgeom_from_text("CIRCULARSTRING(0 0,100 100,200 0)");
 
-	/* Maximum of 45 degrees, asymmetric */
+	/* Maximum of 45 degrees per segment, asymmetric */
 	out = lwcurve_linearize(in, M_PI / 4.0, toltype, 0);
 	str = lwgeom_to_text(out, 2);
 	ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,30 70,100 100,170 70,200 0)");
 	lwfree(str);
 	lwgeom_free(out);
-	/* Maximum of 0 degrees (invalid) */
+	/* Maximum of 0 degrees per segment (invalid) */
 	cu_error_msg_reset();
 	out = lwcurve_linearize(in, 0, toltype, 0);
 	CU_ASSERT( out == NULL );
 	ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max angle must be bigger than 0, got 0");
-	/* Maximum of -2 degrees (invalid) */
+	/* Maximum of -2 degrees per segment (invalid) */
 	cu_error_msg_reset();
 	out = lwcurve_linearize(in, -2, toltype, 0);
 	CU_ASSERT( out == NULL );
 	ASSERT_STRING_EQUAL(cu_error_msg, "lwarc_linearize: max angle must be bigger than 0, got -2");
-	/* Maximum of 360 degrees, just return endpoints... */
+	/* Maximum of 360 degrees per segment, just return minimum of two segments... */
 	cu_error_msg_reset();
 	out = lwcurve_linearize(in, M_PI*4, toltype, 0);
 	str = lwgeom_to_text(out, 2);
-	ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,200 0)");
+	ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,100 100,200 0)");
 	lwfree(str);
 	lwgeom_free(out);
-	/* Maximum of 70 degrees, asymmetric */
+	/* Maximum of 70 degrees per segment, asymmetric */
 	out = lwcurve_linearize(in, 70 * M_PI / 180, toltype, 0);
 	str = lwgeom_to_text(out, 2);
 	ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,66 94,176 64,200 0)");
 	lwfree(str);
 	lwgeom_free(out);
-	/* Maximum of 70 degrees, symmetric */
+	/* Maximum of 70 degrees per segment, symmetric */
 	out = lwcurve_linearize(in, 70 * M_PI / 180, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
 	str = lwgeom_to_text(out, 2);
 	ASSERT_STRING_EQUAL(str, "LINESTRING(0 0,50 86,150 86,200 0)");

Modified: trunk/liblwgeom/lwstroke.c
===================================================================
--- trunk/liblwgeom/lwstroke.c	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/liblwgeom/lwstroke.c	2018-11-08 12:14:54 UTC (rev 16998)
@@ -20,6 +20,7 @@
  *
  * Copyright (C) 2001-2006 Refractions Research Inc.
  * Copyright (C) 2017      Sandro Santilli <strk at kbt.io>
+ * Copyright (C) 2018      Daniel Baston <dbaston at gmail.com>
  *
  **********************************************************************/
 
@@ -114,6 +115,101 @@
 	}
 }
 
+/* Compute the angle covered by a single segment such that
+ * a given number of segments per quadrant is achieved. */
+static double angle_increment_using_segments_per_quad(double tol)
+{
+	double increment;
+	int perQuad = rint(tol);
+	// error out if tol != perQuad ? (not-round)
+	if ( perQuad != tol )
+	{
+		lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
+		return -1;
+	}
+	if ( perQuad < 1 )
+	{
+		lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
+		return -1;
+	}
+	increment = fabs(M_PI_2 / perQuad);
+	LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
+
+	return increment;
+}
+
+/* Compute the angle covered by a single quadrant such that
+ * the segment deviates from the arc by no more than a given
+ * amount. */
+static double angle_increment_using_max_deviation(double max_deviation, double radius)
+{
+	double increment, halfAngle, maxErr;
+	if ( max_deviation <= 0 )
+	{
+		lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", max_deviation);
+		return -1;
+	}
+
+	/*
+	 * Ref: https://en.wikipedia.org/wiki/Sagitta_(geometry)
+	 *
+	 * An arc "sagitta" (distance between middle point of arc and
+	 * middle point of corresponding chord) is defined as:
+	 *
+	 *   sagitta = radius * ( 1 - cos( angle ) );
+	 *
+	 * We want our sagitta to be at most "tolerance" long,
+	 * and we want to find out angle, so we use the inverse
+	 * formula:
+	 *
+	 *   tol = radius * ( 1 - cos( angle ) );
+	 *   1 - cos( angle ) =  tol/radius
+	 *   - cos( angle ) =  tol/radius - 1
+	 *   cos( angle ) =  - tol/radius + 1
+	 *   angle = acos( 1 - tol/radius )
+	 *
+	 * Constraints: 1.0 - tol/radius must be between -1 and 1
+	 * which means tol must be between 0 and 2 times
+	 * the radius, which makes sense as you cannot have a
+	 * sagitta bigger than twice the radius!
+	 *
+	 */
+	maxErr = max_deviation;
+	if ( maxErr > radius * 2 )
+	{
+		maxErr = radius * 2;
+		LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, "
+			    "using arc-max 2 * radius == %g", tol, maxErr);
+	}
+	do {
+		halfAngle = acos( 1.0 - maxErr / radius );
+		/* TODO: avoid a loop here, going rather straight to
+		 *       a minimum angle value */
+		if ( halfAngle != 0 ) break;
+		LWDEBUGF(2, "lwarc_linearize: tolerance %g is too small for this arc"
+								" to compute approximation angle, doubling it", maxErr);
+		maxErr *= 2;
+	} while(1);
+	increment = 2 * halfAngle;
+	LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
+
+	return increment;
+}
+
+/* Check that a given angle is positive and, if so, take
+ * it to be the angle covered by a single segment. */
+static double angle_increment_using_max_angle(double tol)
+{
+	if ( tol <= 0 )
+	{
+		lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
+		return -1;
+	}
+
+	return tol;
+}
+
+
 /**
  * Segmentize an arc
  *
@@ -148,7 +244,7 @@
 	double increment; /* Angle per segment */
 	double angle_shift = 0;
 	double a1, a2, a3, angle;
-	POINTARRAY *pa = to;
+	POINTARRAY *pa;
 	int is_circle = LW_FALSE;
 	int points_added = 0;
 	int reverse = 0;
@@ -162,7 +258,7 @@
 
 	LWDEBUGF(2, " p2 side is %d", p2_side);
 
-	/* Force counterclockwise scan if SYMMETRIC operation is requsested */
+	/* Force counterclockwise scan if SYMMETRIC operation is requested */
 	if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
 	{
 		/* swap p1-p3 */
@@ -181,7 +277,7 @@
 	if ( p1->x == p3->x && p1->y == p3->y )
 		is_circle = LW_TRUE;
 
-	/* Negative radius signals straight line, p1/p2/p3 are colinear */
+	/* Negative radius signals straight line, p1/p2/p3 are collinear */
 	if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
 	    return 0;
 
@@ -192,89 +288,30 @@
 	else
 		clockwise = LW_FALSE;
 
-	if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD )
-	{{
-		int perQuad = rint(tol);
-		// error out if tol != perQuad ? (not-round)
-		if ( perQuad != tol )
-		{
-			lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
-			return -1;
-		}
-		if ( perQuad < 1 )
-		{
-			lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
-			return -1;
-		}
-		increment = fabs(M_PI_2 / perQuad);
-		LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
-
-	}}
-	else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION )
-	{{
-		double halfAngle, maxErr;
-		if ( tol <= 0 )
-		{
-			lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol);
-			return -1;
-		}
-
-		/*
-		 * Ref: https://en.wikipedia.org/wiki/Sagitta_(geometry)
-		 *
-		 * An arc "sagitta" (distance between middle point of arc and
-		 * middle point of corresponding chord) is defined as:
-		 *
-		 *   sagitta = radius * ( 1 - cos( angle ) );
-		 *
-		 * We want our sagitta to be at most "tolerance" long,
-		 * and we want to find out angle, so we use the inverse
-		 * formula:
-		 *
-		 *   tol = radius * ( 1 - cos( angle ) );
-		 *   1 - cos( angle ) =  tol/radius
-		 *   - cos( angle ) =  tol/radius - 1
-		 *   cos( angle ) =  - tol/radius + 1
-		 *   angle = acos( 1 - tol/radius )
-		 *
-		 * Constraints: 1.0 - tol/radius must be between -1 and 1
-		 * which means tol must be between 0 and 2 times
-		 * the radius, which makes sense as you cannot have a
-		 * sagitta bigger than twice the radius!
-		 *
-		 */
-		maxErr = tol;
-		if ( maxErr > radius * 2 )
-		{
-			maxErr = radius * 2;
-			LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, "
-			            "using arc-max 2 * radius == %g", tol, maxErr);
-		}
-		do {
-			halfAngle = acos( 1.0 - maxErr / radius );
-			/* TODO: avoid a loop here, going rather straight to
-			 *       a minimum angle value */
-			if ( halfAngle != 0 ) break;
-			LWDEBUGF(2, "lwarc_linearize: tolerance %g is too small for this arc"
-									" to compute approximation angle, doubling it", maxErr);
-			maxErr *= 2;
-		} while(1);
-		increment = 2 * halfAngle;
-		LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
-	}}
-	else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE )
+	/* Compute the increment (angle per segment) depending on
+	 * our tolerance type. */
+	switch(tolerance_type)
 	{
-		increment = tol;
-		if ( increment <= 0 )
-		{
-			lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
+		case LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD:
+			increment = angle_increment_using_segments_per_quad(tol);
+			break;
+		case LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION:
+			increment = angle_increment_using_max_deviation(tol, radius);
+			break;
+		case LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE:
+			increment = angle_increment_using_max_angle(tol);
+			break;
+		default:
+			lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type);
 			return -1;
-		}
 	}
-	else
+
+	if (increment < 0)
 	{
-		lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type);
-		return LW_FALSE;
+		/* Error occurred in increment calculation somewhere
+		 * (lwerror already called)
+		 */
+		return -1;
 	}
 
 	/* Angles of each point that defines the arc section */
@@ -285,36 +322,55 @@
 	LWDEBUGF(2, "lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)",
 		a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI);
 
+	/* Calculate total arc angle, in radians */
+	double total_angle = clockwise ? a1 - a3 : a3 - a1;
+	if ( total_angle < 0 ) total_angle += M_PI * 2;
+
+	/* At extreme tolerance values (very low or very high, depending on
+	 * the semantic) we may cause our arc to collapse. In this case,
+	 * we want shrink the increment enough so that we get two segments
+	 * for a standard arc, or three segments for a complete circle. */
+	int min_segs = is_circle ? 3 : 2;
+	if ( ceil(total_angle / increment) < min_segs)
+	{
+		increment = total_angle / min_segs;
+	}
+
 	if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC )
 	{{
-		/* Calculate total arc angle, in radians */
-		double angle = clockwise ? a1 - a3 : a3 - a1;
-		if ( angle < 0 ) angle += M_PI * 2;
 		LWDEBUGF(2, "lwarc_linearize SYMMETRIC requested - total angle %g deg",
 			         angle * 180 / M_PI);
+
 		if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE )
 		{{
-			/* Number of steps */
-			int steps = trunc(angle / increment);
-			/* Angle reminder */
-			double angle_reminder = angle - ( increment * steps );
-			angle_shift = angle_reminder / 2.0;
+			/* Number of complete steps */
+			int steps = trunc(total_angle / increment);
 
+			/* Figure out the angle remainder, i.e. the amount of the angle
+			 * that is left after we can take no more complete angle
+			 * increments. */
+			double angle_remainder = total_angle - ( increment * steps );
+
+			/* Shift the starting angle by half of the remainder. This
+			 * will have the effect of evenly distributing the remainder
+			 * among the first and last segments in the arc. */
+			angle_shift = angle_remainder / 2.0;
+
 			LWDEBUGF(2, "lwarc_linearize RETAIN_ANGLE operation requested - "
-			         "total angle %g, steps %d, increment %g, reminder %g",
-			         angle * 180 / M_PI, steps, increment * 180 / M_PI,
-			         angle_reminder * 180 / M_PI);
+			         "total angle %g, steps %d, increment %g, remainder %g",
+			         total_angle * 180 / M_PI, steps, increment * 180 / M_PI,
+			         angle_remainder * 180 / M_PI);
 		}}
 		else
 		{{
 			/* Number of segments in output */
-			int segs = ceil(angle / increment);
+			int segs = ceil(total_angle / increment);
 			/* Tweak increment to be regular for all the arc */
-			increment = angle/segs;
+			increment = total_angle/segs;
 
 			LWDEBUGF(2, "lwarc_linearize SYMMETRIC operation requested - "
 							"total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d -   I:%g",
-							angle*180/M_PI, p1->x, p1->y, center.x, center.y, p3->x, p3->y,
+							total_angle*180/M_PI, p1->x, p1->y, center.x, center.y, p3->x, p3->y,
 							segs, increment*180/M_PI);
 		}}
 	}}
@@ -354,17 +410,24 @@
 	LWDEBUGF(2, "lwarc_linearize angle_shift:%g, increment:%g",
 		angle_shift * 180/M_PI, increment * 180/M_PI);
 
-	if ( reverse ) {{
+	if ( reverse )
+	{
+		/* Append points in order to a temporary POINTARRAY and
+		 * reverse them before writing to the output POINTARRAY. */
 		const int capacity = 8; /* TODO: compute exactly ? */
 		pa = ptarray_construct_empty(ptarray_has_z(to), ptarray_has_m(to), capacity);
-	}}
+	}
+	else
+	{
+		/* Append points directly to the output POINTARRAY,
+		 * starting with p1. */
+		pa = to;
 
-	/* Sweep from a1 to a3 */
-	if ( ! reverse )
-	{
 		ptarray_append_point(pa, p1, LW_FALSE);
+		++points_added;
 	}
-	++points_added;
+
+	/* Sweep from a1 to a3 */
 	if ( angle_shift ) angle_shift -= increment;
 	LWDEBUGF(2, "a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d",
 		a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise);
@@ -377,7 +440,6 @@
 		pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m);
 		ptarray_append_point(pa, &pt, LW_FALSE);
 		++points_added;
-		angle_shift = 0;
 	}
 
 	if ( reverse ) {{

Modified: trunk/regress/core/sql-mm-circularstring_expected
===================================================================
--- trunk/regress/core/sql-mm-circularstring_expected	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/regress/core/sql-mm-circularstring_expected	2018-11-08 12:14:54 UTC (rev 16998)
@@ -44,13 +44,13 @@
 asewkb03|01080000800500000000000000000014c00000000000000000000000000000000000000000000000000000000000001440000000000000f03f000000000000144000000000000000000000000000000040000000000000244000000000000014c000000000000008400000000000002e4000000000000000000000000000001040
 asewkb04|00c00000080000000300000000000000000000000000000000000000000000000000000000000000003fd126145e9ecd563ff00000000000004008000000000000c0000000000000003fe2bec3330188673ff6a09e667f3bcd3ff00000000000004000000000000000
 asewkb04|00c000000800000005c014000000000000000000000000000000000000000000004010000000000000000000000000000040140000000000003ff0000000000000400800000000000040140000000000000000000000000000400000000000000040000000000000004024000000000000c01400000000000040080000000000003ff0000000000000402e000000000000000000000000000040100000000000000000000000000000
-ST_CurveToLine-201|LINESTRING(0 0,0.58578644 1.41421356)
+ST_CurveToLine-201|LINESTRING(0 0,0.15224093 0.76536686,0.58578644 1.41421356)
 ST_CurveToLine-201|LINESTRING(-5 0,-3.53553391 3.53553391,0 5,3.53553391 3.53553391,5 0,6.46446609 -3.53553391,10 -5,13.53553391 -3.53553391,15 0)
-ST_CurveToLine-202|LINESTRINGM(0 0 0,0.58578644 1.41421356 2)
+ST_CurveToLine-202|LINESTRINGM(0 0 0,0.15224093 0.76536686 -1.5,0.58578644 1.41421356 2)
 ST_CurveToLine-202|LINESTRINGM(-5 0 4,-3.53553391 3.53553391 3.5,0 5 3,3.53553391 3.53553391 2.5,5 0 2,6.46446609 -3.53553391 1.5,10 -5 1,13.53553391 -3.53553391 0.5,15 0 0)
-ST_CurveToLine-203|LINESTRING(0 0 0,0.58578644 1.41421356 1)
+ST_CurveToLine-203|LINESTRING(0 0 0,0.15224093 0.76536686 2.25,0.58578644 1.41421356 1)
 ST_CurveToLine-203|LINESTRING(-5 0 0,-3.53553391 3.53553391 0.5,0 5 1,3.53553391 3.53553391 1.5,5 0 2,6.46446609 -3.53553391 2.5,10 -5 3,13.53553391 -3.53553391 3.5,15 0 4)
-ST_CurveToLine-204|LINESTRING(0 0 0 0,0.58578644 1.41421356 1 2)
+ST_CurveToLine-204|LINESTRING(0 0 0 0,0.15224093 0.76536686 2.25 -1.5,0.58578644 1.41421356 1 2)
 ST_CurveToLine-204|LINESTRING(-5 0 0 4,-3.53553391 3.53553391 0.5 3.5,0 5 1 3,3.53553391 3.53553391 1.5 2.5,5 0 2 2,6.46446609 -3.53553391 2.5 1.5,10 -5 3 1,13.53553391 -3.53553391 3.5 0.5,15 0 4 0)
 ST_CurveToLine-401|LINESTRING(0 0,0.15224093 0.76536686,0.58578644 1.41421356)
 ST_CurveToLine-401|LINESTRING(-5 0,-4.61939766 1.91341716,-3.53553391 3.53553391,-1.91341716 4.61939766,0 5,1.91341716 4.61939766,3.53553391 3.53553391,4.61939766 1.91341716,5 0,5.38060234 -1.91341716,6.46446609 -3.53553391,8.08658284 -4.61939766,10 -5,11.91341716 -4.61939766,13.53553391 -3.53553391,14.61939766 -1.91341716,15 0)

Modified: trunk/regress/core/sql-mm-compoundcurve_expected
===================================================================
--- trunk/regress/core/sql-mm-compoundcurve_expected	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/regress/core/sql-mm-compoundcurve_expected	2018-11-08 12:14:54 UTC (rev 16998)
@@ -30,10 +30,10 @@
 asewkb02|01090000400200000001080000400300000000000000000000000000000000000000000000000000000056cd9e5e1426d13f000000000000f03f00000000000000c067880133c3bee23fcd3b7f669ea0f63f000000000000004001020000400300000067880133c3bee23fcd3b7f669ea0f63f0000000000000040000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000
 asewkb03|01090000800200000001080000800300000000000000000000000000000000000000000000000000000056cd9e5e1426d13f000000000000f03f000000000000084067880133c3bee23fcd3b7f669ea0f63f000000000000f03f01020000800300000067880133c3bee23fcd3b7f669ea0f63f000000000000f03f000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000
 asewkb04|01090000c00200000001080000c003000000000000000000000000000000000000000000000000000000000000000000000056cd9e5e1426d13f000000000000f03f000000000000084000000000000000c067880133c3bee23fcd3b7f669ea0f63f000000000000f03f000000000000004001020000c00300000067880133c3bee23fcd3b7f669ea0f63f000000000000f03f000000000000004000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-ST_CurveToLine-201|LINESTRING(0 0,0.58578644 1.41421356,2 0,0 0)
-ST_CurveToLine-202|LINESTRINGM(0 0 0,0.58578644 1.41421356 2,2 0 0,0 0 0)
-ST_CurveToLine-203|LINESTRING(0 0 0,0.58578644 1.41421356 1,2 0 0,0 0 0)
-ST_CurveToLine-204|LINESTRING(0 0 0 0,0.58578644 1.41421356 1 2,2 0 0 0,0 0 0 0)
+ST_CurveToLine-201|LINESTRING(0 0,0.15224093 0.76536686,0.58578644 1.41421356,2 0,0 0)
+ST_CurveToLine-202|LINESTRINGM(0 0 0,0.15224093 0.76536686 -1.5,0.58578644 1.41421356 2,2 0 0,0 0 0)
+ST_CurveToLine-203|LINESTRING(0 0 0,0.15224093 0.76536686 2.25,0.58578644 1.41421356 1,2 0 0,0 0 0)
+ST_CurveToLine-204|LINESTRING(0 0 0 0,0.15224093 0.76536686 2.25 -1.5,0.58578644 1.41421356 1 2,2 0 0 0,0 0 0 0)
 ST_CurveToLine-401|LINESTRING(0 0,0.15224093 0.76536686,0.58578644 1.41421356,2 0,0 0)
 ST_CurveToLine-402|LINESTRINGM(0 0 0,0.15224093 0.76536686 -1.5,0.58578644 1.41421356 2,2 0 0,0 0 0)
 ST_CurveToLine-403|LINESTRING(0 0 0,0.15224093 0.76536686 2.25,0.58578644 1.41421356 1,2 0 0,0 0 0)

Modified: trunk/regress/core/sql-mm-multicurve_expected
===================================================================
--- trunk/regress/core/sql-mm-multicurve_expected	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/regress/core/sql-mm-multicurve_expected	2018-11-08 12:14:54 UTC (rev 16998)
@@ -14,10 +14,10 @@
 asewkb02|004000000b00000002004000000200000004401400000000000040140000000000004008000000000000400800000000000040140000000000004000000000000000400800000000000040080000000000003ff0000000000000000000000000000040080000000000003ff00000000000000040000008000000030000000000000000000000000000000000000000000000003fd126145e9ecd563ff0000000000000c0000000000000003fe2bec3330188673ff6a09e667f3bcd4000000000000000
 asewkb03|010b0000800200000001020000800400000000000000000014400000000000001440000000000000f03f00000000000008400000000000001440000000000000004000000000000008400000000000000840000000000000084000000000000000000000000000000840000000000000f03f01080000800300000000000000000000000000000000000000000000000000000056cd9e5e1426d13f000000000000f03f000000000000084067880133c3bee23fcd3b7f669ea0f63f000000000000f03f
 asewkb04|00c000000b0000000200c000000200000004401400000000000040140000000000003ff0000000000000400800000000000040080000000000004014000000000000400000000000000040000000000000004008000000000000400800000000000040080000000000003ff0000000000000000000000000000040080000000000003ff00000000000003ff000000000000000c00000080000000300000000000000000000000000000000000000000000000000000000000000003fd126145e9ecd563ff00000000000004008000000000000c0000000000000003fe2bec3330188673ff6a09e667f3bcd3ff00000000000004000000000000000
-ST_CurveToLine-201|MULTILINESTRING((5 5,3 5,3 3,0 3),(0 0,0.58578644 1.41421356))
-ST_CurveToLine-202|MULTILINESTRINGM((5 5 3,3 5 2,3 3 1,0 3 1),(0 0 0,0.58578644 1.41421356 2))
-ST_CurveToLine-203|MULTILINESTRING((5 5 1,3 5 2,3 3 3,0 3 1),(0 0 0,0.58578644 1.41421356 1))
-ST_CurveToLine-204|MULTILINESTRING((5 5 1 3,3 5 2 2,3 3 3 1,0 3 1 1),(0 0 0 0,0.58578644 1.41421356 1 2))
+ST_CurveToLine-201|MULTILINESTRING((5 5,3 5,3 3,0 3),(0 0,0.15224093 0.76536686,0.58578644 1.41421356))
+ST_CurveToLine-202|MULTILINESTRINGM((5 5 3,3 5 2,3 3 1,0 3 1),(0 0 0,0.15224093 0.76536686 -1.5,0.58578644 1.41421356 2))
+ST_CurveToLine-203|MULTILINESTRING((5 5 1,3 5 2,3 3 3,0 3 1),(0 0 0,0.15224093 0.76536686 2.25,0.58578644 1.41421356 1))
+ST_CurveToLine-204|MULTILINESTRING((5 5 1 3,3 5 2 2,3 3 3 1,0 3 1 1),(0 0 0 0,0.15224093 0.76536686 2.25 -1.5,0.58578644 1.41421356 1 2))
 ST_CurveToLine-401|MULTILINESTRING((5 5,3 5,3 3,0 3),(0 0,0.15224093 0.76536686,0.58578644 1.41421356))
 ST_CurveToLine-402|MULTILINESTRINGM((5 5 3,3 5 2,3 3 1,0 3 1),(0 0 0,0.15224093 0.76536686 -1.5,0.58578644 1.41421356 2))
 ST_CurveToLine-403|MULTILINESTRING((5 5 1,3 5 2,3 3 3,0 3 1),(0 0 0,0.15224093 0.76536686 2.25,0.58578644 1.41421356 1))

Modified: trunk/regress/core/tickets.sql
===================================================================
--- trunk/regress/core/tickets.sql	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/regress/core/tickets.sql	2018-11-08 12:14:54 UTC (rev 16998)
@@ -1007,6 +1007,10 @@
 -- #3709
 select '#3709', ST_SnapToGrid(ST_Project('SRID=4326;POINT(1 1)'::geography, 100000, 20)::geometry, 0.0001) = ST_SnapToGrid(ST_Project('SRID=4326;POINT(1 1)'::geography, -100000, 20+pi())::geometry, 0.0001);
 
+-- #3719
+select '#3719a', ST_IsValid('CURVEPOLYGON((25495445.625 6671632.625, 25495445.625 6671711.375, 25495555.375 6671711.375, 25495555.375 6671632.625, 25495445.625 6671632.625), COMPOUNDCURVE(CIRCULARSTRING(25495368.0441 6671726.9312,25495368.3959388 6671726.93601515,25495368.7478 6671726.9333), (25495368.7478 6671726.9333,25495368.0441 6671726.9312)))');
+select '#3719b', ST_IsValid('CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING(1.0441 2.9312,1.3959388 2.93601515,1.7478 2.9333), (1.7478 2.9333,1.0441 2.9312)))'::geometry);
+
 -- #3774
 select '#3774', abs(pi() + 2 - st_length('COMPOUNDCURVE(CIRCULARSTRING(0 0, 1 1, 2 0), (2 0, 4 0))'::geometry)) < 0.000000001;
 

Modified: trunk/regress/core/tickets_expected
===================================================================
--- trunk/regress/core/tickets_expected	2018-11-08 06:57:59 UTC (rev 16997)
+++ trunk/regress/core/tickets_expected	2018-11-08 12:14:54 UTC (rev 16998)
@@ -305,6 +305,9 @@
 #3627b|t
 #3704|t
 #3709|t
+NOTICE:  Hole lies outside shell at or near point 25495368.044100001 6671726.9312000005
+#3719a|f
+#3719b|t
 #3774|t
 #1014a|POINT(0 0)
 #1014a|POINT(0 0)



More information about the postgis-tickets mailing list