com.vividsolutions.jts.geom.IntersectionMatrix Maven / Gradle / Ivy
Show all versions of JTSplus Show documentation
/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jts.geom;
/**
* Models a Dimensionally Extended Nine-Intersection Model (DE-9IM) matrix.
* DE-9IM matrices (such as "212FF1FF2")
* specify the topological relationship between two {@link Geometry}s.
* This class can also represent matrix patterns (such as "T*T******")
* which are used for matching instances of DE-9IM matrices.
*
* Methods are provided to:
*
* - set and query the elements of the matrix in a convenient fashion
*
- convert to and from the standard string representation (specified in
* SFS Section 2.1.13.2).
*
- test to see if a matrix matches a given pattern string.
*
*
*
* For a description of the DE-9IM and the spatial predicates derived from it,
* see the OGC 99-049 OpenGIS Simple Features
* Specification for SQL, as well as
* OGC 06-103r4 OpenGIS
* Implementation Standard for Geographic information -
* Simple feature access - Part 1: Common architecture
* (which provides some further details on certain predicate specifications).
*
* The entries of the matrix are defined by the constants in the {@link Dimension} class.
* The indices of the matrix represent the topological locations
* that occur in a geometry (Interior, Boundary, Exterior).
* These are provided as constants in the {@link Location} class.
*
*
*@version 1.7
*/
public class IntersectionMatrix implements Cloneable {
/**
* Internal representation of this IntersectionMatrix
.
*/
private int[][] matrix;
/**
* Creates an IntersectionMatrix
with FALSE
* dimension values.
*/
public IntersectionMatrix() {
matrix = new int[3][3];
setAll(Dimension.FALSE);
}
/**
* Creates an IntersectionMatrix
with the given dimension
* symbols.
*
*@param elements a String of nine dimension symbols in row major order
*/
public IntersectionMatrix(String elements) {
this();
set(elements);
}
/**
* Creates an IntersectionMatrix
with the same elements as
* other
.
*
*@param other an IntersectionMatrix
to copy
*/
public IntersectionMatrix(IntersectionMatrix other) {
this();
matrix[Location.INTERIOR][Location.INTERIOR] = other.matrix[Location.INTERIOR][Location.INTERIOR];
matrix[Location.INTERIOR][Location.BOUNDARY] = other.matrix[Location.INTERIOR][Location.BOUNDARY];
matrix[Location.INTERIOR][Location.EXTERIOR] = other.matrix[Location.INTERIOR][Location.EXTERIOR];
matrix[Location.BOUNDARY][Location.INTERIOR] = other.matrix[Location.BOUNDARY][Location.INTERIOR];
matrix[Location.BOUNDARY][Location.BOUNDARY] = other.matrix[Location.BOUNDARY][Location.BOUNDARY];
matrix[Location.BOUNDARY][Location.EXTERIOR] = other.matrix[Location.BOUNDARY][Location.EXTERIOR];
matrix[Location.EXTERIOR][Location.INTERIOR] = other.matrix[Location.EXTERIOR][Location.INTERIOR];
matrix[Location.EXTERIOR][Location.BOUNDARY] = other.matrix[Location.EXTERIOR][Location.BOUNDARY];
matrix[Location.EXTERIOR][Location.EXTERIOR] = other.matrix[Location.EXTERIOR][Location.EXTERIOR];
}
/**
* Adds one matrix to another.
* Addition is defined by taking the maximum dimension value of each position
* in the summand matrices.
*
* @param im the matrix to add
*/
public void add(IntersectionMatrix im)
{
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
setAtLeast(i, j, im.get(i, j));
}
}
}
/**
* Tests if the dimension value matches TRUE
* (i.e. has value 0, 1, 2 or TRUE).
*
*@param actualDimensionValue a number that can be stored in the IntersectionMatrix
* . Possible values are {TRUE, FALSE, DONTCARE, 0, 1, 2}
.
*@return true if the dimension value matches TRUE
*/
public static boolean isTrue(int actualDimensionValue) {
if (actualDimensionValue >= 0 || actualDimensionValue == Dimension.TRUE) {
return true;
}
return false;
}
/**
* Tests if the dimension value satisfies the dimension symbol.
*
*@param actualDimensionValue a number that can be stored in the IntersectionMatrix
* . Possible values are {TRUE, FALSE, DONTCARE, 0, 1, 2}
.
*@param requiredDimensionSymbol a character used in the string
* representation of an IntersectionMatrix
. Possible values
* are {T, F, * , 0, 1, 2}
.
*@return true if the dimension symbol matches
* the dimension value
*/
public static boolean matches(int actualDimensionValue, char requiredDimensionSymbol) {
if (requiredDimensionSymbol == Dimension.SYM_DONTCARE) {
return true;
}
if (requiredDimensionSymbol == Dimension.SYM_TRUE && (actualDimensionValue >= 0 || actualDimensionValue
== Dimension.TRUE)) {
return true;
}
if (requiredDimensionSymbol == Dimension.SYM_FALSE && actualDimensionValue == Dimension.FALSE) {
return true;
}
if (requiredDimensionSymbol == Dimension.SYM_P && actualDimensionValue == Dimension.P) {
return true;
}
if (requiredDimensionSymbol == Dimension.SYM_L && actualDimensionValue == Dimension.L) {
return true;
}
if (requiredDimensionSymbol == Dimension.SYM_A && actualDimensionValue == Dimension.A) {
return true;
}
return false;
}
/**
* Tests if each of the actual dimension symbols in a matrix string satisfies the
* corresponding required dimension symbol in a pattern string.
*
*@param actualDimensionSymbols nine dimension symbols to validate.
* Possible values are {T, F, * , 0, 1, 2}
.
*@param requiredDimensionSymbols nine dimension symbols to validate
* against. Possible values are {T, F, * , 0, 1, 2}
.
*@return true if each of the required dimension
* symbols encompass the corresponding actual dimension symbol
*/
public static boolean matches(String actualDimensionSymbols, String requiredDimensionSymbols) {
IntersectionMatrix m = new IntersectionMatrix(actualDimensionSymbols);
return m.matches(requiredDimensionSymbols);
}
/**
* Changes the value of one of this IntersectionMatrix
s
* elements.
*
*@param row the row of this IntersectionMatrix
,
* indicating the interior, boundary or exterior of the first Geometry
*@param column the column of this IntersectionMatrix
,
* indicating the interior, boundary or exterior of the second Geometry
*@param dimensionValue the new value of the element
*/
public void set(int row, int column, int dimensionValue) {
matrix[row][column] = dimensionValue;
}
/**
* Changes the elements of this IntersectionMatrix
to the
* dimension symbols in dimensionSymbols
.
*
*@param dimensionSymbols nine dimension symbols to which to set this IntersectionMatrix
* s elements. Possible values are {T, F, * , 0, 1, 2}
*/
public void set(String dimensionSymbols) {
for (int i = 0; i < dimensionSymbols.length(); i++) {
int row = i / 3;
int col = i % 3;
matrix[row][col] = Dimension.toDimensionValue(dimensionSymbols.charAt(i));
}
}
/**
* Changes the specified element to minimumDimensionValue
if the
* element is less.
*
*@param row the row of this IntersectionMatrix
* , indicating the interior, boundary or exterior of the first Geometry
*@param column the column of this IntersectionMatrix
* , indicating the interior, boundary or exterior of the second Geometry
*@param minimumDimensionValue the dimension value with which to compare the
* element. The order of dimension values from least to greatest is
* {DONTCARE, TRUE, FALSE, 0, 1, 2}
.
*/
public void setAtLeast(int row, int column, int minimumDimensionValue) {
if (matrix[row][column] < minimumDimensionValue) {
matrix[row][column] = minimumDimensionValue;
}
}
/**
* If row >= 0 and column >= 0, changes the specified element to minimumDimensionValue
* if the element is less. Does nothing if row <0 or column < 0.
*
*@param row the row of this IntersectionMatrix
* , indicating the interior, boundary or exterior of the first Geometry
*@param column the column of this IntersectionMatrix
* , indicating the interior, boundary or exterior of the second Geometry
*@param minimumDimensionValue the dimension value with which to compare the
* element. The order of dimension values from least to greatest is
* {DONTCARE, TRUE, FALSE, 0, 1, 2}
.
*/
public void setAtLeastIfValid(int row, int column, int minimumDimensionValue) {
if (row >= 0 && column >= 0) {
setAtLeast(row, column, minimumDimensionValue);
}
}
/**
* For each element in this IntersectionMatrix
, changes the
* element to the corresponding minimum dimension symbol if the element is
* less.
*
*@param minimumDimensionSymbols nine dimension symbols with which to
* compare the elements of this IntersectionMatrix
. The
* order of dimension values from least to greatest is {DONTCARE, TRUE, FALSE, 0, 1, 2}
* .
*/
public void setAtLeast(String minimumDimensionSymbols) {
for (int i = 0; i < minimumDimensionSymbols.length(); i++) {
int row = i / 3;
int col = i % 3;
setAtLeast(row, col, Dimension.toDimensionValue(minimumDimensionSymbols.charAt(i)));
}
}
/**
* Changes the elements of this IntersectionMatrix
to dimensionValue
* .
*
*@param dimensionValue the dimension value to which to set this IntersectionMatrix
* s elements. Possible values {TRUE, FALSE, DONTCARE, 0, 1, 2}
* .
*/
public void setAll(int dimensionValue) {
for (int ai = 0; ai < 3; ai++) {
for (int bi = 0; bi < 3; bi++) {
matrix[ai][bi] = dimensionValue;
}
}
}
/**
* Returns the value of one of this matrix
* entries.
* The value of the provided index is one of the
* values from the {@link Location} class.
* The value returned is a constant
* from the {@link Dimension} class.
*
*@param row the row of this IntersectionMatrix
, indicating
* the interior, boundary or exterior of the first Geometry
*@param column the column of this IntersectionMatrix
,
* indicating the interior, boundary or exterior of the second Geometry
*@return the dimension value at the given matrix position.
*/
public int get(int row, int column) {
return matrix[row][column];
}
/**
* Returns true
if this IntersectionMatrix
is
* FF*FF****.
*
*@return true
if the two Geometry
s related by
* this IntersectionMatrix
are disjoint
*/
public boolean isDisjoint() {
return
matrix[Location.INTERIOR][Location.INTERIOR] == Dimension.FALSE &&
matrix[Location.INTERIOR][Location.BOUNDARY] == Dimension.FALSE &&
matrix[Location.BOUNDARY][Location.INTERIOR] == Dimension.FALSE &&
matrix[Location.BOUNDARY][Location.BOUNDARY] == Dimension.FALSE;
}
/**
* Returns true
if isDisjoint
returns false.
*
*@return true
if the two Geometry
s related by
* this IntersectionMatrix
intersect
*/
public boolean isIntersects() {
return ! isDisjoint();
}
/**
* Returns true
if this IntersectionMatrix
is
* FT*******, F**T***** or F***T****.
*
*@param dimensionOfGeometryA the dimension of the first Geometry
*@param dimensionOfGeometryB the dimension of the second Geometry
*@return true
if the two Geometry
* s related by this IntersectionMatrix
touch; Returns false
* if both Geometry
s are points.
*/
public boolean isTouches(int dimensionOfGeometryA, int dimensionOfGeometryB) {
if (dimensionOfGeometryA > dimensionOfGeometryB) {
//no need to get transpose because pattern matrix is symmetrical
return isTouches(dimensionOfGeometryB, dimensionOfGeometryA);
}
if ((dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.A) ||
(dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) ||
(dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.A) ||
(dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.A) ||
(dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L)) {
return matrix[Location.INTERIOR][Location.INTERIOR] == Dimension.FALSE &&
(isTrue(matrix[Location.INTERIOR][Location.BOUNDARY])
|| isTrue(matrix[Location.BOUNDARY][Location.INTERIOR])
|| isTrue(matrix[Location.BOUNDARY][Location.BOUNDARY]));
}
return false;
}
/**
* Tests whether this geometry crosses the
* specified geometry.
*
* The crosses
predicate has the following equivalent definitions:
*
* - The geometries have some but not all interior points in common.
*
- The DE-9IM Intersection Matrix for the two geometries is
*
* - T*T****** (for P/L, P/A, and L/A situations)
*
- T*****T** (for L/P, L/A, and A/L situations)
*
- 0******** (for L/L situations)
*
*
* For any other combination of dimensions this predicate returns false
.
*
* The SFS defined this predicate only for P/L, P/A, L/L, and L/A situations.
* JTS extends the definition to apply to L/P, A/P and A/L situations as well.
* This makes the relation symmetric.
*
*@param dimensionOfGeometryA the dimension of the first Geometry
*@param dimensionOfGeometryB the dimension of the second Geometry
*@return true
if the two Geometry
s
* related by this IntersectionMatrix
cross.
*/
public boolean isCrosses(int dimensionOfGeometryA, int dimensionOfGeometryB) {
if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L) ||
(dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.A) ||
(dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.A)) {
return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
isTrue(matrix[Location.INTERIOR][Location.EXTERIOR]);
}
if ((dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.P) ||
(dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.P) ||
(dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.L)) {
return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
isTrue(matrix[Location.EXTERIOR][Location.INTERIOR]);
}
if (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) {
return matrix[Location.INTERIOR][Location.INTERIOR] == 0;
}
return false;
}
/**
* Tests whether this IntersectionMatrix
is
* T*F**F***.
*
*@return true
if the first Geometry
is within
* the second
*/
public boolean isWithin() {
return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE;
}
/**
* Tests whether this IntersectionMatrix
is
* T*****FF*.
*
*@return true
if the first Geometry
contains the
* second
*/
public boolean isContains() {
return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
}
/**
* Returns true
if this IntersectionMatrix
is
* T*****FF*
* or *T****FF*
* or ***T**FF*
* or ****T*FF*
*
*@return true
if the first Geometry
covers the
* second
*/
public boolean isCovers() {
boolean hasPointInCommon =
isTrue(matrix[Location.INTERIOR][Location.INTERIOR])
|| isTrue(matrix[Location.INTERIOR][Location.BOUNDARY])
|| isTrue(matrix[Location.BOUNDARY][Location.INTERIOR])
|| isTrue(matrix[Location.BOUNDARY][Location.BOUNDARY]);
return hasPointInCommon &&
matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
}
/**
* Returns true
if this IntersectionMatrix
is
* T*F**F***
* or *TF**F***
* or **FT*F***
* or **F*TF***
*
*@return true
if the first Geometry
* is covered by the second
*/
public boolean isCoveredBy() {
boolean hasPointInCommon =
isTrue(matrix[Location.INTERIOR][Location.INTERIOR])
|| isTrue(matrix[Location.INTERIOR][Location.BOUNDARY])
|| isTrue(matrix[Location.BOUNDARY][Location.INTERIOR])
|| isTrue(matrix[Location.BOUNDARY][Location.BOUNDARY]);
return hasPointInCommon &&
matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE;
}
/**
* Tests whether the argument dimensions are equal and
* this IntersectionMatrix
matches
* the pattern T*F**FFF*.
*
* Note: This pattern differs from the one stated in
* Simple feature access - Part 1: Common architecture.
* That document states the pattern as TFFFTFFFT. This would
* specify that
* two identical POINTs are not equal, which is not desirable behaviour.
* The pattern used here has been corrected to compute equality in this situation.
*
*@param dimensionOfGeometryA the dimension of the first Geometry
*@param dimensionOfGeometryB the dimension of the second Geometry
*@return true
if the two Geometry
s
* related by this IntersectionMatrix
are equal; the
* Geometry
s must have the same dimension to be equal
*/
public boolean isEquals(int dimensionOfGeometryA, int dimensionOfGeometryB) {
if (dimensionOfGeometryA != dimensionOfGeometryB) {
return false;
}
return isTrue(matrix[Location.INTERIOR][Location.INTERIOR]) &&
matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE &&
matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
}
/**
* Returns true
if this IntersectionMatrix
is
*
* - T*T***T** (for two points or two surfaces)
*
- 1*T***T** (for two curves)
*
.
*
*@param dimensionOfGeometryA the dimension of the first Geometry
*@param dimensionOfGeometryB the dimension of the second Geometry
*@return true
if the two Geometry
s
* related by this IntersectionMatrix
overlap. For this
* function to return true
, the Geometry
s must
* be two points, two curves or two surfaces.
*/
public boolean isOverlaps(int dimensionOfGeometryA, int dimensionOfGeometryB) {
if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.P) ||
(dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.A)) {
return isTrue(matrix[Location.INTERIOR][Location.INTERIOR])
&& isTrue(matrix[Location.INTERIOR][Location.EXTERIOR])
&& isTrue(matrix[Location.EXTERIOR][Location.INTERIOR]);
}
if (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) {
return matrix[Location.INTERIOR][Location.INTERIOR] == 1
&& isTrue(matrix[Location.INTERIOR][Location.EXTERIOR])
&& isTrue(matrix[Location.EXTERIOR][Location.INTERIOR]);
}
return false;
}
/**
* Returns whether the elements of this IntersectionMatrix
* satisfies the required dimension symbols.
*
*@param requiredDimensionSymbols nine dimension symbols with which to
* compare the elements of this IntersectionMatrix
. Possible
* values are {T, F, * , 0, 1, 2}
.
*@return true
if this IntersectionMatrix
* matches the required dimension symbols
*/
public boolean matches(String requiredDimensionSymbols) {
if (requiredDimensionSymbols.length() != 9) {
throw new IllegalArgumentException("Should be length 9: " + requiredDimensionSymbols);
}
for (int ai = 0; ai < 3; ai++) {
for (int bi = 0; bi < 3; bi++) {
if (!matches(matrix[ai][bi], requiredDimensionSymbols.charAt(3 * ai +
bi))) {
return false;
}
}
}
return true;
}
/**
* Transposes this IntersectionMatrix.
*
*@return this IntersectionMatrix
as a convenience
*/
public IntersectionMatrix transpose() {
int temp = matrix[1][0];
matrix[1][0] = matrix[0][1];
matrix[0][1] = temp;
temp = matrix[2][0];
matrix[2][0] = matrix[0][2];
matrix[0][2] = temp;
temp = matrix[2][1];
matrix[2][1] = matrix[1][2];
matrix[1][2] = temp;
return this;
}
/**
* Returns a nine-character String
representation of this IntersectionMatrix
* .
*
*@return the nine dimension symbols of this IntersectionMatrix
* in row-major order.
*/
public String toString() {
StringBuffer buf = new StringBuffer("123456789");
for (int ai = 0; ai < 3; ai++) {
for (int bi = 0; bi < 3; bi++) {
buf.setCharAt(3 * ai + bi, Dimension.toDimensionSymbol(matrix[ai][bi]));
}
}
return buf.toString();
}
}