]> granicus.if.org Git - postgis/commitdiff
Fix infinite loop in linearization of a big radius small arc
authorSandro Santilli <strk@kbt.io>
Fri, 15 Jun 2018 22:23:30 +0000 (22:23 +0000)
committerSandro Santilli <strk@kbt.io>
Fri, 15 Jun 2018 22:23:30 +0000 (22:23 +0000)
Closes #4058 in 2.4 branch (2.4.5dev)
Includes unit test

git-svn-id: http://svn.osgeo.org/postgis/branches/2.4@16619 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
liblwgeom/cunit/cu_lwstroke.c
liblwgeom/lwstroke.c

diff --git a/NEWS b/NEWS
index ed852e29c95ed0713f94d509823aa2c4298abe51..b29935aaf40acf796f2fd90008d09039e7cf403b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ PostGIS 2.4.5
 
   - #4031, Survive to big MaxError tolerances passed to ST_CurveToLine
            (Sandro Santilli)
+  - #4058, Fix infinite loop in linearization of a big radius small arc
+           (Sandro Santilli)
   - #4071, ST_ClusterKMeans crash on NULL/EMPTY fixed (Darafei Praliaskouski)
   - #4079, ensure St_AsMVTGeom outputs CW oriented polygons (Paul Ramsey)
   - #4070, use standard interruption error code on GEOS interruptions
index c43f5fdb468d34a359a6e576f9de4cb1e681d7e3..c151c821c21823a7505da18ac1fe36292f07987f 100644 (file)
@@ -237,6 +237,39 @@ static void test_lwcurve_linearize(void)
 
        lwgeom_free(in);
 
+       /*
+        * ROBUSTNESS: big radius, small tolerance
+        * See https://trac.osgeo.org/postgis/ticket/4058
+        * NOTE: we are really only interested in not enterying
+        *       an infinite loop here
+        */
+       toltype = LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION;
+       in = lwgeom_from_text("CIRCULARSTRING("
+                       "2696000.553 1125699.831999999936670, "
+                       "2695950.552000000141561 1125749.833000000100583, "
+                       "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)");
+       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)");
+       lwfree(str);
+       lwgeom_free(out);
+#ifndef SKIP_TEST_RETAIN_ANGLE
+       out = lwcurve_linearize(in, 0.0001, toltype,
+               LW_LINEARIZE_FLAG_SYMMETRIC |
+               LW_LINEARIZE_FLAG_RETAIN_ANGLE
+       );
+       str = lwgeom_to_text(out, 2);
+       ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695932 1125768,2695866 1125836)");
+       lwfree(str);
+       lwgeom_free(out);
+#endif /* SKIP_TEST_RETAIN_ANGLE */
+       lwgeom_free(in);
+
        /***********************************************************
         *
         *  Maximum angle tolerance type
index a89f0c4c1604c69882b688a2e858d04dd81c537c..71fe74f6f75d923ab8bb263b3db014fe13a27b18 100644 (file)
@@ -154,8 +154,13 @@ lwarc_linearize(POINTARRAY *to,
 
        LWDEBUG(2, "lwarc_linearize called.");
 
+       LWDEBUGF(2, " curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)",
+               t1->x, t1->y, t2->x, t2->y, t3->x, t3->y);
+
        p2_side = lw_segment_side(t1, t3, t2);
 
+       LWDEBUGF(2, " p2 side is %d", p2_side);
+
        /* Force counterclockwise scan if SYMMETRIC operation is requsested */
        if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
        {
@@ -232,7 +237,7 @@ lwarc_linearize(POINTARRAY *to,
                 *   angle = acos( 1 - tol/radius )
                 *
                 * Constraints: 1.0 - tol/radius must be between -1 and 1
-                * which means tol/radius must be between 0 and 2 times
+                * 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!
                 *
@@ -244,7 +249,15 @@ lwarc_linearize(POINTARRAY *to,
                        LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, "
                                    "using arc-max 2 * radius == %g", tol, maxErr);
                }
-               halfAngle = acos( 1.0 - maxErr / radius );
+               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);
        }}