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(); } } }