using System; using System.Collections; using Core.Gis.GeoApi.Geometries; using Core.Gis.GeoApi.Operation.Buffer; using Core.GIS.NetTopologySuite.Algorithm; using Core.GIS.NetTopologySuite.IO; using Core.GIS.NetTopologySuite.Operation.Buffer; using Core.GIS.NetTopologySuite.Operation.Distance; using Core.GIS.NetTopologySuite.Operation.Overlay; using Core.GIS.NetTopologySuite.Operation.Overlay.Snap; using Core.GIS.NetTopologySuite.Operation.Predicate; using Core.GIS.NetTopologySuite.Operation.Relate; using Core.GIS.NetTopologySuite.Operation.Valid; using Core.GIS.NetTopologySuite.Utilities; namespace Core.GIS.NetTopologySuite.Geometries { /// /// Basic implementation of Geometry. /// Clone returns a deep copy of the object. /// /// Binary Predicates: /// Because it is not clear at this time what semantics for spatial /// analysis methods involving GeometryCollections would be useful, /// GeometryCollections are not supported as arguments to binary /// predicates (other than ConvexHull) or the Relate method. /// /// /// Set-Theoretic Methods: /// The spatial analysis methods will /// return the most specific class possible to represent the result. If the /// result is homogeneous, a Point, LineString, or /// Polygon will be returned if the result contains a single /// element; otherwise, a MultiPoint, MultiLineString, /// or MultiPolygon will be returned. If the result is /// heterogeneous a GeometryCollection will be returned. /// /// /// Representation of Computed Geometries: /// The SFS states that the result /// of a set-theoretic method is the "point-set" result of the usual /// set-theoretic definition of the operation (SFS 3.2.21.1). However, there are /// sometimes many ways of representing a point set as a Geometry. /// The SFS does not specify an unambiguous representation of a given point set /// returned from a spatial analysis method. One goal of NTS is to make this /// specification precise and unambiguous. NTS will use a canonical form for /// Geometrys returned from spatial analysis methods. The canonical /// form is a Geometry which is simple and noded: /// Simple means that the Geometry returned will be simple according to /// the NTS definition of IsSimple. /// Noded applies only to overlays involving LineStrings. It /// means that all intersection points on LineStrings will be /// present as endpoints of LineStrings in the result. /// This definition implies that non-simple geometries which are arguments to /// spatial analysis methods must be subjected to a line-dissolve process to /// ensure that the results are simple. /// /// /// Constructed Points And The Precision Model: /// The results computed by the set-theoretic methods may /// contain constructed points which are not present in the input Geometrys. /// These new points arise from intersections between line segments in the /// edges of the input Geometrys. In the general case it is not /// possible to represent constructed points exactly. This is due to the fact /// that the coordinates of an intersection point may contain twice as many bits /// of precision as the coordinates of the input line segments. In order to /// represent these constructed points explicitly, NTS must truncate them to fit /// the PrecisionModel. /// Unfortunately, truncating coordinates moves them slightly. Line segments /// which would not be coincident in the exact result may become coincident in /// the truncated representation. This in turn leads to "topology collapses" -- /// situations where a computed element has a lower dimension than it would in /// the exact result. /// When NTS detects topology collapses during the computation of spatial /// analysis methods, it will throw an exception. If possible the exception will /// report the location of the collapse. /// /// /// /// and are not overridden, so that when two /// topologically equal Geometries are added to Collections and Dictionaries, they /// remain distinct. This behaviour is desired in many cases. /// [Serializable] public abstract class Geometry : IGeometry { /* BEGIN ADDED BY MPAUL42: monoGIS team */ /// /// A predefined with == . /// /// /// public static readonly IGeometryFactory DefaultFactory = GeometryFactory.Default; /// /// /// private static readonly Type[] SortedClasses = new Type[] { typeof(Point), typeof(MultiPoint), typeof(LineString), typeof(LinearRing), typeof(MultiLineString), typeof(Polygon), typeof(MultiPolygon), typeof(GeometryCollection), }; /// /// The bounding box of this Geometry. /// protected IEnvelope envelope; private IGeometryFactory factory = null; private object userData = null; // The ID of the Spatial Reference System used by this Geometry private int srid; private Dimensions dimension; private IGeometry boundary; private Dimensions boundaryDimension; private int hashcode; /// /// /// /// public Geometry(IGeometryFactory factory) { this.factory = factory; srid = factory.SRID; } /// /// Gets the factory which contains the context in which this point was created. /// /// The factory for this point. public IGeometryFactory Factory { get { return factory; } } /// /// Gets/Sets the user data object for this point, if any. /// A simple scheme for applications to add their own custom data to a Geometry. /// An example use might be to add an object representing a Coordinate Reference System. /// Note that user data objects are not present in geometries created by /// construction methods. /// public object UserData { get { return userData; } set { userData = value; } } /// /// Gets/Sets the ID of the Spatial Reference System used by the Geometry. /// NTS supports Spatial Reference System information in the simple way /// defined in the SFS. A Spatial Reference System ID (SRID) is present in /// each Geometry object. Geometry provides basic /// accessor operations for this field, but no others. The SRID is represented /// as an integer. /// public int SRID { get { return srid; } set { srid = value; IGeometryCollection collection = this as IGeometryCollection; if (collection != null) { foreach (IGeometry geometry in collection.Geometries) { geometry.SRID = value; } } factory = new GeometryFactory(factory.PrecisionModel, value, factory.CoordinateSequenceFactory); } } /// /// Returns the name of this object's interface. /// /// The name of this Geometrys most specific interface. public abstract string GeometryType { get; } /// /// Returns the PrecisionModel used by the Geometry. /// /// /// the specification of the grid of allowable points, for this /// Geometry and all other Geometrys. /// public IPrecisionModel PrecisionModel { get { return Factory.PrecisionModel; } } /// /// Returns a vertex of this Geometry. /// /// /// a Coordinate which is a vertex of this Geometry. /// Returns null if this Geometry is empty. /// public abstract ICoordinate Coordinate { get; } /// /// Returns this Geometry s vertices. If you modify the coordinates /// in this array, be sure to call GeometryChanged afterwards. /// The Geometrys contained by composite Geometrys /// must be Geometry's; that is, they must implement Coordinates. /// /// The vertices of this Geometry. public abstract ICoordinate[] Coordinates { get; } /// /// Returns the count of this Geometrys vertices. The Geometry /// s contained by composite Geometrys must be /// Geometry's; that is, they must implement NumPoints. /// /// The number of vertices in this Geometry. public abstract int NumPoints { get; } /// /// Returns the number of Geometryes in a GeometryCollection, /// or 1, if the geometry is not a collection. /// public virtual int NumGeometries { get { return 1; } } /// /// Returns false if the Geometry not simple. /// Subclasses provide their own definition of "simple". If /// this Geometry is empty, returns true. /// In general, the SFS specifications of simplicity seem to follow the /// following rule: /// A Geometry is simple if the only self-intersections are at boundary points. /// For all empty Geometrys, IsSimple==true. /// /// /// true if this Geometry has any points of /// self-tangency, self-intersection or other anomalous points. /// public abstract bool IsSimple { get; } /// /// Tests the validity of this Geometry. /// Subclasses provide their own definition of "valid". /// /// true if this Geometry is valid. public virtual bool IsValid { get { IsValidOp isValidOp = new IsValidOp(this); return isValidOp.IsValid; } } /// /// Returns whether or not the set of points in this Geometry is empty. /// /// true if this Geometry equals the empty point. public abstract bool IsEmpty { get; } /// /// Returns the area of this Geometry. /// Areal Geometries have a non-zero area. /// They override this function to compute the area. /// Others return 0.0 /// /// The area of the Geometry. public virtual double Area { get { return 0.0; } } /// /// Returns the length of this Geometry. /// Linear geometries return their length. /// Areal geometries return their perimeter. /// They override this function to compute the length. /// Others return 0.0 /// /// The length of the Geometry. public virtual double Length { get { return 0.0; } } /// /// Computes the centroid of this Geometry. /// The centroid is equal to the centroid of the set of component Geometries of highest /// dimension (since the lower-dimension geometries contribute zero "weight" to the centroid). /// /// A Point which is the centroid of this Geometry. public IPoint Centroid { get { if (IsEmpty) { return null; } ICoordinate centPt; Dimensions dim = Dimension; if (dim == Dimensions.Point) { CentroidPoint cent = new CentroidPoint(); cent.Add(this); centPt = cent.Centroid; } else if (dim == Dimensions.Curve) { CentroidLine cent = new CentroidLine(); cent.Add(this); centPt = cent.Centroid; } else { CentroidArea cent = new CentroidArea(); cent.Add(this); centPt = cent.Centroid; } return CreatePointFromInternalCoord(centPt, this); } } /// /// Computes an interior point of this Geometry. /// An interior point is guaranteed to lie in the interior of the Geometry, /// if it possible to calculate such a point exactly. Otherwise, /// the point may lie on the boundary of the point. /// /// A Point which is in the interior of this Geometry. public IPoint InteriorPoint { get { ICoordinate interiorPt = null; Dimensions dim = Dimension; if (dim == Dimensions.Point) { InteriorPointPoint intPt = new InteriorPointPoint(this); interiorPt = intPt.InteriorPoint; } else if (dim == Dimensions.Curve) { InteriorPointLine intPt = new InteriorPointLine(this); interiorPt = intPt.InteriorPoint; } else { InteriorPointArea intPt = new InteriorPointArea(this); interiorPt = intPt.InteriorPoint; } return CreatePointFromInternalCoord(interiorPt, this); } } /// /// /// public IPoint PointOnSurface { get { return InteriorPoint; } } /// /// Returns the dimension of this Geometry. /// /// /// The dimension of the class implementing this interface, whether /// or not this object is the empty point. /// public virtual Dimensions Dimension { get { return dimension; } set { dimension = value; } } /// /// Returns the boundary, or the empty point if this Geometry /// is empty. For a discussion of this function, see the OpenGIS Simple /// Features Specification. As stated in SFS Section 2.1.13.1, "the boundary /// of a Geometry is a set of Geometries of the next lower dimension." /// /// The closure of the combinatorial boundary of this Geometry. public virtual IGeometry Boundary { get { return boundary; } set { boundary = value; } } /// /// Returns the dimension of this Geometrys inherent boundary. /// /// /// The dimension of the boundary of the class implementing this /// interface, whether or not this object is the empty point. Returns /// Dimension.False if the boundary is the empty point. /// public virtual Dimensions BoundaryDimension { get { return boundaryDimension; } set { boundaryDimension = value; } } /// /// Returns this Geometrys bounding box. If this Geometry /// is the empty point, returns an empty Point. If the Geometry /// is a point, returns a non-empty Point. Otherwise, returns a /// Polygon whose points are (minx, miny), (maxx, miny), (maxx, /// maxy), (minx, maxy), (minx, miny). /// /// /// An empty Point (for empty Geometrys), a /// Point (for Points) or a Polygon /// (in all other cases). /// public IGeometry Envelope { get { return Factory.ToGeometry(EnvelopeInternal); } } /// /// Returns the minimum and maximum x and y values in this Geometry /// , or a null Envelope if this Geometry is empty. /// /// /// This Geometrys bounding box; if the Geometry /// is empty, Envelope.IsNull will return true. /// public IEnvelope EnvelopeInternal { get { if (envelope == null) { envelope = ComputeEnvelopeInternal(); } return envelope; } } /// /// /// /// public virtual bool IsRectangle { get { // Polygon overrides to check for actual rectangle return false; } } /// /// Returns true if the array contains any null elements. /// /// an array to validate. /// true if any of arrays elements are null. public static bool HasNullElements(object[] array) { foreach (object o in array) { if (o == null) { return true; } } return false; } /// /// /// /// /// /// public static bool operator ==(Geometry obj1, IGeometry obj2) { return Equals(obj1, obj2); } /// /// /// /// /// /// public static bool operator !=(Geometry obj1, IGeometry obj2) { return !(obj1 == obj2); } /// /// Returns the Well-known Text representation of this Geometry. /// For a definition of the Well-known Text format, see the OpenGIS Simple /// Features Specification. /// /// /// The Well-known Text representation of this Geometry. /// public string ToText() { WKTWriter writer = new WKTWriter(); return writer.Write(this); } /// /// Returns the Well-known Binary representation of this Geometry. /// For a definition of the Well-known Binary format, see the OpenGIS Simple /// Features Specification. /// /// The Well-known Binary representation of this Geometry. public byte[] ToBinary() { WKBWriter writer = new WKBWriter(); return writer.Write(this); } /// /// /// /// /// public override bool Equals(object obj) { if (obj == null) { return false; } if (ReferenceEquals(obj, this)) { return true; } if (GetType().Namespace != obj.GetType().Namespace) { return false; } if (obj is IGeometry) { return Equals((IGeometry) obj); } return false; } /// /// /// public override int GetHashCode() { if (hashcode == -1) { int result = 17; foreach (Coordinate coord in Coordinates) { result = 37*result + coord.X.GetHashCode(); result = 37*result + coord.Y.GetHashCode(); result = 37*result + coord.Z.GetHashCode(); } hashcode = result; } return hashcode; } /// /// Returns the Well-known Text representation of this Geometry. /// For a definition of the Well-known Text format, see the OpenGIS Simple /// Features Specification. /// /// /// The Well-known Text representation of this Geometry. /// public override string ToString() { return ToText(); } /// /// Returns an element Geometry from a GeometryCollection, /// or this, if the geometry is not a collection. /// /// The index of the geometry element. /// The n'th geometry contained in this geometry. public virtual IGeometry GetGeometryN(int n) { return this; } /// /// Returns the minimum distance between this Geometry /// and the Geometry g. /// /// The Geometry from which to compute the distance. Assumed not to be null. public double Distance(IGeometry g) { return DistanceOp.Distance(this, g); } /// /// Tests whether the distance from this Geometry /// to another is less than or equal to a specified value. /// /// the Geometry to check the distance to. /// the distance value to compare. /// true if the geometries are less than distance apart. public bool IsWithinDistance(IGeometry geom, double distance) { double envDist = EnvelopeInternal.Distance(geom.EnvelopeInternal); if (envDist > distance) { return false; } return DistanceOp.IsWithinDistance(this, geom, distance); } /// /// Notifies this Geometry that its Coordinates have been changed by an external /// party (using a CoordinateFilter, for example). The Geometry will flush /// and/or update any information it has cached (such as its Envelope). /// public void GeometryChanged() { Apply(new GeometryChangedFilter()); } /// /// Notifies this Geometry that its Coordinates have been changed by an external /// party. When GeometryChanged is called, this method will be called for /// this Geometry and its component Geometries. /// public void GeometryChangedAction() { envelope = null; hashcode = -1; } /// /// Returns true if the DE-9IM intersection matrix for the two /// Geometrys is FF*FF****. /// /// The Geometry with which to compare this Geometry. /// true if the two Geometrys are disjoint. public bool Disjoint(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Intersects(g.EnvelopeInternal)) { return true; } return Relate(g).IsDisjoint(); } /// /// Returns true if the DE-9IM intersection matrix for the two /// Geometrys is FT*******, F**T***** or F***T****. /// /// The Geometry with which to compare this Geometry. /// /// true if the two Geometrys touch; /// Returns false if both Geometrys are points. /// public bool Touches(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Intersects(g.EnvelopeInternal)) { return false; } return Relate(g).IsTouches(Dimension, g.Dimension); } /// /// Returns true if disjoint returns false. /// /// The Geometry with which to compare this Geometry. /// true if the two Geometrys intersect. public bool Intersects(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Intersects(g.EnvelopeInternal)) { return false; } // optimizations for rectangle arguments if (IsRectangle) { return RectangleIntersects.Intersects((IPolygon) this, g); } if (g.IsRectangle) { return RectangleIntersects.Intersects((IPolygon) g, this); } return Relate(g).IsIntersects(); } /// /// Returns true if the DE-9IM intersection matrix for the two /// Geometrys 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 Geometry with which to compare this Geometry. /// /// true if the two Geometrys cross. /// For this function to return true, the Geometry /// s must be a point and a curve; a point and a surface; two curves; or a /// curve and a surface. /// public bool Crosses(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Intersects(g.EnvelopeInternal)) { return false; } return Relate(g).IsCrosses(Dimension, g.Dimension); } /// /// Returns true if the DE-9IM intersection matrix for the two /// Geometrys is T*F**F***. /// /// The Geometry with which to compare this Geometry. /// true if this Geometry is within other. public bool Within(IGeometry g) { return g.Contains(this); } /// /// Returns true if other.within(this) returns true. /// /// The Geometry with which to compare this Geometry. /// true if this Geometry contains other. public bool Contains(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Contains(g.EnvelopeInternal)) { return false; } // optimizations for rectangle arguments if (IsRectangle) { return RectangleContains.Contains((IPolygon) this, g); } // general case return Relate(g).IsContains(); } /// /// Returns true if the DE-9IM intersection matrix for the two /// Geometrys is /// T*T***T** (for two points or two surfaces) /// 1*T***T** (for two curves). /// /// The Geometry with which to compare this Geometry. /// /// true if the two Geometrys overlap. /// For this function to return true, the Geometry /// s must be two points, two curves or two surfaces. /// public bool Overlaps(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Intersects(g.EnvelopeInternal)) { return false; } return Relate(g).IsOverlaps(Dimension, g.Dimension); } /// /// Returns true if this geometry covers the specified geometry. /// /// The Covers predicate has the following equivalent definitions: /// - Every point of the other geometry is a point of this geometry. /// - The DE-9IM Intersection Matrix for the two geometries is T*****FF* or *T****FF* or ***T**FF* or ****T*FF*. /// - g.CoveredBy(this) (Covers is the inverse of CoveredBy). /// /// Note the difference between Covers and Contains: Covers is a more inclusive relation. /// In particular, unlike Contains it does not distinguish between /// points in the boundary and in the interior of geometries. /// /// /// For most situations, Covers should be used in preference to Contains. /// As an added benefit, Covers is more amenable to optimization, and hence should be more performant. /// /// The Geometry with which to compare this Geometry /// true if this Geometry covers /// /// public bool Covers(IGeometry g) { // short-circuit test if (!EnvelopeInternal.Contains(g.EnvelopeInternal)) { return false; } // optimization for rectangle arguments if (IsRectangle) { return EnvelopeInternal.Contains(g.EnvelopeInternal); } return Relate(g).IsCovers(); } /// /// Returns true if this geometry is covered by the specified geometry. /// /// The CoveredBy predicate has the following equivalent definitions: /// - Every point of this geometry is a point of the other geometry. /// - The DE-9IM Intersection Matrix for the two geometries is T*F**F*** or *TF**F*** or **FT*F*** or **F*TF***. /// - g.Covers(this) (CoveredBy is the inverse of Covers). /// /// Note the difference between CoveredBy and Within: CoveredBy is a more inclusive relation. /// /// The Geometry with which to compare this Geometry. /// true if this Geometry is covered by . /// /// public bool CoveredBy(IGeometry g) { return g.Covers(this); } /// /// Returns true if the elements in the DE-9IM intersection /// matrix for the two Geometrys match the elements in intersectionPattern /// , which may be: /// 0 /// 1 /// 2 /// T ( = 0, 1 or 2) /// F ( = -1) /// * ( = -1, 0, 1 or 2) /// For more information on the DE-9IM, see the OpenGIS Simple Features /// Specification. /// /// The Geometry with which to compare this Geometry. /// The pattern against which to check the intersection matrix for the two Geometrys. /// true if the DE-9IM intersection matrix for the two Geometrys match intersectionPattern. public bool Relate(IGeometry g, string intersectionPattern) { return Relate(g).Matches(intersectionPattern); } /// /// Returns the DE-9IM intersection matrix for the two Geometrys. /// /// The Geometry with which to compare this Geometry /// /// A matrix describing the intersections of the interiors, /// boundaries and exteriors of the two Geometrys. /// public IntersectionMatrix Relate(IGeometry g) { CheckNotGeometryCollection(this); CheckNotGeometryCollection(g); return RelateOp.Relate(this, g); } /// /// Returns true if the DE-9IM intersection matrix for the two /// Geometrys is T*F**FFF*. /// /// The Geometry with which to compare this Geometry. /// true if the two Geometrys are equal. public bool Equals(IGeometry g) { if (ReferenceEquals(g, this)) { return true; } if (IsEmpty && g.IsEmpty) { return true; } // Short-circuit test if (!EnvelopeInternal.Intersects(g.EnvelopeInternal)) { return false; } // We use an alternative method for compare GeometryCollections (but not subclasses!), if (isGeometryCollection(this) || isGeometryCollection(g)) { return CompareGeometryCollections(this, g); } // Use RelateOp comparation method return Relate(g).IsEquals(Dimension, g.Dimension); } /// /// /// /// public string AsText() { return ToText(); } /// /// /// /// public byte[] AsBinary() { return ToBinary(); } /// /// Returns a buffer region around this Geometry having the given width. /// The buffer of a Geometry is the Minkowski sum or difference of the Geometry with a disc of radius distance. /// /// /// The width of the buffer, interpreted according to the /// PrecisionModel of the Geometry. /// /// /// All points whose distance from this Geometry /// are less than or equal to distance. /// public IGeometry Buffer(double distance) { return BufferOp.Buffer(this, distance); } /// /// Returns a buffer region around this Geometry having the given width. /// The buffer of a Geometry is the Minkowski sum or difference of the Geometry with a disc of radius distance. /// /// /// The width of the buffer, interpreted according to the /// PrecisionModel of the Geometry. /// /// Cap Style to use for compute buffer. /// /// All points whose distance from this Geometry /// are less than or equal to distance. /// public IGeometry Buffer(double distance, BufferStyle endCapStyle) { return BufferOp.Buffer(this, distance, endCapStyle); } /// /// Returns a buffer region around this Geometry having the given /// width and with a specified number of segments used to approximate curves. /// The buffer of a Geometry is the Minkowski sum of the Geometry with /// a disc of radius distance. Curves in the buffer polygon are /// approximated with line segments. This method allows specifying the /// accuracy of that approximation. /// /// /// The width of the buffer, interpreted according to the /// PrecisionModel of the Geometry. /// /// The number of segments to use to approximate a quadrant of a circle. /// /// All points whose distance from this Geometry /// are less than or equal to distance. /// public IGeometry Buffer(double distance, int quadrantSegments) { return BufferOp.Buffer(this, distance, quadrantSegments); } /// /// Returns a buffer region around this Geometry having the given /// width and with a specified number of segments used to approximate curves. /// The buffer of a Geometry is the Minkowski sum of the Geometry with /// a disc of radius distance. Curves in the buffer polygon are /// approximated with line segments. This method allows specifying the /// accuracy of that approximation. /// /// /// The width of the buffer, interpreted according to the /// PrecisionModel of the Geometry. /// /// The number of segments to use to approximate a quadrant of a circle. /// Cap Style to use for compute buffer. /// /// All points whose distance from this Geometry /// are less than or equal to distance. /// public IGeometry Buffer(double distance, int quadrantSegments, BufferStyle endCapStyle) { return BufferOp.Buffer(this, distance, quadrantSegments, endCapStyle); } /// /// Returns the smallest convex Polygon that contains all the /// points in the Geometry. This obviously applies only to Geometry /// s which contain 3 or more points. /// /// the minimum-area convex polygon containing this Geometry's points. public virtual IGeometry ConvexHull() { return (new ConvexHull(this)).GetConvexHull(); } /// /// Returns a Geometry representing the points shared by this /// Geometry and other. /// /// The Geometry with which to compute the intersection. /// The points common to the two Geometrys. public IGeometry Intersection(IGeometry other) { // Special case: if one input is empty ==> empty if (IsEmpty) { return Factory.CreateGeometryCollection(null); } if (other.IsEmpty) { return Factory.CreateGeometryCollection(null); } CheckNotGeometryCollection(this); CheckNotGeometryCollection(other); return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.Intersection); } /// /// Returns a Geometry representing all the points in this Geometry /// and other. /// /// The Geometry with which to compute the union. /// A set combining the points of this Geometry and the points of other. public IGeometry Union(IGeometry other) { // Special case: if either input is empty ==> other input if (IsEmpty) { return (IGeometry) other.Clone(); } if (other.IsEmpty) { return (IGeometry) Clone(); } CheckNotGeometryCollection(this); CheckNotGeometryCollection(other); return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.Union); } /// /// Returns a Geometry representing the points making up this /// Geometry that do not make up other. This method /// returns the closure of the resultant Geometry. /// /// The Geometry with which to compute the difference. /// The point set difference of this Geometry with other. public IGeometry Difference(IGeometry other) { // Special case: if A.isEmpty ==> empty; if B.isEmpty ==> A if (IsEmpty) { return Factory.CreateGeometryCollection(null); } if (other.IsEmpty) { return (IGeometry) Clone(); } CheckNotGeometryCollection(this); CheckNotGeometryCollection(other); return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.Difference); } /// /// Returns a set combining the points in this Geometry not in /// other, and the points in other not in this /// Geometry. This method returns the closure of the resultant /// Geometry. /// /// The Geometry with which to compute the symmetric difference. /// The point set symmetric difference of this Geometry with other. public IGeometry SymmetricDifference(IGeometry other) { // Special case: if either input is empty ==> other input if (IsEmpty) { return (IGeometry) other.Clone(); } if (other.IsEmpty) { return (IGeometry) Clone(); } CheckNotGeometryCollection(this); CheckNotGeometryCollection(other); return SnapIfNeededOverlayOp.Overlay(this, other, SpatialFunction.SymDifference); } /// /// Returns true if the two Geometrys are exactly equal, /// up to a specified tolerance. /// Two Geometries are exactly within a tolerance equal iff: /// they have the same class, /// they have the same values of Coordinates, /// within the given tolerance distance, in their internal /// Coordinate lists, in exactly the same order. /// If this and the other Geometrys are /// composites and any children are not Geometrys, returns /// false. /// /// The Geometry with which to compare this Geometry /// Distance at or below which two Coordinates will be considered equal. /// /// true if this and the other Geometry /// are of the same class and have equal internal data. /// public abstract bool EqualsExact(IGeometry other, double tolerance); /// /// Returns true if the two Geometrys are exactly equal. /// Two Geometries are exactly equal iff: /// they have the same class, /// they have the same values of Coordinates in their internal /// Coordinate lists, in exactly the same order. /// If this and the other Geometrys are /// composites and any children are not Geometrys, returns /// false. /// This provides a stricter test of equality than equals. /// /// The Geometry with which to compare this Geometry. /// /// true if this and the other Geometry /// are of the same class and have equal internal data. /// public bool EqualsExact(IGeometry other) { return EqualsExact(other, 0); } /// /// Performs an operation with or on this Geometry's /// coordinates. If you are using this method to modify the point, be sure /// to call GeometryChanged() afterwards. Note that you cannot use this /// method to /// modify this Geometry if its underlying CoordinateSequence's Get method /// returns a copy of the Coordinate, rather than the actual Coordinate stored /// (if it even stores Coordinates at all). /// /// The filter to apply to this Geometry's coordinates public abstract void Apply(ICoordinateFilter filter); /// /// Performs an operation with or on this Geometry and its /// subelement Geometrys (if any). /// Only GeometryCollections and subclasses /// have subelement Geometry's. /// /// /// The filter to apply to this Geometry (and /// its children, if it is a GeometryCollection). /// public abstract void Apply(IGeometryFilter filter); /// /// Performs an operation with or on this Geometry and its /// component Geometry's. Only GeometryCollections and /// Polygons have component Geometry's; for Polygons they are the LinearRings /// of the shell and holes. /// /// The filter to apply to this Geometry. public abstract void Apply(IGeometryComponentFilter filter); /// /// /// /// public virtual object Clone() { Geometry clone = (Geometry) MemberwiseClone(); if (clone.envelope != null) { clone.envelope = new Envelope(clone.envelope); } return clone; } /// /// Converts this Geometry to normal form (or /// canonical form ). Normal form is a unique representation for Geometry /// s. It can be used to test whether two Geometrys are equal /// in a way that is independent of the ordering of the coordinates within /// them. Normal form equality is a stronger condition than topological /// equality, but weaker than pointwise equality. The definitions for normal /// form use the standard lexicographical ordering for coordinates. "Sorted in /// order of coordinates" means the obvious extension of this ordering to /// sequences of coordinates. /// public abstract void Normalize(); /// /// Returns whether this Geometry is greater than, equal to, /// or less than another Geometry. /// If their classes are different, they are compared using the following /// ordering: /// Point (lowest), /// MultiPoint, /// LineString, /// LinearRing, /// MultiLineString, /// Polygon, /// MultiPolygon, /// GeometryCollection (highest). /// If the two Geometrys have the same class, their first /// elements are compared. If those are the same, the second elements are /// compared, etc. /// /// A Geometry with which to compare this Geometry /// /// A positive number, 0, or a negative number, depending on whether /// this object is greater than, equal to, or less than o, as /// defined in "Normal Form For Geometry" in the NTS Technical /// Specifications. /// public int CompareTo(object o) { return CompareTo((IGeometry) o); } /// /// /// /// /// public int CompareTo(IGeometry geom) { Geometry other = (Geometry) geom; if (ClassSortIndex != other.ClassSortIndex) { return ClassSortIndex - other.ClassSortIndex; } if (IsEmpty && other.IsEmpty) { return 0; } if (IsEmpty) { return -1; } if (other.IsEmpty) { return 1; } return CompareToSameClass(geom); } /// /// Returns whether this Geometry is greater than, equal to, /// or less than another Geometry having the same class. /// /// A Geometry having the same class as this Geometry. /// /// A positive number, 0, or a negative number, depending on whether /// this object is greater than, equal to, or less than o, as /// defined in "Normal Form For Geometry" in the NTS Technical /// Specifications. /// protected internal abstract int CompareToSameClass(object o); /// /// Returns true if the array contains any non-empty Geometrys. /// /// an array of Geometrys; no elements may be null /// /// true if any of the Geometrys /// IsEmpty methods return false. /// protected static bool HasNonEmptyElements(IGeometry[] geometries) { foreach (IGeometry g in geometries) { if (!g.IsEmpty) { return true; } } return false; } /// /// Returns whether the two Geometrys are equal, from the point /// of view of the EqualsExact method. Called by EqualsExact /// . In general, two Geometry classes are considered to be /// "equivalent" only if they are the same class. An exception is LineString /// , which is considered to be equivalent to its subclasses. /// /// The Geometry with which to compare this Geometry for equality. /// /// true if the classes of the two Geometry /// s are considered to be equal by the equalsExact method. /// protected bool IsEquivalentClass(IGeometry other) { return GetType().FullName == other.GetType().FullName; } /// /// Throws an exception if g's class is GeometryCollection. /// (its subclasses do not trigger an exception). /// /// The Geometry to check. /// /// if g is a GeometryCollection, but not one of its subclasses. /// protected void CheckNotGeometryCollection(IGeometry g) { if (isGeometryCollection(g)) { throw new ArgumentException("This method does not support GeometryCollection arguments"); } } /// /// Returns the minimum and maximum x and y values in this Geometry /// , or a null Envelope if this Geometry is empty. /// Unlike EnvelopeInternal, this method calculates the Envelope /// each time it is called; EnvelopeInternal caches the result /// of this method. /// /// /// This Geometrys bounding box; if the Geometry /// is empty, Envelope.IsNull will return true. /// protected abstract IEnvelope ComputeEnvelopeInternal(); /// /// Returns the first non-zero result of CompareTo encountered as /// the two Collections are iterated over. If, by the time one of /// the iterations is complete, no non-zero result has been encountered, /// returns 0 if the other iteration is also complete. If b /// completes before a, a positive number is returned; if a /// before b, a negative number. /// /// A Collection of IComparables. /// A Collection of IComparables. /// The first non-zero compareTo result, if any; otherwise, zero. protected int Compare(ArrayList a, ArrayList b) { IEnumerator i = a.GetEnumerator(); IEnumerator j = b.GetEnumerator(); while (i.MoveNext() && j.MoveNext()) { IComparable aElement = (IComparable) i.Current; IComparable bElement = (IComparable) j.Current; int comparison = aElement.CompareTo(bElement); if (comparison != 0) { return comparison; } } if (i.MoveNext()) { return 1; } if (j.MoveNext()) { return -1; } return 0; } /// /// /// /// /// /// /// protected bool Equal(ICoordinate a, ICoordinate b, double tolerance) { if (tolerance == 0) { return a.Equals(b); } return a.Distance(b) <= tolerance; } /// /// /// private int ClassSortIndex { get { for (int i = 0; i < SortedClasses.Length; i++) { if (GetType().Equals(SortedClasses[i])) { return i; } } Assert.ShouldNeverReachHere(String.Format("Class not supported: {0}", GetType().FullName)); return -1; } } /// /// /// /// /// /// private static bool CompareGeometryCollections(IGeometry obj1, IGeometry obj2) { IGeometryCollection coll1 = obj1 as IGeometryCollection; IGeometryCollection coll2 = obj2 as IGeometryCollection; if (coll1 == null || coll2 == null) { return false; } // Short-circuit test if (coll1.NumGeometries != coll2.NumGeometries) { return false; } // Deep test for (int i = 0; i < coll1.NumGeometries; i++) { IGeometry geom1 = coll1[i]; IGeometry geom2 = coll2[i]; if (!geom1.Equals(geom2)) { return false; } } return true; } /// /// Returns true if g's class is GeometryCollection. /// (its subclasses do not trigger an exception). /// /// The Geometry to check. /// /// If g is a GeometryCollection, but not one of its subclasses. /// private bool isGeometryCollection(IGeometry g) { return g.GetType().Name == "GeometryCollection" && g.GetType().Namespace == GetType().Namespace; } /// /// /// /// /// /// private IPoint CreatePointFromInternalCoord(ICoordinate coord, IGeometry exemplar) { exemplar.PrecisionModel.MakePrecise(coord); return exemplar.Factory.CreatePoint(coord); } private class GeometryChangedFilter : IGeometryComponentFilter { public void Filter(IGeometry geom) { geom.GeometryChangedAction(); } }; /* END ADDED BY MPAUL42: monoGIS team */ } }