using System; using GeoAPI.Geometries; namespace GisSharpBlog.NetTopologySuite.Geometries { /// /// Defines a rectangular region of the 2D coordinate plane. /// It is often used to represent the bounding box of a Geometry, /// e.g. the minimum and maximum x and y values of the Coordinates. /// Note that Envelopes support infinite or half-infinite regions, by using the values of /// Double.PositiveInfinity and Double.NegativeInfinity. /// When Envelope objects are created or initialized, /// the supplies extent values are automatically sorted into the correct order. /// [Serializable] public class Envelope : IEnvelope { /* * the minimum x-coordinate */ /* * the maximum x-coordinate */ /* * the minimum y-coordinate */ /* * the maximum y-coordinate */ /// /// Creates a null Envelope. /// public Envelope() { Init(); } /// /// Creates an Envelope for a region defined by maximum and minimum values. /// /// The first x-value. /// The second x-value. /// The first y-value. /// The second y-value. public Envelope(double x1, double x2, double y1, double y2) { Init(x1, x2, y1, y2); } /// /// Creates an Envelope for a region defined by two Coordinates. /// /// The first Coordinate. /// The second Coordinate. public Envelope(ICoordinate p1, ICoordinate p2) { Init(p1, p2); } /// /// Creates an Envelope for a region defined by a single Coordinate. /// /// The Coordinate. public Envelope(ICoordinate p) { Init(p); } /// /// Create an Envelope from an existing Envelope. /// /// The Envelope to initialize from. public Envelope(IEnvelope env) { Init(env); } /// /// Returns true if this Envelope is a "null" envelope. /// /// /// true if this Envelope is uninitialized /// or is the envelope of the empty point. /// public bool IsNull { get { return MaxX < MinX; } } /// /// Returns the difference between the maximum and minimum x values. /// /// max x - min x, or 0 if this is a null Envelope. public double Width { get { if (IsNull) { return 0; } return MaxX - MinX; } } /// /// Returns the difference between the maximum and minimum y values. /// /// max y - min y, or 0 if this is a null Envelope. public double Height { get { if (IsNull) { return 0; } return MaxY - MinY; } } /// /// Returns the Envelopes minimum x-value. min x > max x /// indicates that this is a null Envelope. /// /// The minimum x-coordinate. public double MinX { get; private set; } /// /// Returns the Envelopes maximum x-value. min x > max x /// indicates that this is a null Envelope. /// /// The maximum x-coordinate. public double MaxX { get; private set; } /// /// Returns the Envelopes minimum y-value. min y > max y /// indicates that this is a null Envelope. /// /// The minimum y-coordinate. public double MinY { get; private set; } /// /// Returns the Envelopes maximum y-value. min y > max y /// indicates that this is a null Envelope. /// /// The maximum y-coordinate. public double MaxY { get; private set; } /// /// Computes the coordinate of the centre of this envelope (as long as it is non-null). /// /// /// The centre coordinate of this envelope, /// or null if the envelope is null. /// . public ICoordinate Centre { get { if (IsNull) { return null; } return new Coordinate((MinX + MaxX)/2.0, (MinY + MaxY)/2.0); } } /* BEGIN ADDED BY MPAUL42: monoGIS team */ /// /// Returns the area of the envelope. /// public double Area { get { double area = 1; area = area*(MaxX - MinX); area = area*(MaxY - MinY); return area; } } /// /// Test the point q to see whether it intersects the Envelope /// defined by p1-p2. /// /// One extremal point of the envelope. /// Another extremal point of the envelope. /// Point to test for intersection. /// true if q intersects the envelope p1-p2. public static bool Intersects(ICoordinate p1, ICoordinate p2, ICoordinate q) { if (((q.X >= (p1.X < p2.X ? p1.X : p2.X)) && (q.X <= (p1.X > p2.X ? p1.X : p2.X))) && ((q.Y >= (p1.Y < p2.Y ? p1.Y : p2.Y)) && (q.Y <= (p1.Y > p2.Y ? p1.Y : p2.Y)))) { return true; } return false; } /// /// Test the envelope defined by p1-p2 for intersection /// with the envelope defined by q1-q2 /// /// One extremal point of the envelope Point. /// Another extremal point of the envelope Point. /// One extremal point of the envelope Q. /// Another extremal point of the envelope Q. /// true if Q intersects Point public static bool Intersects(ICoordinate p1, ICoordinate p2, ICoordinate q1, ICoordinate q2) { double minq = Math.Min(q1.X, q2.X); double maxq = Math.Max(q1.X, q2.X); double minp = Math.Min(p1.X, p2.X); double maxp = Math.Max(p1.X, p2.X); if (minp > maxq) { return false; } if (maxp < minq) { return false; } minq = Math.Min(q1.Y, q2.Y); maxq = Math.Max(q1.Y, q2.Y); minp = Math.Min(p1.Y, p2.Y); maxp = Math.Max(p1.Y, p2.Y); if (minp > maxq) { return false; } if (maxp < minq) { return false; } return true; } /// /// /// /// /// /// public static bool operator ==(Envelope obj1, Envelope obj2) { return Equals(obj1, obj2); } /// /// /// /// /// /// public static bool operator !=(Envelope obj1, Envelope obj2) { return !(obj1 == obj2); } /// /// Creates a deep copy of the current envelope. /// /// public IEnvelope Clone() { return new Envelope(MinX, MaxX, MinY, MaxY); } /// /// /// /// /// public override bool Equals(object other) { if (other == null) { return false; } if (!(other is Envelope)) { return false; } return Equals((IEnvelope) other); } /// /// /// public override int GetHashCode() { int result = 17; result = 37*result + GetHashCode(MinX); result = 37*result + GetHashCode(MaxX); result = 37*result + GetHashCode(MinY); result = 37*result + GetHashCode(MaxY); return result; } /// /// /// /// public override string ToString() { return "Env[" + MinX + " : " + MaxX + ", " + MinY + " : " + MaxY + "]"; } /// /// Initialize to a null Envelope. /// public void Init() { SetToNull(); } /// /// Initialize an Envelope for a region defined by maximum and minimum values. /// /// The first x-value. /// The second x-value. /// The first y-value. /// The second y-value. public void Init(double x1, double x2, double y1, double y2) { if (x1 < x2) { MinX = x1; MaxX = x2; } else { MinX = x2; MaxX = x1; } if (y1 < y2) { MinY = y1; MaxY = y2; } else { MinY = y2; MaxY = y1; } } /// /// Initialize an Envelope for a region defined by two Coordinates. /// /// The first Coordinate. /// The second Coordinate. public void Init(ICoordinate p1, ICoordinate p2) { Init(p1.X, p2.X, p1.Y, p2.Y); } /// /// Initialize an Envelope for a region defined by a single Coordinate. /// /// The Coordinate. public void Init(ICoordinate p) { Init(p.X, p.X, p.Y, p.Y); } /// /// Initialize an Envelope from an existing Envelope. /// /// The Envelope to initialize from. public void Init(IEnvelope env) { MinX = env.MinX; MaxX = env.MaxX; MinY = env.MinY; MaxY = env.MaxY; } /// /// Makes this Envelope a "null" envelope.. /// public void SetToNull() { MinX = 0; MaxX = -1; MinY = 0; MaxY = -1; } /// /// Expands this envelope by a given distance in all directions. /// Both positive and negative distances are supported. /// /// The distance to expand the envelope. public void ExpandBy(double distance) { ExpandBy(distance, distance); } /// /// Expands this envelope by a given distance in all directions. /// Both positive and negative distances are supported. /// /// The distance to expand the envelope along the the X axis. /// The distance to expand the envelope along the the Y axis. public void ExpandBy(double deltaX, double deltaY) { if (IsNull) { return; } MinX -= deltaX; MaxX += deltaX; MinY -= deltaY; MaxY += deltaY; // check for envelope disappearing if (MinX > MaxX || MinY > MaxY) { SetToNull(); } } /// /// Enlarges the boundary of the Envelope so that it contains (p). /// Does nothing if (p) is already on or within the boundaries. /// /// The Coordinate. public void ExpandToInclude(ICoordinate p) { ExpandToInclude(p.X, p.Y); } /// /// Enlarges the boundary of the Envelope so that it contains /// (x,y). Does nothing if (x,y) is already on or within the boundaries. /// /// The value to lower the minimum x to or to raise the maximum x to. /// The value to lower the minimum y to or to raise the maximum y to. public void ExpandToInclude(double x, double y) { if (IsNull) { MinX = x; MaxX = x; MinY = y; MaxY = y; } else { if (x < MinX) { MinX = x; } if (x > MaxX) { MaxX = x; } if (y < MinY) { MinY = y; } if (y > MaxY) { MaxY = y; } } } /// /// Enlarges the boundary of the Envelope so that it contains /// other. Does nothing if other is wholly on or /// within the boundaries. /// /// the Envelope to merge with. public void ExpandToInclude(IEnvelope other) { if (other == null || other.IsNull) { return; } if (IsNull) { MinX = other.MinX; MaxX = other.MaxX; MinY = other.MinY; MaxY = other.MaxY; } else { if (other.MinX < MinX) { MinX = other.MinX; } if (other.MaxX > MaxX) { MaxX = other.MaxX; } if (other.MinY < MinY) { MinY = other.MinY; } if (other.MaxY > MaxY) { MaxY = other.MaxY; } } } /// /// Translates this envelope by given amounts in the X and Y direction. /// /// The amount to translate along the X axis. /// The amount to translate along the Y axis. public void Translate(double transX, double transY) { if (IsNull) { return; } Init(MinX + transX, MaxX + transX, MinY + transY, MaxY + transY); } /// /// /// /// /// public IEnvelope Intersection(IEnvelope env) { if (IsNull || env.IsNull || !Intersects(env)) { return new Envelope(); } return new Envelope(Math.Max(MinX, env.MinX), Math.Min(MaxX, env.MaxX), Math.Max(MinY, env.MinY), Math.Min(MaxY, env.MaxY)); } /// /// Check if the region defined by other /// overlaps (intersects) the region of this Envelope. /// /// the Envelope which this Envelope is /// being checked for overlapping. /// /// /// true if the Envelopes overlap. /// public bool Intersects(IEnvelope other) { if (IsNull || other.IsNull) { return false; } return !(other.MinX > MaxX || other.MaxX < MinX || other.MinY > MaxY || other.MaxY < MinY); } /// /// Use Intersects instead. In the future, Overlaps may be /// changed to be a true overlap check; that is, whether the intersection is /// two-dimensional. /// /// /// [Obsolete("Use Intersects instead")] public bool Overlaps(IEnvelope other) { return Intersects(other); } /// /// Use Intersects instead. /// /// /// [Obsolete("Use Intersects instead")] public bool Overlaps(ICoordinate p) { return Intersects(p); } /// /// Use Intersects instead. /// /// /// /// [Obsolete("Use Intersects instead")] public bool Overlaps(double x, double y) { return Intersects(x, y); } /// /// Check if the point p overlaps (lies inside) the region of this Envelope. /// /// the Coordinate to be tested. /// true if the point overlaps this Envelope. public bool Intersects(ICoordinate p) { return Intersects(p.X, p.Y); } /// /// Check if the point (x, y) overlaps (lies inside) the region of this Envelope. /// /// the x-ordinate of the point. /// the y-ordinate of the point. /// true if the point overlaps this Envelope. public bool Intersects(double x, double y) { return !(x > MaxX || x < MinX || y > MaxY || y < MinY); } /// /// Returns true if the given point lies in or on the envelope. /// /// the point which this Envelope is /// being checked for containing. /// /// true if the point lies in the interior or /// on the boundary of this Envelope. /// public bool Contains(ICoordinate p) { return Contains(p.X, p.Y); } /// /// Returns true if the given point lies in or on the envelope. /// /// the x-coordinate of the point which this Envelope is /// being checked for containing. /// the y-coordinate of the point which this Envelope is /// being checked for containing. /// true if (x, y) lies in the interior or /// on the boundary of this Envelope. public bool Contains(double x, double y) { return x >= MinX && x <= MaxX && y >= MinY && y <= MaxY; } /// /// Returns true if the Envelope other /// lies wholely inside this Envelope (inclusive of the boundary). /// /// the Envelope which this Envelope is being checked for containing. /// true if other is contained in this Envelope. public bool Contains(IEnvelope other) { if (IsNull || other.IsNull) { return false; } return other.MinX >= MinX && other.MaxX <= MaxX && other.MinY >= MinY && other.MaxY <= MaxY; } /// /// Computes the distance between this and another /// Envelope. /// The distance between overlapping Envelopes is 0. Otherwise, the /// distance is the Euclidean distance between the closest points. /// /// The distance between this and another Envelope. public double Distance(IEnvelope env) { if (Intersects(env)) { return 0; } double dx = 0.0; if (MaxX < env.MinX) { dx = env.MinX - MaxX; } if (MinX > env.MaxX) { dx = MinX - env.MaxX; } double dy = 0.0; if (MaxY < env.MinY) { dy = env.MinY - MaxY; } if (MinY > env.MaxY) { dy = MinY - env.MaxY; } // if either is zero, the envelopes overlap either vertically or horizontally if (dx == 0.0) { return dy; } if (dy == 0.0) { return dx; } return Math.Sqrt(dx*dx + dy*dy); } /// /// /// /// /// public bool Equals(IEnvelope other) { if (IsNull) { return other.IsNull; } if (other == null) { return false; } return MaxX == other.MaxX && MaxY == other.MaxY && MinX == other.MinX && MinY == other.MinY; } /// /// /// /// /// public int CompareTo(object other) { return CompareTo((IEnvelope) other); } /// /// /// /// /// public int CompareTo(IEnvelope other) { if (IsNull && other.IsNull) { return 0; } else if (!IsNull && other.IsNull) { return 1; } else if (IsNull && !other.IsNull) { return -1; } if (Area > other.Area) { return 1; } if (Area < other.Area) { return -1; } return 0; } /// /// Creates a new object that is a copy of the current instance. /// /// A new object that is a copy of this instance. object ICloneable.Clone() { return Clone(); } /// /// Calculates the union of the current box and the given point. /// public IEnvelope Union(IPoint point) { return Union(point.Coordinate); } /// /// Calculates the union of the current box and the given coordinate. /// public IEnvelope Union(ICoordinate coord) { Envelope env = (Envelope) Clone(); env.ExpandToInclude(coord); return env; } /// /// Calculates the union of the current box and the given box. /// public IEnvelope Union(IEnvelope box) { if (box.IsNull) { return this; } if (IsNull) { return box; } return new Envelope(Math.Min(MinX, box.MinX), Math.Max(MaxX, box.MaxX), Math.Min(MinY, box.MinY), Math.Max(MaxY, box.MaxY)); } /// /// Moves the envelope to the indicated coordinate. /// /// The new centre coordinate. public void SetCentre(ICoordinate centre) { SetCentre(centre, Width, Height); } /// /// Moves the envelope to the indicated point. /// /// The new centre point. public void SetCentre(IPoint centre) { SetCentre(centre.Coordinate, Width, Height); } /// /// Resizes the envelope to the indicated point. /// /// The new width. /// The new height. public void SetCentre(double width, double height) { SetCentre(Centre, width, height); } /// /// Moves and resizes the current envelope. /// /// The new centre point. /// The new width. /// The new height. public void SetCentre(IPoint centre, double width, double height) { SetCentre(centre.Coordinate, width, height); } /// /// Moves and resizes the current envelope. /// /// The new centre coordinate. /// The new width. /// The new height. public void SetCentre(ICoordinate centre, double width, double height) { MinX = centre.X - (width/2); MaxX = centre.X + (width/2); MinY = centre.Y - (height/2); MaxY = centre.Y + (height/2); } /// /// Zoom the box. /// Possible values are e.g. 50 (to zoom in a 50%) or -50 (to zoom out a 50%). /// /// /// Negative do Envelope smaller. /// Positive do Envelope bigger. /// /// /// perCent = -50 compact the envelope a 50% (make it smaller). /// perCent = 200 enlarge envelope by 2. /// public void Zoom(double perCent) { double w = (Width*perCent/100); double h = (Height*perCent/100); SetCentre(w, h); } /// /// Return HashCode. /// /// Value from HashCode computation. private static int GetHashCode(double value) { long f = BitConverter.DoubleToInt64Bits(value); return (int) (f ^ (f >> 32)); } /* END ADDED BY MPAUL42: monoGIS team */ } }