[geos-commits] [SCM] GEOS branch main updated. 8b04f6d04b3c63e8a8c0edb2315be9197ec45bef

git at osgeo.org git at osgeo.org
Fri May 2 10:56:38 PDT 2025


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 "GEOS".

The branch, main has been updated
       via  8b04f6d04b3c63e8a8c0edb2315be9197ec45bef (commit)
       via  4d5497c828b8431d80589413e160dd4b70e52cfe (commit)
      from  4ae3f0536735923dc93d6ea8a1e0dadafc791af1 (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 8b04f6d04b3c63e8a8c0edb2315be9197ec45bef
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri May 2 10:47:59 2025 -0700

    FIx MaximumIInscribedCircle isRadiusWithin robustness issue, JTS commit 8d5ced1b784d232e1eecf5df4b71873e8822336a

diff --git a/src/algorithm/construct/ExactMaxInscribedCircle.cpp b/src/algorithm/construct/ExactMaxInscribedCircle.cpp
index 561133af4..3491d8703 100644
--- a/src/algorithm/construct/ExactMaxInscribedCircle.cpp
+++ b/src/algorithm/construct/ExactMaxInscribedCircle.cpp
@@ -115,9 +115,11 @@ ExactMaxInscribedCircle::computeConvexQuadrilateral(const CoordinateSequence* ri
     }
 
     double diameter = ringCW->getEnvelope().getDiameter();
+    //-- expand diameter for robustness
+    double diamWithTolerance = 2 * diameter;
 
     //-- compute corner bisectors
-    std::array<LineSegment, 4> bisector = computeBisectors(ringCW, diameter);
+    std::array<LineSegment, 4> bisector = computeBisectors(ringCW, diamWithTolerance);
     //-- compute nodes and find interior one farthest from sides
     double maxDist = -1;
     CoordinateXY center;
@@ -129,6 +131,10 @@ ExactMaxInscribedCircle::computeConvexQuadrilateral(const CoordinateSequence* ri
 
         CoordinateXY nodePt = b1.intersection(b2);
 
+        if (nodePt.isNull()) {
+            continue;
+        }
+
         //-- only interior nodes are considered
         if (! isPointInConvexRing(ringCW, nodePt)) {
             continue;
diff --git a/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp b/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp
index b1f3729aa..0c1fe944c 100644
--- a/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp
+++ b/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp
@@ -61,5 +61,17 @@ void object::test<1> ()
     checkRadiusWithin(wkt, 0.2, true);
 }
 
+//
+// testThinQuad
+//
+template<>
+template<>
+void object::test<2> ()
+{
+    std::string wkt = "POLYGON ((1158415.1 6142668.8001, 1158415.0999843003 6142668.800147668, 1158416.4403733865 6142679.523159596, 1158416.4404 6142679.5232, 1158415.1 6142668.8001))";
+    checkRadiusWithin(wkt, 1.0e-5, false);
+    checkRadiusWithin(wkt, 1.0e-3, true);
+}
+
 
 } // namespace tut

commit 4d5497c828b8431d80589413e160dd4b70e52cfe
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri May 2 10:37:53 2025 -0700

    Add MaximumInscribedCircle.isRadiusWithin function, port of https://github.com/locationtech/jts/pull/1125

diff --git a/include/geos/algorithm/construct/MaximumInscribedCircle.h b/include/geos/algorithm/construct/MaximumInscribedCircle.h
index 17d2a88a3..1a71fb0b0 100644
--- a/include/geos/algorithm/construct/MaximumInscribedCircle.h
+++ b/include/geos/algorithm/construct/MaximumInscribedCircle.h
@@ -3,6 +3,7 @@
  * GEOS - Geometry Engine Open Source
  * http://geos.osgeo.org
  *
+ * Copyright (c) 2020 Martin Davis
  * Copyright (C) 2020 Paul Ramsey <pramsey at cleverelephant.ca>
  *
  * This is free software; you can redistribute and/or modify it under
@@ -13,7 +14,7 @@
  **********************************************************************
  *
  * Last port: algorithm/construct/MaximumInscribedCircle.java
- * https://github.com/locationtech/jts/commit/98274a7ea9b40651e9de6323dc10fb2cac17a245
+ * https://github.com/locationtech/jts/commit/f0b9a808bdf8a973de435f737e37b7a221e231cb
  *
  **********************************************************************/
 
@@ -46,11 +47,48 @@ namespace algorithm { // geos::algorithm
 namespace construct { // geos::algorithm::construct
 
 /**
- * Computes the Euclidean distance (L2 metric) from a Point to a Geometry.
+ * Constructs the Maximum Inscribed Circle for a
+ * polygonal Geometry, up to a specified tolerance.
+ * The Maximum Inscribed Circle is determined by a point in the interior of the area
+ * which has the farthest distance from the area boundary,
+ * along with a boundary point at that distance.
+ *
+ * In the context of geography the center of the Maximum Inscribed Circle
+ * is known as the **Pole of Inaccessibility**.
+ * A cartographic use case is to determine a suitable point
+ * to place a map label within a polygon.
+ *
+ * The radius length of the Maximum Inscribed Circle is a
+ * measure of how "narrow" a polygon is. It is the
+ * distance at which the negative buffer becomes empty.
+ *
+ * The class supports testing whether a polygon is "narrower"
+ * than a specified distance via
+ * isRadiusWithin(Geometry, double) or
+ * isRadiusWithin(double).
+ * Testing for the maximum radius is generally much faster
+ * than computing the actual radius value, since short-circuiting
+ * is used to limit the approximation iterations.
+ *
+ * The class supports polygons with holes and multipolygons.
+ *
+ * The implementation uses a successive-approximation technique
+ * over a grid of square cells covering the area geometry.
+ * The grid is refined using a branch-and-bound algorithm.
+ * Point containment and distance are computed in a performant
+ * way by using spatial indexes.
+ *
+ * Future Enhancements
+ *
+ *   * Support a polygonal constraint on placement of center point,
+ *     for example to produce circle-packing constructions,
+ *     or support multiple labels.
+ *
+ * @author Martin Davis
  *
- * Also computes two points which are separated by the distance.
  */
 class GEOS_DLL MaximumInscribedCircle {
+
     using IndexedPointInAreaLocator = geos::algorithm::locate::IndexedPointInAreaLocator;
     using IndexedFacetDistance = geos::operation::distance::IndexedFacetDistance;
 
@@ -82,10 +120,24 @@ public:
     /**
     * Gets a line representing a radius of the Largest Empty Circle.
     *
-    * @return a line from the center of the circle to a point on the edge
+    * @return a 2-point line from the center of the circle to a point on the edge
     */
     std::unique_ptr<geom::LineString> getRadiusLine();
 
+    /**
+    * Tests if the radius of the maximum inscribed circle
+    * is no longer than the specified distance.
+    * This method determines the distance tolerance automatically
+    * as a fraction of the maxRadius value.
+    * After this method is called the center and radius
+    * points provide locations demonstrating where
+    * the radius exceeds the specified maximum.
+    *
+    * @param maxRadius the (non-negative) radius value to test
+    * @return true if the max in-circle radius is no longer than the max radius
+    */
+    bool isRadiusWithin(double maxRadius);
+
     /**
     * Computes the center point of the Maximum Inscribed Circle
     * of a polygonal geometry, up to a given tolerance distance.
@@ -106,6 +158,18 @@ public:
     */
     static std::unique_ptr<geom::LineString> getRadiusLine(const geom::Geometry* polygonal, double tolerance);
 
+    /**
+    * Tests if the radius of the maximum inscribed circle
+    * is no longer than the specified distance.
+    * This method determines the distance tolerance automatically
+    * as a fraction of the maxRadius value.
+    *
+    * @param polygonal a polygonal geometry
+    * @param maxRadius the radius value to test
+    * @return true if the max in-circle radius is no longer than the max radius
+    */
+    static bool isRadiusWithin(const geom::Geometry* polygonal, double maxRadius);
+
     /**
      * Computes the maximum number of iterations allowed.
      * Uses a heuristic based on the area of the input geometry
@@ -134,6 +198,10 @@ private:
     bool done;
     geom::CoordinateXY centerPt;
     geom::CoordinateXY radiusPt;
+    double maximumRadius = -1;
+
+    /* private constant */
+    static constexpr double MAX_RADIUS_FRACTION = 0.0001;
 
     /* private methods */
     double distanceToBoundary(const geom::Point& pt);
diff --git a/src/algorithm/construct/MaximumInscribedCircle.cpp b/src/algorithm/construct/MaximumInscribedCircle.cpp
index 686b9aa30..a39640227 100644
--- a/src/algorithm/construct/MaximumInscribedCircle.cpp
+++ b/src/algorithm/construct/MaximumInscribedCircle.cpp
@@ -13,7 +13,7 @@
  **********************************************************************
  *
  * Last port: algorithm/construct/MaximumInscribedCircle.java
- * https://github.com/locationtech/jts/commit/98274a7ea9b40651e9de6323dc10fb2cac17a245
+ * https://github.com/locationtech/jts/commit/f0b9a808bdf8a973de435f737e37b7a221e231cb
  *
  **********************************************************************/
 
@@ -80,6 +80,14 @@ MaximumInscribedCircle::getRadiusLine(const Geometry* polygonal, double toleranc
     return mic.getRadiusLine();
 }
 
+/* public static */
+bool
+MaximumInscribedCircle::isRadiusWithin(const Geometry* polygonal, double maxRadius)
+{
+    MaximumInscribedCircle mic(polygonal, -1);
+    return mic.isRadiusWithin(maxRadius);
+}
+
 /* public static */
 std::size_t
 MaximumInscribedCircle::computeMaximumIterations(const Geometry* geom, double toleranceDist)
@@ -171,6 +179,26 @@ MaximumInscribedCircle::createInteriorPointCell(const Geometry* geom)
     return cell;
 }
 
+
+/* public */
+bool
+MaximumInscribedCircle::isRadiusWithin(double maxRadius)
+{
+    if (maxRadius < 0) {
+        throw util::IllegalArgumentException("Radius length must be non-negative");
+    }
+    //-- handle 0 corner case, to provide maximum domain
+    if (maxRadius == 0) {
+        return false;
+    }
+    maximumRadius = maxRadius;
+    tolerance = maxRadius * MAX_RADIUS_FRACTION;
+    compute();
+    double radius = centerPt.distance(radiusPt);
+    return radius <= maximumRadius;
+}
+
+
 /* private */
 void
 MaximumInscribedCircle::compute()
@@ -197,6 +225,11 @@ MaximumInscribedCircle::compute()
         return;
     }
 
+    //-- only needed for approximation
+    if (tolerance <= 0) {
+        throw util::IllegalArgumentException("Tolerance must be positive");
+    }
+
     computeApproximation();
 }
 
@@ -238,7 +271,7 @@ MaximumInscribedCircle::computeApproximation()
             GEOS_CHECK_FOR_INTERRUPTS();
         }
 
