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 */
}
}