Coverage Report

Created: 2026-09-14 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/operation/overlay/snap/GeometrySnapper.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2009-2010  Sandro Santilli <strk@kbt.io>
7
 * Copyright (C) 2006 Refractions Research Inc.
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: operation/overlay/snap/GeometrySnapper.java r320 (JTS-1.12)
17
 *
18
 **********************************************************************/
19
20
#include <geos/operation/overlay/snap/GeometrySnapper.h>
21
#include <geos/operation/overlay/snap/LineStringSnapper.h>
22
#include <geos/geom/util/GeometryTransformer.h> // inherit. of SnapTransformer
23
#include <geos/geom/util/GeometryFixer.h>
24
#include <geos/geom/CoordinateSequence.h>
25
#include <geos/geom/Polygon.h>
26
#include <geos/geom/MultiPolygon.h>
27
#include <geos/geom/Coordinate.h>
28
#include <geos/geom/GeometryFactory.h>
29
#include <geos/geom/PrecisionModel.h>
30
#include <geos/util/UniqueCoordinateArrayFilter.h>
31
#include <geos/util.h>
32
33
#include <vector>
34
#include <memory>
35
#include <algorithm>
36
37
//
38
using namespace geos::geom;
39
40
namespace geos {
41
namespace operation { // geos.operation
42
namespace overlay { // geos.operation.overlay
43
namespace snap { // geos.operation.overlay.snap
44
45
const double GeometrySnapper::snapPrecisionFactor = 1e-9;
46
47
class SnapTransformer: public geos::geom::util::GeometryTransformer {
48
49
private:
50
51
    double snapTol;
52
53
    const Coordinate::ConstVect& snapPts;
54
55
    CoordinateSequence::Ptr
56
    snapLine(
57
        const CoordinateSequence* srcPts)
58
0
    {
59
0
        using std::unique_ptr;
60
61
0
        assert(srcPts);
62
0
        auto coords = detail::make_unique<CoordinateSequence>();
63
0
        coords->add(*srcPts);
64
0
        LineStringSnapper snapper(*coords, snapTol);
65
0
        return snapper.snapTo(snapPts);
66
0
    }
67
68
public:
69
70
    SnapTransformer(double nSnapTol,
71
                    const Coordinate::ConstVect& nSnapPts)
72
        :
73
0
        snapTol(nSnapTol),
74
0
        snapPts(nSnapPts)
75
0
    {
76
0
    }
77
78
    CoordinateSequence::Ptr
79
    transformCoordinates(
80
        const CoordinateSequence* coords,
81
        const Geometry* parent) override
82
0
    {
83
0
        ::geos::ignore_unused_variable_warning(parent);
84
0
        return snapLine(coords);
85
0
    }
86
87
88
};
89
90
GeometrySnapper::GeometrySnapper(const Geometry& g)
91
0
    : srcGeom(g)
92
0
{
93
0
    util::ensureNoCurvedComponents(g);
94
0
}
95
96
/*private*/
97
std::unique_ptr<Coordinate::ConstVect>
98
GeometrySnapper::extractTargetCoordinates(const Geometry& g)
99
0
{
100
0
    auto snapPts = detail::make_unique<Coordinate::ConstVect>();
101
0
    util::UniqueCoordinateArrayFilter filter(*snapPts);
102
0
    g.apply_ro(&filter);
103
    // integrity check
104
0
    assert(snapPts->size() <= g.getNumPoints());
105
0
    return snapPts;
106
0
}
107
108
/*public*/
109
std::unique_ptr<geom::Geometry>
110
GeometrySnapper::snapTo(const geom::Geometry& g, double snapTolerance)
111
0
{
112
113
0
    using std::unique_ptr;
114
0
    using geom::util::GeometryTransformer;
115
116
    // Get snap points
117
0
    std::unique_ptr<Coordinate::ConstVect> snapPts = extractTargetCoordinates(g);
118
119
    // Apply a SnapTransformer to source geometry
120
    // (we need a pointer for dynamic polymorphism)
121
0
    std::unique_ptr<GeometryTransformer> snapTrans(new SnapTransformer(snapTolerance, *snapPts));
122
0
    return snapTrans->transform(&srcGeom);
123
0
}
124
125
/*public*/
126
std::unique_ptr<geom::Geometry>
127
GeometrySnapper::snapToSelf(double snapTolerance, bool cleanResult)
128
0
{
129
130
0
    using std::unique_ptr;
131
0
    using geom::util::GeometryTransformer;
132
133
    // Get snap points
134
0
    std::unique_ptr<Coordinate::ConstVect> snapPts = extractTargetCoordinates(srcGeom);
135
136
    // Apply a SnapTransformer to source geometry
137
    // (we need a pointer for dynamic polymorphism)
138
0
    std::unique_ptr<GeometryTransformer> snapTrans(new SnapTransformer(snapTolerance, *snapPts));
139
140
0
    std::unique_ptr<Geometry> result = snapTrans->transform(&srcGeom);
141
142
0
    if (cleanResult && (result->getGeometryTypeId() == GEOS_POLYGON ||
143
0
                        result->getGeometryTypeId() == GEOS_MULTIPOLYGON)) {
144
0
        result = geom::util::GeometryFixer::fix(result.get());
145
0
    }
146
147
0
    return result;
148
0
}
149
150
/*public static*/
151
double
152
GeometrySnapper::computeSizeBasedSnapTolerance(const geom::Geometry& g)
153
0
{
154
0
    const Envelope* env = g.getEnvelopeInternal();
155
0
    double minDimension = std::min(env->getHeight(), env->getWidth());
156
0
    double snapTol = minDimension * snapPrecisionFactor;
157
0
    return snapTol;
158
0
}
159
160
/*public static*/
161
double
162
GeometrySnapper::computeOverlaySnapTolerance(const geom::Geometry& g)
163
0
{
164
0
    double snapTolerance = computeSizeBasedSnapTolerance(g);
165
166
    /*
167
     * Overlay is carried out in the precision model
168
     * of the two inputs.
169
     * If this precision model is of type FIXED, then the snap tolerance
170
     * must reflect the precision grid size.
171
     * Specifically, the snap tolerance should be at least
172
     * the distance from a corner of a precision grid cell
173
     * to the centre point of the cell.
174
     */
175
0
    assert(g.getPrecisionModel());
176
0
    const PrecisionModel& pm = *(g.getPrecisionModel());
177
0
    if(pm.getType() == PrecisionModel::FIXED) {
178
0
        double fixedSnapTol = (1 / pm.getScale()) * 2 / 1.415;
179
0
        if(fixedSnapTol > snapTolerance) {
180
0
            snapTolerance = fixedSnapTol;
181
0
        }
182
0
    }
183
0
    return snapTolerance;
184
0
}
185
186
/*public static*/
187
double
188
GeometrySnapper::computeOverlaySnapTolerance(const geom::Geometry& g1,
189
        const geom::Geometry& g2)
190
0
{
191
0
    return std::min(computeOverlaySnapTolerance(g1), computeOverlaySnapTolerance(g2));
192
0
}
193
194
/* public static */
195
void
196
GeometrySnapper::snap(const geom::Geometry& g0,
197
                      const geom::Geometry& g1,
198
                      double snapTolerance,
199
                      geom::GeomPtrPair& snapGeom)
200
0
{
201
0
    GeometrySnapper snapper0(g0);
202
0
    snapGeom.first = snapper0.snapTo(g1, snapTolerance);
203
204
    /*
205
     * Snap the second geometry to the snapped first geometry
206
     * (this strategy minimizes the number of possible different
207
     * points in the result)
208
     */
209
0
    GeometrySnapper snapper1(g1);
210
0
    snapGeom.second = snapper1.snapTo(*snapGeom.first, snapTolerance);
211
212
//  std::cout << *snapGeom.first << std::endl;
213
//  std::cout << *snapGeom.second << std::endl;
214
215
0
}
216
217
/* public static */
218
GeometrySnapper::GeomPtr
219
GeometrySnapper::snapToSelf(const geom::Geometry& g,
220
                            double snapTolerance,
221
                            bool cleanResult)
222
0
{
223
0
    GeometrySnapper snapper0(g);
224
0
    return snapper0.snapToSelf(snapTolerance, cleanResult);
225
0
}
226
227
} // namespace geos.operation.snap
228
} // namespace geos.operation.overlay
229
} // namespace geos.operation
230
} // namespace geos