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 { /// /// 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; } /* * the minimum x-coordinate */ private double minx; /* * the maximum x-coordinate */ private double maxx; /* * the minimum y-coordinate */ private double miny; /* * the maximum y-coordinate */ private double maxy; /// /// 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); } /// /// 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) { this.minx = env.MinX; this.maxx = env.MaxX; this.miny = env.MinY; this.maxy = env.MaxY; } /// /// Makes this Envelope a "null" envelope.. /// public void SetToNull() { minx = 0; maxx = -1; miny = 0; maxy = -1; } /// /// 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 { return minx; } } /// /// 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 { return maxx; } } /// /// 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 { return miny; } } /// /// 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 { return maxy; } } /// /// 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); } /// /// 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); } } /// /// /// /// /// 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 override bool Equals(object other) { if (other == null) return false; if (!(other is Envelope)) return false; return Equals((IEnvelope) other); } /// /// /// /// /// 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; } /// /// /// 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; } /// /// Return HashCode. /// /// Value from HashCode computation. private static int GetHashCode(double value) { long f = BitConverter.DoubleToInt64Bits(value); return (int) (f ^ (f >> 32)); } /// /// /// /// /// /// public static bool operator ==(Envelope obj1, Envelope obj2) { return Equals(obj1, obj2); } /// /// /// /// /// /// public static bool operator !=(Envelope obj1, Envelope obj2) { return !(obj1 == obj2); } /// /// /// /// public override string ToString() { return "Env[" + minx + " : " + maxx + ", " + miny + " : " + maxy + "]"; } /// /// 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(); } /* 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; } } /// /// Creates a deep copy of the current envelope. /// /// public IEnvelope Clone() { return new Envelope(minx, maxx, miny, maxy); } /// /// 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) this.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 (this.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 = (this.Width * perCent / 100); double h = (this.Height * perCent / 100); SetCentre(w, h); } /* END ADDED BY MPAUL42: monoGIS team */ } }