Coverage Report

Created: 2025-12-18 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/include/geos/algorithm/construct/MaximumInscribedCircle.h
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (c) 2020 Martin Davis
7
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
8
 *
9
 * This is free software; you can redistribute and/or modify it under
10
 * the terms of the GNU Lesser General Public Licence as published
11
 * by the Free Software Foundation.
12
 * See the COPYING file for more information.
13
 *
14
 **********************************************************************
15
 *
16
 * Last port: algorithm/construct/MaximumInscribedCircle.java
17
 * https://github.com/locationtech/jts/commit/f0b9a808bdf8a973de435f737e37b7a221e231cb
18
 *
19
 **********************************************************************/
20
21
#pragma once
22
23
#include <geos/geom/Coordinate.h>
24
#include <geos/geom/Point.h>
25
#include <geos/geom/Envelope.h>
26
#include <geos/algorithm/locate/IndexedPointInAreaLocator.h>
27
#include <geos/operation/distance/IndexedFacetDistance.h>
28
29
#include <memory>
30
#include <queue>
31
32
33
34
namespace geos {
35
namespace geom {
36
class Coordinate;
37
class Envelope;
38
class Geometry;
39
class GeometryFactory;
40
class LineString;
41
class Point;
42
}
43
}
44
45
namespace geos {
46
namespace algorithm { // geos::algorithm
47
namespace construct { // geos::algorithm::construct
48
49
/**
50
 * Constructs the Maximum Inscribed Circle for a
51
 * polygonal Geometry, up to a specified tolerance.
52
 * The Maximum Inscribed Circle is determined by a point in the interior of the area
53
 * which has the farthest distance from the area boundary,
54
 * along with a boundary point at that distance.
55
 *
56
 * In the context of geography the center of the Maximum Inscribed Circle
57
 * is known as the **Pole of Inaccessibility**.
58
 * A cartographic use case is to determine a suitable point
59
 * to place a map label within a polygon.
60
 *
61
 * The radius length of the Maximum Inscribed Circle is a
62
 * measure of how "narrow" a polygon is. It is the
63
 * distance at which the negative buffer becomes empty.
64
 *
65
 * The class supports testing whether a polygon is "narrower"
66
 * than a specified distance via
67
 * isRadiusWithin(Geometry, double) or
68
 * isRadiusWithin(double).
69
 * Testing for the maximum radius is generally much faster
70
 * than computing the actual radius value, since short-circuiting
71
 * is used to limit the approximation iterations.
72
 *
73
 * The class supports polygons with holes and multipolygons.
74
 *
75
 * The implementation uses a successive-approximation technique
76
 * over a grid of square cells covering the area geometry.
77
 * The grid is refined using a branch-and-bound algorithm.
78
 * Point containment and distance are computed in a performant
79
 * way by using spatial indexes.
80
 *
81
 * Future Enhancements
82
 *
83
 *   * Support a polygonal constraint on placement of center point,
84
 *     for example to produce circle-packing constructions,
85
 *     or support multiple labels.
86
 *
87
 * @author Martin Davis
88
 *
89
 */
90
class GEOS_DLL MaximumInscribedCircle {
91
92
    using IndexedPointInAreaLocator = geos::algorithm::locate::IndexedPointInAreaLocator;
93
    using IndexedFacetDistance = geos::operation::distance::IndexedFacetDistance;
94
95
public:
96
97
    MaximumInscribedCircle(const geom::Geometry* polygonal, double tolerance);
98
0
    ~MaximumInscribedCircle() = default;
99
100
    /**
101
    * Gets the center point of the maximum inscribed circle
102
    * (up to the tolerance distance).
103
    *
104
    * @return the center point of the maximum inscribed circle
105
    */
106
    std::unique_ptr<geom::Point> getCenter();
107
108
    /**
109
    * Gets a point defining the radius of the Maximum Inscribed Circle.
110
    * This is a point on the boundary which is
111
    * nearest to the computed center of the Maximum Inscribed Circle.
112
    * The line segment from the center to this point
113
    * is a radius of the constructed circle, and this point
114
    * lies on the boundary of the circle.
115
    *
116
    * @return a point defining the radius of the Maximum Inscribed Circle
117
    */
118
    std::unique_ptr<geom::Point> getRadiusPoint();
119
120
    /**
121
    * Gets a line representing a radius of the Largest Empty Circle.
122
    *
123
    * @return a 2-point line from the center of the circle to a point on the edge
124
    */
125
    std::unique_ptr<geom::LineString> getRadiusLine();
126
127
    /**
128
    * Tests if the radius of the maximum inscribed circle
129
    * is no longer than the specified distance.
130
    * This method determines the distance tolerance automatically
131
    * as a fraction of the maxRadius value.
132
    * After this method is called the center and radius
133
    * points provide locations demonstrating where
134
    * the radius exceeds the specified maximum.
135
    *
136
    * @param maxRadius the (non-negative) radius value to test
137
    * @return true if the max in-circle radius is no longer than the max radius
138
    */
139
    bool isRadiusWithin(double maxRadius);
140
141
    /**
142
    * Computes the center point of the Maximum Inscribed Circle
143
    * of a polygonal geometry, up to a given tolerance distance.
144
    *
145
    * @param polygonal a polygonal geometry
146
    * @param tolerance the distance tolerance for computing the center point
147
    * @return the center point of the maximum inscribed circle
148
    */
149
    static std::unique_ptr<geom::Point> getCenter(const geom::Geometry* polygonal, double tolerance);
150
151
    /**
152
    * Computes a radius line of the Maximum Inscribed Circle
153
    * of a polygonal geometry, up to a given tolerance distance.
154
    *
155
    * @param polygonal a polygonal geometry
156
    * @param tolerance the distance tolerance for computing the center point
157
    * @return a line from the center to a point on the circle
158
    */
159
    static std::unique_ptr<geom::LineString> getRadiusLine(const geom::Geometry* polygonal, double tolerance);
160
161
    /**
162
    * Tests if the radius of the maximum inscribed circle
163
    * is no longer than the specified distance.
164
    * This method determines the distance tolerance automatically
165
    * as a fraction of the maxRadius value.
166
    *
167
    * @param polygonal a polygonal geometry
168
    * @param maxRadius the radius value to test
169
    * @return true if the max in-circle radius is no longer than the max radius
170
    */
171
    static bool isRadiusWithin(const geom::Geometry* polygonal, double maxRadius);
172
173
    /**
174
     * Computes the maximum number of iterations allowed.
175
     * Uses a heuristic based on the area of the input geometry
176
     * and the tolerance distance.
177
     * The number of tolerance-sized cells that cover the input geometry area
178
     * is computed, times a safety factor.
179
     * This prevents massive numbers of iterations and created cells
180
     * for casees where the input geometry has extremely small area
181
     * (e.g. is very thin).
182
     *
183
     * @param geom the input geometry
184
     * @param toleranceDist the tolerance distance
185
     * @return the maximum number of iterations allowed
186
     */
187
    static std::size_t computeMaximumIterations(const geom::Geometry* geom, double toleranceDist);
188
189
private:
190
191
    /* private members */
192
    const geom::Geometry* inputGeom;
193
    std::unique_ptr<geom::Geometry> inputGeomBoundary;
194
    double tolerance;
195
    IndexedFacetDistance indexedDistance;
196
    IndexedPointInAreaLocator ptLocator;
197
    const geom::GeometryFactory* factory;
198
    bool done;
199
    geom::CoordinateXY centerPt;
200
    geom::CoordinateXY radiusPt;
201
    double maximumRadius = -1;
202
203
    /* private constant */
204
    static constexpr double MAX_RADIUS_FRACTION = 0.0001;
205
206
    /* private methods */
207
    double distanceToBoundary(const geom::Point& pt);
208
    double distanceToBoundary(double x, double y);
209
    void compute();
210
    void computeApproximation();
211
    void createResult(const geom::CoordinateXY& c, const geom::CoordinateXY& r);
212
213
    /* private class */
214
    class Cell {
215
    private:
216
        static constexpr double SQRT2 = 1.4142135623730951;
217
        double x;
218
        double y;
219
        double hSize;
220
        double distance;
221
        double maxDist;
222
223
    public:
224
        Cell(double p_x, double p_y, double p_hSize, double p_distanceToBoundary)
225
0
            : x(p_x)
226
0
            , y(p_y)
227
0
            , hSize(p_hSize)
228
0
            , distance(p_distanceToBoundary)
229
0
            , maxDist(p_distanceToBoundary+(p_hSize*SQRT2))
230
0
        {};
231
232
        geom::Envelope getEnvelope() const
233
0
        {
234
0
            geom::Envelope env(x-hSize, x+hSize, y-hSize, y+hSize);
235
0
            return env;
236
0
        }
237
238
        double getMaxDistance() const
239
0
        {
240
0
            return maxDist;
241
0
        }
242
        double getDistance() const
243
0
        {
244
0
            return distance;
245
0
        }
246
        double getHSize() const
247
0
        {
248
0
            return hSize;
249
0
        }
250
        double getX() const
251
0
        {
252
0
            return x;
253
0
        }
254
        double getY() const
255
0
        {
256
0
            return y;
257
0
        }
258
259
        bool operator< (const Cell& rhs) const
260
0
        {
261
0
            return maxDist <  rhs.maxDist;
262
0
        }
263
264
        bool operator> (const Cell& rhs) const
265
0
        {
266
0
            return maxDist >  rhs.maxDist;
267
0
        }
268
269
        bool operator==(const Cell& rhs) const
270
0
        {
271
0
            return maxDist == rhs.maxDist;
272
0
        }
273
274
        /**
275
         * The Cell priority queue is sorted by the natural order of maxDistance.
276
         * std::priority_queue sorts with largest first,
277
         * which is what is needed for this algorithm.
278
         */
279
        using CellQueue = std::priority_queue<Cell>;
280
    };
281
282
    void createInitialGrid(const geom::Envelope* env, Cell::CellQueue& cellQueue);
283
    Cell createInteriorPointCell(const geom::Geometry* geom);
284
285
};
286
287
288
} // geos::algorithm::construct
289
} // geos::algorithm
290
} // geos