Coverage Report

Created: 2025-07-23 06:51

/src/geos/src/operation/relateng/RelateEdge.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) 2024 Martin Davis
7
 * Copyright (C) 2024 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
#include <geos/algorithm/PolygonNodeTopology.h>
17
#include <geos/operation/relateng/RelateEdge.h>
18
#include <geos/operation/relateng/RelateNode.h>
19
#include <geos/operation/relateng/RelateGeometry.h>
20
#include <geos/geom/Coordinate.h>
21
#include <geos/geom/Dimension.h>
22
#include <geos/geom/Position.h>
23
#include <geos/io/WKTWriter.h>
24
#include <geos/constants.h>
25
#include <sstream>
26
27
using geos::algorithm::PolygonNodeTopology;
28
using geos::geom::CoordinateXY;
29
using geos::geom::Dimension;
30
using geos::geom::Position;
31
using geos::geom::Location;
32
using geos::io::WKTWriter;
33
34
35
namespace geos {      // geos
36
namespace operation { // geos.operation
37
namespace relateng {  // geos.operation.relateng
38
39
40
/* public */
41
RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, bool isA, bool isForward)
42
0
    : node(rNode)
43
0
    , dirPt(pt)
44
0
{
45
0
    setLocationsArea(isA, isForward);
46
0
}
47
48
49
/* public */
50
RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, bool isA)
51
0
    : node(rNode)
52
0
    , dirPt(pt)
53
0
{
54
0
    setLocationsLine(isA);
55
0
}
56
57
58
/* public */
59
RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt,
60
    bool isA, Location locLeft, Location locRight, Location locLine)
61
0
    : node(rNode)
62
0
    , dirPt(pt)
63
0
{
64
0
    setLocations(isA, locLeft, locRight, locLine);
65
0
}
66
67
68
/* public static */
69
RelateEdge *
70
RelateEdge::create(const RelateNode* node, const CoordinateXY* dirPt, bool isA, int dim, bool isForward)
71
0
{
72
0
    if (dim == Dimension::A)
73
        //-- create an area edge
74
0
        return new RelateEdge(node, dirPt, isA, isForward);
75
    //-- create line edge
76
0
    return new RelateEdge(node, dirPt, isA);
77
0
}
78
79
80
/* public static */
81
std::size_t
82
RelateEdge::findKnownEdgeIndex(
83
    std::vector<std::unique_ptr<RelateEdge>>& edges,
84
    bool isA)
85
0
{
86
0
    for (std::size_t i = 0; i < edges.size(); i++) {
87
0
        auto& e = edges[i];
88
0
        if (e->isKnown(isA))
89
0
            return i;
90
0
    }
91
0
    return INDEX_UNKNOWN;
92
0
}
93
94
95
/* public static */
96
void
97
RelateEdge::setAreaInterior(
98
    std::vector<std::unique_ptr<RelateEdge>>& edges,
99
    bool isA)
