Coverage Report

Created: 2022-08-24 06:40

/src/geos/src/operation/overlayng/OverlayUtil.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca>
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
#include <geos/operation/overlayng/OverlayUtil.h>
16
17
#include <geos/operation/overlayng/InputGeometry.h>
18
#include <geos/operation/overlayng/OverlayGraph.h>
19
#include <geos/geom/Coordinate.h>
20
#include <geos/geom/Envelope.h>
21
#include <geos/geom/GeometryFactory.h>
22
#include <geos/geom/PrecisionModel.h>
23
#include <geos/operation/overlayng/OverlayNG.h>
24
#include <geos/operation/overlayng/RobustClipEnvelopeComputer.h>
25
#include <geos/util/Assert.h>
26
27
28
29
namespace geos {      // geos
30
namespace operation { // geos.operation
31
namespace overlayng { // geos.operation.overlayng
32
33
using namespace geos::geom;
34
35
/*public static*/
36
bool
37
OverlayUtil::isFloating(const PrecisionModel* pm)
38
251k
{
39
251k
    if (pm == nullptr) return true;
40
216k
    return pm->isFloating();
41
251k
}
42
43
/*private static*/
44
double
45
OverlayUtil::safeExpandDistance(const Envelope* env, const PrecisionModel* pm)
46
24.4k
{
47
24.4k
    double envExpandDist;
48
24.4k
    if (isFloating(pm)) {
49
        // if PM is FLOAT then there is no scale factor, so add 10%
50
23.1k
        double minSize = std::min(env->getHeight(), env->getWidth());
51
        // heuristic to ensure zero-width envelopes don't cause total clipping
52
23.1k
        if (minSize <= 0.0) {
53
912
            minSize = std::max(env->getHeight(), env->getWidth());
54
912
        }
55
23.1k
        envExpandDist = SAFE_ENV_BUFFER_FACTOR * minSize;
56
23.1k
    }
57
1.32k
    else {
58
        // if PM is fixed, add a small multiple of the grid size
59
1.32k
        double gridSize = 1.0 / pm->getScale();
60
1.32k
        envExpandDist = SAFE_ENV_GRID_FACTOR * gridSize;
61
1.32k
    }
62
24.4k
    return envExpandDist;
63
24.4k
}
64
65
/*private static*/
66
bool
67
OverlayUtil::safeEnv(const Envelope* env, const PrecisionModel* pm, Envelope& rsltEnvelope)
68
24.4k
{
69
24.4k
    double envExpandDist = safeExpandDistance(env, pm);
70
24.4k
    rsltEnvelope = *env;
71
24.4k
    rsltEnvelope.expandBy(envExpandDist);
72
24.4k
    return true;
73
24.4k
}
74
75
/*private static*/
76
bool
77
OverlayUtil::resultEnvelope(int opCode, const InputGeometry* inputGeom, const PrecisionModel* pm, Envelope& rsltEnvelope)
78
68.3k
{
79
68.3k
    switch (opCode) {
80
4.79k
        case OverlayNG::INTERSECTION: {
81
            // use safe envelopes for intersection to ensure they contain rounded coordinates
82
4.79k
            Envelope envA, envB;
83
4.79k
            safeEnv(inputGeom->getEnvelope(0), pm, envA);
84
4.79k
            safeEnv(inputGeom->getEnvelope(1), pm, envB);
85
4.79k
            envA.intersection(envB, rsltEnvelope);
86
4.79k
            return true;
87
0
        }
88
5.04k
        case OverlayNG::DIFFERENCE: {
89
5.04k
            safeEnv(inputGeom->getEnvelope(0), pm, rsltEnvelope);
90
5.04k
            return true;
91
0
        }
92
68.3k
    }
93
    // return false for UNION and SYMDIFFERENCE to indicate no clipping
94
58.4k
    return false;
95
68.3k
}
96
97
/*public static*/
98
bool
99
OverlayUtil::clippingEnvelope(int opCode, const InputGeometry* inputGeom, const PrecisionModel* pm, Envelope& rsltEnvelope)
100
68.3k
{
101
68.3k
    bool resultEnv = resultEnvelope(opCode, inputGeom, pm, rsltEnvelope);
102
68.3k
    if (!resultEnv)
103
58.4k
      return false;
104
105
9.83k
    Envelope clipEnv = RobustClipEnvelopeComputer::getEnvelope(
106
9.83k
        inputGeom->getGeometry(0),
107
9.83k
        inputGeom->getGeometry(1),
108
9.83k
        &rsltEnvelope);
109
110
9.83k
    return safeEnv(&clipEnv, pm, rsltEnvelope);
111
68.3k
}
112
113
114
/*public static*/
115
bool
116
OverlayUtil::isEmptyResult(int opCode, const Geometry* a, const Geometry* b, const PrecisionModel* pm)
117
124k
{
118
124k
    switch (opCode) {
119
6.40k
    case OverlayNG::INTERSECTION: {
120
6.40k
        if (isEnvDisjoint(a, b, pm))
121
24
            return true;
122
6.38k
        break;
123
6.40k
        }
124
8.98k
    case OverlayNG::DIFFERENCE: {
125
8.98k
        if (isEmpty(a))
126
38
            return true;
127
8.95k
        break;
128
8.98k
        }
129
109k
    case OverlayNG::UNION:
130
109k
    case OverlayNG::SYMDIFFERENCE: {
131
109k
        if (isEmpty(a) && isEmpty(b))
132
47.6k
            return true;
133
61.7k
        break;
134
109k
        }
135
124k
    }
136
77.0k
    return false;
137
124k
}
138
139
/*private*/
140
bool
141
OverlayUtil::isEmpty(const Geometry* geom)
142
178k
{
143
178k
    return geom == nullptr || geom->isEmpty();
144
178k
}
145
146
/*public static*/
147
bool
148
OverlayUtil::isEnvDisjoint(const Geometry* a, const Geometry* b, const PrecisionModel* pm)
149
6.40k
{
150
6.40k
    if (isEmpty(a) || isEmpty(b)) {
151
13
        return true;
152
13
    }
153
6.39k
    if (isFloating(pm)) {
154
6.13k
        return a->getEnvelopeInternal()->disjoint(b->getEnvelopeInternal());
155
6.13k
    }
156
265
    return isDisjoint(a->getEnvelopeInternal(), b->getEnvelopeInternal(), pm);
157
6.39k
}
158
159
/*private static*/
160
bool
161
OverlayUtil::isDisjoint(const Envelope* envA, const Envelope* envB, const PrecisionModel* pm)
162
265
{
163
265
    if (pm->makePrecise(envB->getMinX()) > pm->makePrecise(envA->getMaxX()))
164
0
        return true;
165
265
    if (pm->makePrecise(envB->getMaxX()) < pm->makePrecise(envA->getMinX()))
166
2
        return true;
167
263
    if (pm->makePrecise(envB->getMinY()) > pm->makePrecise(envA->getMaxY()))
168
0
        return true;
169
263
    if (pm->makePrecise(envB->getMaxY()) < pm->makePrecise(envA->getMinY()))
170
9
        return true;
171
254
    return false;
172
263
}
173
174
/*public static*/
175
std::unique_ptr<Geometry>
176
OverlayUtil::createEmptyResult(int dim, const GeometryFactory* geomFact)
177
51.3k
{
178
51.3k
    std::unique_ptr<Geometry> result(nullptr);
179
51.3k
    switch (dim) {
180
897
    case 0:
181
897
        result = geomFact->createPoint();
182
897
        break;
183
681
    case 1:
184
681
        result = geomFact->createLineString();
185
681
        break;
186
49.7k
    case 2:
187
49.7k
        result = geomFact->createPolygon();
188
49.7k
        break;
189
0
    case -1:
190
0
        result = geomFact->createGeometryCollection();
191
0
        break;
192
0
    default:
193
0
        util::Assert::shouldNeverReachHere("Unable to determine overlay result geometry dimension");
194
51.3k
    }
195
51.3k
    return result;
196
51.3k
}
197
198
/*public static*/
199
int
200
OverlayUtil::resultDimension(int opCode, int dim0, int dim1)
201
59.9k
{
202
59.9k
    int resultDimension = -1;
203
59.9k
    switch (opCode) {
204
2.05k
    case OverlayNG::INTERSECTION:
205
2.05k
        resultDimension = std::min(dim0, dim1);
206
2.05k
        break;
207
53.7k
    case OverlayNG::UNION:
208
53.7k
        resultDimension = std::max(dim0, dim1);
209
53.7k
        break;
210
4.13k
    case OverlayNG::DIFFERENCE:
211
4.13k
        resultDimension = dim0;
212
4.13k
        break;
213
0
    case OverlayNG::SYMDIFFERENCE:
214
        /**
215
        * This result is chosen because
216
        * <pre>
217
        * SymDiff = Union( Diff(A, B), Diff(B, A) )
218
        * </pre>
219
        * and Union has the dimension of the highest-dimension argument.
220
        */
221
0
        resultDimension = std::max(dim0, dim1);
222
0
        break;
223
59.9k
    }
224
59.9k
    return resultDimension;
225
59.9k
}
226
227
/* public static */
228
bool
229
OverlayUtil::isResultAreaConsistent(
230
    const Geometry* geom0, const Geometry* geom1,
231
    int opCode, const Geometry* result)
232
11.9k
{
233
11.9k
    if (geom0 == nullptr || geom1 == nullptr)
234
6.97k
        return true;
235
236
5.00k
    double areaResult = result->getArea();
237
5.00k
    double areaA = geom0->getArea();
238
5.00k
    double areaB = geom1->getArea();
239
5.00k
    bool isConsistent = true;
240
241
5.00k
    switch (opCode) {
242
1.88k
    case OverlayNG::INTERSECTION:
243
1.88k
        isConsistent = isLess(areaResult, areaA, AREA_HEURISTIC_TOLERANCE)
244
1.88k
                    && isLess(areaResult, areaB, AREA_HEURISTIC_TOLERANCE);
245
1.88k
        break;
246
1.70k
    case OverlayNG::DIFFERENCE:
247
1.70k
        isConsistent = isLess(areaResult, areaA, AREA_HEURISTIC_TOLERANCE)
248
1.70k
                    && isGreater(areaResult, areaA - areaB, AREA_HEURISTIC_TOLERANCE);
249
1.70k
        break;
250
0
    case OverlayNG::SYMDIFFERENCE:
251
0
        isConsistent = isLess(areaResult, areaA + areaB, AREA_HEURISTIC_TOLERANCE);
252
0
        break;
253
1.41k
    case OverlayNG::UNION:
254
1.41k
        isConsistent = isLess(areaA, areaResult, AREA_HEURISTIC_TOLERANCE)
255
1.41k
                    && isLess(areaB, areaResult, AREA_HEURISTIC_TOLERANCE)
256
1.41k
                    && isGreater(areaResult, areaA - areaB, AREA_HEURISTIC_TOLERANCE);
257
1.41k
        break;
258
5.00k
    }
259
5.00k
    return isConsistent;
260
5.00k
}
261
262
263
/*public static*/
264
std::unique_ptr<Geometry>
265
OverlayUtil::createResultGeometry(
266
    std::vector<std::unique_ptr<Polygon>>& resultPolyList,
267
    std::vector<std::unique_ptr<LineString>>& resultLineList,
268
    std::vector<std::unique_ptr<Point>>& resultPointList,
269
    const GeometryFactory* geometryFactory)
270
45.2k
{
271
45.2k
    std::vector<std::unique_ptr<Geometry>> geomList;
272
273
    // TODO: for mixed dimension, return collection of Multigeom for each dimension (breaking change)
274
275
    // element geometries of the result are always in the order A,L,P
276
45.2k
    if (resultPolyList.size() > 0)
277
40.9k
        moveGeometry(resultPolyList, geomList);
278
45.2k
    if (resultLineList.size() > 0)
279
4.59k
        moveGeometry(resultLineList, geomList);
280
45.2k
    if (resultPointList.size() > 0)
281
293
        moveGeometry(resultPointList, geomList);
282
283
    // build the most specific geometry possible
284
    // TODO: perhaps do this internally to give more control?
285
45.2k
    return geometryFactory->buildGeometry(std::move(geomList));
286
45.2k
}
287
288
/*public static*/
289
std::unique_ptr<Geometry>
290
OverlayUtil::toLines(OverlayGraph* graph, bool isOutputEdges, const GeometryFactory* geomFact)
291
0
{
292
0
    std::vector<std::unique_ptr<LineString>> lines;
293
0
    std::vector<OverlayEdge*>& edges = graph->getEdges();
294
0
    for (OverlayEdge* edge : edges) {
295
0
      bool includeEdge = isOutputEdges || edge->isInResultArea();
296
0
      if (! includeEdge) continue;
297
298
0
      std::unique_ptr<CoordinateSequence> pts = edge->getCoordinatesOriented();
299
0
      std::unique_ptr<LineString> line = geomFact->createLineString(std::move(pts));
300
      // line->setUserData(labelForResult(edge));
301
0
      lines.push_back(std::move(line));
302
0
    }
303
0
    return geomFact->buildGeometry(std::move(lines));
304
0
}
305
306
/*public static*/
307
bool
308
OverlayUtil::round(const Point* pt, const PrecisionModel* pm, Coordinate& rsltCoord)
309
75.1k
{
310
75.1k
    if (pt->isEmpty()) return false;
311
75.1k
    const Coordinate* p = pt->getCoordinate();
312
75.1k
    rsltCoord = *p;
313
75.1k
    if (! isFloating(pm)) {
314
420
        pm->makePrecise(rsltCoord);
315
420
    }
316
75.1k
    return true;
317
75.1k
}
318
319
320
321
} // namespace geos.operation.overlayng
322
} // namespace geos.operation
323
} // namespace geos