Coverage Report

Created: 2026-01-22 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/include/geos/operation/relateng/RelatePredicate.h
Line
Count
Source
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
#pragma once
17
18
#include <geos/geom/Location.h>
19
#include <geos/operation/relateng/BasicPredicate.h>
20
#include <geos/operation/relateng/IMPatternMatcher.h>
21
#include <geos/operation/relateng/IMPredicate.h>
22
#include <geos/operation/relateng/RelateGeometry.h>
23
#include <geos/geom/Envelope.h>
24
#include <geos/export.h>
25
26
namespace geos {      // geos.
27
namespace operation { // geos.operation.
28
namespace relateng {  // geos.operation.relateng
29
30
31
class GEOS_DLL RelatePredicate {
32
    using Envelope = geos::geom::Envelope;
33
    using Location = geos::geom::Location;
34
35
public:
36
37
/************************************************************************
38
 *
39
 * Creates a predicate to determine whether two geometries intersect.
40
 *
41
 * The intersects predicate has the following equivalent definitions:
42
 *
43
 *  * The two geometries have at least one point in common
44
 *  * The DE-9IM Intersection Matrix for the two geometries matches
45
 *    at least one of the patterns
46
 *
47
 *    [T********]
48
 *    [*T*******]
49
 *    [***T*****]
50
 *    [****T****]
51
 *
52
 *  disjoint() = false
53
 *  (intersects is the inverse of disjoint)
54
 *
55
 * @return the predicate instance
56
 *
57
 * @see disjoint()
58
 */
59
class IntersectsPredicate : public BasicPredicate {
60
61
public:
62
63
0
    std::string name() const override {
64
0
        return std::string("intersects");
65
0
    }
66
67
0
    bool requireSelfNoding() const override {
68
        //-- self-noding is not required to check for a simple interaction
69
0
        return false;
70
0
    }
71
72
0
    bool requireExteriorCheck(bool isSourceA) const override {
73
0
        (void)isSourceA;
74
        //-- intersects only requires testing interaction
75
0
        return false;
76
0
    }
77
78
0
    void init(const Envelope& envA, const Envelope& envB) override {
79
0
        require(envA.intersects(envB));
80
0
    }
81
82
0
    void updateDimension(Location locA, Location locB, int dimension) override {
83
0
        (void)dimension;
84
0
        setValueIf(true, isIntersection(locA, locB));
85
0
    }
86
87
0
    void finish() override {
88
        //-- if no intersecting locations were found
89
0
        setValue(false);
90
0
    }
91
92
};
93
94
static std::unique_ptr<BasicPredicate> intersects();
95
96
/************************************************************************
97
 *
98
 * Creates a predicate to determine whether two geometries are disjoint.
99
 *
100
 * The disjoint predicate has the following equivalent definitions:
101
 *
102
 *   * The two geometries have no point in common
103
 *   * The DE-9IM Intersection Matrix for the two geometries matches
104
 *        [FF*FF****]
105
 *   * intersects() = false
106
 *     (disjoint is the inverse of intersects)
107
 *
108
 * @return the predicate instance
109
 *
110
 * @see intersects()
111
 */
112
class DisjointPredicate : public BasicPredicate {
113
114
0
    std::string name() const override {
115
0
        return std::string("disjoint");
116
0
    }
117
118
0
    bool requireSelfNoding() const override {
119
        //-- self-noding is not required to check for a simple interaction
120
0
        return false;
121
0
    }
122
123
0
    bool requireInteraction() const override {
124
        //-- ensure entire matrix is computed
125
0
        return false;
126
0
    }
127
128
0
    bool requireExteriorCheck(bool isSourceA) const override {
129
0
        (void)isSourceA;
130
        //-- intersects only requires testing interaction
131
0
        return false;
132
0
    }
133
134
0
    void init(const Envelope& envA, const Envelope& envB) override {
135
0
        setValueIf(true, envA.disjoint(envB));
136
0
    }
137
138
0
    void updateDimension(Location locA, Location locB, int dimension) override {
139
0
        (void)dimension;
140
0
        setValueIf(false, isIntersection(locA, locB));
141
0
    }
142
143
0
    void finish() override {
144
        //-- if no intersecting locations were found
145
0
        setValue(true);
146
0
    }
147
};
148
149
static std::unique_ptr<BasicPredicate> disjoint();
150
151
/************************************************************************
152
 * Creates a predicate to determine whether a geometry contains another geometry.
153
 *
154
 * The contains predicate has the following equivalent definitions:
155
 *
156
 *   * Every point of the other geometry is a point of this geometry,
157
 *     and the interiors of the two geometries have at least one point in common.
158
 *   * The DE-9IM Intersection Matrix for the two geometries matches
159
 *     the pattern
160
 *       [T*****FF*]
161
 *   * within(B, A) = true
162
 *     (contains is the converse of within)
163
 *
164
 * An implication of the definition is that "Geometries do not
165
 * contain their boundary".  In other words, if a geometry A is a subset of
166
 * the points in the boundary of a geometry B, B.contains(A) = false.
167
 * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.)
168
 * For a predicate with similar behavior but avoiding
169
 * this subtle limitation, see covers().
170
 *
171
 * @return the predicate instance
172
 *
173
 * @see within()
174
 */
175
class ContainsPredicate : public IMPredicate {
176
177
0
    std::string name() const override {
178
0
        return std::string("contains");
179
0
    }
180
181
0
    bool requireCovers(bool isSourceA) override {
182
0
        return isSourceA == RelateGeometry::GEOM_A;
183
0
    }
184
185
0
    bool requireExteriorCheck(bool isSourceA) const override {
186
        //-- only need to check B against Exterior of A
187
0
        return isSourceA == RelateGeometry::GEOM_B;
188
0
    }
189
190
0
    void init(int _dimA, int _dimB) override {
191
0
        IMPredicate::init(_dimA, _dimB);
192
0
        require(isDimsCompatibleWithCovers(dimA, dimB));
193
0
    }
194
195
0
    void init(const Envelope& envA, const Envelope& envB) override {
196
0
        BasicPredicate::requireCovers(envA, envB);
197
0
    }
198
199
0
    bool isDetermined() const override {
200
0
        return intersectsExteriorOf(RelateGeometry::GEOM_A);
201
0
    }
202
203
0
    bool valueIM() override {
204
0
        return intMatrix.isContains();
205
0
    }
206
};
207
208
static std::unique_ptr<IMPredicate> contains();
209
210
211
212
/************************************************************************
213
 * Creates a predicate to determine whether a geometry is within another geometry.
214
 *
215
 * The within predicate has the following equivalent definitions:
216
 *
217
 *   * Every point of this geometry is a point of the other geometry,
218
 *     and the interiors of the two geometries have at least one point in common.
219
 *   * The DE-9IM Intersection Matrix for the two geometries matches
220
 *     [T*F**F***]
221
 *   *  contains(B, A) = true
222
 *      (within is the converse of  contains())
223
 *
224
 * An implication of the definition is that
225
 * "The boundary of a Geometry is not within the Geometry".
226
 * In other words, if a geometry A is a subset of
227
 * the points in the boundary of a geometry B, within(B, A) = false
228
 * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.)
229
 * For a predicate with similar behavior but avoiding
230
 * this subtle limitation, see coveredimBy().
231
 *
232
 * @return the predicate instance
233
 *
234
 * @see #contains()
235
 */
236
class WithinPredicate : public IMPredicate {
237
238
0
    std::string name() const override {
239
0
        return std::string("within");
240
0
    }
241
242
0
    bool requireCovers(bool isSourceA) override {
243
0
        return isSourceA == RelateGeometry::GEOM_B;
244
0
    }
245
246
0
    bool requireExteriorCheck(bool isSourceA) const override {
247
        //-- only need to check B against Exterior of A
248
0
        return isSourceA == RelateGeometry::GEOM_A;
249
0
    }
250
251
0
    void init(int _dimA, int _dimB) override {
252
0
        IMPredicate::init(_dimA, _dimB);
253
0
        require(isDimsCompatibleWithCovers(dimB, dimA));
254
0
    }
255
256
0
    void init(const Envelope& envA, const Envelope& envB) override {
257
0
        BasicPredicate::requireCovers(envB, envA);
258
0
    }
259
260
0
    bool isDetermined() const override {
261
0
        return intersectsExteriorOf(RelateGeometry::GEOM_B);
262
0
    }
263
264
0
    bool valueIM() override {
265
0
        return intMatrix.isWithin();
266
0
    }
267
};
268
269
static std::unique_ptr<IMPredicate> within();
270
271
272
273
/************************************************************************
274
 * Creates a predicate to determine whether a geometry covers another geometry.
275
 *
276
 * The covers predicate has the following equivalent definitions:
277
 *
278
 * Every point of the other geometry is a point of this geometry.
279
 * The DE-9IM Intersection Matrix for the two geometries matches
280
 * at least one of the following patterns:
281
 *
282
 *  * [T*****FF*]
283
 *  * [*T****FF*]
284
 *  * [***T**FF*]
285
 *  * [****T*FF*]
286
 *
287
 * coveredimBy(b, a) = true
288
 * (covers is the converse of coveredimBy())
289
 *
290
 * If either geometry is empty, the value of this predicate is false.
291
 *
292
 * This predicate is similar to contains(),
293
 * but is more inclusive (i.e. returns true for more cases).
294
 * In particular, unlike contains it does not distinguish between
295
 * points in the boundary and in the interior of geometries.
296
 * For most cases, covers should be used in preference to contains.
297
 * As an added benefit, covers is more amenable to optimization,
298
 * and hence should be more performant.
299
 *
300
 * @return the predicate instance
301
 *
302
 * @see #coveredimBy()
303
 */
304
class CoversPredicate : public IMPredicate {
305
306
0
    std::string name() const override {
307
0
        return std::string("covers");
308
0
    }
309
310
0
    bool requireCovers(bool isSourceA) override {
311
0
        return isSourceA == RelateGeometry::GEOM_A;
312
0
    }
313
314
0
    bool requireExteriorCheck(bool isSourceA) const override {
315
        //-- only need to check B against Exterior of A
316
0
        return isSourceA == RelateGeometry::GEOM_B;
317
0
    }
318
319
0
    void init(int _dimA, int _dimB) override {
320
0
        IMPredicate::init(_dimA, _dimB);
321
0
        require(isDimsCompatibleWithCovers(dimA, dimB));
322
0
    }
323
324
0
    void init(const Envelope& envA, const Envelope& envB) override {
325
0
        BasicPredicate::requireCovers(envA, envB);
326
327
0
    }
328
329
0
    bool isDetermined() const override {
330
0
        return intersectsExteriorOf(RelateGeometry::GEOM_A);
331
0
    }
332
333
0
    bool valueIM() override {
334
0
        return intMatrix.isCovers();
335
0
    }
336
};
337
338
static std::unique_ptr<IMPredicate> covers();
339
340
341
/************************************************************************
342
* Creates a predicate to determine whether a geometry is covered
343
* by another geometry.
344
*
345
* The coveredimBy predicate has the following equivalent definitions:
346
*
347
* Every point of this geometry is a point of the other geometry.
348
* The DE-9IM Intersection Matrix for the two geometries matches
349
* at least one of the following patterns:
350
*
351
*   [T*F**F***]
352
*   [*TF**F***]
353
*   [**FT*F***]
354
*   [**F*TF***]
355
*
356
* covers(B, A) = true
357
* (coveredimBy is the converse of covers())
358
*
359
* If either geometry is empty, the value of this predicate is false.
360
*
361
* This predicate is similar to within(),
362
* but is more inclusive (i.e. returns true for more cases).
363
*
364
* @return the predicate instance
365
*
366
* @see #covers()
367
*/
368
class CoveredByPredicate : public IMPredicate {
369
370
0
    std::string name() const override {
371
0
        return std::string("coveredBy");
372
0
    }
373
374
0
    bool requireCovers(bool isSourceA) override {
375
0
        return isSourceA == RelateGeometry::GEOM_B;
376
0
    }
377
378
0
    bool requireExteriorCheck(bool isSourceA) const override {
379
        //-- only need to check B against Exterior of A
380
0
        return isSourceA == RelateGeometry::GEOM_A;
381
0
    }
382
383
0
    void init(int _dimA, int _dimB) override {
384
0
        IMPredicate::init(_dimA, _dimB);
385
0
        require(isDimsCompatibleWithCovers(dimB, dimA));
386
0
    }
387
388
0
    void init(const Envelope& envA, const Envelope& envB) override {
389
0
        BasicPredicate::requireCovers(envB, envA);
390
0
    }
391
392
0
    bool isDetermined() const override {
393
0
        return intersectsExteriorOf(RelateGeometry::GEOM_B);
394
0
    }
395
396
0
    bool valueIM() override {
397
0
        return intMatrix.isCoveredBy();
398
0
    }
399
400
};
401
402
static std::unique_ptr<IMPredicate> coveredBy();
403
404
405
/************************************************************************
406
* Creates a predicate to determine whether a geometry crosses another geometry.
407
*
408
* The crosses predicate has the following equivalent definitions:
409
*
410
* The geometries have some but not all interior points in common.
411
* The DE-9IM Intersection Matrix for the two geometries matches
412
* one of the following patterns:
413
*
414
*    [T*T******] (for P/L, P/A, and L/A cases)
415
*    [T*****T**] (for L/P, A/P, and A/L cases)
416
*    [0********] (for L/L cases)
417
*
418
*
419
* For the A/A and P/P cases this predicate returns false.
420
*
421
* The SFS defined this predicate only for P/L, P/A, L/L, and L/A cases.
422
* To make the relation symmetric
423
* JTS extends the definition to apply to L/P, A/P and A/L cases as well.
424
*
425
* @return the predicate instance
426
*/
427
428
class CrossesPredicate : public IMPredicate {
429
430
0
    std::string name() const override {
431
0
        return std::string("crosses");
432
0
    }
433
434
0
    void init(int _dimA, int _dimB) override {
435
0
        IMPredicate::init(_dimA, _dimB);
436
0
        bool isBothPointsOrAreas =
437
0
            (dimA == Dimension::P && dimB == Dimension::P) ||
438
0
            (dimA == Dimension::A && dimB == Dimension::A);
439
0
        require(!isBothPointsOrAreas);
440
0
    }
441
442
0
    bool isDetermined() const override {
443
0
        if (dimA == Dimension::L && dimB == Dimension::L) {
444
            //-- L/L interaction can only be dim = P
445
0
            if (getDimension(Location::INTERIOR, Location::INTERIOR) > Dimension::P)
446
0
                return true;
447
0
        }
448
0
        else if (dimA < dimB) {
449
0
            if (isIntersects(Location::INTERIOR, Location::INTERIOR) &&
450
0
                isIntersects(Location::INTERIOR, Location::EXTERIOR)) {
451
0
                return true;
452
0
            }
453
0
        }
454
0
        else if (dimA > dimB) {
455
0
            if (isIntersects(Location::INTERIOR, Location::INTERIOR) &&
456
0
                isIntersects(Location::EXTERIOR, Location::INTERIOR)) {
457
0
                return true;
458
0
            }
459
0
        }
460
0
        return false;
461
0
    }
462
463
0
    bool valueIM() override {
464
0
        return intMatrix.isCrosses(dimA, dimB);
465
0
    }
466
};
467
468
static std::unique_ptr<IMPredicate> crosses();
469
470
471
/************************************************************************
472
* Creates a predicate to determine whether two geometries are
473
* topologically equal.
474
*
475
* The equals predicate has the following equivalent definitions:
476
*
477
* The two geometries have at least one point in common,
478
* and no point of either geometry lies in the exterior of the other geometry.
479
* The DE-9IM Intersection Matrix for the two geometries matches
480
* the pattern T*F**FFF*
481
*
482
* @return the predicate instance
483
*/
484
class EqualsTopoPredicate : public IMPredicate {
485
486
0
    std::string name() const override {
487
0
        return std::string("equals");
488
0
    }
489
490
0
    bool requireInteraction() const override {
491
        //-- allow EMPTY = EMPTY
492
0
        return false;
493
0
    };
494
495
0
    void init(int _dimA, int _dimB) override {
496
0
        IMPredicate::init(_dimA, _dimB);
497
        //-- don't require equal dims, because EMPTY = EMPTY for all dims
498
0
    }
499
500
0
    void init(const Envelope& envA, const Envelope& envB) override {
501
        //-- handle EMPTY = EMPTY cases
502
0
        setValueIf(true, envA.isNull() && envB.isNull());
503
504
0
        require(envA.equals(&envB));
505
0
    }
506
507
0
    bool isDetermined() const override {
508
0
        bool isEitherExteriorIntersects =
509
0
            isIntersects(Location::INTERIOR, Location::EXTERIOR) ||
510
0
            isIntersects(Location::BOUNDARY, Location::EXTERIOR) ||
511
0
            isIntersects(Location::EXTERIOR, Location::INTERIOR) ||
512
0
            isIntersects(Location::EXTERIOR, Location::BOUNDARY);
513
514
0
        return isEitherExteriorIntersects;
515
0
    }
516
517
0
    bool valueIM() override {
518
0
        return intMatrix.isEquals(dimA, dimB);
519
0
    }
520
521
};
522
523
static std::unique_ptr<IMPredicate> equalsTopo();
524
525
526
/************************************************************************
527
 * Creates a predicate to determine whether a geometry overlaps another geometry.
528
 *
529
 * The overlaps predicate has the following equivalent definitions:
530
 *
531
 * The geometries have at least one point each not shared by the other
532
 *     (or equivalently neither covers the other),
533
 *     they have the same dimension,
534
 *     and the intersection of the interiors of the two geometries has
535
 *     the same dimension as the geometries themselves.
536
 * The DE-9IM Intersection Matrix for the two geometries matches
537
 *     [T*T***T**] (for P/P and A/A cases)
538
 *     or [1*T***T**] (for L/L cases)
539
 *
540
 * If the geometries are of different dimension this predicate returns false.
541
 * This predicate is symmetric.
542
 *
543
 * @return the predicate instance
544
 */
545
class OverlapsPredicate : public IMPredicate {
546
547
0
    std::string name() const override {
548
0
        return std::string("overlaps");
549
0
    }
550
551
0
    void init(int _dimA, int _dimB) override {
552
0
        IMPredicate::init(_dimA, _dimB);
553
0
        require(dimA == dimB);
554
0
    }
555
556
0
    bool isDetermined() const override {
557
0
        if (dimA == Dimension::A || dimA == Dimension::P) {
558
0
            if (isIntersects(Location::INTERIOR, Location::INTERIOR) &&
559
0
                isIntersects(Location::INTERIOR, Location::EXTERIOR) &&
560
0
                isIntersects(Location::EXTERIOR, Location::INTERIOR))
561
0
            return true;
562
0
        }
563
0
        if (dimA == Dimension::L) {
564
0
            if (isDimension(Location::INTERIOR, Location::INTERIOR, Dimension::L) &&
565
0
                isIntersects(Location::INTERIOR, Location::EXTERIOR) &&
566
0
                isIntersects(Location::EXTERIOR, Location::INTERIOR))
567
0
            return true;
568
0
        }
569
0
        return false;
570
0
    }
571
572
0
    bool valueIM() override {
573
0
        return intMatrix.isOverlaps(dimA, dimB);
574
0
    }
575
};
576
577
static std::unique_ptr<IMPredicate> overlaps();
578
579
580
581
582
583
/************************************************************************
584
* Creates a predicate to determine whether a geometry touches another geometry.
585
*
586
* The touches predicate has the following equivalent definitions:
587
*
588
* The geometries have at least one point in common,
589
* but their interiors do not intersect.
590
* The DE-9IM Intersection Matrix for the two geometries matches
591
* at least one of the following patterns
592
*
593
*   [FT*******]
594
*   [F**T*****]
595
*   [F***T****]
596
*
597
*
598
* If both geometries have dimension 0, the predicate returns false,
599
* since points have only interiors.
600
* This predicate is symmetric.
601
*
602
* @return the predicate instance
603
*/
604
class TouchesPredicate : public IMPredicate {
605
606
0
    std::string name() const override {
607
0
        return std::string("touches");
608
0
    }
609
610
0
    void init(int _dimA, int _dimB) override {
611
0
        IMPredicate::init(_dimA, _dimB);
612
0
        bool isBothPoints = (dimA == 0 && dimB == 0);
613
0
        require(! isBothPoints);
614
0
    }
615
616
0
    bool isDetermined() const override {
617
0
        bool isInteriorsIntersects = isIntersects(Location::INTERIOR, Location::INTERIOR);
618
0
        return isInteriorsIntersects;
619
0
    }
620
621
0
    bool valueIM() override {
622
0
        return intMatrix.isTouches(dimA, dimB);
623
0
    }
624
};
625
626
static std::unique_ptr<IMPredicate> touches();
627
628
/**
629
 * Creates a predicate that matches a DE-9IM matrix pattern.
630
 *
631
 * @param imPattern the pattern to match
632
 * @return a predicate that matches the pattern
633
 *
634
 * @see IntersectionMatrixPattern
635
 */
636
static std::unique_ptr<TopologyPredicate> matches(const std::string& imPattern)
637
0
{
638
0
    return std::unique_ptr<TopologyPredicate>(new IMPatternMatcher(imPattern));
639
0
}
640
641
642
643
}; // !RelatePredicate
644
645
646
} // namespace geos.operation.relateng
647
} // namespace geos.operation
648
} // namespace geos
649