100
0
{
101
0
    for (auto& e : edges) {
102
0
        e->setAreaInterior(isA);
103
0
    }
104
0
}
105
106
107
/* private */
108
void
109
RelateEdge::setLocations(bool isA, Location locLeft, Location locRight, Location locLine)
110
0
{
111
0
    if (isA) {
112
0
        aDim = 2;
113
0
        aLocLeft = locLeft;
114
0
        aLocRight = locRight;
115
0
        aLocLine = locLine;
116
0
    }
117
0
    else {
118
0
        bDim = 2;
119
0
        bLocLeft = locLeft;
120
0
        bLocRight = locRight;
121
0
        bLocLine = locLine;
122
0
    }
123
0
}
124
125
126
/* private */
127
void
128
RelateEdge::setLocationsLine(bool isA)
129
0
{
130
0
    if (isA) {
131
0
        aDim = 1;
132
0
        aLocLeft = Location::EXTERIOR;
133
0
        aLocRight = Location::EXTERIOR;
134
0
        aLocLine = Location::INTERIOR;
135
0
    }
136
0
    else {
137
0
        bDim = 1;
138
0
        bLocLeft = Location::EXTERIOR;
139
0
        bLocRight = Location::EXTERIOR;
140
0
        bLocLine = Location::INTERIOR;
141
0
    }
142
0
}
143
144
145
/* private */
146
void
147
RelateEdge::setLocationsArea(bool isA, bool isForward)
148
0
{
149
0
    Location locLeft = isForward ? Location::EXTERIOR : Location::INTERIOR;
150
0
    Location locRight = isForward ? Location::INTERIOR : Location::EXTERIOR;
151
0
    if (isA) {
152
0
        aDim = 2;
153
0
        aLocLeft = locLeft;
154
0
        aLocRight = locRight;
155
0
        aLocLine = Location::BOUNDARY;
156
0
    }
157
0
    else {
158
0
        bDim = 2;
159
0
        bLocLeft = locLeft;
160
0
        bLocRight = locRight;
161
0
        bLocLine = Location::BOUNDARY;
162
0
    }
163
0
}
164
165
166
/* public */
167
int
168
RelateEdge::compareToEdge(const CoordinateXY* edgeDirPt) const
169
0
{
170
0
    return PolygonNodeTopology::compareAngle(
171
0
        node->getCoordinate(),
172
0
        dirPt,
173
0
        edgeDirPt);
174
0
}
175
176
177
/* public */
178
void
179
RelateEdge::merge(bool isA, int dim, bool isForward)
180
0
{
181
0
    Location locEdge = Location::INTERIOR;
182
0
    Location locLeft = Location::EXTERIOR;
183
0
    Location locRight = Location::EXTERIOR;
184
0
    if (dim == Dimension::A) {
185
0
        locEdge = Location::BOUNDARY;
186
0
        locLeft = isForward ? Location::EXTERIOR : Location::INTERIOR;
187
0
        locRight = isForward ? Location::INTERIOR : Location::EXTERIOR;
188
0
    }
189
190
0
    if (! isKnown(isA)) {
191
0
        setDimension(isA, dim);
192
0
        setOn(isA, locEdge);
193
0
        setLeft(isA, locLeft);
194
0
        setRight(isA, locRight);
195
0
        return;
196
0
    }
197
198
    // Assert: node-dirpt is collinear with node-pt
199
0
    mergeDimEdgeLoc(isA, locEdge);
200
0
    mergeSideLocation(isA, Position::LEFT, locLeft);
201
0
    mergeSideLocation(isA, Position::RIGHT, locRight);
202
0
}
203
204
/**
205
* Area edges override Line edges.
206
* Merging edges of same dimension is a no-op for
207
* the dimension and on location.
208
* But merging an area edge into a line edge
209
* sets the dimension to A and the location to BOUNDARY.
210
*
211
* @param isA
212
* @param locEdge
213
*/
214
/* private */
215
void
216
RelateEdge::mergeDimEdgeLoc(bool isA, Location locEdge)
217
0
{
218
    //TODO: this logic needs work - ie handling A edges marked as Interior
219
0
    int dim = (locEdge == Location::BOUNDARY) ? Dimension::A : Dimension::L;
220
0
    if (dim == Dimension::A && dimension(isA) == Dimension::L) {
221
0
        setDimension(isA, dim);
222
0
        setOn(isA, Location::BOUNDARY);
223
0
    }
224
0
}
225
226
227
/* private */
228
void
229
RelateEdge::mergeSideLocation(bool isA, int pos, Location loc)
230
0
{
231
0
    Location currLoc = location(isA, pos);
232
    //-- INTERIOR takes precedence over EXTERIOR
233
0
    if (currLoc != Location::INTERIOR) {
234
0
        setLocation(isA, pos, loc);
235
0
    }
236
0
}
237
238
239
/* private */
240
void
241
RelateEdge::setDimension(bool isA, int dimension)
242
0
{
243
0
    if (isA) {
244
0
        aDim = dimension;
245
0
    }
246
0
    else {
247
0
        bDim = dimension;
248
0
    }
249
0
}
250
251
252
/* public */
253
void
254
RelateEdge::setLocation(bool isA, int pos, Location loc)
255
0
{
256
0
    switch (pos) {
257
0
    case Position::LEFT:
258
0
        setLeft(isA, loc);
259
0
        break;
260
0
    case Position::RIGHT:
261
0
        setRight(isA, loc);
262
0
        break;
263
0
    case Position::ON:
264
0
        setOn(isA, loc);
265
0
        break;
266
0
    }
267
0
}
268
269
270
/* public */
271
void
272
RelateEdge::setAllLocations(bool isA, Location loc)
273
0
{
274
0
    setLeft(isA, loc);
275
0
    setRight(isA, loc);
276
0
    setOn(isA, loc);
277
0
}
278
279
280
/* public */
281
void
282
RelateEdge::setUnknownLocations(bool isA, Location loc)
283
0
{
284
0
    if (! isKnown(isA, Position::LEFT)) {
285
0
        setLocation(isA, Position::LEFT, loc);
286
0
    }
287
0
    if (! isKnown(isA, Position::RIGHT)) {
288
0
        setLocation(isA, Position::RIGHT, loc);
289
0
    }
290
0
    if (! isKnown(isA, Position::ON)) {
291
0
        setLocation(isA, Position::ON, loc);
292
0
    }
293
0
}
294
295
296
/* private */
297
void
298
RelateEdge::setLeft(bool isA, Location loc)
299
0
{
300
0
    if (isA) {
301
0
        aLocLeft = loc;
302
0
    }
303
0
    else {
304
0
        bLocLeft = loc;
305
0
    }
306
0
}
307
308
309
/* private */
310
void
311
RelateEdge::setRight(bool isA, Location loc)
312
0
{
313
0
    if (isA) {
314
0
        aLocRight = loc;
315
0
    }
316
0
    else {
317
0
        bLocRight = loc;
318
0
    }
319
0
}
320
321
322
/* private */
323
void
324
RelateEdge::setOn(bool isA, Location loc)
325
0
{
326
0
    if (isA) {
327
0
        aLocLine = loc;
328
0
    }
329
0
    else {
330
0
        bLocLine = loc;
331
0
    }
332
0
}
333
334
335
/* public */
336
Location
337
RelateEdge::location(bool isA, int position) const
338
0
{
339
0
    if (isA) {
340
0
        switch (position) {
341
0
            case Position::LEFT: return aLocLeft;
342
0
            case Position::RIGHT: return aLocRight;
343
0
            case Position::ON: return aLocLine;
344
0
        }
345
0
    }
346
0
    else {
347
0
        switch (position) {
348
0
            case Position::LEFT: return bLocLeft;
349
0
            case Position::RIGHT: return bLocRight;
350
0
            case Position::ON: return bLocLine;
351
0
        }
352
0
    }
353
0
    assert(false && "never get here");
354
0
    return LOC_UNKNOWN;
355
0
}
356
357
/* private */
358
int
359
RelateEdge::dimension(bool isA) const
360
0
{
361
0
    return isA ? aDim : bDim;
362
0
}
363
364
/* private */
365
bool
366
RelateEdge::isKnown(bool isA) const
367
0
{
368
0
    if (isA)
369
0
        return aDim != DIM_UNKNOWN;
370
0
    return bDim != DIM_UNKNOWN;
371
0
}
372
373
/* private */
374
bool
375
RelateEdge::isKnown(bool isA, int pos) const
376
0
{
377
0
    return location(isA, pos) != LOC_UNKNOWN;
378
0
}
379
380
381
/* public */
382
bool
383
RelateEdge::isInterior(bool isA, int position) const
384
0
{
385
0
    return location(isA, position) == Location::INTERIOR;
386
0
}
387
388
389
/* public */
390
void
391
RelateEdge::setDimLocations(bool isA, int dim, Location loc)
392
0
{
393
0
    if (isA) {
394
0
        aDim = dim;
395
0
        aLocLeft = loc;
396
0
        aLocRight = loc;
397
0
        aLocLine = loc;
398
0
    }
399
0
    else {
400
0
        bDim = dim;
401
0
        bLocLeft = loc;
402
0
        bLocRight = loc;
403
0
        bLocLine = loc;
404
0
    }
405
0
}
406
407
408
/* public */
409
void
410
RelateEdge::setAreaInterior(bool isA)
411
0
{
412
0
    if (isA) {
413
0
        aLocLeft = Location::INTERIOR;
414
0
        aLocRight = Location::INTERIOR;
415
0
        aLocLine = Location::INTERIOR;
416
0
    }
417
0
    else {
418
0
        bLocLeft = Location::INTERIOR;
419
0
        bLocRight = Location::INTERIOR;
420
0
        bLocLine = Location::INTERIOR;
421
0
    }
422
0
}
423
424
425
/* public */
426
std::string
427
RelateEdge::toString() const
428
0
{
429
0
    std::stringstream ss;
430
0
    ss << WKTWriter::toLineString(*(node->getCoordinate()), *dirPt);
431
0
    ss << " - " << labelString();
432
0
    return ss.str();
433
0
}
434
435
436
/* private */
437
std::string
438
RelateEdge::labelString() const
439
0
{
440
0
    std::stringstream ss;
441
0
    ss << "A:";
442
0
    ss << locationString(RelateGeometry::GEOM_A);
443
0
    ss << "/B:";
444
0
    ss << locationString(RelateGeometry::GEOM_B);
445
0
    return ss.str();
446
0
}
447
448
449
/* private */
450
std::string
451
RelateEdge::locationString(bool isA) const
452
0
{
453
0
    std::stringstream ss;
454
0
    ss << location(isA, Position::LEFT);
455
0
    ss << location(isA, Position::ON);
456
0
    ss << location(isA, Position::RIGHT);
457
0
    return ss.str();
458
0
}
459
460
461
/* public friend */
462
std::ostream&
463
operator<<(std::ostream& os, const RelateEdge& re)
464
0
{
465
0
    os << re.toString();
466
0
    return os;
467
0
}
468
469
470
} // namespace geos.operation.overlayng
471
} // namespace geos.operation
472
} // namespace geos
473
474
475
476