Coverage Report

Created: 2026-09-14 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/precision/MinimumClearance.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2016 Daniel Baston
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Public Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************
14
 *
15
 * Last port: precision/MinimumClearance.java (f6187ee2 JTS-1.14)
16
 *
17
 **********************************************************************/
18
19
#include <geos/algorithm/Distance.h>
20
#include <geos/precision/MinimumClearance.h>
21
#include <geos/index/strtree/STRtree.h>
22
#include <geos/geom/GeometryFactory.h>
23
#include <geos/operation/distance/FacetSequenceTreeBuilder.h>
24
#include <geos/geom/LineSegment.h>
25
26
using namespace geos::geom;
27
using namespace geos::operation::distance;
28
using namespace geos::index::strtree;
29
30
namespace geos {
31
namespace precision {
32
33
0
MinimumClearance::MinimumClearance(const Geometry* g) : inputGeom(g) {}
34
35
double
36
MinimumClearance::getDistance()
37
0
{
38
0
    compute();
39
0
    return minClearance;
40
0
}
41
42
std::unique_ptr<LineString>
43
MinimumClearance::getLine()
44
0
{
45
0
    compute();
46
47
    // return empty line string if no min pts were found
48
0
    if (minClearance == DoubleInfinity) {
49
0
        return inputGeom->getFactory()->createLineString();
50
0
    }
51
52
0
    return inputGeom->getFactory()->createLineString(minClearancePts->clone());
53
0
}
54
55
void
56
MinimumClearance::compute()
57
0
{
58
0
    class MinClearanceDistance {
59
0
    private:
60
0
        double minDist;
61
0
        std::vector<Coordinate> minPts;
62
63
0
        void
64
0
        updatePts(const Coordinate& p, const Coordinate& seg0, const Coordinate& seg1)
65
0
        {
66
0
            LineSegment seg(seg0, seg1);
67
68
0
            minPts[0] = p;
69
0
            seg.closestPoint(p, minPts[1]);
70
0
        }
71
72
0
    public:
73
0
        MinClearanceDistance() :
74
0
            minDist(DoubleInfinity),
75
0
            minPts(std::vector<Coordinate>(2))
76
0
        {}
77
78
0
        const std::vector<Coordinate>*
79
0
        getCoordinates()
80
0
        {
81
0
            return &minPts;
82
0
        }
83
84
0
        double operator()(const FacetSequence* fs1, const FacetSequence* fs2)
85
0
        {
86
0
            minDist = DoubleInfinity;
87
0
            return distance(fs1, fs2);
88
0
        }
89
90
0
        double
91
0
        distance(const FacetSequence* fs1, const FacetSequence* fs2)
92
0
        {
93
            // Compute MinClearance distance metric
94
95
0
            vertexDistance(fs1, fs2);
96
0
            if(fs1->size() == 1 && fs2->size() == 1) {
97
0
                return minDist;
98
0
            }
99
0
            if(minDist <= 0.0) {
100
0
                return minDist;
101
0
            }
102
103
0
            segmentDistance(fs1, fs2);
104
0
            if(minDist <= 0.0) {
105
0
                return minDist;
106
0
            }
107
108
0
            segmentDistance(fs2, fs1);
109
0
            return minDist;
110
0
        }
111
112
0
        double
113
0
        vertexDistance(const FacetSequence* fs1, const FacetSequence* fs2)
114
0
        {
115
0
            for(std::size_t i1 = 0; i1 < fs1->size(); i1++) {
116
0
                for(std::size_t i2 = 0; i2 < fs2->size(); i2++) {
117
0
                    const Coordinate* p1 = fs1->getCoordinate(i1);
118
0
                    const Coordinate* p2 = fs2->getCoordinate(i2);
119
0
                    if(!p1->equals2D(*p2)) {
120
0
                        double d = p1->distance(*p2);
121
0
                        if(d < minDist) {
122
0
                            minDist = d;
123
0
                            minPts[0] = *p1;
124
0
                            minPts[1] = *p2;
125
0
                            if(d == 0.0) {
126
0
                                return d;
127
0
                            }
128
0
                        }
129
0
                    }
130
0
                }
131
0
            }
132
0
            return minDist;
133
0
        }
134
135
0
        double
136
0
        segmentDistance(const FacetSequence* fs1, const FacetSequence* fs2)
137
0
        {
138
0
            for(std::size_t i1 = 0; i1 < fs1->size(); i1++) {
139
0
                for(std::size_t i2 = 1; i2 < fs2->size(); i2++) {
140
0
                    const Coordinate* p = fs1->getCoordinate(i1);
141
142
0
                    const Coordinate* seg0 = fs2->getCoordinate(i2 - 1);
143
0
                    const Coordinate* seg1 = fs2->getCoordinate(i2);
144
145
0
                    if(!(p->equals2D(*seg0) || p->equals2D(*seg1))) {
146
0
                        double d = geos::algorithm::Distance::pointToSegment(*p, *seg0, *seg1);
147
0
                        if(d < minDist) {
148
0
                            minDist = d;
149
0
                            updatePts(*p, *seg0, *seg1);
150
0
                            if(d == 0.0) {
151
0
                                return d;
152
0
                            }
153
0
                        }
154
0
                    }
155
0
                }
156
0
            }
157
0
            return minDist;
158
0
        }
159
0
    };
160
161
    // already computed
162
0
    if(minClearancePts != nullptr) {
163
0
        return;
164
0
    }
165
166
    // initialize to "No Distance Exists" state
167
0
    minClearancePts = detail::make_unique<CoordinateSequence>(2u, 2u);
168
0
    minClearance = DoubleInfinity;
169
170
    // handle empty geometries
171
0
    if(inputGeom->isEmpty()) {
172
0
        return;
173
0
    }
174
175
0
    auto tree = FacetSequenceTreeBuilder::build(inputGeom);
176
0
    MinClearanceDistance mcd;
177
0
    auto nearest = tree->nearestNeighbour(mcd);
178
179
0
    if (nearest.first == nullptr || nearest.second == nullptr) {
180
0
        throw util::GEOSException("Failed to find nearest items");
181
0
    }
182
183
0
    minClearance = mcd.distance(nearest.first, nearest.second);
184
185
0
    const std::vector<Coordinate>* minClearancePtsVec = mcd.getCoordinates();
186
0
    minClearancePts->setAt((*minClearancePtsVec)[0], 0);
187
0
    minClearancePts->setAt((*minClearancePtsVec)[1], 1);
188
0
}
189
190
191
}
192
}