/*******************************************************************************
* Copyright (c) 2015 Voyager Search and MITRE
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License, Version 2.0 which
* accompanies this distribution and is available at
* http://www.apache.org/licenses/LICENSE-2.0.txt
******************************************************************************/
package org.locationtech.spatial4j.shape;
/**
* The set of spatial relationships. Naming is somewhat consistent with OGC spec
* conventions as seen in SQL/MM and others.
* <p>
* There is no equality case. If two Shape instances are equal then the result
* might be CONTAINS (preferred) or WITHIN. Client logic may have to be aware
* of this edge condition; Spatial4j testing certainly does.
* <p>
* The "CONTAINS" and "WITHIN" wording here is inconsistent with OGC; these here map to OGC
* "COVERS" and "COVERED BY", respectively. The distinction is in the boundaries; in Spatial4j
* there is no boundary distinction -- boundaries are part of the shape as if it was an "interior",
* with respect to OGC's terminology.
*/
public enum SpatialRelation {
//see http://docs.geotools.org/latest/userguide/library/jts/dim9.html#preparedgeometry
/**
* The shape is within the target geometry. It's the converse of {@link #CONTAINS}.
* Boundaries of shapes count too. OGC specs refer to this relation as "COVERED BY";
* WITHIN is differentiated there by not including boundaries.
*/
WITHIN,
/**
* The shape contains the target geometry. It's the converse of {@link #WITHIN}.
* Boundaries of shapes count too. OGC specs refer to this relation as "COVERS";
* CONTAINS is differentiated there by not including boundaries.
*/
CONTAINS,
/**
* The shape shares no point in common with the target shape.
*/
DISJOINT,
/**
* The shape shares some points/overlap with the target shape, and the relation is
* not more specifically {@link #WITHIN} or {@link #CONTAINS}.
*/
INTERSECTS;
//Don't have these: TOUCHES, CROSSES, OVERLAPS, nor distinction between CONTAINS/COVERS
/**
* Given the result of <code>shapeA.relate(shapeB)</code>, transposing that
* result should yield the result of <code>shapeB.relate(shapeA)</code>. There
* is a corner case is when the shapes are equal, in which case actually
* flipping the relate() call will result in the same value -- either CONTAINS
* or WITHIN; this method can't possible check for that so the caller might
* have to.
*/
public SpatialRelation transpose() {
switch(this) {
case CONTAINS: return SpatialRelation.WITHIN;
case WITHIN: return SpatialRelation.CONTAINS;
default: return this;
}
}
/**
* If you were to call aShape.relate(bShape) and aShape.relate(cShape), you
* could call this to merge the intersect results as if bShape & cShape were
* combined into {@link ShapeCollection}. If {@code other} is null then the
* result is "this".
*/
public SpatialRelation combine(SpatialRelation other) {
// You can think of this algorithm as a state transition / automata.
// 1. The answer must be the same no matter what the order is.
// 2. If any INTERSECTS, then the result is INTERSECTS (done).
// 3. A DISJOINT + WITHIN == INTERSECTS (done).
// 4. A DISJOINT + CONTAINS == CONTAINS.
// 5. A CONTAINS + WITHIN == INTERSECTS (done). (weird scenario)
// 6. X + X == X.)
// 7. X + null == X;
if (other == this || other == null)
return this;
if (this == DISJOINT && other == CONTAINS
|| this == CONTAINS && other == DISJOINT)
return CONTAINS;
return INTERSECTS;
}
/** Not DISJOINT, i.e. there is some sort of intersection. */
public boolean intersects() {
return this != DISJOINT;
}
/**
* If <code>aShape.relate(bShape)</code> is r, then <code>r.inverse()</code>
* is <code> inverse(aShape).relate(bShape)</code> whereas
* <code>inverse(shape)</code> is theoretically the opposite area covered by a
* shape, i.e. everywhere but where the shape is.
* <p>
* Note that it's not commutative! <code>WITHIN.inverse().inverse() !=
* WITHIN</code>.
*/
public SpatialRelation inverse() {
switch(this) {
case DISJOINT: return CONTAINS;
case CONTAINS: return DISJOINT;
case WITHIN: return INTERSECTS;//not commutative!
}
return INTERSECTS;
}
}