using System;
using System.Text;
namespace Core.GIS.GeoAPI.Geometries
{
///
/// A Dimensionally Extended Nine-Intersection Model (DE-9IM) matrix. This class
/// can used to represent both computed DE-9IM's (like 212FF1FF2) as well as
/// patterns for matching them (like T*T******).
///
/// 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, see the
/// OpenGIS Simple Features Specification for SQL.
///
///
public class IntersectionMatrix
{
///
/// Internal representation of this .
///
private readonly Dimensions[,] matrix;
///
/// Creates an with null location values.
///
public IntersectionMatrix()
{
matrix = new Dimensions[3, 3];
SetAll(Dimensions.False);
}
///
/// Creates an with the given dimension
/// symbols.
///
/// A string of nine dimension symbols in row major order.
public IntersectionMatrix(string elements) : this()
{
Set(elements);
}
///
/// Creates an with the same elements as
/// other.
///
/// An to copy.
public IntersectionMatrix(IntersectionMatrix other) : this()
{
matrix[(int) Locations.Interior, (int) Locations.Interior] = other.matrix[(int) Locations.Interior, (int) Locations.Interior];
matrix[(int) Locations.Interior, (int) Locations.Boundary] = other.matrix[(int) Locations.Interior, (int) Locations.Boundary];
matrix[(int) Locations.Interior, (int) Locations.Exterior] = other.matrix[(int) Locations.Interior, (int) Locations.Exterior];
matrix[(int) Locations.Boundary, (int) Locations.Interior] = other.matrix[(int) Locations.Boundary, (int) Locations.Interior];
matrix[(int) Locations.Boundary, (int) Locations.Boundary] = other.matrix[(int) Locations.Boundary, (int) Locations.Boundary];
matrix[(int) Locations.Boundary, (int) Locations.Exterior] = other.matrix[(int) Locations.Boundary, (int) Locations.Exterior];
matrix[(int) Locations.Exterior, (int) Locations.Interior] = other.matrix[(int) Locations.Exterior, (int) Locations.Interior];
matrix[(int) Locations.Exterior, (int) Locations.Boundary] = other.matrix[(int) Locations.Exterior, (int) Locations.Boundary];
matrix[(int) Locations.Exterior, (int) Locations.Exterior] = other.matrix[(int) Locations.Exterior, (int) Locations.Exterior];
}
///
/// See methods Get(int, int) and Set(int, int, int value)
///
public Dimensions this[Locations row, Locations column]
{
get
{
return Get(row, column);
}
set
{
Set(row, column, value);
}
}
///
/// Adds one matrix to another.
/// Addition is defined by taking the maximum dimension value of each position
/// in the summand matrices.
///
/// The matrix to add.
public void Add(IntersectionMatrix im)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
SetAtLeast((Locations) i, (Locations) j, im.Get((Locations) i, (Locations) j));
}
}
}
///
/// Returns true if the dimension value satisfies the dimension symbol.
///
///
/// A number that can be stored in the .
/// Possible values are True, False, Dontcare, 0, 1, 2.
///
///
/// A character used in the string
/// representation of an .
/// Possible values are T, F, * , 0, 1, 2.
///
///
/// true if the dimension symbol encompasses the dimension value.
///
public static bool Matches(Dimensions actualDimensionValue, char requiredDimensionSymbol)
{
if (requiredDimensionSymbol == '*')
{
return true;
}
if (requiredDimensionSymbol == 'T' && (actualDimensionValue >= Dimensions.Point || actualDimensionValue == Dimensions.True))
{
return true;
}
if (requiredDimensionSymbol == 'F' && actualDimensionValue == Dimensions.False)
{
return true;
}
if (requiredDimensionSymbol == '0' && actualDimensionValue == Dimensions.Point)
{
return true;
}
if (requiredDimensionSymbol == '1' && actualDimensionValue == Dimensions.Curve)
{
return true;
}
if (requiredDimensionSymbol == '2' && actualDimensionValue == Dimensions.Surface)
{
return true;
}
return false;
}
///
/// Returns true if each of the actual dimension symbols satisfies the
/// corresponding required dimension symbol.
///
///
/// Nine dimension symbols to validate.
/// Possible values are T, F, * , 0, 1, 2.
///
///
/// Nine dimension symbols to validate
/// against. Possible values are T, F, * , 0, 1, 2.
///
///
/// true if each of the required dimension
/// symbols encompass the corresponding actual dimension symbol.
///
public static bool Matches(string actualDimensionSymbols, string requiredDimensionSymbols)
{
IntersectionMatrix m = new IntersectionMatrix(actualDimensionSymbols);
return m.Matches(requiredDimensionSymbols);
}
///
/// Changes the value of one of this elements.
///
///
/// The row of this ,
/// indicating the interior, boundary or exterior of the first
///
///
/// The column of this ,
/// indicating the interior, boundary or exterior of the second
///
/// The new value of the element
public void Set(Locations row, Locations column, Dimensions dimensionValue)
{
matrix[(int) row, (int) column] = dimensionValue;
}
///
/// Changes the elements of this to the
/// dimension symbols in dimensionSymbols.
///
///
/// Nine dimension symbols to which to set this
/// 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[i]);
}
}
///
/// Changes the specified element to minimumDimensionValue if the element is less.
///
///
/// The row of this ,
/// indicating the interior, boundary or exterior of the first .
///
///
/// The column of this ,
/// indicating the interior, boundary or exterior of the second .
///
///
/// The dimension value with which to compare the
/// element. The order of dimension values from least to greatest is
/// True, False, Dontcare, 0, 1, 2.
///
public void SetAtLeast(Locations row, Locations column, Dimensions minimumDimensionValue)
{
if (matrix[(int) row, (int) column] < minimumDimensionValue)
{
matrix[(int) row, (int) column] = minimumDimensionValue;
}
}
///
/// If row >= 0 and column >= 0, changes the specified element to minimumDimensionValue
/// if the element is less. Does nothing if row is smaller to 0 or column is smaller to 0.
///
///
///
///
public void SetAtLeastIfValid(Locations row, Locations column, Dimensions minimumDimensionValue)
{
if (row >= Locations.Interior && column >= Locations.Interior)
{
SetAtLeast(row, column, minimumDimensionValue);
}
}
///
/// For each element in this , changes the
/// element to the corresponding minimum dimension symbol if the element is
/// less.
///
///
/// Nine dimension symbols with which to
/// compare the elements of this . 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((Locations) row, (Locations) col, Dimension.ToDimensionValue(minimumDimensionSymbols[i]));
}
}
///
/// Changes the elements of this to dimensionValue.
///
///
/// The dimension value to which to set this
/// s elements. Possible values True, False, Dontcare, 0, 1, 2}.
///
public void SetAll(Dimensions 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 s
/// elements.
///
///
/// The row of this , indicating
/// the interior, boundary or exterior of the first .
///
///
/// The column of this ,
/// indicating the interior, boundary or exterior of the second .
///
/// The dimension value at the given matrix position.
public Dimensions Get(Locations row, Locations column)
{
return matrix[(int) row, (int) column];
}
///
/// Returns true if this is FF*FF****.
///
///
/// true if the two 's related by
/// this are disjoint.
///
public bool IsDisjoint()
{
return
matrix[(int) Locations.Interior, (int) Locations.Interior] == Dimensions.False &&
matrix[(int) Locations.Interior, (int) Locations.Boundary] == Dimensions.False &&
matrix[(int) Locations.Boundary, (int) Locations.Interior] == Dimensions.False &&
matrix[(int) Locations.Boundary, (int) Locations.Boundary] == Dimensions.False;
}
///
/// Returns true if isDisjoint returns false.
///
///
/// true if the two 's related by
/// this intersect.
///
public bool IsIntersects()
{
return !IsDisjoint();
}
///
/// Returns true if this is
/// FT*******, F**T***** or F***T****.
///
/// The dimension of the first .
/// The dimension of the second .
///
/// true if the two
/// s related by this touch; Returns false
/// if both s are points.
///
public bool IsTouches(Dimensions dimensionOfGeometryA, Dimensions dimensionOfGeometryB)
{
if (dimensionOfGeometryA > dimensionOfGeometryB)
{
//no need to get transpose because pattern matrix is symmetrical
return IsTouches(dimensionOfGeometryB, dimensionOfGeometryA);
}
if ((dimensionOfGeometryA == Dimensions.Surface && dimensionOfGeometryB == Dimensions.Surface) ||
(dimensionOfGeometryA == Dimensions.Curve && dimensionOfGeometryB == Dimensions.Curve) ||
(dimensionOfGeometryA == Dimensions.Curve && dimensionOfGeometryB == Dimensions.Surface) ||
(dimensionOfGeometryA == Dimensions.Point && dimensionOfGeometryB == Dimensions.Surface) ||
(dimensionOfGeometryA == Dimensions.Point && dimensionOfGeometryB == Dimensions.Curve))
{
return matrix[(int) Locations.Interior, (int) Locations.Interior] == Dimensions.False &&
(Matches(matrix[(int) Locations.Interior, (int) Locations.Boundary], 'T') ||
Matches(matrix[(int) Locations.Boundary, (int) Locations.Interior], 'T') ||
Matches(matrix[(int) Locations.Boundary, (int) Locations.Boundary], 'T'));
}
return false;
}
///
/// Returns true if this is
/// T*T****** (for a point and a curve, a point and an area or a line
/// and an area) 0******** (for two curves).
///
/// The dimension of the first .
/// The dimension of the second .
///
/// true if the two
/// s related by this cross. For this
/// function to return true, the s must
/// be a point and a curve; a point and a surface; two curves; or a curve
/// and a surface.
///
public bool IsCrosses(Dimensions dimensionOfGeometryA, Dimensions dimensionOfGeometryB)
{
if ((dimensionOfGeometryA == Dimensions.Point && dimensionOfGeometryB == Dimensions.Curve) ||
(dimensionOfGeometryA == Dimensions.Point && dimensionOfGeometryB == Dimensions.Surface) ||
(dimensionOfGeometryA == Dimensions.Curve && dimensionOfGeometryB == Dimensions.Surface))
{
return Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T') &&
Matches(matrix[(int) Locations.Interior, (int) Locations.Exterior], 'T');
}
if ((dimensionOfGeometryA == Dimensions.Curve && dimensionOfGeometryB == Dimensions.Point) ||
(dimensionOfGeometryA == Dimensions.Surface && dimensionOfGeometryB == Dimensions.Point) ||
(dimensionOfGeometryA == Dimensions.Surface && dimensionOfGeometryB == Dimensions.Curve))
{
return Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T') &&
Matches(matrix[(int) Locations.Exterior, (int) Locations.Interior], 'T');
}
if (dimensionOfGeometryA == Dimensions.Curve && dimensionOfGeometryB == Dimensions.Curve)
{
return matrix[(int) Locations.Interior, (int) Locations.Interior] == 0;
}
return false;
}
///
/// Returns true if this is
/// T*F**F***.
///
/// true if the first is within the second.
public bool IsWithin()
{
return Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T') &&
matrix[(int) Locations.Interior, (int) Locations.Exterior] == Dimensions.False &&
matrix[(int) Locations.Boundary, (int) Locations.Exterior] == Dimensions.False;
}
///
/// Returns true if this is
/// T*****FF*.
///
/// true if the first contains the second.
public bool IsContains()
{
return Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T') &&
matrix[(int) Locations.Exterior, (int) Locations.Interior] == Dimensions.False &&
matrix[(int) Locations.Exterior, (int) Locations.Boundary] == Dimensions.False;
}
///
/// Returns true if this is T*****FF*
/// or *T****FF* or ***T**FF* or ****T*FF*.
///
/// true if the first covers the second
public bool IsCovers()
{
bool hasPointInCommon = Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T')
|| Matches(matrix[(int) Locations.Interior, (int) Locations.Boundary], 'T')
|| Matches(matrix[(int) Locations.Boundary, (int) Locations.Interior], 'T')
|| Matches(matrix[(int) Locations.Boundary, (int) Locations.Boundary], 'T');
return hasPointInCommon &&
matrix[(int) Locations.Exterior, (int) Locations.Interior] == Dimensions.False &&
matrix[(int) Locations.Exterior, (int) Locations.Boundary] == Dimensions.False;
}
///
/// Returns true if this is T*F**FFF*.
///
/// The dimension of the first .
/// The dimension of the second .
///
/// true if the two
/// s related by this are equal; the
/// s must have the same dimension for this function
/// to return true.
///
public bool IsEquals(Dimensions dimensionOfGeometryA, Dimensions dimensionOfGeometryB)
{
if (dimensionOfGeometryA != dimensionOfGeometryB)
{
return false;
}
return Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T') &&
matrix[(int) Locations.Exterior, (int) Locations.Interior] == Dimensions.False &&
matrix[(int) Locations.Interior, (int) Locations.Exterior] == Dimensions.False &&
matrix[(int) Locations.Exterior, (int) Locations.Boundary] == Dimensions.False &&
matrix[(int) Locations.Boundary, (int) Locations.Exterior] == Dimensions.False;
}
///
/// Returns true if this is
/// T*T***T** (for two points or two surfaces)
/// 1*T***T** (for two curves).
///
/// The dimension of the first .
/// The dimension of the second .
///
/// true if the two
/// s related by this overlap. For this
/// function to return true, the s must
/// be two points, two curves or two surfaces.
///
public bool IsOverlaps(Dimensions dimensionOfGeometryA, Dimensions dimensionOfGeometryB)
{
if ((dimensionOfGeometryA == Dimensions.Point && dimensionOfGeometryB == Dimensions.Point) ||
(dimensionOfGeometryA == Dimensions.Surface && dimensionOfGeometryB == Dimensions.Surface))
{
return Matches(matrix[(int) Locations.Interior, (int) Locations.Interior], 'T') &&
Matches(matrix[(int) Locations.Interior, (int) Locations.Exterior], 'T') &&
Matches(matrix[(int) Locations.Exterior, (int) Locations.Interior], 'T');
}
if (dimensionOfGeometryA == Dimensions.Curve && dimensionOfGeometryB == Dimensions.Curve)
{
return matrix[(int) Locations.Interior, (int) Locations.Interior] == Dimensions.Curve &&
Matches(matrix[(int) Locations.Interior, (int) Locations.Exterior], 'T') &&
Matches(matrix[(int) Locations.Exterior, (int) Locations.Interior], 'T');
}
return false;
}
///
/// Returns whether the elements of this
/// satisfies the required dimension symbols.
///
///
/// Nine dimension symbols with which to
/// compare the elements of this . Possible
/// values are {T, F, * , 0, 1, 2}.
///
///
/// true if this
/// matches the required dimension symbols.
///
public bool Matches(string requiredDimensionSymbols)
{
if (requiredDimensionSymbols.Length != 9)
{
throw new ArgumentException("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[3*ai + bi]))
{
return false;
}
}
}
return true;
}
///
/// Transposes this IntersectionMatrix.
///
/// This as a convenience,
public IntersectionMatrix Transpose()
{
Dimensions 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 .
///
///
/// The nine dimension symbols of this
/// in row-major order.
///
public override string ToString()
{
StringBuilder buf = new StringBuilder("123456789");
for (int ai = 0; ai < 3; ai++)
{
for (int bi = 0; bi < 3; bi++)
{
buf[3*ai + bi] = Dimension.ToDimensionSymbol((Dimensions) matrix[ai, bi]);
}
}
return buf.ToString();
}
}
}