-        //-- if cell must be closer than furthest, terminate since all remaining cells in queue are even closer.
+        //-- if cell must be closer than farthest, terminate since all remaining cells in queue are even closer.
         if (cell.getMaxDistance() < farthestCell.getDistance())
             break;
 
@@ -246,6 +279,17 @@ MaximumInscribedCircle::computeApproximation()
         if (cell.getDistance() > farthestCell.getDistance()) {
             farthestCell = cell;
         }
+
+        //-- search termination when checking max radius predicate
+        if (maximumRadius >= 0) {
+            //-- found a inside point further than max radius
+            if (farthestCell.getDistance() > maximumRadius)
+                break;
+        //-- no cells can have larger radius
+        if (cell.getMaxDistance() < maximumRadius)
+            break;
+        }
+
         /**
         * Refine this cell if the potential distance improvement
         * is greater than the required tolerance.
diff --git a/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp b/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp
new file mode 100644
index 000000000..b1f3729aa
--- /dev/null
+++ b/tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp
@@ -0,0 +1,65 @@
+//
+// Test Suite for geos::algorithm::construct::MaximumInscribedCircle
+
+#include <tut/tut.hpp>
+// geos
+#include <geos/algorithm/construct/MaximumInscribedCircle.h>
+#include <geos/geom/Coordinate.h>
+#include <geos/geom/Dimension.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/PrecisionModel.h>
+#include <geos/io/WKTReader.h>
+#include <geos/io/WKTWriter.h>
+#include <geos/util.h>
+// std
+#include <sstream>
+#include <string>
+#include <memory>
+
+
+using namespace geos;
+using namespace geos::geom;
+
+using geos::algorithm::construct::MaximumInscribedCircle;
+
+namespace tut {
+//
+// Test Group
+//
+
+// dummy data, not used
+struct test_micrw_data {
+
+    geos::io::WKTReader reader_;
+
+    void
+    checkRadiusWithin(const std::string& wkt, double maxRadius, bool expected)
+    {
+        std::unique_ptr<Geometry> geom = reader_.read(wkt);
+        bool actual = MaximumInscribedCircle::isRadiusWithin(geom.get(), maxRadius);
+        ensure_equals("checkRadiusWithin", expected, actual);
+    }
+
+};
+
+typedef test_group<test_micrw_data> group;
+typedef group::object object;
+
+group test_micrw_group("geos::algorithm::construct::MaxInscribedCircleRadiusWithin");
+
+//
+// testIsland
+//
+template<>
+template<>
+void object::test<1> ()
+{
+    std::string wkt = "POLYGON ((0.041 0.3017, 0.1251 0.3137, 0.3414 0.3297, 0.7217 0.3351, 0.8979 0.3537, 1.0351 0.3838, 1.2432 0.4438, 1.3694 0.4492, 1.5145 0.4325, 1.6167 0.4151, 1.8529 0.4178, 1.94 0.4078, 2.0601 0.3798, 2.2283 0.3784, 2.3084 0.3677, 2.3254 0.347, 2.3414 0.2856, 2.3294 0.2643, 2.2934 0.2483, 2.2633 0.2376, 2.2613 0.2216, 2.2854 0.2069, 2.3414 0.1949, 2.3474 0.1802, 2.3394 0.1615, 2.3034 0.1402, 2.3133 0.1268, 2.3654 0.1228, 2.3855 0.1095, 2.3835 0.0708, 2.2984 0.0541, 2.2844 0.0407, 2.2704 0.0301, 2.2563 0.0327, 2.2203 0.0501, 2.1622 0.0514, 2.1422 0.0461, 2.1382 0.0314, 2.0882 0.0301, 2.0221 0.0407, 1.9761 0.0301, 1.932 0.022, 1.863 0, 1.8409 0.0053, 1.8309 0.0214, 1.8189 0.0147, 1.7869 0.0013, 1.7528 0.004, 1.7108 0.024, 1.6848 0.0547, 1.6888 0.0721, 1.6688 0.0841, 1.6207 0.0948, 1.5907 0.1201, 1.5606 0.1508, 1.4766 0.1628, 1.3054 0.1595, 1.2233 0.1782, 1.0872 0.1742, 0.907 0.1728, 0.7618 0.1935, 0.6317 0.2095, 0.4715 0.2015, 0.3974 0.2055, 0.2282 0.2409, 0.08
  0.2382, 0.018 0.2329, 0 0.2422, 0.01 0.2729, 0.022 0.297, 0.041 0.3017))";
+    checkRadiusWithin(wkt, 0.1, false);
+    checkRadiusWithin(wkt, 0.2, true);
+}
+
+
+} // namespace tut

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

Summary of changes:
 .../algorithm/construct/MaximumInscribedCircle.h   | 76 +++++++++++++++++++--
 .../construct/ExactMaxInscribedCircle.cpp          |  8 ++-
 src/algorithm/construct/MaximumInscribedCircle.cpp | 48 +++++++++++++-
 .../MaxInscribedCircleRadiusWithinTest.cpp         | 77 ++++++++++++++++++++++
 4 files changed, 202 insertions(+), 7 deletions(-)
 create mode 100644 tests/unit/algorithm/construct/MaxInscribedCircleRadiusWithinTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list