diff --git a/SharpSbn/DataStructures/Envelope.cs b/SharpSbn/DataStructures/Envelope.cs
index 2cc0bed..04c3e04 100644
--- a/SharpSbn/DataStructures/Envelope.cs
+++ b/SharpSbn/DataStructures/Envelope.cs
@@ -1,861 +1,884 @@
-#if !UseGeoAPI
-using System;
-using System.Globalization;
-using System.Text;
-
-namespace SharpSbn.DataStructures
-{
- public class Coordinate
- {
- public double X;
- public double Y;
-
- public Coordinate(double x, double y)
- {
- X = x;
- Y = y;
- }
-
- public bool IsNull { get { return double.IsNaN(X); }}
- }
-
- ///
- /// 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.
- ///
- public class Envelope //: IEquatable, IComparable//, IIntersectable, IExpandable
- {
- /////
- ///// 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(Coordinate p1, Coordinate p2, Coordinate q)
- //{
- // return ((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)));
- //}
-
- /////
- ///// Tests whether the envelope defined by p1-p2
- ///// and the envelope defined by q1-q2
- ///// intersect.
- /////
- ///// 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(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2)
- //{
- // double minp = Math.Min(p1.X, p2.X);
- // double maxq = Math.Max(q1.X, q2.X);
- // if (minp > maxq)
- // return false;
-
- // double minq = Math.Min(q1.X, q2.X);
- // double maxp = Math.Max(p1.X, p2.X);
- // if (maxp < minq)
- // return false;
-
- // minp = Math.Min(p1.Y, p2.Y);
- // maxq = Math.Max(q1.Y, q2.Y);
- // if (minp > maxq)
- // return false;
-
- // minq = Math.Min(q1.Y, q2.Y);
- // maxp = Math.Max(p1.Y, p2.Y);
- // 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(Coordinate p1, Coordinate p2)
- {
- Init(p1.X, p2.X, p1.Y, p2.Y);
- }
-
- ///
- /// Creates an Envelope for a region defined by a single Coordinate.
- ///
- /// The Coordinate.
- public Envelope(Coordinate p)
- {
- Init(p.X, p.X, p.Y, p.Y);
- }
-
- ///
- /// Create an Envelope from an existing Envelope.
- ///
- /// The Envelope to initialize from.
- public Envelope(Envelope 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(Coordinate p1, Coordinate 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(Coordinate 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(Envelope 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;
- }
-
- ///
- /// 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; }
- }
-
- ///
- /// Gets the area of this envelope.
- ///
- /// The area of the envelope, or 0.0 if envelope is null
- public double Area
- {
- get
- {
- return Width * Height;
- }
- }
-
- /////
- ///// 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();
- //}
-
- /////
- ///// Gets the minimum extent of this envelope across both dimensions.
- /////
- /////
- //public double MinExtent
- //{
- // get
- // {
- // if (IsNull) return 0.0;
- // double w = Width;
- // double h = Height;
- // if (w < h) return w;
- // return h;
- // }
- //}
-
- /////
- ///// Gets the maximum extent of this envelope across both dimensions.
- /////
- /////
- //public double MaxExtent
- //{
- // get
- // {
- // if (IsNull) return 0.0;
- // double w = Width;
- // double h = Height;
- // if (w > h) return w;
- // return h;
- // }
- //}
-
- /////
- ///// Enlarges this Envelope
so that it contains
- ///// the given .
- ///// Has no effect if the point is already on or within the envelope.
- /////
- ///// The Coordinate.
- //public void ExpandToInclude(Coordinate p)
- //{
- // ExpandToInclude(p.X, p.Y);
- //}
-
- /////
- ///// Enlarges this Envelope so that it contains
- ///// the given .
- /////
- ///// Has no effect if the point is already on or within the envelope.
- ///// 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 this Envelope so that it contains
- ///// the other Envelope.
- ///// Has no effect if other is wholly on or
- ///// within the envelope.
- /////
- ///// the Envelope to expand to include.
- //public void ExpandToInclude(Envelope other)
- //{
- // if (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;
- // }
- //}
-
- /////
- ///// Enlarges this Envelope so that it contains
- ///// the other Envelope.
- ///// Has no effect if other is wholly on or
- ///// within the envelope.
- /////
- ///// the Envelope to expand to include.
- //public Envelope ExpandedBy(Envelope other)
- //{
- // if (other.IsNull)
- // return this;
- // if (IsNull)
- // return other;
-
- // var minx = (other._minx < _minx) ? other._minx : _minx;
- // var maxx = (other._maxx > _maxx) ? other._maxx : _maxx;
- // var miny = (other._miny < _miny) ? other._miny : _miny;
- // var maxy = (other._maxy > _maxy) ? other._maxy : _maxy;
- // return new Envelope(minx, maxx, miny, 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 Coordinate Centre
- {
- get
- {
- return IsNull ? null : new Coordinate((MinX + MaxX) / 2.0, (MinY + MaxY) / 2.0);
- }
- }
-
- ///
- /// Computes the intersection of two s.
- ///
- /// The envelope to intersect with
- ///
- /// A new Envelope representing the intersection of the envelopes (this will be
- /// the null envelope if either argument is null, or they do not intersect
- ///
- public Envelope Intersection(Envelope 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(Envelope 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(Envelope other)
- //{
- // return Intersects(other);
- //}
-
- /////
- ///// Use Intersects instead.
- /////
- /////
- /////
- //[Obsolete("Use Intersects instead")]
- //public bool Overlaps(Coordinate 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(Coordinate 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);
- }
-
- ///
- /// Tests if the Envelope other lies wholely inside this Envelope (inclusive of the boundary).
- ///
- ///
- /// Note that this is not the same definition as the SFS contains,
- /// which would exclude the envelope boundary.
- ///
- /// The Envelope to check
- /// true if other is contained in this Envelope
- ///
- public bool Contains(Envelope other)
- {
- return Covers(other);
- }
-
- ///
- /// Tests if the given point lies in or on the envelope.
- ///
- ///
- /// Note that this is not the same definition as the SFS contains,
- /// which would exclude the envelope boundary.
- ///
- /// 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(Coordinate p)
- {
- return Covers(p);
- }
-
- ///
- /// Tests if the given point lies in or on the envelope.
- ///
- ///
- /// Note that this is not the same definition as the SFS contains, which would exclude the envelope boundary.
- ///
- /// 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 Covers(x, y);
- }
-
- ///
- /// Tests 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 Covers(double x, double y)
- {
- if (IsNull) return false;
- return x >= _minx &&
- x <= _maxx &&
- y >= _miny &&
- y <= _maxy;
- }
-
- ///
- /// Tests 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 Covers(Coordinate p)
- {
- return Covers(p.X, p.Y);
- }
-
- ///
- /// Tests if the Envelope other lies wholely inside this Envelope (inclusive of the boundary).
- ///
- /// the Envelope to check
- /// true if this Envelope covers the other
- public bool Covers(Envelope 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(Envelope env)
- {
- if (Intersects(env))
- return 0;
-
- double dx = 0.0;
-
- if (_maxx < env.MinX)
- dx = env.MinX - _maxx;
- else if (_minx > env.MaxX)
- dx = _minx - env.MaxX;
-
- double dy = 0.0;
-
- if (_maxy < env.MinY)
- dy = env.MinY - _maxy;
- else 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;
-
- var otherE = other as Envelope;
- if (otherE != null)
- return Equals(otherE);
- return false;
- }
-
-
- ///
- public bool Equals(Envelope other)
- {
- if (IsNull)
- return other.IsNull;
-
- return _maxx == other.MaxX && _maxy == other.MaxY &&
- _minx == other.MinX && _miny == other.MinY;
- }
-
- ///
- public int CompareTo(object other)
- {
- return CompareTo((Envelope)other);
- }
-
- ///
- public int CompareTo(Envelope other)
- {
- if (IsNull && other.IsNull)
- return 0;
- if (!IsNull && other.IsNull)
- return 1;
- if (IsNull && !other.IsNull)
- return -1;
-
- if (Area > other.Area)
- return 1;
- if (Area < other.Area)
- return -1;
- return 0;
- }
-
- ///
- public override int GetHashCode()
- {
- var result = 17;
- // ReSharper disable NonReadonlyFieldInGetHashCode
- result = 37 * result + GetHashCode(_minx);
- result = 37 * result + GetHashCode(_maxx);
- result = 37 * result + GetHashCode(_miny);
- result = 37 * result + GetHashCode(_maxy);
- // ReSharper restore NonReadonlyFieldInGetHashCode
- return result;
- }
-
- private static int GetHashCode(double value)
- {
- var 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()
- {
- var sb = new StringBuilder("Env[");
- if (IsNull)
- {
- sb.Append("Null]");
- }
- else
- {
- sb.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:R} : {1:R}, ", _minx, _maxx);
- sb.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:R} : {1:R}]", _miny, _maxy);
- }
- return sb.ToString();
-
- //return "Env[" + _minx + " : " + _maxx + ", " + _miny + " : " + _maxy + "]";
- }
-
- ///
- /// Method to parse an envelope from its value
- ///
- /// The envelope string
- /// The envelope
- public static Envelope Parse(string envelope)
- {
- if (string.IsNullOrEmpty(envelope))
- throw new ArgumentNullException("envelope");
- if (!(envelope.StartsWith("Env[") && envelope.EndsWith("]")))
- throw new ArgumentException("Not a valid envelope string", "envelope");
-
- // test for null
- envelope = envelope.Substring(4, envelope.Length - 5);
- if (envelope == "Null")
- return new Envelope();
-
- // Parse values
- var ordinatesValues = new double[4];
- var ordinateLabel = new[] { "x", "y" };
- var j = 0;
-
- // split into ranges
- var parts = envelope.Split(',');
- if (parts.Length != 2)
- throw new ArgumentException("Does not provide two ranges", "envelope");
-
- foreach (var part in parts)
- {
- // Split int min/max
- var ordinates = part.Split(':');
- if (ordinates.Length != 2)
- throw new ArgumentException("Does not provide just min and max values", "envelope");
-
- if (!double.TryParse(ordinates[0].Trim(), NumberStyles.Number, NumberFormatInfo.InvariantInfo, out ordinatesValues[2 * j]))
- throw new ArgumentException(string.Format("Could not parse min {0}-Ordinate", ordinateLabel[j]), "envelope");
- if (!double.TryParse(ordinates[1].Trim(), NumberStyles.Number, NumberFormatInfo.InvariantInfo, out ordinatesValues[2 * j + 1]))
- throw new ArgumentException(string.Format("Could not parse max {0}-Ordinate", ordinateLabel[j]), "envelope");
- j++;
- }
-
- return new Envelope(ordinatesValues[0], ordinatesValues[1],
- ordinatesValues[2], ordinatesValues[3]);
- }
- }
-}
+#if !UseGeoAPI
+using System;
+using System.Globalization;
+using System.Text;
+
+namespace SharpSbn.DataStructures
+{
+ ///
+ /// A 2D coordinate class
+ ///
+ public class Coordinate
+ {
+ ///
+ /// The x-ordinate value
+ ///
+ public double X;
+ ///
+ /// The y-ordinate value
+ ///
+ public double Y;
+
+ ///
+ /// Creates an instance of this class
+ ///
+ /// The x-ordinate value
+ /// The y-ordinate value
+ public Coordinate(double x, double y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ ///
+ /// Function to check if this coordinate is null
+ ///
+ public bool IsNull { get { return double.IsNaN(X); }}
+ }
+
+ ///
+ /// 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.
+ ///
+ public class Envelope //: IEquatable, IComparable//, IIntersectable, IExpandable
+ {
+ /////
+ ///// 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(Coordinate p1, Coordinate p2, Coordinate q)
+ //{
+ // return ((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)));
+ //}
+
+ /////
+ ///// Tests whether the envelope defined by p1-p2
+ ///// and the envelope defined by q1-q2
+ ///// intersect.
+ /////
+ ///// 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(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2)
+ //{
+ // double minp = Math.Min(p1.X, p2.X);
+ // double maxq = Math.Max(q1.X, q2.X);
+ // if (minp > maxq)
+ // return false;
+
+ // double minq = Math.Min(q1.X, q2.X);
+ // double maxp = Math.Max(p1.X, p2.X);
+ // if (maxp < minq)
+ // return false;
+
+ // minp = Math.Min(p1.Y, p2.Y);
+ // maxq = Math.Max(q1.Y, q2.Y);
+ // if (minp > maxq)
+ // return false;
+
+ // minq = Math.Min(q1.Y, q2.Y);
+ // maxp = Math.Max(p1.Y, p2.Y);
+ // 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(Coordinate p1, Coordinate p2)
+ {
+ Init(p1.X, p2.X, p1.Y, p2.Y);
+ }
+
+ ///
+ /// Creates an Envelope for a region defined by a single Coordinate.
+ ///
+ /// The Coordinate.
+ public Envelope(Coordinate p)
+ {
+ Init(p.X, p.X, p.Y, p.Y);
+ }
+
+ ///
+ /// Create an Envelope from an existing Envelope.
+ ///
+ /// The Envelope to initialize from.
+ public Envelope(Envelope 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(Coordinate p1, Coordinate 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(Coordinate 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(Envelope 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;
+ }
+
+ ///
+ /// 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; }
+ }
+
+ ///
+ /// Gets the area of this envelope.
+ ///
+ /// The area of the envelope, or 0.0 if envelope is null
+ public double Area
+ {
+ get
+ {
+ return Width * Height;
+ }
+ }
+
+ /////
+ ///// 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();
+ //}
+
+ /////
+ ///// Gets the minimum extent of this envelope across both dimensions.
+ /////
+ /////
+ //public double MinExtent
+ //{
+ // get
+ // {
+ // if (IsNull) return 0.0;
+ // double w = Width;
+ // double h = Height;
+ // if (w < h) return w;
+ // return h;
+ // }
+ //}
+
+ /////
+ ///// Gets the maximum extent of this envelope across both dimensions.
+ /////
+ /////
+ //public double MaxExtent
+ //{
+ // get
+ // {
+ // if (IsNull) return 0.0;
+ // double w = Width;
+ // double h = Height;
+ // if (w > h) return w;
+ // return h;
+ // }
+ //}
+
+ /////
+ ///// Enlarges this Envelope
so that it contains
+ ///// the given .
+ ///// Has no effect if the point is already on or within the envelope.
+ /////
+ ///// The Coordinate.
+ //public void ExpandToInclude(Coordinate p)
+ //{
+ // ExpandToInclude(p.X, p.Y);
+ //}
+
+ /////
+ ///// Enlarges this Envelope so that it contains
+ ///// the given .
+ /////
+ ///// Has no effect if the point is already on or within the envelope.
+ ///// 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 this Envelope so that it contains
+ ///// the other Envelope.
+ ///// Has no effect if other is wholly on or
+ ///// within the envelope.
+ /////
+ ///// the Envelope to expand to include.
+ //public void ExpandToInclude(Envelope other)
+ //{
+ // if (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;
+ // }
+ //}
+
+ /////
+ ///// Enlarges this Envelope so that it contains
+ ///// the other Envelope.
+ ///// Has no effect if other is wholly on or
+ ///// within the envelope.
+ /////
+ ///// the Envelope to expand to include.
+ //public Envelope ExpandedBy(Envelope other)
+ //{
+ // if (other.IsNull)
+ // return this;
+ // if (IsNull)
+ // return other;
+
+ // var minx = (other._minx < _minx) ? other._minx : _minx;
+ // var maxx = (other._maxx > _maxx) ? other._maxx : _maxx;
+ // var miny = (other._miny < _miny) ? other._miny : _miny;
+ // var maxy = (other._maxy > _maxy) ? other._maxy : _maxy;
+ // return new Envelope(minx, maxx, miny, 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 Coordinate Centre
+ {
+ get
+ {
+ return IsNull ? null : new Coordinate((MinX + MaxX) / 2.0, (MinY + MaxY) / 2.0);
+ }
+ }
+
+ ///
+ /// Computes the intersection of two s.
+ ///
+ /// The envelope to intersect with
+ ///
+ /// A new Envelope representing the intersection of the envelopes (this will be
+ /// the null envelope if either argument is null, or they do not intersect
+ ///
+ public Envelope Intersection(Envelope 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(Envelope 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(Envelope other)
+ //{
+ // return Intersects(other);
+ //}
+
+ /////
+ ///// Use Intersects instead.
+ /////
+ /////
+ /////
+ //[Obsolete("Use Intersects instead")]
+ //public bool Overlaps(Coordinate 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(Coordinate 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);
+ }
+
+ ///
+ /// Tests if the Envelope other lies wholely inside this Envelope (inclusive of the boundary).
+ ///
+ ///
+ /// Note that this is not the same definition as the SFS contains,
+ /// which would exclude the envelope boundary.
+ ///
+ /// The Envelope to check
+ /// true if other is contained in this Envelope
+ ///
+ public bool Contains(Envelope other)
+ {
+ return Covers(other);
+ }
+
+ ///
+ /// Tests if the given point lies in or on the envelope.
+ ///
+ ///
+ /// Note that this is not the same definition as the SFS contains,
+ /// which would exclude the envelope boundary.
+ ///
+ /// 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(Coordinate p)
+ {
+ return Covers(p);
+ }
+
+ ///
+ /// Tests if the given point lies in or on the envelope.
+ ///
+ ///
+ /// Note that this is not the same definition as the SFS contains, which would exclude the envelope boundary.
+ ///
+ /// 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 Covers(x, y);
+ }
+
+ ///
+ /// Tests 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 Covers(double x, double y)
+ {
+ if (IsNull) return false;
+ return x >= _minx &&
+ x <= _maxx &&
+ y >= _miny &&
+ y <= _maxy;
+ }
+
+ ///
+ /// Tests 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 Covers(Coordinate p)
+ {
+ return Covers(p.X, p.Y);
+ }
+
+ ///
+ /// Tests if the Envelope other lies wholely inside this Envelope (inclusive of the boundary).
+ ///
+ /// the Envelope to check
+ /// true if this Envelope covers the other
+ public bool Covers(Envelope 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(Envelope env)
+ {
+ if (Intersects(env))
+ return 0;
+
+ double dx = 0.0;
+
+ if (_maxx < env.MinX)
+ dx = env.MinX - _maxx;
+ else if (_minx > env.MaxX)
+ dx = _minx - env.MaxX;
+
+ double dy = 0.0;
+
+ if (_maxy < env.MinY)
+ dy = env.MinY - _maxy;
+ else 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;
+
+ var otherE = other as Envelope;
+ if (otherE != null)
+ return Equals(otherE);
+ return false;
+ }
+
+
+ ///
+ public bool Equals(Envelope other)
+ {
+ if (IsNull)
+ return other.IsNull;
+
+ return _maxx == other.MaxX && _maxy == other.MaxY &&
+ _minx == other.MinX && _miny == other.MinY;
+ }
+
+ ///
+ public int CompareTo(object other)
+ {
+ return CompareTo((Envelope)other);
+ }
+
+ ///
+ public int CompareTo(Envelope other)
+ {
+ if (IsNull && other.IsNull)
+ return 0;
+ if (!IsNull && other.IsNull)
+ return 1;
+ if (IsNull && !other.IsNull)
+ return -1;
+
+ if (Area > other.Area)
+ return 1;
+ if (Area < other.Area)
+ return -1;
+ return 0;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ var result = 17;
+ // ReSharper disable NonReadonlyFieldInGetHashCode
+ result = 37 * result + GetHashCode(_minx);
+ result = 37 * result + GetHashCode(_maxx);
+ result = 37 * result + GetHashCode(_miny);
+ result = 37 * result + GetHashCode(_maxy);
+ // ReSharper restore NonReadonlyFieldInGetHashCode
+ return result;
+ }
+
+ private static int GetHashCode(double value)
+ {
+ var 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);
+ //}
+
+ ///
+ /// Method to print out this
+ ///
+ ///
+ /// A text describing this
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder("Env[");
+ if (IsNull)
+ {
+ sb.Append("Null]");
+ }
+ else
+ {
+ sb.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:R} : {1:R}, ", _minx, _maxx);
+ sb.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:R} : {1:R}]", _miny, _maxy);
+ }
+ return sb.ToString();
+
+ //return "Env[" + _minx + " : " + _maxx + ", " + _miny + " : " + _maxy + "]";
+ }
+
+ ///
+ /// Method to parse an envelope from its value
+ ///
+ /// The envelope string
+ /// The envelope
+ public static Envelope Parse(string envelope)
+ {
+ if (string.IsNullOrEmpty(envelope))
+ throw new ArgumentNullException("envelope");
+ if (!(envelope.StartsWith("Env[") && envelope.EndsWith("]")))
+ throw new ArgumentException("Not a valid envelope string", "envelope");
+
+ // test for null
+ envelope = envelope.Substring(4, envelope.Length - 5);
+ if (envelope == "Null")
+ return new Envelope();
+
+ // Parse values
+ var ordinatesValues = new double[4];
+ var ordinateLabel = new[] { "x", "y" };
+ var j = 0;
+
+ // split into ranges
+ var parts = envelope.Split(',');
+ if (parts.Length != 2)
+ throw new ArgumentException("Does not provide two ranges", "envelope");
+
+ foreach (var part in parts)
+ {
+ // Split int min/max
+ var ordinates = part.Split(':');
+ if (ordinates.Length != 2)
+ throw new ArgumentException("Does not provide just min and max values", "envelope");
+
+ if (!double.TryParse(ordinates[0].Trim(), NumberStyles.Number, NumberFormatInfo.InvariantInfo, out ordinatesValues[2 * j]))
+ throw new ArgumentException(string.Format("Could not parse min {0}-Ordinate", ordinateLabel[j]), "envelope");
+ if (!double.TryParse(ordinates[1].Trim(), NumberStyles.Number, NumberFormatInfo.InvariantInfo, out ordinatesValues[2 * j + 1]))
+ throw new ArgumentException(string.Format("Could not parse max {0}-Ordinate", ordinateLabel[j]), "envelope");
+ j++;
+ }
+
+ return new Envelope(ordinatesValues[0], ordinatesValues[1],
+ ordinatesValues[2], ordinatesValues[3]);
+ }
+ }
+}
#endif
\ No newline at end of file
diff --git a/SharpSbn/FrameworkReplacements.cs b/SharpSbn/FrameworkReplacements.cs
index 9c931bb..8583e9b 100644
--- a/SharpSbn/FrameworkReplacements.cs
+++ b/SharpSbn/FrameworkReplacements.cs
@@ -1,55 +1,77 @@
-using System;
-using System.Collections.Generic;
-
-
-// ReSharper disable once CheckNamespace
-namespace FrameworkReplacements
-{
-#if !(NET40 || NET45)
- public class Tuple
- {
- public T1 Item1 { get; set; }
- public T2 Item2 { get; set; }
- }
-
- public static class Tuple
- {
- public static Tuple Create(T1 item1, T2 item2)
- {
- return new Tuple {Item1 = item1, Item2 = item2};
- }
- }
-#endif
-
- namespace Linq
- {
- internal static class Enumerable
- {
-#if !(NET40 || NET45)
- public static IEnumerable Skip(IEnumerable items, int count)
- {
- var i = 0;
- foreach (var item in items)
- {
- if (i >= count)
- yield return item;
- i++;
- }
- }
-#endif
- internal static IEnumerable GetRange(IList list, int start, int count)
- {
- for (var i = 0; i < count; i++)
- yield return list[start + i];
- }
-
- internal static T[] GetRange(T[] list, int start, int count)
- {
- var res = new T[count];
- Array.Copy(list, start, res, 0, count);
- return res;
- }
- }
- }
-}
-
+using System;
+using System.Collections.Generic;
+
+
+// ReSharper disable once CheckNamespace
+namespace FrameworkReplacements
+{
+#if !(NET40 || NET45)
+ ///
+ /// A framework replacement for System.Tuple<T1, T2>.
+ ///
+ /// The type of the first item
+ /// The type of the second item
+ public class Tuple
+ {
+ ///
+ /// Gets or sets a value indicating the first item
+ ///
+ public T1 Item1 { get; set; }
+ ///
+ /// Gets or sets a value indicating the second item
+ ///
+ public T2 Item2 { get; set; }
+ }
+
+ ///
+ /// A utility class to create items.
+ ///
+ public static class Tuple
+ {
+ ///
+ /// A Factory method to create items.
+ ///
+ /// The type of the first item
+ /// The type of the second item
+ /// The first item
+ /// The second item
+ ///
+ public static Tuple Create(T1 item1, T2 item2)
+ {
+ return new Tuple {Item1 = item1, Item2 = item2};
+ }
+ }
+#endif
+
+ namespace Linq
+ {
+ internal static class Enumerable
+ {
+#if !(NET40 || NET45)
+ public static IEnumerable Skip(IEnumerable items, int count)
+ {
+ var i = 0;
+ foreach (var item in items)
+ {
+ if (i >= count)
+ yield return item;
+ i++;
+ }
+ }
+#endif
+ internal static IEnumerable GetRange(IList list, int start, int count)
+ {
+ for (var i = 0; i < count; i++)
+ yield return list[start + i];
+ }
+
+ internal static T[] GetRange(T[] list, int start, int count)
+ {
+ var res = new T[count];
+ Array.Copy(list, start, res, 0, count);
+ return res;
+ }
+ }
+ }
+}
+
diff --git a/SharpSbn/Properties/AssemblyInfo.cs b/SharpSbn/Properties/AssemblyInfo.cs
index 875ea70..16da903 100644
--- a/SharpSbn/Properties/AssemblyInfo.cs
+++ b/SharpSbn/Properties/AssemblyInfo.cs
@@ -1,42 +1,42 @@
-using System;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// Allgemeine Informationen über eine Assembly werden über die folgenden
-// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
-// die mit einer Assembly verknüpft sind.
-[assembly: AssemblyTitle("SbnIndex")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SbnIndex")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-#if !PCL
-// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
-// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
-// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
-[assembly: ComVisible(false)]
-// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
-[assembly: Guid("82daf214-e442-4608-8d60-cfb7d0f15a95")]
-#endif
-
-// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
-//
-// Hauptversion
-// Nebenversion
-// Buildnummer
-// Revision
-//
-// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
-// übernehmen, indem Sie "*" eingeben:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.1.0")]
-[assembly: InternalsVisibleTo("SharpSbn.Test")]
-#if !PCL
-[assembly: CLSCompliant(true)]
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die mit einer Assembly verknüpft sind.
+[assembly: AssemblyTitle("SharpSbn")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SharpSbn")]
+[assembly: AssemblyCopyright("Copyright © Felix Obermaier 2013-2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+#if !PCL
+// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
+// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
+// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
+[assembly: ComVisible(false)]
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("82daf214-e442-4608-8d60-cfb7d0f15a95")]
+#endif
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
+// übernehmen, indem Sie "*" eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.1.0.0")]
+[assembly: AssemblyFileVersion("1.1.0.0")]
+[assembly: InternalsVisibleTo("SharpSbn.Test")]
+#if !PCL
+[assembly: CLSCompliant(true)]
#endif
\ No newline at end of file
diff --git a/SharpSbn/SbnFeature.cs b/SharpSbn/SbnFeature.cs
index 93a159d..efd92fb 100644
--- a/SharpSbn/SbnFeature.cs
+++ b/SharpSbn/SbnFeature.cs
@@ -1,163 +1,199 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-#if UseGeoAPI
-using Envelope = GeoAPI.Geometries.Envelope;
-#else
-using Envelope = SharpSbn.DataStructures.Envelope;
-#endif
-
-namespace SharpSbn
-{
- ///
- /// An entry in the Sbn index
- ///
- [StructLayout(LayoutKind.Explicit)]
- public struct SbnFeature : IEquatable
- {
- [FieldOffset(4)]
- private readonly uint _fid;
-
- [FieldOffset(0)]
- internal readonly byte MinX;
- [FieldOffset(1)]
- internal readonly byte MaxX;
- [FieldOffset(2)]
- internal readonly byte MinY;
- [FieldOffset(3)]
- internal readonly byte MaxY;
-
- ///
- /// Creates an instance of this class using a binary reader
- ///
- /// The binary reader
- internal SbnFeature(BinaryReader sr)
- {
- MinX = sr.ReadByte();
- MinY = sr.ReadByte();
- MaxX = sr.ReadByte();
- MaxY = sr.ReadByte();
- _fid = BinaryIOExtensions.ReadUInt32BE(sr);
- }
-
- ///
- /// Creates an instance of this class using the provided and
- ///
- /// The header of the index
- /// The feature's id
- /// The feature's extent
- [CLSCompliant(false)]
- public SbnFeature(SbnHeader header, uint fid, Envelope extent)
- :this(header.Extent, fid, extent)
- {
- }
-
- ///
- /// Creates an instance of this class using the provided and
- ///
- /// The extent of the index
- /// The feature's id
- /// The feature's extent
- [CLSCompliant(false)]
- public SbnFeature(Envelope sfExtent, uint fid, Envelope extent)
- {
- _fid = fid;
- ClampUtility.Clamp(sfExtent, extent, out MinX, out MinY, out MaxX, out MaxY);
- }
-
- //public SbnFeature(byte[] featureBytes)
- //{
- // MinX = featureBytes[0];
- // MinY = featureBytes[1];
- // MaxX = featureBytes[2];
- // MaxY = featureBytes[3];
- // _fid = BitConverter.ToUInt32(featureBytes, 4);
- //}
-
- /////
- ///// Method to get an approximate extent of the this feature
- /////
- //public Envelope GetExtent(SbnHeader header)
- //{
- // return new Envelope(
- // header.XRange.Min + MinX*header.XRange.Width/255d,
- // header.XRange.Max + MaxX*header.XRange.Width/255d,
- // header.YRange.Min + MinY*header.YRange.Width/255d,
- // header.YRange.Max + MaxY*header.YRange.Width/255d);
- //}
-
- ///
- /// Intersection predicate
- ///
- /// The lower x-ordinate
- /// The upper x-ordinate
- /// The lower y-ordinate
- /// The upper y-ordinate
- /// true if this feature intersects the bounds
- internal bool Intersects(byte minX, byte maxX, byte minY, byte maxY)
- {
- return !(minX > MaxX || maxX < MinX || minY > MaxY || maxY < MinY);
- }
-
- ///
- /// Gets the id of this feature
- ///
- [CLSCompliant(false)]
- public uint Fid { get { return _fid; } }
-
- ///
- /// Method to write the feature to an index
- ///
- ///
- internal void Write(BinaryWriter writer)
- {
- writer.Write(MinX);
- writer.Write(MinY);
- writer.Write(MaxX);
- writer.Write(MaxY);
- BinaryIOExtensions.WriteBE(writer, _fid);
- }
-
- public bool Equals(SbnFeature other)
- {
- return other.Fid == _fid;
- }
-
- public override bool Equals(object other)
- {
- if (other is SbnFeature)
- return Equals((SbnFeature)other);
- return false;
- }
-
- public override int GetHashCode()
- {
- return 31 * typeof(SbnFeature).GetHashCode() * _fid.GetHashCode();
- }
-
- public override string ToString()
- {
- return string.Format("[SbnFeature {0}: ({1}-{2},{3}-{4})]", _fid, MinX, MaxX, MinY, MaxY);
- }
-
- internal Array AsBytes()
- {
- var res = new byte[8];
- res[0] = MinX;
- res[1] = MinY;
- res[2] = MaxX;
- res[3] = MaxY;
- Buffer.BlockCopy(BitConverter.GetBytes(Fid), 0, res, 4, 4);
- return res;
- }
-
- public static bool operator ==(SbnFeature lhs, SbnFeature rhs)
- {
- return lhs.Equals(rhs);
- }
- public static bool operator !=(SbnFeature lhs, SbnFeature rhs)
- {
- return !lhs.Equals(rhs);
- }
- }
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+#if UseGeoAPI
+using Envelope = GeoAPI.Geometries.Envelope;
+#else
+using Envelope = SharpSbn.DataStructures.Envelope;
+#endif
+
+namespace SharpSbn
+{
+ ///
+ /// An entry in the Sbn index
+ ///
+ [StructLayout(LayoutKind.Explicit)]
+ public struct SbnFeature : IEquatable
+ {
+ [FieldOffset(4)]
+ private readonly uint _fid;
+
+ [FieldOffset(0)]
+ internal readonly byte MinX;
+ [FieldOffset(1)]
+ internal readonly byte MaxX;
+ [FieldOffset(2)]
+ internal readonly byte MinY;
+ [FieldOffset(3)]
+ internal readonly byte MaxY;
+
+ ///
+ /// Creates an instance of this class using a binary reader
+ ///
+ /// The binary reader
+ internal SbnFeature(BinaryReader sr)
+ {
+ MinX = sr.ReadByte();
+ MinY = sr.ReadByte();
+ MaxX = sr.ReadByte();
+ MaxY = sr.ReadByte();
+ _fid = BinaryIOExtensions.ReadUInt32BE(sr);
+ }
+
+ ///
+ /// Creates an instance of this class using the provided and
+ ///
+ /// The header of the index
+ /// The feature's id
+ /// The feature's extent
+#pragma warning disable 3001
+ public SbnFeature(SbnHeader header, uint fid, Envelope extent)
+ :this(header.Extent, fid, extent)
+ {
+ }
+
+ ///
+ /// Creates an instance of this class using the provided and
+ ///
+ /// The extent of the index
+ /// The feature's id
+ /// The feature's extent
+ public SbnFeature(Envelope sfExtent, uint fid, Envelope extent)
+ {
+ _fid = fid;
+ ClampUtility.Clamp(sfExtent, extent, out MinX, out MinY, out MaxX, out MaxY);
+ }
+#pragma warning restore 3001
+
+ //public SbnFeature(byte[] featureBytes)
+ //{
+ // MinX = featureBytes[0];
+ // MinY = featureBytes[1];
+ // MaxX = featureBytes[2];
+ // MaxY = featureBytes[3];
+ // _fid = BitConverter.ToUInt32(featureBytes, 4);
+ //}
+
+ /////
+ ///// Method to get an approximate extent of the this feature
+ /////
+ //public Envelope GetExtent(SbnHeader header)
+ //{
+ // return new Envelope(
+ // header.XRange.Min + MinX*header.XRange.Width/255d,
+ // header.XRange.Max + MaxX*header.XRange.Width/255d,
+ // header.YRange.Min + MinY*header.YRange.Width/255d,
+ // header.YRange.Max + MaxY*header.YRange.Width/255d);
+ //}
+
+ ///
+ /// Intersection predicate
+ ///
+ /// The lower x-ordinate
+ /// The upper x-ordinate
+ /// The lower y-ordinate
+ /// The upper y-ordinate
+ /// true if this feature intersects the bounds
+ internal bool Intersects(byte minX, byte maxX, byte minY, byte maxY)
+ {
+ return !(minX > MaxX || maxX < MinX || minY > MaxY || maxY < MinY);
+ }
+
+ ///
+ /// Gets the id of this feature
+ ///
+ [CLSCompliant(false)]
+ public uint Fid { get { return _fid; } }
+
+ ///
+ /// Method to write the feature to an index
+ ///
+ ///
+ internal void Write(BinaryWriter writer)
+ {
+ writer.Write(MinX);
+ writer.Write(MinY);
+ writer.Write(MaxX);
+ writer.Write(MaxY);
+ BinaryIOExtensions.WriteBE(writer, _fid);
+ }
+
+ ///
+ /// Function to test if this equals
+ ///
+ /// The other feature
+ /// true if the this equals 's
+ public bool Equals(SbnFeature other)
+ {
+ return other.Fid == _fid;
+ }
+
+ ///
+ /// Function to test if this equals
+ ///
+ /// The other feature
+ /// true if the this equals 's
+ public override bool Equals(object other)
+ {
+ if (other is SbnFeature)
+ return Equals((SbnFeature)other);
+ return false;
+ }
+
+ ///
+ /// Function to return the hashcode for this object.
+ ///
+ ///
+ /// A hashcode
+ ///
+ public override int GetHashCode()
+ {
+ return 31 * typeof(SbnFeature).GetHashCode() * _fid.GetHashCode();
+ }
+
+ ///
+ /// Method to print out this
+ ///
+ /// A text describing this
+ public override string ToString()
+ {
+ return string.Format("[SbnFeature {0}: ({1}-{2},{3}-{4})]", _fid, MinX, MaxX, MinY, MaxY);
+ }
+
+ ///
+ /// Method to convert the feature to a byte array
+ ///
+ /// An array of bytes
+ internal Array AsBytes()
+ {
+ var res = new byte[8];
+ res[0] = MinX;
+ res[1] = MinY;
+ res[2] = MaxX;
+ res[3] = MaxY;
+ Buffer.BlockCopy(BitConverter.GetBytes(Fid), 0, res, 4, 4);
+ return res;
+ }
+
+ ///
+ /// An operator for equuality comarison
+ ///
+ /// The value on the left-hand-side
+ /// The value on the right-hand-side
+ /// true if ==
+ public static bool operator ==(SbnFeature lhs, SbnFeature rhs)
+ {
+ return lhs.Equals(rhs);
+ }
+ ///
+ /// An operator for inequuality comarison
+ ///
+ /// The value on the left-hand-side
+ /// The value on the right-hand-side
+ /// true if !=
+ public static bool operator !=(SbnFeature lhs, SbnFeature rhs)
+ {
+ return !lhs.Equals(rhs);
+ }
+ }
}
\ No newline at end of file
diff --git a/SharpSbn/SbnHeader.cs b/SharpSbn/SbnHeader.cs
index 48d3d61..c47e165 100644
--- a/SharpSbn/SbnHeader.cs
+++ b/SharpSbn/SbnHeader.cs
@@ -1,187 +1,201 @@
-using System.IO;
-using System.Text;
-#if UseGeoAPI
-using Interval = GeoAPI.DataStructures.Interval;
-using Envelope = GeoAPI.Geometries.Envelope;
-#else
-using Interval = SharpSbn.DataStructures.Interval;
-using Envelope = SharpSbn.DataStructures.Envelope;
-#endif
-namespace SharpSbn
-{
- ///
- /// A class containing ESRI SBN Index information
- ///
- public class SbnHeader
- {
- ///
- /// A magic number identifying this file as belonging to the ShapeFile family
- ///
- private const int FileCode = 9994;
-
- ///
- /// (Assumption)
- /// A magic number identifying this file is an index, not a shapefile (assumption)
- ///
- private const int FileCodeIndex = -400;
-
- ///
- /// Creates an instance of this class
- ///
- public SbnHeader()
- {
- FileLength = 100;
- NumRecords = 0;
- XRange = Interval.Create();
- YRange = Interval.Create();
- ZRange = Interval.Create();
- MRange = Interval.Create();
- }
-
- ///
- /// Creates an instance of this class
- ///
- /// The number of features
- /// The extent
- public SbnHeader(int numRecords, Envelope extent)
- {
- NumRecords = numRecords;
- XRange = Interval.Create(extent.MinX, extent.MaxX);
- YRange = Interval.Create(extent.MinY, extent.MaxY);
- ZRange = Interval.Create();
- MRange = Interval.Create();
- }
-
- public SbnHeader(int numRecords, Interval xInterval, Interval yInterval, Interval zInterval, Interval mInterval)
- {
- NumRecords = numRecords;
- XRange = xInterval;
- YRange = yInterval;
- ZRange = zInterval;
- MRange = mInterval;
- }
-
- ///
- /// Gets the number of records in this index (Features in the shapefile)
- ///
- public int NumRecords { get; private set; }
-
- ///
- /// Gets the length of the index file in bytes
- ///
- public int FileLength { get; private set; }
-
- ///
- /// Gets a value indicating the area covered by this index
- ///
- internal Envelope Extent
- {
- get
- {
- return new Envelope(XRange.Min, XRange.Max,
- YRange.Min, YRange.Max);
- }
- }
-
- ///
- /// Gets the x-ordinate range covered by this index
- ///
- public Interval XRange { get; private set; }
-
- ///
- /// Gets the y-ordinate range covered by this index
- ///
- public Interval YRange { get; private set; }
-
- ///
- /// Gets the z-ordinate range covered by this index
- ///
- public Interval ZRange { get; private set; }
-
- ///
- /// Gets the m-ordinate range covered by this index
- ///
- public Interval MRange { get; private set; }
-
- ///
- /// Method to read the index header using the provided reader
- ///
- /// The reader to use
- public void Read(BinaryReader reader)
- {
- var fileCode = BinaryIOExtensions.ReadInt32BE(reader);
- var fileCodeIndex = BinaryIOExtensions.ReadInt32BE(reader);
- if (fileCode != FileCode || fileCodeIndex != FileCodeIndex)
- throw new SbnException("Not a Shapefile index file");
-
- reader.BaseStream.Seek(16, SeekOrigin.Current);
-
- FileLength = BinaryIOExtensions.ReadInt32BE(reader) * 2;
- NumRecords = BinaryIOExtensions.ReadInt32BE(reader);
-
- var minX = BinaryIOExtensions.ReadDoubleBE(reader);
- var minY = BinaryIOExtensions.ReadDoubleBE(reader);
- XRange = Interval.Create(minX, BinaryIOExtensions.ReadDoubleBE(reader));
- YRange = Interval.Create(minY, BinaryIOExtensions.ReadDoubleBE(reader));
- ZRange = Interval.Create(BinaryIOExtensions.ReadDoubleBE(reader), BinaryIOExtensions.ReadDoubleBE(reader));
- MRange = Interval.Create(BinaryIOExtensions.ReadDoubleBE(reader), BinaryIOExtensions.ReadDoubleBE(reader));
-
- reader.BaseStream.Seek(4, SeekOrigin.Current);
- }
-
- ///
- /// Method to write the index header using the provided writer
- ///
- /// The writer to use
- ///
- internal void Write(BinaryWriter writer, int? fileLength = null)
- {
- BinaryIOExtensions.WriteBE(writer, FileCode);
- BinaryIOExtensions.WriteBE(writer, FileCodeIndex);
-
- writer.Write(new byte[16]);
-
- BinaryIOExtensions.WriteBE(writer, (fileLength ?? FileLength) / 2);
- BinaryIOExtensions.WriteBE(writer, NumRecords);
-
- BinaryIOExtensions.WriteBE(writer, XRange.Min);
- BinaryIOExtensions.WriteBE(writer, YRange.Min);
- BinaryIOExtensions.WriteBE(writer, XRange.Max);
- BinaryIOExtensions.WriteBE(writer, YRange.Max);
-
- BinaryIOExtensions.WriteBE(writer, ZRange);
- BinaryIOExtensions.WriteBE(writer, MRange);
-
- writer.Write(0);
- }
-
- internal void AddFeature(uint id, Envelope geometry, Interval? zRange, Interval? mRange)
- {
- NumRecords++;
- XRange = XRange.ExpandedByInterval(Interval.Create(geometry.MinX, geometry.MaxX));
- YRange = YRange.ExpandedByInterval(Interval.Create(geometry.MinY, geometry.MaxY));
- ZRange = ZRange.ExpandedByInterval(zRange ?? Interval.Create());
- MRange = MRange.ExpandedByInterval(mRange ?? Interval.Create());
- }
-
- internal void RemoveFeature()
- {
- NumRecords--;
- }
-
- public override string ToString()
- {
- var res = new StringBuilder();
- res.AppendLine("[SbnHeader");
- res.AppendFormat(" FileCode: {0}\n", FileCode);
- res.AppendFormat(" FileCode2: {0}\n", FileCodeIndex);
- res.AppendFormat(" NumRecords: {0}\n", NumRecords);
- res.AppendFormat(" FileLength: {0}\n", FileLength);
- res.AppendFormat(" XRange: {0}\n", XRange);
- res.AppendFormat(" YRange: {0}\n", YRange);
- res.AppendFormat(" ZRange: {0}\n", ZRange);
- res.AppendFormat(" MRange: {0}]", MRange);
- return res.ToString();
- }
- }
+using System.IO;
+using System.Text;
+#if UseGeoAPI
+using Interval = GeoAPI.DataStructures.Interval;
+using Envelope = GeoAPI.Geometries.Envelope;
+#else
+using Interval = SharpSbn.DataStructures.Interval;
+using Envelope = SharpSbn.DataStructures.Envelope;
+#endif
+namespace SharpSbn
+{
+ ///
+ /// A class containing ESRI SBN Index information
+ ///
+ public class SbnHeader
+ {
+ ///
+ /// A magic number identifying this file as belonging to the ShapeFile family
+ ///
+ private const int FileCode = 9994;
+
+ ///
+ /// (Assumption)
+ /// A magic number identifying this file is an index, not a shapefile (assumption)
+ ///
+ private const int FileCodeIndex = -400;
+
+ ///
+ /// Creates an instance of this class
+ ///
+ public SbnHeader()
+ {
+ FileLength = 100;
+ NumRecords = 0;
+ XRange = Interval.Create();
+ YRange = Interval.Create();
+ ZRange = Interval.Create();
+ MRange = Interval.Create();
+ }
+
+ ///
+ /// Creates an instance of this class
+ ///
+ /// The number of features
+ /// The extent
+ public SbnHeader(int numRecords, Envelope extent)
+ {
+ NumRecords = numRecords;
+ XRange = Interval.Create(extent.MinX, extent.MaxX);
+ YRange = Interval.Create(extent.MinY, extent.MaxY);
+ ZRange = Interval.Create();
+ MRange = Interval.Create();
+ }
+
+ ///
+ /// Creates an instance of this class
+ ///
+ /// The number of features
+ /// The x-Oridnate extent
+ /// The y-Oridnate extent
+ /// The z-Oridnate extent
+ /// The m-Oridnate extent
+ public SbnHeader(int numRecords, Interval xInterval, Interval yInterval, Interval zInterval, Interval mInterval)
+ {
+ NumRecords = numRecords;
+ XRange = xInterval;
+ YRange = yInterval;
+ ZRange = zInterval;
+ MRange = mInterval;
+ }
+
+ ///
+ /// Gets the number of records in this index (Features in the shapefile)
+ ///
+ public int NumRecords { get; private set; }
+
+ ///
+ /// Gets the length of the index file in bytes
+ ///
+ public int FileLength { get; private set; }
+
+ ///
+ /// Gets a value indicating the area covered by this index
+ ///
+ internal Envelope Extent
+ {
+ get
+ {
+ return new Envelope(XRange.Min, XRange.Max,
+ YRange.Min, YRange.Max);
+ }
+ }
+
+ ///
+ /// Gets the x-ordinate range covered by this index
+ ///
+ public Interval XRange { get; private set; }
+
+ ///
+ /// Gets the y-ordinate range covered by this index
+ ///
+ public Interval YRange { get; private set; }
+
+ ///
+ /// Gets the z-ordinate range covered by this index
+ ///
+ public Interval ZRange { get; private set; }
+
+ ///
+ /// Gets the m-ordinate range covered by this index
+ ///
+ public Interval MRange { get; private set; }
+
+ ///
+ /// Method to read the index header using the provided reader
+ ///
+ /// The reader to use
+ public void Read(BinaryReader reader)
+ {
+ var fileCode = BinaryIOExtensions.ReadInt32BE(reader);
+ var fileCodeIndex = BinaryIOExtensions.ReadInt32BE(reader);
+ if (fileCode != FileCode || fileCodeIndex != FileCodeIndex)
+ throw new SbnException("Not a Shapefile index file");
+
+ reader.BaseStream.Seek(16, SeekOrigin.Current);
+
+ FileLength = BinaryIOExtensions.ReadInt32BE(reader) * 2;
+ NumRecords = BinaryIOExtensions.ReadInt32BE(reader);
+
+ var minX = BinaryIOExtensions.ReadDoubleBE(reader);
+ var minY = BinaryIOExtensions.ReadDoubleBE(reader);
+ XRange = Interval.Create(minX, BinaryIOExtensions.ReadDoubleBE(reader));
+ YRange = Interval.Create(minY, BinaryIOExtensions.ReadDoubleBE(reader));
+ ZRange = Interval.Create(BinaryIOExtensions.ReadDoubleBE(reader), BinaryIOExtensions.ReadDoubleBE(reader));
+ MRange = Interval.Create(BinaryIOExtensions.ReadDoubleBE(reader), BinaryIOExtensions.ReadDoubleBE(reader));
+
+ reader.BaseStream.Seek(4, SeekOrigin.Current);
+ }
+
+ ///
+ /// Method to write the index header using the provided writer
+ ///
+ /// The writer to use
+ ///
+ internal void Write(BinaryWriter writer, int? fileLength = null)
+ {
+ BinaryIOExtensions.WriteBE(writer, FileCode);
+ BinaryIOExtensions.WriteBE(writer, FileCodeIndex);
+
+ writer.Write(new byte[16]);
+
+ BinaryIOExtensions.WriteBE(writer, (fileLength ?? FileLength) / 2);
+ BinaryIOExtensions.WriteBE(writer, NumRecords);
+
+ BinaryIOExtensions.WriteBE(writer, XRange.Min);
+ BinaryIOExtensions.WriteBE(writer, YRange.Min);
+ BinaryIOExtensions.WriteBE(writer, XRange.Max);
+ BinaryIOExtensions.WriteBE(writer, YRange.Max);
+
+ BinaryIOExtensions.WriteBE(writer, ZRange);
+ BinaryIOExtensions.WriteBE(writer, MRange);
+
+ writer.Write(0);
+ }
+
+ internal void AddFeature(uint id, Envelope geometry, Interval? zRange, Interval? mRange)
+ {
+ NumRecords++;
+ XRange = XRange.ExpandedByInterval(Interval.Create(geometry.MinX, geometry.MaxX));
+ YRange = YRange.ExpandedByInterval(Interval.Create(geometry.MinY, geometry.MaxY));
+ ZRange = ZRange.ExpandedByInterval(zRange ?? Interval.Create());
+ MRange = MRange.ExpandedByInterval(mRange ?? Interval.Create());
+ }
+
+ internal void RemoveFeature()
+ {
+ NumRecords--;
+ }
+
+ ///
+ /// Method to print out this
+ ///
+ ///
+ /// A text describing this
+ ///
+ public override string ToString()
+ {
+ var res = new StringBuilder();
+ res.AppendLine("[SbnHeader");
+ res.AppendFormat(" FileCode: {0}\n", FileCode);
+ res.AppendFormat(" FileCode2: {0}\n", FileCodeIndex);
+ res.AppendFormat(" NumRecords: {0}\n", NumRecords);
+ res.AppendFormat(" FileLength: {0}\n", FileLength);
+ res.AppendFormat(" XRange: {0}\n", XRange);
+ res.AppendFormat(" YRange: {0}\n", YRange);
+ res.AppendFormat(" ZRange: {0}\n", ZRange);
+ res.AppendFormat(" MRange: {0}]", MRange);
+ return res.ToString();
+ }
+ }
}
\ No newline at end of file
diff --git a/SharpSbn/SbnNode.cs b/SharpSbn/SbnNode.cs
index 0c05585..9b44131 100644
--- a/SharpSbn/SbnNode.cs
+++ b/SharpSbn/SbnNode.cs
@@ -1,624 +1,642 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-
-namespace SharpSbn
-{
- public class SbnNode : IEnumerable
- {
- private readonly SbnTree _tree;
- private readonly byte _minX, _minY, _maxX, _maxY;
- private bool _full;
-
- ///
- /// Creates an instance of this class
- ///
- /// The tree this node belongs to
- /// The node's id
- public SbnNode(SbnTree tree, int nid)
- {
- _tree = tree;
- Nid = nid;
- Full = nid == 1 || nid >= _tree.FirstLeafNodeId;
- }
-
- ///
- /// Creates an instance of this class
- ///
- /// The tree this node belongs to
- /// The node's id
- /// The lower x-ordinate
- /// The lower y-ordinate
- /// The upper x-ordinate
- /// The upper y-ordinate
- public SbnNode(SbnTree tree, int nid, byte minx, byte miny, byte maxx, byte maxy)
- :this(tree, nid)
- {
- _minX = minx;
- _minY = miny;
- _maxX = maxx;
- _maxY = maxy;
- }
-
- ///
- /// Method to add a bin to this node
- ///
- /// The bin to add
- /// A value indicating that all parent nodes should be set to
- internal void AddBin(SbnBin addBin, bool setFull = false)
- {
- if (FirstBin == null)
- {
- FirstBin = addBin;
- if (setFull)
- {
- if (Parent != null)
- Parent.Full = true;
- }
- }
-
- else
- {
- var bin = FirstBin;
- while (bin.Next != null)
- bin = bin.Next;
- bin.Next = addBin;
- }
- }
-
- ///
- /// Gets the id of the current node
- ///
- public int Nid { get; private set; }
-
- ///
- /// The first bin associated with this node
- ///
- internal SbnBin FirstBin { get; set; }
-
- ///
- /// The last bin associated with this node
- ///
- internal SbnBin LastBin
- {
- get
- {
- if (FirstBin == null)
- return null;
-
- var res = FirstBin;
- while (res.Next != null)
- res = res.Next;
- return res;
- }
- }
-
- ///
- /// Gets the parent of this node
- ///
- public SbnNode Parent
- {
- get
- {
- if (Nid == 1)
- return null;
-
- var firstSiblingId = Nid - Nid % 2;
- return _tree.Nodes[firstSiblingId / 2];
- }
- }
-
- ///
- /// Gets the first child of this node
- ///
- public SbnNode Child1
- {
- get
- {
- if (Nid >= _tree.FirstLeafNodeId)
- return null;
- return _tree.Nodes[Nid * 2];
- }
- }
-
- ///
- /// Gets the second child of this node
- ///
- public SbnNode Child2
- {
- get
- {
- if (Nid >= _tree.FirstLeafNodeId)
- return null;
- return _tree.Nodes[Nid * 2 + 1];
- }
- }
-
- ///
- /// Gets the sibling of this node
- ///
- public SbnNode Sibling
- {
- get
- {
- if (Nid == 1)
- return null;
-
- if (Nid - Nid % 2 == Nid)
- return _tree.Nodes[Nid + 1];
- return _tree.Nodes[Nid - 1];
- }
- }
-
- ///
- /// Property to indicate that the node is full, it has had more than 8 features once and was then split
- ///
- internal bool Full
- {
- get { return _full; }
- private set
- {
- if (Nid > 1 && !Parent.Full)
- Parent.Full = true;
-
- _full = value;
- }
- }
-
- ///
- /// Gets the node's level
- ///
- public int Level { get { return (int) Math.Log(Nid, 2) + 1; }}
-
- ///
- /// Gets the number of features in this node
- ///
- public int FeatureCount
- {
- get
- {
- if (FirstBin == null)
- return 0;
-
- var count = 0;
- var bin = FirstBin;
- while (bin != null)
- {
- count += bin.NumFeatures;
- bin = bin.Next;
- }
- return count;
- }
- }
-
- ///
- /// Add the child nodes
- ///
- public void AddChildren()
- {
- if (Nid >= _tree.FirstLeafNodeId) return;
-
- var splitBounds = GetSplitBounds(1);
- var childId = Nid*2;
- _tree.Nodes[childId] = new SbnNode(_tree, childId++, splitBounds[0], splitBounds[1], splitBounds[2], splitBounds[3]);
- Child1.AddChildren();
-
- splitBounds = GetSplitBounds(2);
- _tree.Nodes[childId] = new SbnNode(_tree, childId, splitBounds[0], splitBounds[1], splitBounds[2], splitBounds[3]);
- Child2.AddChildren();
- }
-
- ///
- /// Compute the split ordinate for a given
- ///
- /// The axis
- /// The ordinate
- private byte GetSplitOridnate(int splitAxis)
- {
- var mid = (splitAxis == 1)
- ? /*(int)*/ (byte)((_minX + _maxX) / 2.0 + 1)
- : /*(int)*/ (byte)((_minY + _maxY) / 2.0 + 1);
-
- return (byte) (mid - mid%2);
- }
-
- ///
- /// Get the bounds for one of the child nodes
- ///
- /// The index of the child node
- /// The split bounds
- private byte[] GetSplitBounds(int childIndex)
- {
- var splitAxis = Level % 2;// == 1 ? 'x' : 'y';
-
- var mid = GetSplitOridnate(splitAxis);
-
- var res = new[] {_minX, _minY, _maxX, _maxY};
- switch (splitAxis)
- {
- case 1: // x-ordinate
- switch (childIndex)
- {
- case 1:
- res[0] = (byte)(mid + 1);
- break;
- case 2:
- res[2] = mid;
- break;
- }
- break;
- case 0: // y-ordinate
- switch (childIndex)
- {
- case 1:
- res[1] = (byte)(mid + 1);
- break;
- case 2:
- res[3] = mid;
- break;
- }
- break;
- default:
- throw new ArgumentOutOfRangeException("childIndex");
- }
-
- return res;
- }
-
- ///
- /// function to count all features in this node and all child nodes
- ///
- /// The number of features in all this and all child nodes
- public int CountAllFeatures()
- {
- var res = FeatureCount;
- if (Nid < _tree.FirstLeafNodeId)
- res += Child1.CountAllFeatures() + Child2.CountAllFeatures();
- return res;
- }
-
- ///
- /// Method to query all the ids of features in this node that intersect the box defined
- /// by , ,
- /// and
- ///
- /// The lower x-ordinate
- /// The lower y-ordinate
- /// The upper x-ordinate
- /// The upper y-ordinate
- /// A list of feature ids to add to
- /// An enumeration of feature ids
- internal void QueryFids(byte minx, byte miny, byte maxx, byte maxy, List fidList)
- {
- if (ContainedBy(minx, miny, maxx, maxy))
- {
- AddAllFidsInNode(fidList);
- return;
- }
-
- foreach (var feature in this)
- {
- if (feature.Intersects(minx, maxx, miny, maxy))
- fidList.Add(feature.Fid);
- }
-
- if (Nid < _tree.FirstLeafNodeId)
- {
- if (Child1.Intersects(minx, miny, maxx, maxy))
- Child1.QueryFids(minx, miny, maxx, maxy, fidList);
- if (Child2.Intersects(minx, miny, maxx, maxy))
- Child2.QueryFids(minx, miny, maxx, maxy, fidList);
- }
- }
-
- ///
- /// Helper method to add all the ids of features in this node and its descendants
- ///
- /// An enumeration of feature ids
- private void AddAllFidsInNode(List fidList)
- {
- if (FeatureCount > 0)
- {
- var bin = FirstBin;
- while (bin != null)
- {
- fidList.AddRange(bin.GetAllFidsInBin());
- bin = bin.Next;
- }
- }
-
- if (Nid < _tree.FirstLeafNodeId)
- {
- Child1.AddAllFidsInNode(fidList);
- Child2.AddAllFidsInNode(fidList);
- }
- }
-
- ///
- ///
- ///
- ///
- public override string ToString()
- {
- return string.Format("[SbnNode {0}: ({1}-{2},{3}-{4})/{5}/{6}]", Nid, _minX, _maxX, _minY, _maxY,
- GetSplitOridnate(Level%2), FeatureCount);
- }
-
- ///
- /// Intersection predicate function
- ///
- /// lower x-ordinate
- /// lower y-ordinate
- /// upper x-ordinate
- /// upper y-ordinate
- /// true if this node's bounding box intersect with the bounding box defined by , , and , otherwise false
- internal bool Intersects(byte minX, byte minY, byte maxX, byte maxY)
- {
- return !(minX > _maxX || maxX < _minX || minY > _maxY || maxY < _minY);
- }
-
- ///
- /// Contains predicate function
- ///
- /// lower x-ordinate
- /// lower y-ordinate
- /// upper x-ordinate
- /// upper y-ordinate
- /// true if this node's bounding box contains the bounding box defined by , , and , otherwise false
- internal bool Contains(byte minX, byte minY, byte maxX, byte maxY)
- {
- return minX >= _minX && maxX <= _maxX &&
- minY >= _minY && maxY <= _maxY;
- }
-
- ///
- /// ContainedBy predicate function
- ///
- /// lower x-ordinate
- /// lower y-ordinate
- /// upper x-ordinate
- /// upper y-ordinate
- /// true if this node's bounding box contains the bounding box defined by , , and , otherwise false
- internal bool ContainedBy(byte minX, byte minY, byte maxX, byte maxY)
- {
- return _minX >= minX && _maxX <= maxX &&
- _minY >= minY && _maxY <= maxY;
- }
- public IEnumerator GetEnumerator()
- {
- return new SbnFeatureEnumerator(FirstBin);
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- ///
- /// Private helper class to enumerate feature ids
- ///
- private class SbnFeatureEnumerator : IEnumerator
- {
- private SbnBin _firstBin;
- private SbnBin _currentBin;
-
- private int _index = -1;
-
- internal SbnFeatureEnumerator(SbnBin firstBin)
- {
- _firstBin = firstBin;
- }
-
- void IDisposable.Dispose()
- {
- _firstBin = null;
- _currentBin = null;
- }
-
- public bool MoveNext()
- {
- // We don't have a bin at all!
- if (_firstBin == null) return false;
-
- // We were resetted or havn't started
- if (_index == -1)
- _currentBin = _firstBin;
-
- // did we reach the end!
- if (_index == _currentBin.NumFeatures)
- return false;
-
- // Increment
- _index++;
-
- // Did we reach the end of the bin now?
- if (_index == 100)
- {
- //If so move to next
- _currentBin = _currentBin.Next;
- //was there another one?
- if (_currentBin == null) return false;
- _index = 0;
- }
-
- return _index < _currentBin.NumFeatures;
- }
-
- public void Reset()
- {
- _index = -1;
- _currentBin = null;
- }
-
- public SbnFeature Current
- {
- get { return _index == -1 ? new SbnFeature() : _currentBin[_index]; }
- }
-
- object IEnumerator.Current
- {
- get { return Current; }
- }
- }
-
- public bool VerifyBins()
- {
-#if DEBUG
- foreach (var feature in this)
- {
- if (!Contains(feature.MinX, feature.MinY, feature.MaxX, feature.MaxY))
- return false;
- }
-#endif
- return true;
- }
-
- ///
- /// Method to insert a feature at this node
- ///
- /// The feature to add
- public void Insert(SbnFeature feature)
- {
- // if this is leaf, just take the feature
- if (Nid >= _tree.FirstLeafNodeId)
- {
- AddFeature(feature);
- return;
- }
-
- // it takes 8 features to split a node
- // so we'll hold 8 features first
- if (Nid > 1)
- {
- if (!Full)
- {
-
- if (FeatureCount < 8)
- {
- AddFeature(feature);
- return;
- }
- if (FeatureCount == 8)
- {
- var bin = FirstBin;
- FirstBin = new SbnBin();
- Full = true;
- bin.AddFeature(feature);
- for (var i = 0; i < 9; i ++)
- {
- Insert(bin[i]);
- }
- return;
- }
- }
-
- }
-
- // The node is split so we can sort features
- int min, max; //, smin, smax;
- var splitAxis = Level%2;
- if (splitAxis == 1)
- {
- min = feature.MinX;
- max = feature.MaxX;
- //smin = feature.MinY;
- //smax = feature.MaxY;
- }
- else
- {
- min = feature.MinY;
- max = feature.MaxY;
- //smin = feature.MinX;
- //smax = feature.MaxX;
- }
- var seam = GetSplitOridnate(splitAxis);
-
- // Grab features on the seam we can't split
- if (min <= seam && max > seam)
- {
- AddFeature(feature);
- }
-
- else if (min < seam)
- Child2.Insert(feature);
- else
- Child1.Insert(feature);
- }
-
- ///
- /// Method to actually add a feature to this node
- ///
- ///
- private void AddFeature(SbnFeature feature)
- {
- if (FeatureCount % 100 == 0)
- AddBin(new SbnBin());
-
- var addBin = FirstBin;
- while (addBin.NumFeatures == 100)
- addBin = addBin.Next;
-
- addBin.AddFeature(feature);
- }
-
-#if VERBOSE
- public string ToStringVerbose()
- {
- return string.Format("{0,5} {1,4}-{2,4} {3,4}-{4,4} {5} {6,4} {7,1}", Nid, _minX, _maxX, _minY, _maxY, Full ? 1 : 0, Full ? FeatureCount : 0, Full ? 0 : FeatureCount);
- }
-#endif
-
- ///
- /// Method to remove a feature from a bin
- ///
- ///
- ///
- internal bool Remove(SbnFeature searchFeature)
- {
- if (!Intersects(searchFeature.MinX, searchFeature.MinY, searchFeature.MaxX, searchFeature.MaxY))
- return false;
-
- if (FeatureCount > 0)
- {
- var searchBin = FirstBin;
- for (var i = 0; i < FeatureCount; i++)
- {
- var j = i % 100;
- if (i > 100 && j == 0)
- searchBin = searchBin.Next;
- if (searchBin[j].Fid == searchFeature.Fid)
- {
- searchBin.RemoveAt(j);
- return true;
- }
- }
- }
-
- if (Nid < _tree.FirstLeafNodeId)
- {
- return Child1.Remove(searchFeature) ||
- Child2.Remove(searchFeature);
- }
-
- return false;
- }
-
- internal SbnFeature RemoveAt(int index)
- {
- var bin = FirstBin;
- while (index >= 100)
- {
- bin = bin.Next;
- index -= 100;
- }
-
- var result = bin[index];
- bin.RemoveAt(index);
-
- if (FeatureCount == 0)
- FirstBin = null;
-
- return result;
- }
- }
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace SharpSbn
+{
+ ///
+ /// A Node in the
+ ///
+ public class SbnNode : IEnumerable
+ {
+ private readonly SbnTree _tree;
+ private readonly byte _minX, _minY, _maxX, _maxY;
+ private bool _full;
+
+ ///
+ /// Creates an instance of this class
+ ///
+ /// The tree this node belongs to
+ /// The node's id
+ public SbnNode(SbnTree tree, int nid)
+ {
+ _tree = tree;
+ Nid = nid;
+ Full = nid == 1 || nid >= _tree.FirstLeafNodeId;
+ }
+
+ ///
+ /// Creates an instance of this class
+ ///
+ /// The tree this node belongs to
+ /// The node's id
+ /// The lower x-ordinate
+ /// The lower y-ordinate
+ /// The upper x-ordinate
+ /// The upper y-ordinate
+ public SbnNode(SbnTree tree, int nid, byte minx, byte miny, byte maxx, byte maxy)
+ :this(tree, nid)
+ {
+ _minX = minx;
+ _minY = miny;
+ _maxX = maxx;
+ _maxY = maxy;
+ }
+
+ ///
+ /// Method to add a bin to this node
+ ///
+ /// The bin to add
+ /// A value indicating that all parent nodes should be set to
+ internal void AddBin(SbnBin addBin, bool setFull = false)
+ {
+ if (FirstBin == null)
+ {
+ FirstBin = addBin;
+ if (setFull)
+ {
+ if (Parent != null)
+ Parent.Full = true;
+ }
+ }
+
+ else
+ {
+ var bin = FirstBin;
+ while (bin.Next != null)
+ bin = bin.Next;
+ bin.Next = addBin;
+ }
+ }
+
+ ///
+ /// Gets the id of the current node
+ ///
+ public int Nid { get; private set; }
+
+ ///
+ /// The first bin associated with this node
+ ///
+ internal SbnBin FirstBin { get; set; }
+
+ ///
+ /// The last bin associated with this node
+ ///
+ internal SbnBin LastBin
+ {
+ get
+ {
+ if (FirstBin == null)
+ return null;
+
+ var res = FirstBin;
+ while (res.Next != null)
+ res = res.Next;
+ return res;
+ }
+ }
+
+ ///
+ /// Gets the parent of this node
+ ///
+ public SbnNode Parent
+ {
+ get
+ {
+ if (Nid == 1)
+ return null;
+
+ var firstSiblingId = Nid - Nid % 2;
+ return _tree.Nodes[firstSiblingId / 2];
+ }
+ }
+
+ ///
+ /// Gets the first child of this node
+ ///
+ public SbnNode Child1
+ {
+ get
+ {
+ if (Nid >= _tree.FirstLeafNodeId)
+ return null;
+ return _tree.Nodes[Nid * 2];
+ }
+ }
+
+ ///
+ /// Gets the second child of this node
+ ///
+ public SbnNode Child2
+ {
+ get
+ {
+ if (Nid >= _tree.FirstLeafNodeId)
+ return null;
+ return _tree.Nodes[Nid * 2 + 1];
+ }
+ }
+
+ ///
+ /// Gets the sibling of this node
+ ///
+ public SbnNode Sibling
+ {
+ get
+ {
+ if (Nid == 1)
+ return null;
+
+ if (Nid - Nid % 2 == Nid)
+ return _tree.Nodes[Nid + 1];
+ return _tree.Nodes[Nid - 1];
+ }
+ }
+
+ ///
+ /// Property to indicate that the node is full, it has had more than 8 features once and was then split
+ ///
+ internal bool Full
+ {
+ get { return _full; }
+ private set
+ {
+ if (Nid > 1 && !Parent.Full)
+ Parent.Full = true;
+
+ _full = value;
+ }
+ }
+
+ ///
+ /// Gets the node's level
+ ///
+ public int Level { get { return (int) Math.Log(Nid, 2) + 1; }}
+
+ ///
+ /// Gets the number of features in this node
+ ///
+ public int FeatureCount
+ {
+ get
+ {
+ if (FirstBin == null)
+ return 0;
+
+ var count = 0;
+ var bin = FirstBin;
+ while (bin != null)
+ {
+ count += bin.NumFeatures;
+ bin = bin.Next;
+ }
+ return count;
+ }
+ }
+
+ ///
+ /// Add the child nodes
+ ///
+ public void AddChildren()
+ {
+ if (Nid >= _tree.FirstLeafNodeId) return;
+
+ var splitBounds = GetSplitBounds(1);
+ var childId = Nid*2;
+ _tree.Nodes[childId] = new SbnNode(_tree, childId++, splitBounds[0], splitBounds[1], splitBounds[2], splitBounds[3]);
+ Child1.AddChildren();
+
+ splitBounds = GetSplitBounds(2);
+ _tree.Nodes[childId] = new SbnNode(_tree, childId, splitBounds[0], splitBounds[1], splitBounds[2], splitBounds[3]);
+ Child2.AddChildren();
+ }
+
+ ///
+ /// Compute the split ordinate for a given
+ ///
+ /// The axis
+ /// The ordinate
+ private byte GetSplitOridnate(int splitAxis)
+ {
+ var mid = (splitAxis == 1)
+ ? /*(int)*/ (byte)((_minX + _maxX) / 2.0 + 1)
+ : /*(int)*/ (byte)((_minY + _maxY) / 2.0 + 1);
+
+ return (byte) (mid - mid%2);
+ }
+
+ ///
+ /// Get the bounds for one of the child nodes
+ ///
+ /// The index of the child node
+ /// The split bounds
+ private byte[] GetSplitBounds(int childIndex)
+ {
+ var splitAxis = Level % 2;// == 1 ? 'x' : 'y';
+
+ var mid = GetSplitOridnate(splitAxis);
+
+ var res = new[] {_minX, _minY, _maxX, _maxY};
+ switch (splitAxis)
+ {
+ case 1: // x-ordinate
+ switch (childIndex)
+ {
+ case 1:
+ res[0] = (byte)(mid + 1);
+ break;
+ case 2:
+ res[2] = mid;
+ break;
+ }
+ break;
+ case 0: // y-ordinate
+ switch (childIndex)
+ {
+ case 1:
+ res[1] = (byte)(mid + 1);
+ break;
+ case 2:
+ res[3] = mid;
+ break;
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("childIndex");
+ }
+
+ return res;
+ }
+
+ ///
+ /// function to count all features in this node and all child nodes
+ ///
+ /// The number of features in all this and all child nodes
+ public int CountAllFeatures()
+ {
+ var res = FeatureCount;
+ if (Nid < _tree.FirstLeafNodeId)
+ res += Child1.CountAllFeatures() + Child2.CountAllFeatures();
+ return res;
+ }
+
+ ///
+ /// Method to query all the ids of features in this node that intersect the box defined
+ /// by , ,
+ /// and
+ ///
+ /// The lower x-ordinate
+ /// The lower y-ordinate
+ /// The upper x-ordinate
+ /// The upper y-ordinate
+ /// A list of feature ids to add to
+ /// An enumeration of feature ids
+ internal void QueryFids(byte minx, byte miny, byte maxx, byte maxy, List fidList)
+ {
+ if (ContainedBy(minx, miny, maxx, maxy))
+ {
+ AddAllFidsInNode(fidList);
+ return;
+ }
+
+ foreach (var feature in this)
+ {
+ if (feature.Intersects(minx, maxx, miny, maxy))
+ fidList.Add(feature.Fid);
+ }
+
+ if (Nid < _tree.FirstLeafNodeId)
+ {
+ if (Child1.Intersects(minx, miny, maxx, maxy))
+ Child1.QueryFids(minx, miny, maxx, maxy, fidList);
+ if (Child2.Intersects(minx, miny, maxx, maxy))
+ Child2.QueryFids(minx, miny, maxx, maxy, fidList);
+ }
+ }
+
+ ///
+ /// Helper method to add all the ids of features in this node and its descendants
+ ///
+ /// An enumeration of feature ids
+ private void AddAllFidsInNode(List fidList)
+ {
+ if (FeatureCount > 0)
+ {
+ var bin = FirstBin;
+ while (bin != null)
+ {
+ fidList.AddRange(bin.GetAllFidsInBin());
+ bin = bin.Next;
+ }
+ }
+
+ if (Nid < _tree.FirstLeafNodeId)
+ {
+ Child1.AddAllFidsInNode(fidList);
+ Child2.AddAllFidsInNode(fidList);
+ }
+ }
+
+ ///
+ /// Method to print out this
+ ///
+ ///
+ /// A text describing this
+ ///
+ public override string ToString()
+ {
+ return string.Format("[SbnNode {0}: ({1}-{2},{3}-{4})/{5}/{6}]", Nid, _minX, _maxX, _minY, _maxY,
+ GetSplitOridnate(Level%2), FeatureCount);
+ }
+
+ ///
+ /// Intersection predicate function
+ ///
+ /// lower x-ordinate
+ /// lower y-ordinate
+ /// upper x-ordinate
+ /// upper y-ordinate
+ /// true if this node's bounding box intersect with the bounding box defined by , , and , otherwise false
+ internal bool Intersects(byte minX, byte minY, byte maxX, byte maxY)
+ {
+ return !(minX > _maxX || maxX < _minX || minY > _maxY || maxY < _minY);
+ }
+
+ ///
+ /// Contains predicate function
+ ///
+ /// lower x-ordinate
+ /// lower y-ordinate
+ /// upper x-ordinate
+ /// upper y-ordinate
+ /// true if this node's bounding box contains the bounding box defined by , , and , otherwise false
+ internal bool Contains(byte minX, byte minY, byte maxX, byte maxY)
+ {
+ return minX >= _minX && maxX <= _maxX &&
+ minY >= _minY && maxY <= _maxY;
+ }
+
+ ///
+ /// ContainedBy predicate function
+ ///
+ /// lower x-ordinate
+ /// lower y-ordinate
+ /// upper x-ordinate
+ /// upper y-ordinate
+ /// true if this node's bounding box contains the bounding box defined by , , and , otherwise false
+ internal bool ContainedBy(byte minX, byte minY, byte maxX, byte maxY)
+ {
+ return _minX >= minX && _maxX <= maxX &&
+ _minY >= minY && _maxY <= maxY;
+ }
+
+ ///
+ /// Function to get an iterator over this node's s
+ ///
+ /// An iterator
+ public IEnumerator GetEnumerator()
+ {
+ return new SbnFeatureEnumerator(FirstBin);
+ }
+
+ ///
+ /// Function to get an iterator over this node's s
+ ///
+ /// An iterator
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Private helper class to enumerate feature ids
+ ///
+ private class SbnFeatureEnumerator : IEnumerator
+ {
+ private SbnBin _firstBin;
+ private SbnBin _currentBin;
+
+ private int _index = -1;
+
+ internal SbnFeatureEnumerator(SbnBin firstBin)
+ {
+ _firstBin = firstBin;
+ }
+
+ void IDisposable.Dispose()
+ {
+ _firstBin = null;
+ _currentBin = null;
+ }
+
+ public bool MoveNext()
+ {
+ // We don't have a bin at all!
+ if (_firstBin == null) return false;
+
+ // We were resetted or havn't started
+ if (_index == -1)
+ _currentBin = _firstBin;
+
+ // did we reach the end!
+ if (_index == _currentBin.NumFeatures)
+ return false;
+
+ // Increment
+ _index++;
+
+ // Did we reach the end of the bin now?
+ if (_index == 100)
+ {
+ //If so move to next
+ _currentBin = _currentBin.Next;
+ //was there another one?
+ if (_currentBin == null) return false;
+ _index = 0;
+ }
+
+ return _index < _currentBin.NumFeatures;
+ }
+
+ public void Reset()
+ {
+ _index = -1;
+ _currentBin = null;
+ }
+
+ public SbnFeature Current
+ {
+ get { return _index == -1 ? new SbnFeature() : _currentBin[_index]; }
+ }
+
+ object IEnumerator.Current
+ {
+ get { return Current; }
+ }
+ }
+
+ ///
+ /// Method to verify this node's bins
+ ///
+ /// true if all bins are valid
+ public bool VerifyBins()
+ {
+#if DEBUG
+ foreach (var feature in this)
+ {
+ if (!Contains(feature.MinX, feature.MinY, feature.MaxX, feature.MaxY))
+ return false;
+ }
+#endif
+ return true;
+ }
+
+ ///
+ /// Method to insert a feature at this node
+ ///
+ /// The feature to add
+ public void Insert(SbnFeature feature)
+ {
+ // if this is leaf, just take the feature
+ if (Nid >= _tree.FirstLeafNodeId)
+ {
+ AddFeature(feature);
+ return;
+ }
+
+ // it takes 8 features to split a node
+ // so we'll hold 8 features first
+ if (Nid > 1)
+ {
+ if (!Full)
+ {
+
+ if (FeatureCount < 8)
+ {
+ AddFeature(feature);
+ return;
+ }
+ if (FeatureCount == 8)
+ {
+ var bin = FirstBin;
+ FirstBin = new SbnBin();
+ Full = true;
+ bin.AddFeature(feature);
+ for (var i = 0; i < 9; i ++)
+ {
+ Insert(bin[i]);
+ }
+ return;
+ }
+ }
+
+ }
+
+ // The node is split so we can sort features
+ int min, max; //, smin, smax;
+ var splitAxis = Level%2;
+ if (splitAxis == 1)
+ {
+ min = feature.MinX;
+ max = feature.MaxX;
+ //smin = feature.MinY;
+ //smax = feature.MaxY;
+ }
+ else
+ {
+ min = feature.MinY;
+ max = feature.MaxY;
+ //smin = feature.MinX;
+ //smax = feature.MaxX;
+ }
+ var seam = GetSplitOridnate(splitAxis);
+
+ // Grab features on the seam we can't split
+ if (min <= seam && max > seam)
+ {
+ AddFeature(feature);
+ }
+
+ else if (min < seam)
+ Child2.Insert(feature);
+ else
+ Child1.Insert(feature);
+ }
+
+ ///
+ /// Method to actually add a feature to this node
+ ///
+ ///
+ private void AddFeature(SbnFeature feature)
+ {
+ if (FeatureCount % 100 == 0)
+ AddBin(new SbnBin());
+
+ var addBin = FirstBin;
+ while (addBin.NumFeatures == 100)
+ addBin = addBin.Next;
+
+ addBin.AddFeature(feature);
+ }
+
+#if VERBOSE
+ public string ToStringVerbose()
+ {
+ return string.Format("{0,5} {1,4}-{2,4} {3,4}-{4,4} {5} {6,4} {7,1}", Nid, _minX, _maxX, _minY, _maxY, Full ? 1 : 0, Full ? FeatureCount : 0, Full ? 0 : FeatureCount);
+ }
+#endif
+
+ ///
+ /// Method to remove a feature from a bin
+ ///
+ ///
+ ///
+ internal bool Remove(SbnFeature searchFeature)
+ {
+ if (!Intersects(searchFeature.MinX, searchFeature.MinY, searchFeature.MaxX, searchFeature.MaxY))
+ return false;
+
+ if (FeatureCount > 0)
+ {
+ var searchBin = FirstBin;
+ for (var i = 0; i < FeatureCount; i++)
+ {
+ var j = i % 100;
+ if (i > 100 && j == 0)
+ searchBin = searchBin.Next;
+ if (searchBin[j].Fid == searchFeature.Fid)
+ {
+ searchBin.RemoveAt(j);
+ return true;
+ }
+ }
+ }
+
+ if (Nid < _tree.FirstLeafNodeId)
+ {
+ return Child1.Remove(searchFeature) ||
+ Child2.Remove(searchFeature);
+ }
+
+ return false;
+ }
+
+ internal SbnFeature RemoveAt(int index)
+ {
+ var bin = FirstBin;
+ while (index >= 100)
+ {
+ bin = bin.Next;
+ index -= 100;
+ }
+
+ var result = bin[index];
+ bin.RemoveAt(index);
+
+ if (FeatureCount == 0)
+ FirstBin = null;
+
+ return result;
+ }
+ }
}
\ No newline at end of file
diff --git a/SharpSbn/SbnQueryOnlyTree.cs b/SharpSbn/SbnQueryOnlyTree.cs
index 1dfb4a4..04f2a54 100644
--- a/SharpSbn/SbnQueryOnlyTree.cs
+++ b/SharpSbn/SbnQueryOnlyTree.cs
@@ -1,532 +1,535 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-#if UseGeoAPI
-using Interval = GeoAPI.DataStructures.Interval;
-using Envelope = GeoAPI.Geometries.Envelope;
-#else
-using Interval = SharpSbn.DataStructures.Interval;
-using Envelope = SharpSbn.DataStructures.Envelope;
-#endif
-
-namespace SharpSbn
-{
- ///
- /// A readonly implementation of an sbn index tree
- ///
- public class SbnQueryOnlyTree : IDisposable
- {
- private readonly Stream _sbnStream;
- private readonly Stream _sbxStream;
-
- private readonly SbnBinIndex _sbnBinIndex;
-
- private readonly SbnHeader _sbnHeader;
- private readonly object _indexLock = new object();
-
- private static int _defaultMaxCacheLevel;
-
- ///
- /// Method to open an sbn index file
- ///
- /// The sbn index filename>
- /// An sbn index query structure
- public static SbnQueryOnlyTree Open(string sbnFilename)
- {
- if (string.IsNullOrEmpty(sbnFilename))
- throw new ArgumentNullException(sbnFilename);
-
- if (!File.Exists(sbnFilename))
- throw new FileNotFoundException("File not found", sbnFilename);
-
- var sbxFilename = Path.ChangeExtension(sbnFilename, "sbx");
- if (!File.Exists(sbxFilename))
- throw new FileNotFoundException("File not found", sbxFilename);
-
- var res = new SbnQueryOnlyTree(sbnFilename, sbxFilename);
-
- var sbxHeader = new SbnHeader();
- sbxHeader.Read(new BinaryReader(res._sbxStream));
-
- if (res._sbnHeader.NumRecords != sbxHeader.NumRecords)
- throw new SbnException("Sbn and Sbx do not serve the same number of features!");
-
- return res;
- }
-
- ///
- /// Static constructor for this class
- ///
- static SbnQueryOnlyTree()
- {
- DefaultMaxCacheLevel = 8;
- }
-
- ///
- /// Creates an istance of this class
- ///
- ///
- ///
- private SbnQueryOnlyTree(string sbnFilename, string sbxFilename)
- {
- _sbnStream = new FileStream(sbnFilename, FileMode.Open, FileAccess.Read, FileShare.Read, 8192);
- _sbnHeader = new SbnHeader();
- var sbnReader = new BinaryReader(_sbnStream);
- _sbnHeader.Read(sbnReader);
- _sbnBinIndex = SbnBinIndex.Read(sbnReader);
-
- _sbxStream = new FileStream(sbxFilename, FileMode.Open, FileAccess.Read, FileShare.Read, 8192);
-
- FirstLeafNodeId = (int)Math.Pow(2, GetNumberOfLevels(_sbnHeader.NumRecords) -1);
- MaxCacheLevel = DefaultMaxCacheLevel;
-
- SbnQueryOnlyNode.CreateRoot(this);
- _sbxStream.Position = 0;
- }
-
- ///
- /// Function to compute the number of levels required for the number of features to add
- ///
- /// The number of features a tree should take
- /// The number of levels
- private static int GetNumberOfLevels(int featureCount)
- {
- var levels = (int)Math.Log(((featureCount - 1) / 8.0 + 1), 2) + 1;
- if (levels < 2) levels = 2;
- if (levels > 24) levels = 24;
-
- return levels;
- }
-
- ///
- /// Method to query the feature's ids
- ///
- /// The extent in which to look for features
- /// An enumeration of feature ids
- public IEnumerable QueryFids(Envelope envelope)
- {
- if (envelope == null || envelope.IsNull)
- return null;
-
- envelope = envelope.Intersection(_sbnHeader.Extent);
- var res = new List();
- if (envelope.IsNull) return res;
-
- byte minx, miny, maxx, maxy;
- ClampUtility.Clamp(_sbnHeader.Extent, envelope, out minx, out miny, out maxx, out maxy);
-
- var nodes = new List();
- Root.QueryNodes(minx, miny, maxx, maxy, nodes);
- nodes.Sort();
- foreach (var node in nodes)
- {
- node.QueryFids(minx, miny, maxx, maxy, res, false);
- }
-
- //Root.QueryFids(minx, miny, maxx, maxy, res);
-
- res.Sort();
- return res;
- }
-
- ///
- /// Gets a value indicating the root node for this tree
- ///
- private SbnQueryOnlyNode Root { get { return GetNode(1); } }
-
- ///
- /// Gets or sets a value indicating the default maximum level that is being cached
- ///
- internal int MaxCacheLevel { get; private set; }
-
- ///
- /// Gets or sets a value indicating the default maximum level that is being cached
- ///
- /// Must be greater or equal to 1
- public static int DefaultMaxCacheLevel
- {
- get { return _defaultMaxCacheLevel; }
- set
- {
- if (value < 1)
- throw new ArgumentException("The default max cache level must be greater or equal 1");
- _defaultMaxCacheLevel = value;
- }
- }
-
- private SbnQueryOnlyNode GetNode(int id)
- {
- var level = (int)Math.Log(id, 2) + 1;
- if (level <= MaxCacheLevel)
- {
- return _sbnBinIndex.GetNode(id);
- }
- return null;
- }
-
- private byte[] GetBinData(int binIndex)
- {
- binIndex--;
- Monitor.Enter(_indexLock);
- _sbxStream.Seek(100 + binIndex*8, SeekOrigin.Begin);
- var sbxReader = new BinaryReader(_sbxStream);
- var sbnPosition = BinaryIOExtensions.ReadUInt32BE(sbxReader)*2;
- var sbnSize = 8 + BinaryIOExtensions.ReadInt32BE(sbxReader) * 2;
- _sbnStream.Seek(sbnPosition, SeekOrigin.Begin);
- var res = new byte[sbnSize];
- _sbnStream.Read(res, 0, sbnSize);
- Monitor.Exit(_indexLock);
- return res;
- }
-
- ///
- /// Gets or sets a value indicating the first leaf node id
- ///
- internal int FirstLeafNodeId { get; private set; }
-
- ///
- /// Gets a value indicating the 2d extent of the tree
- ///
- public Envelope Extent { get { return _sbnHeader.Extent; } }
-
- ///
- /// Gets a value indicating the range of the z-ordinates
- ///
- public Interval ZRange { get { return _sbnHeader.ZRange; } }
-
- ///
- /// Gets a value indicating the range of the m-ordinates
- ///
- public Interval MRange { get { return _sbnHeader.MRange; } }
-
-
- private class SbnQueryOnlyNode : IComparable, IComparable
- {
- private readonly SbnQueryOnlyTree _tree;
- private readonly SbnFeature[] _features;
- private readonly byte _minX, _minY, _maxX, _maxY;
-
- internal int Nid { get; private set; }
-
- internal static void CreateRoot(SbnQueryOnlyTree tree)
- {
- var root = new SbnQueryOnlyNode(tree, 1, new byte[] {0, 0, 255, 255});
- }
-
- private SbnQueryOnlyNode(SbnQueryOnlyTree tree, int nid, byte[] splitBounds)
- {
- _tree = tree;
- Nid = nid;
- _minX = splitBounds[0];
- _minY = splitBounds[1];
- _maxX = splitBounds[2];
- _maxY = splitBounds[3];
-
- _features = ReadBins(nid, _tree._sbnBinIndex);
- if (Level <= _tree.MaxCacheLevel)
- _tree._sbnBinIndex.CacheNode(this);
- }
-
- private SbnFeature[] ReadBins(int nid, SbnBinIndex binIndex)
- {
- var numFeatures = binIndex.GetNumFeatures(nid);
- var res = new SbnFeature[numFeatures];
- if (numFeatures == 0)
- {
- return res;
- }
-
- var firstBinIndex = binIndex.GetFirstBinIndex(nid);
- var numBins = (int)Math.Ceiling(numFeatures / 100d);
-
- for (var i = 0; i < numBins; i++)
- {
- using (var ms = new BinaryReader(new MemoryStream(_tree.GetBinData(firstBinIndex + i))))
- {
- var bin = new SbnBin();
- var binId = bin.Read(ms);
- if (binId != firstBinIndex + i)
- throw new SbnException("Corrupt sbn file");
- bin.CopyTo(res, i * 100);
- }
- }
- return res;
- }
-
- internal void QueryFids(byte minx, byte miny, byte maxx, byte maxy, List fids, bool checkChildren)
- {
- if (ContainedBy(minx, miny, maxx, maxy))
- {
- AddAllFidsInNode(fids, checkChildren);
- return;
- }
-
- foreach (var feature in _features)
- {
- if (feature.Intersects(minx, maxx, miny, maxy))
- fids.Add(feature.Fid);
- }
-
- if (checkChildren && Nid < _tree.FirstLeafNodeId)
- {
- var child = GetChild(0);
- if (child.Intersects(minx, miny, maxx, maxy))
- child.QueryFids(minx, miny, maxx, maxy, fids, true);
-
- child = GetChild(1);
- if (child.Intersects(minx, miny, maxx, maxy))
- child.QueryFids(minx, miny, maxx, maxy, fids, true);
- }
- }
-
- private void AddAllFidsInNode(List list, bool checkChildren)
- {
- foreach (var sbnFeature in _features)
- {
- list.Add(sbnFeature.Fid);
- }
-
- if (checkChildren && Nid < _tree.FirstLeafNodeId)
- {
- GetChild(0).AddAllFidsInNode(list, true);
- GetChild(1).AddAllFidsInNode(list, true);
- }
- }
-
- private SbnQueryOnlyNode GetChild(int childIndex)
- {
- var nodeIndex = Nid * 2 + childIndex;
- SbnQueryOnlyNode res = null;
- if (Level <= _tree.MaxCacheLevel)
- res = _tree.GetNode(nodeIndex);
- if (res != null)
- return res;
-
- res = new SbnQueryOnlyNode(_tree, nodeIndex, GetSplitBounds(childIndex));
- return res;
-
- }
-
- private byte[] GetSplitBounds(int childIndex)
- {
- var splitAxis = Level % 2;// == 1 ? 'x' : 'y';
-
- var mid = GetSplitOridnate(splitAxis);
-
- var res = new[] { _minX, _minY, _maxX, _maxY };
- switch (splitAxis)
- {
- case 1: // x-ordinate
- switch (childIndex)
- {
- case 0:
- res[0] = (byte)(mid + 1);
- break;
- case 1:
- res[2] = mid;
- break;
- }
- break;
- case 0: // y-ordinate
- switch (childIndex)
- {
- case 0:
- res[1] = (byte)(mid + 1);
- break;
- case 1:
- res[3] = mid;
- break;
- }
- break;
- default:
- throw new ArgumentOutOfRangeException("childIndex");
- }
-
- return res;
- }
-
- ///
- /// Compute the split ordinate for a given
- ///
- /// The axis
- /// The ordinate
- private byte GetSplitOridnate(int splitAxis)
- {
- var mid = (splitAxis == 1)
- ? /*(int)*/ (byte)((_minX + _maxX) / 2.0 + 1)
- : /*(int)*/ (byte)((_minY + _maxY) / 2.0 + 1);
-
- return (byte)(mid - mid % 2);
- }
- ///
- /// Gets the node's level
- ///
- private int Level { get { return (int)Math.Log(Nid, 2) + 1; } }
-
- ///
- /// Intersection predicate function
- ///
- /// lower x-ordinate
- /// lower y-ordinate
- /// upper x-ordinate
- /// upper y-ordinate
- /// true if this node's bounding box intersect with the bounding box defined by , , and , otherwise false
- private bool Intersects(byte minX, byte minY, byte maxX, byte maxY)
- {
- return !(minX > _maxX || maxX < _minX || minY > _maxY || maxY < _minY);
- }
-
- ///
- /// ContainedBy predicate function
- ///
- /// lower x-ordinate
- /// lower y-ordinate
- /// upper x-ordinate
- /// upper y-ordinate
- /// true if this node's bounding box contains the bounding box defined by , , and , otherwise false
- private bool ContainedBy(byte minX, byte minY, byte maxX, byte maxY)
- {
- return _minX >= minX && _maxX <= maxX &&
- _minY >= minY && _maxY <= maxY;
- }
-
- public void QueryNodes(byte minx, byte miny, byte maxx, byte maxy, List nodes)
- {
-
- if (!Intersects(minx, miny, maxx, maxy))
- return;
-
- if (ContainedBy(minx, miny, maxx, maxy))
- {
- AddAllNodes(nodes);
- return;
- }
-
- // Add this node
- nodes.Add(this);
-
- // Test if child nodes are to be added
- if (Nid < _tree.FirstLeafNodeId)
- {
- GetChild(0).QueryNodes(minx, miny, maxx, maxy, nodes);
- GetChild(1).QueryNodes(minx, miny, maxx, maxy, nodes);
- }
- }
-
- private void AddAllNodes(List nodes)
- {
- nodes.Add(this);
- if (Nid < _tree.FirstLeafNodeId)
- {
- GetChild(0).AddAllNodes(nodes);
- GetChild(1).AddAllNodes(nodes);
- }
- }
-
- int IComparable.CompareTo(object obj)
- {
- if (obj == null)
- throw new ArgumentNullException();
- if (!(obj is SbnQueryOnlyNode))
- throw new ArgumentException("Object not a SbnQueryOnlyNode", "obj");
-
- return ((IComparable) this).CompareTo((SbnQueryOnlyNode) obj);
- }
-
- int IComparable.CompareTo(SbnQueryOnlyNode other)
- {
- if (other == null)
- throw new ArgumentNullException("other");
-
- if (Nid < other.Nid) return -1;
- if (Nid > other.Nid) return 1;
- return 0;
- }
- }
-
- private class SbnBinIndex
- {
- private struct SbnNodeToBinIndexEntry
- {
- internal Int32 FirstBinIndex;
- internal Int32 NumFeatures;
- public SbnQueryOnlyNode Node
- { get; internal set; }
-
-
- }
-
- private readonly SbnNodeToBinIndexEntry[] _nodeToBin;
-
- internal static SbnBinIndex Read(BinaryReader reader)
- {
- if (BinaryIOExtensions.ReadInt32BE(reader) != 1)
- throw new SbnException("Sbn file corrupt");
-
- var length = BinaryIOExtensions.ReadInt32BE(reader);
- var maxNodeId = length / 4;
- var nodeToBin = new SbnNodeToBinIndexEntry[maxNodeId + 1];
- for (var i = 1; i <= maxNodeId; i++)
- {
- var binIndex = BinaryIOExtensions.ReadInt32BE(reader);
- var numFeatures = BinaryIOExtensions.ReadInt32BE(reader);
- if (binIndex > 0)
- nodeToBin[i] = new SbnNodeToBinIndexEntry { FirstBinIndex = binIndex, NumFeatures = numFeatures };
- }
-
- return new SbnBinIndex(nodeToBin);
- }
-
- private SbnBinIndex(SbnNodeToBinIndexEntry[] nodeToBin)
- {
- _nodeToBin = nodeToBin;
- }
-
- internal Int32 GetFirstBinIndex(int nodeIndex)
- {
- if (nodeIndex < 1 || nodeIndex > _nodeToBin.GetUpperBound(0))
- throw new ArgumentOutOfRangeException("nodeIndex");
-
- return _nodeToBin[nodeIndex].FirstBinIndex;
- }
-
- internal Int32 GetNumFeatures(int nodeIndex)
- {
- if (nodeIndex < 1 || nodeIndex > _nodeToBin.GetUpperBound(0))
- return 0;
-
- return _nodeToBin[nodeIndex].NumFeatures;
- }
-
- public SbnQueryOnlyNode GetNode(int id)
- {
- if (id < _nodeToBin.Length)
- return _nodeToBin[id].Node;
- return null;
- }
-
- public void CacheNode(SbnQueryOnlyNode sbnQueryOnlyNode)
- {
- if (sbnQueryOnlyNode.Nid < _nodeToBin.Length)
- _nodeToBin[sbnQueryOnlyNode.Nid].Node = sbnQueryOnlyNode;
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (_sbnStream != null) _sbnStream.Dispose();
- if (_sbxStream != null) _sbxStream.Dispose();
- }
-
- }
- }
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+#if UseGeoAPI
+using Interval = GeoAPI.DataStructures.Interval;
+using Envelope = GeoAPI.Geometries.Envelope;
+#else
+using Interval = SharpSbn.DataStructures.Interval;
+using Envelope = SharpSbn.DataStructures.Envelope;
+#endif
+
+namespace SharpSbn
+{
+ ///
+ /// A readonly implementation of an sbn index tree
+ ///
+ public class SbnQueryOnlyTree : IDisposable
+ {
+ private readonly Stream _sbnStream;
+ private readonly Stream _sbxStream;
+
+ private readonly SbnBinIndex _sbnBinIndex;
+
+ private readonly SbnHeader _sbnHeader;
+ private readonly object _indexLock = new object();
+
+ private static int _defaultMaxCacheLevel;
+
+ ///
+ /// Method to open an sbn index file
+ ///
+ /// The sbn index filename>
+ /// An sbn index query structure
+ public static SbnQueryOnlyTree Open(string sbnFilename)
+ {
+ if (string.IsNullOrEmpty(sbnFilename))
+ throw new ArgumentNullException(sbnFilename);
+
+ if (!File.Exists(sbnFilename))
+ throw new FileNotFoundException("File not found", sbnFilename);
+
+ var sbxFilename = Path.ChangeExtension(sbnFilename, "sbx");
+ if (!File.Exists(sbxFilename))
+ throw new FileNotFoundException("File not found", sbxFilename);
+
+ var res = new SbnQueryOnlyTree(sbnFilename, sbxFilename);
+
+ var sbxHeader = new SbnHeader();
+ sbxHeader.Read(new BinaryReader(res._sbxStream));
+
+ if (res._sbnHeader.NumRecords != sbxHeader.NumRecords)
+ throw new SbnException("Sbn and Sbx do not serve the same number of features!");
+
+ return res;
+ }
+
+ ///
+ /// Static constructor for this class
+ ///
+ static SbnQueryOnlyTree()
+ {
+ DefaultMaxCacheLevel = 8;
+ }
+
+ ///
+ /// Creates an istance of this class
+ ///
+ ///
+ ///
+ private SbnQueryOnlyTree(string sbnFilename, string sbxFilename)
+ {
+ _sbnStream = new FileStream(sbnFilename, FileMode.Open, FileAccess.Read, FileShare.Read, 8192);
+ _sbnHeader = new SbnHeader();
+ var sbnReader = new BinaryReader(_sbnStream);
+ _sbnHeader.Read(sbnReader);
+ _sbnBinIndex = SbnBinIndex.Read(sbnReader);
+
+ _sbxStream = new FileStream(sbxFilename, FileMode.Open, FileAccess.Read, FileShare.Read, 8192);
+
+ FirstLeafNodeId = (int)Math.Pow(2, GetNumberOfLevels(_sbnHeader.NumRecords) -1);
+ MaxCacheLevel = DefaultMaxCacheLevel;
+
+ SbnQueryOnlyNode.CreateRoot(this);
+ _sbxStream.Position = 0;
+ }
+
+ ///
+ /// Function to compute the number of levels required for the number of features to add
+ ///
+ /// The number of features a tree should take
+ /// The number of levels
+ private static int GetNumberOfLevels(int featureCount)
+ {
+ var levels = (int)Math.Log(((featureCount - 1) / 8.0 + 1), 2) + 1;
+ if (levels < 2) levels = 2;
+ if (levels > 24) levels = 24;
+
+ return levels;
+ }
+
+ ///
+ /// Method to query the feature's ids
+ ///
+ /// The extent in which to look for features
+ /// An enumeration of feature ids
+ public IEnumerable QueryFids(Envelope envelope)
+ {
+ if (envelope == null || envelope.IsNull)
+ return null;
+
+ envelope = envelope.Intersection(_sbnHeader.Extent);
+ var res = new List();
+ if (envelope.IsNull) return res;
+
+ byte minx, miny, maxx, maxy;
+ ClampUtility.Clamp(_sbnHeader.Extent, envelope, out minx, out miny, out maxx, out maxy);
+
+ var nodes = new List();
+ Root.QueryNodes(minx, miny, maxx, maxy, nodes);
+ nodes.Sort();
+ foreach (var node in nodes)
+ {
+ node.QueryFids(minx, miny, maxx, maxy, res, false);
+ }
+
+ //Root.QueryFids(minx, miny, maxx, maxy, res);
+
+ res.Sort();
+ return res;
+ }
+
+ ///
+ /// Gets a value indicating the root node for this tree
+ ///
+ private SbnQueryOnlyNode Root { get { return GetNode(1); } }
+
+ ///
+ /// Gets or sets a value indicating the default maximum level that is being cached
+ ///
+ internal int MaxCacheLevel { get; private set; }
+
+ ///
+ /// Gets or sets a value indicating the default maximum level that is being cached
+ ///
+ /// Must be greater or equal to 1
+ public static int DefaultMaxCacheLevel
+ {
+ get { return _defaultMaxCacheLevel; }
+ set
+ {
+ if (value < 1)
+ throw new ArgumentException("The default max cache level must be greater or equal 1");
+ _defaultMaxCacheLevel = value;
+ }
+ }
+
+ private SbnQueryOnlyNode GetNode(int id)
+ {
+ var level = (int)Math.Log(id, 2) + 1;
+ if (level <= MaxCacheLevel)
+ {
+ return _sbnBinIndex.GetNode(id);
+ }
+ return null;
+ }
+
+ private byte[] GetBinData(int binIndex)
+ {
+ binIndex--;
+ Monitor.Enter(_indexLock);
+ _sbxStream.Seek(100 + binIndex*8, SeekOrigin.Begin);
+ var sbxReader = new BinaryReader(_sbxStream);
+ var sbnPosition = BinaryIOExtensions.ReadUInt32BE(sbxReader)*2;
+ var sbnSize = 8 + BinaryIOExtensions.ReadInt32BE(sbxReader) * 2;
+ _sbnStream.Seek(sbnPosition, SeekOrigin.Begin);
+ var res = new byte[sbnSize];
+ _sbnStream.Read(res, 0, sbnSize);
+ Monitor.Exit(_indexLock);
+ return res;
+ }
+
+ ///
+ /// Gets or sets a value indicating the first leaf node id
+ ///
+ internal int FirstLeafNodeId { get; private set; }
+
+ ///
+ /// Gets a value indicating the 2d extent of the tree
+ ///
+ public Envelope Extent { get { return _sbnHeader.Extent; } }
+
+ ///
+ /// Gets a value indicating the range of the z-ordinates
+ ///
+ public Interval ZRange { get { return _sbnHeader.ZRange; } }
+
+ ///
+ /// Gets a value indicating the range of the m-ordinates
+ ///
+ public Interval MRange { get { return _sbnHeader.MRange; } }
+
+
+ private class SbnQueryOnlyNode : IComparable, IComparable
+ {
+ private readonly SbnQueryOnlyTree _tree;
+ private readonly SbnFeature[] _features;
+ private readonly byte _minX, _minY, _maxX, _maxY;
+
+ internal int Nid { get; private set; }
+
+ internal static void CreateRoot(SbnQueryOnlyTree tree)
+ {
+ var root = new SbnQueryOnlyNode(tree, 1, new byte[] {0, 0, 255, 255});
+ }
+
+ private SbnQueryOnlyNode(SbnQueryOnlyTree tree, int nid, byte[] splitBounds)
+ {
+ _tree = tree;
+ Nid = nid;
+ _minX = splitBounds[0];
+ _minY = splitBounds[1];
+ _maxX = splitBounds[2];
+ _maxY = splitBounds[3];
+
+ _features = ReadBins(nid, _tree._sbnBinIndex);
+ if (Level <= _tree.MaxCacheLevel)
+ _tree._sbnBinIndex.CacheNode(this);
+ }
+
+ private SbnFeature[] ReadBins(int nid, SbnBinIndex binIndex)
+ {
+ var numFeatures = binIndex.GetNumFeatures(nid);
+ var res = new SbnFeature[numFeatures];
+ if (numFeatures == 0)
+ {
+ return res;
+ }
+
+ var firstBinIndex = binIndex.GetFirstBinIndex(nid);
+ var numBins = (int)Math.Ceiling(numFeatures / 100d);
+
+ for (var i = 0; i < numBins; i++)
+ {
+ using (var ms = new BinaryReader(new MemoryStream(_tree.GetBinData(firstBinIndex + i))))
+ {
+ var bin = new SbnBin();
+ var binId = bin.Read(ms);
+ if (binId != firstBinIndex + i)
+ throw new SbnException("Corrupt sbn file");
+ bin.CopyTo(res, i * 100);
+ }
+ }
+ return res;
+ }
+
+ internal void QueryFids(byte minx, byte miny, byte maxx, byte maxy, List fids, bool checkChildren)
+ {
+ if (ContainedBy(minx, miny, maxx, maxy))
+ {
+ AddAllFidsInNode(fids, checkChildren);
+ return;
+ }
+
+ foreach (var feature in _features)
+ {
+ if (feature.Intersects(minx, maxx, miny, maxy))
+ fids.Add(feature.Fid);
+ }
+
+ if (checkChildren && Nid < _tree.FirstLeafNodeId)
+ {
+ var child = GetChild(0);
+ if (child.Intersects(minx, miny, maxx, maxy))
+ child.QueryFids(minx, miny, maxx, maxy, fids, true);
+
+ child = GetChild(1);
+ if (child.Intersects(minx, miny, maxx, maxy))
+ child.QueryFids(minx, miny, maxx, maxy, fids, true);
+ }
+ }
+
+ private void AddAllFidsInNode(List list, bool checkChildren)
+ {
+ foreach (var sbnFeature in _features)
+ {
+ list.Add(sbnFeature.Fid);
+ }
+
+ if (checkChildren && Nid < _tree.FirstLeafNodeId)
+ {
+ GetChild(0).AddAllFidsInNode(list, true);
+ GetChild(1).AddAllFidsInNode(list, true);
+ }
+ }
+
+ private SbnQueryOnlyNode GetChild(int childIndex)
+ {
+ var nodeIndex = Nid * 2 + childIndex;
+ SbnQueryOnlyNode res = null;
+ if (Level <= _tree.MaxCacheLevel)
+ res = _tree.GetNode(nodeIndex);
+ if (res != null)
+ return res;
+
+ res = new SbnQueryOnlyNode(_tree, nodeIndex, GetSplitBounds(childIndex));
+ return res;
+
+ }
+
+ private byte[] GetSplitBounds(int childIndex)
+ {
+ var splitAxis = Level % 2;// == 1 ? 'x' : 'y';
+
+ var mid = GetSplitOridnate(splitAxis);
+
+ var res = new[] { _minX, _minY, _maxX, _maxY };
+ switch (splitAxis)
+ {
+ case 1: // x-ordinate
+ switch (childIndex)
+ {
+ case 0:
+ res[0] = (byte)(mid + 1);
+ break;
+ case 1:
+ res[2] = mid;
+ break;
+ }
+ break;
+ case 0: // y-ordinate
+ switch (childIndex)
+ {
+ case 0:
+ res[1] = (byte)(mid + 1);
+ break;
+ case 1:
+ res[3] = mid;
+ break;
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("childIndex");
+ }
+
+ return res;
+ }
+
+ ///
+ /// Compute the split ordinate for a given
+ ///
+ /// The axis
+ /// The ordinate
+ private byte GetSplitOridnate(int splitAxis)
+ {
+ var mid = (splitAxis == 1)
+ ? /*(int)*/ (byte)((_minX + _maxX) / 2.0 + 1)
+ : /*(int)*/ (byte)((_minY + _maxY) / 2.0 + 1);
+
+ return (byte)(mid - mid % 2);
+ }
+ ///
+ /// Gets the node's level
+ ///
+ private int Level { get { return (int)Math.Log(Nid, 2) + 1; } }
+
+ ///
+ /// Intersection predicate function
+ ///
+ /// lower x-ordinate
+ /// lower y-ordinate
+ /// upper x-ordinate
+ /// upper y-ordinate
+ /// true if this node's bounding box intersect with the bounding box defined by , , and , otherwise false
+ private bool Intersects(byte minX, byte minY, byte maxX, byte maxY)
+ {
+ return !(minX > _maxX || maxX < _minX || minY > _maxY || maxY < _minY);
+ }
+
+ ///
+ /// ContainedBy predicate function
+ ///
+ /// lower x-ordinate
+ /// lower y-ordinate
+ /// upper x-ordinate
+ /// upper y-ordinate
+ /// true if this node's bounding box contains the bounding box defined by , , and , otherwise false
+ private bool ContainedBy(byte minX, byte minY, byte maxX, byte maxY)
+ {
+ return _minX >= minX && _maxX <= maxX &&
+ _minY >= minY && _maxY <= maxY;
+ }
+
+ public void QueryNodes(byte minx, byte miny, byte maxx, byte maxy, List nodes)
+ {
+
+ if (!Intersects(minx, miny, maxx, maxy))
+ return;
+
+ if (ContainedBy(minx, miny, maxx, maxy))
+ {
+ AddAllNodes(nodes);
+ return;
+ }
+
+ // Add this node
+ nodes.Add(this);
+
+ // Test if child nodes are to be added
+ if (Nid < _tree.FirstLeafNodeId)
+ {
+ GetChild(0).QueryNodes(minx, miny, maxx, maxy, nodes);
+ GetChild(1).QueryNodes(minx, miny, maxx, maxy, nodes);
+ }
+ }
+
+ private void AddAllNodes(List nodes)
+ {
+ nodes.Add(this);
+ if (Nid < _tree.FirstLeafNodeId)
+ {
+ GetChild(0).AddAllNodes(nodes);
+ GetChild(1).AddAllNodes(nodes);
+ }
+ }
+
+ int IComparable.CompareTo(object obj)
+ {
+ if (obj == null)
+ throw new ArgumentNullException();
+ if (!(obj is SbnQueryOnlyNode))
+ throw new ArgumentException("Object not a SbnQueryOnlyNode", "obj");
+
+ return ((IComparable) this).CompareTo((SbnQueryOnlyNode) obj);
+ }
+
+ int IComparable.CompareTo(SbnQueryOnlyNode other)
+ {
+ if (other == null)
+ throw new ArgumentNullException("other");
+
+ if (Nid < other.Nid) return -1;
+ if (Nid > other.Nid) return 1;
+ return 0;
+ }
+ }
+
+ private class SbnBinIndex
+ {
+ private struct SbnNodeToBinIndexEntry
+ {
+ internal Int32 FirstBinIndex;
+ internal Int32 NumFeatures;
+ public SbnQueryOnlyNode Node
+ { get; internal set; }
+
+
+ }
+
+ private readonly SbnNodeToBinIndexEntry[] _nodeToBin;
+
+ internal static SbnBinIndex Read(BinaryReader reader)
+ {
+ if (BinaryIOExtensions.ReadInt32BE(reader) != 1)
+ throw new SbnException("Sbn file corrupt");
+
+ var length = BinaryIOExtensions.ReadInt32BE(reader);
+ var maxNodeId = length / 4;
+ var nodeToBin = new SbnNodeToBinIndexEntry[maxNodeId + 1];
+ for (var i = 1; i <= maxNodeId; i++)
+ {
+ var binIndex = BinaryIOExtensions.ReadInt32BE(reader);
+ var numFeatures = BinaryIOExtensions.ReadInt32BE(reader);
+ if (binIndex > 0)
+ nodeToBin[i] = new SbnNodeToBinIndexEntry { FirstBinIndex = binIndex, NumFeatures = numFeatures };
+ }
+
+ return new SbnBinIndex(nodeToBin);
+ }
+
+ private SbnBinIndex(SbnNodeToBinIndexEntry[] nodeToBin)
+ {
+ _nodeToBin = nodeToBin;
+ }
+
+ internal Int32 GetFirstBinIndex(int nodeIndex)
+ {
+ if (nodeIndex < 1 || nodeIndex > _nodeToBin.GetUpperBound(0))
+ throw new ArgumentOutOfRangeException("nodeIndex");
+
+ return _nodeToBin[nodeIndex].FirstBinIndex;
+ }
+
+ internal Int32 GetNumFeatures(int nodeIndex)
+ {
+ if (nodeIndex < 1 || nodeIndex > _nodeToBin.GetUpperBound(0))
+ return 0;
+
+ return _nodeToBin[nodeIndex].NumFeatures;
+ }
+
+ public SbnQueryOnlyNode GetNode(int id)
+ {
+ if (id < _nodeToBin.Length)
+ return _nodeToBin[id].Node;
+ return null;
+ }
+
+ public void CacheNode(SbnQueryOnlyNode sbnQueryOnlyNode)
+ {
+ if (sbnQueryOnlyNode.Nid < _nodeToBin.Length)
+ _nodeToBin[sbnQueryOnlyNode.Nid].Node = sbnQueryOnlyNode;
+ }
+ }
+
+ ///
+ /// Method to dispose this object
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_sbnStream != null) _sbnStream.Dispose();
+ if (_sbxStream != null) _sbxStream.Dispose();
+ }
+
+ }
+ }
}
\ No newline at end of file
diff --git a/SharpSbn/SbnTree.cs b/SharpSbn/SbnTree.cs
index a3ea1cd..fc5d1e4 100644
--- a/SharpSbn/SbnTree.cs
+++ b/SharpSbn/SbnTree.cs
@@ -1,968 +1,984 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-//#if !(NET40 || NET45 || PCL)
-//using FrameworkReplacemets;
-//#if !NET35
-//using FrameworkReplacemets.Linq;
-//#endif
-//using Enumerable = System.Linq.Enumerable;
-//#else
-//using System.Linq;
-//#endif
-#if !NET35
-using Enumerable = FrameworkReplacements.Linq.Enumerable;
-#else
-using Enumerable = System.Linq.Enumerable;
-#endif
-#if !(NET40 || NET45 || PCL)
-using FrameworkReplacements;
-#endif
-#if UseGeoAPI
-using Interval = GeoAPI.DataStructures.Interval;
-using GeoAPI.Geometries;
-#else
-using Interval = SharpSbn.DataStructures.Interval;
-using Envelope = SharpSbn.DataStructures.Envelope;
-#endif
-using System.Threading;
-using SbnEnumerable = FrameworkReplacements.Linq.Enumerable;
-
-namespace SharpSbn
-{
- ///
- /// A Sbn spatial tree
- ///
- public class SbnTree
- {
- ///
- /// Property to test if GeoAPI is used or not!
- ///
- public static bool HasGeoAPISupport
- {
- get
- {
- return
-#if UseGeoAPI
- true;
-#else
- false;
-#endif
- }
- }
-
-#if !PCL
- ///
- /// Method to describe the tree's content
- ///
- ///
- ///
- public static void SbnToText(string sbnTree, TextWriter writer)
- {
- using (var br = new BinaryReader(File.OpenRead(sbnTree)))
- {
- // header
- var header = new SbnHeader();
- header.Read(br);
- writer.WriteLine(header.ToString());
-
- // Bin header
- writer.WriteLine("[BinHeader]");
-
- if (BinaryIOExtensions.ReadUInt32BE(br) != 1)
- throw new SbnException("Invalid format, expecting 1");
-
- var maxNodeId = BinaryIOExtensions.ReadInt32BE(br) / 4;
- writer.WriteLine("#1, {0} => MaxNodeId = {1}", maxNodeId * 4, maxNodeId);
-
- var ms = new MemoryStream(br.ReadBytes(maxNodeId * 8));
-
- using (var msReader = new BinaryReader(ms))
- {
- var index = 2;
- while (msReader.BaseStream.Position < msReader.BaseStream.Length)
- {
- writer.WriteLine("#{2}, Index {0}, NumFeatures={1}",
- BinaryIOExtensions.ReadInt32BE(msReader), BinaryIOExtensions.ReadInt32BE(msReader), index++);
- }
- }
-
- writer.WriteLine("[Bins]");
- while (br.BaseStream.Position < br.BaseStream.Length)
- {
- var bin = new SbnBin();
- var binId = bin.Read(br);
- writer.Write("[SbnBin {0}: {1}]\n", binId, bin.NumFeatures);
- for (var i = 0; i < bin.NumFeatures;i++)
- writer.WriteLine(" "+ bin[i]);
- }
-
- }
- writer.Flush();
- }
-#endif
-
-#if !PCL
-
- ///
- /// Method to load an SBN index from a file
- ///
- /// The filename
- /// The SBN index
- public static SbnTree Load(string sbnFilename)
- {
- if (string.IsNullOrEmpty(sbnFilename))
- throw new ArgumentNullException("sbnFilename");
-
- if (!File.Exists(sbnFilename))
- throw new FileNotFoundException("File not found", sbnFilename);
-
- using (var stream = new FileStream(sbnFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
- {
- return Load(stream);
- }
- }
-#endif
-
- ///
- /// Method to load an SBN index from a stream
- ///
- /// The stream
- /// The SBN index
- public static SbnTree Load(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException("stream");
-
- using (var reader = new BinaryReader(stream))
- {
- return new SbnTree(reader);
- }
- }
-
- private readonly SbnHeader _header = new SbnHeader();
- internal SbnNode[] Nodes;
-#if NET40 || NET45
- private readonly HashSet _featureIds = new HashSet();
-#else
- private readonly Dictionary _featureIds = new Dictionary();
-#endif
- private readonly object _syncRoot = new object();
-
- ///
- /// Creates the tree reading data from the
- ///
- /// The reader to use
- private SbnTree(BinaryReader reader)
- {
- Monitor.Enter(_syncRoot);
-
- _header = new SbnHeader();
- _header.Read(reader);
-
- BuildTree(_header.NumRecords);
-
- if (BinaryIOExtensions.ReadUInt32BE(reader) != 1)
- throw new SbnException("Invalid format, expecting 1");
-
- var maxNodeId = BinaryIOExtensions.ReadInt32BE(reader) / 4;
- var ms = new MemoryStream(reader.ReadBytes(maxNodeId * 8));
- using (var msReader = new BinaryReader(ms))
- {
- var indexNodeId = 1;
- while (msReader.BaseStream.Position < msReader.BaseStream.Length)
- {
- var nid = BinaryIOExtensions.ReadInt32BE(msReader);
- var featureCount = BinaryIOExtensions.ReadInt32BE(msReader);
-
- if (nid > 1)
- {
- var node = Nodes[indexNodeId];
- while (node.FeatureCount < featureCount)
- {
- var bin = new SbnBin();
- bin.Read(reader);
- node.AddBin(bin, true);
- }
- Debug.Assert(node.VerifyBins());
- }
- indexNodeId++;
- }
- }
-
- //Gather all feature ids
- GatherFids();
-
- //Assertions
- Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length);
- Debug.Assert(_featureIds.Count == _header.NumRecords);
-
-
- Monitor.Exit(_syncRoot);
- }
-
- public SbnTree(SbnHeader header)
- {
- _header = header;
- BuildTree(_header.NumRecords);
- }
-
- ///
- /// Gets a value Synchronization object
- ///
- public object SyncRoot { get { return _syncRoot; } }
-
- ///
- /// Get a value indicating if this tree is synchronized
- ///
- public bool IsSynchronized
- {
- get
- {
- if (!Monitor.TryEnter(_syncRoot))
- return false;
- Monitor.Exit(_syncRoot);
- return true;
- }
- }
-
- ///
- /// Method to collect all feature ids in the tree
- ///
- private void GatherFids()
- {
- foreach (SbnNode sbnNode in Enumerable.Skip(Nodes, 1))
- {
- if (sbnNode == null) continue;
-
- foreach (var feature in sbnNode)
-#if NET40 || NET45
- _featureIds.Add(feature.Fid);
-#else
- _featureIds.Add(feature.Fid, 0);
-#endif
- }
- }
-
- ///
- /// Method to build the tree
- ///
- /// The number of features in the tree
- private void BuildTree(int numFeatures)
- {
- Built = false;
- NumLevels = GetNumberOfLevels(numFeatures);
- FirstLeafNodeId = (int)Math.Pow(2, NumLevels - 1);
- CreateNodes((int)Math.Pow(2, NumLevels));
- }
-
- ///
- /// Gets a value indicating the 2d extent of the tree
- ///
- public Envelope Extent { get { return _header.Extent; }}
-
- ///
- /// Gets a value indicating the range of the z-ordinates
- ///
- public Interval ZRange { get { return _header.ZRange; }}
-
- ///
- /// Gets a value indicating the range of the m-ordinates
- ///
- public Interval MRange { get { return _header.MRange; } }
-
- ///
- /// Event raised when a rebuild of the tree is required,
- /// that requires access to all feature information.
- ///
- public event EventHandler RebuildRequried;
-
- ///
- /// Event invoker for the event
- ///
- ///
- protected virtual void OnRebuildRequired(SbnTreeRebuildRequiredEventArgs e)
- {
- var handler = RebuildRequried;
- if (handler != null)
- handler(this, e);
- }
-
-#if UseGeoAPI
- ///
- /// Method to insert a new feature to the tree
- ///
- /// The feature's id
- /// The feature's geometry
- [CLSCompliant(false)]
- public void Insert(uint fid, GeoAPI.Geometries.IGeometry geometry)
- {
- Interval x, y, z, m;
- GeometryMetricExtensions.GetMetric(geometry, out x, out y, out z, out m);
- Insert(fid, geometry.EnvelopeInternal, z, m);
-
- }
-
- ///
- /// Method to create an from an id and a geometry
- ///
- /// The feature's id
- /// The geometry
- /// A sbnfeature
- private SbnFeature ToSbnFeature(uint fid, GeoAPI.Geometries.IGeometry geometry)
- {
- return new SbnFeature(_header.Extent, fid, geometry.EnvelopeInternal);
- }
-
-#endif
-
- ///
- /// Method to insert a new feature to the tree
- ///
- /// The feature's id
- /// The feature's geometry
- [CLSCompliant(false)]
- public void Insert(uint fid, Envelope envelope, Interval? zRange = null, Interval? mRange = null)
- {
- // lock the tree
- Monitor.Enter(_syncRoot);
-
- // Convert to an sbnfeature
- var sbnFeature = ToSbnFeature(fid, envelope);
-
- var inserted = false;
- // Has the tree already been built?
- if (Built)
- {
- // Does the feature fit into the current tree, signal that
- // the tree needs to be recreated in order to function properly.
- if (!_header.Extent.Contains(envelope))
- {
- OnRebuildRequired(new SbnTreeRebuildRequiredEventArgs(fid, envelope, zRange, mRange));
- Monitor.Exit(_syncRoot);
- return;
- }
-
- // Compute number of features in tree
- var featureCount = FeatureCount + 1;
-
- // Does the new number of features require more levels?
- if (GetNumberOfLevels(featureCount) != NumLevels)
- {
- // This can be done inplace.
- RebuildTree(featureCount, sbnFeature);
- inserted = true;
- }
- }
-
- //Insert the feature
- if (!inserted) Insert(sbnFeature);
-
- // Update the header metrics
- _header.AddFeature(fid, envelope, zRange ?? Interval.Create(), mRange ?? Interval.Create());
-
- // unlock the tree
- Monitor.Exit(_syncRoot);
-
- }
-
- ///
- /// Method to -inplace- rebuild the tree
- ///
- /// The number of features for the tree
- /// The new feature to add
- private void RebuildTree(int featureCount, SbnFeature newFeature)
- {
- var nodes = Nodes;
- _featureIds.Clear();
-
- BuildTree(featureCount);
-
- for (var i = 1; i < nodes.Length; i++)
- {
- foreach (var feature in nodes[i])
- Insert(feature);
- }
- Insert(newFeature);
-
- CompactSeamFeatures();
- }
-
- ///
- /// Method to remove the feature
- ///
- /// The id of the feature
- /// The envelope in which to search for the feature
- [CLSCompliant(false)]
- public void Remove(uint fid, Envelope envelope = null)
- {
- Monitor.Enter(_syncRoot);
-
- envelope = envelope ?? _header.Extent;
- var searchFeature = new SbnFeature(_header, fid, envelope);
- Root.Remove(searchFeature);
-
- Monitor.Exit(_syncRoot);
- }
-
- ///
- /// Method to insert a feature to the tree
- ///
- ///
- internal void Insert(SbnFeature feature)
- {
- // Insert a feature into the tree
- Root.Insert(feature);
-#if (NET40 || NET45)
- _featureIds.Add(feature.Fid);
-#else
- _featureIds.Add(feature.Fid, 0);
-#endif
- }
-
- ///
- /// Gets a value indicating that that the tree has been built.
- ///
- internal bool Built { get; set; }
-
- ///
- /// Gets a value indicating the number of levels in this tree
- ///
- public int NumLevels { get; private set; }
-
- ///
- /// Get a value indicating the number of features in the index
- ///
- public int FeatureCount { get { return _header.NumRecords; /*Root.CountAllFeatures();*/ } }
-
- ///
- /// Gets a value indicating the id of the first leaf
- ///
- internal int FirstLeafNodeId { get; private set; }
-
- ///
- /// Gets the id of the last leaf node
- ///
- internal int LastLeafNodeId { get { return FirstLeafNodeId * 2 - 1; } }
-
- ///
- /// Method to create the nodes for this tree
- ///
- /// The number of nodes
- private void CreateNodes(int numNodes)
- {
- Nodes = new SbnNode[numNodes];
- Nodes[1] = new SbnNode(this, 1, 0, 0, 255, 255);
- Nodes[1].AddChildren();
- }
-
- ///
- /// Function to compute the number of levels required for the number of features to add
- ///
- /// The number of features a tree should take
- /// The number of levels
- private static int GetNumberOfLevels(int featureCount)
- {
- var levels = (int)Math.Log(((featureCount - 1) / 8.0 + 1), 2) + 1;
- if (levels < 2) levels = 2;
- if (levels > 24) levels = 24;
-
- return levels;
- }
-
- ///
- /// The root node
- ///
- internal SbnNode Root { get { return Nodes[1]; } }
-
-#if !PCL
- ///
- /// Method to save the tree to a file
- ///
- /// The filename
- public void Save(string sbnName)
- {
- Monitor.Enter(_syncRoot);
-
- if (string.IsNullOrEmpty(sbnName))
- throw new ArgumentNullException("sbnName");
-
- var sbxName = Path.ChangeExtension(sbnName, "sbx");
-
- if (File.Exists(sbnName)) File.Delete(sbnName);
- if (File.Exists(sbxName)) File.Delete(sbxName);
-
- var sbnStream = new FileStream(sbnName, FileMode.Create, FileAccess.Write, FileShare.None);
- var sbxStream = new FileStream(sbxName, FileMode.Create, FileAccess.Write, FileShare.None);
- using (var sbnWriter = new BinaryWriter(sbnStream))
- using (var sbxWriter = new BinaryWriter(sbxStream))
- Write(sbnWriter, sbxWriter);
-
- Monitor.Exit(_syncRoot);
- }
-#endif
-
- ///
- /// Method to get header values for the shapefile header record
- ///
- /// The number of bins
- /// The index of the last bin that contains features
- private void GetHeaderValues(out int numBins, out int lastBinIndex)
- {
- numBins = 0;
- lastBinIndex = 0;
- for (var i = 1; i < Nodes.Length; i++)
- {
- if (Nodes[i].FeatureCount > 0)
- {
- var numBinsForNode = (int)Math.Ceiling(Nodes[i].FeatureCount/100d);
- lastBinIndex = i;
- numBins += numBinsForNode;
- }
- }
- }
-
- ///
- /// Method to write the tree
- ///
- /// A writer for the sbn stream
- /// A writer for the sbx stream
- private void Write(BinaryWriter sbnsw, BinaryWriter sbxsw)
- {
- // Gather header data
- int numBins, lastBinIndex;
- GetHeaderValues(out numBins, out lastBinIndex);
-
- // we have one additional bin
- numBins++;
-
- // first bin descriptors
- var numBinHeaderRecords = lastBinIndex;
- var binHeaderSize = (numBinHeaderRecords) * 8;
-
- // then bins with features
- var usedBinSize = numBins * 8;
-
- var sbxSize = 100 + usedBinSize;
- var sbnSize = 100 + binHeaderSize + usedBinSize + FeatureCount*8;
-
- // Write headers
- _header.Write(sbnsw, sbnSize);
- _header.Write(sbxsw, sbxSize);
-
- // sbn and sbx records
- // first create bin descriptors record
- var recLen = (numBinHeaderRecords) * 4;
- BinaryIOExtensions.WriteBE(sbnsw, 1);
- BinaryIOExtensions.WriteBE(sbnsw, recLen);
-
- BinaryIOExtensions.WriteBE(sbxsw, 50);
- BinaryIOExtensions.WriteBE(sbxsw, recLen);
-
- WriteBinHeader(sbnsw, lastBinIndex);
-
- WriteBins(sbnsw, sbxsw);
- }
-
- ///
- /// Method to write the bin header to the sbn file
- ///
- ///
- ///
- private void WriteBinHeader(BinaryWriter sbnsw, int lastBinIndex)
- {
- var binIndex = 2;
- for (var i = 1; i <= lastBinIndex; i++)
- {
- if (Nodes[i].FeatureCount > 0)
- {
- BinaryIOExtensions.WriteBE(sbnsw, binIndex);
- BinaryIOExtensions.WriteBE(sbnsw, Nodes[i].FeatureCount);
- binIndex += (int) Math.Ceiling(Nodes[i].FeatureCount/100d);
- }
- else
- {
- BinaryIOExtensions.WriteBE(sbnsw, -1);
- BinaryIOExtensions.WriteBE(sbnsw, 0);
- }
- }
- }
-
- ///
- /// Method to write the bins
- ///
- /// The writer for the sbn file
- /// The writer for the sbx file
- private void WriteBins(BinaryWriter sbnWriter, BinaryWriter sbxWriter)
- {
- var binid = 2;
- for (var i = 1; i < Nodes.Length; i++)
- {
- if (Nodes[i].FirstBin != null)
- Nodes[i].FirstBin.Write(ref binid, sbnWriter, sbxWriter);
- }
-
- /*
- using (var binIt = new SbnBinEnumerator(this))
- while (binIt.MoveNext())
- {
- binIt.Current.Write(ref binid, sbnWriter, sbxWriter);
- }*/
- }
-
- public bool VerifyNodes()
- {
-#if DEBUG
- foreach (var node in Enumerable.Skip(Nodes, 1))
- {
- if (!node.VerifyBins())
- return false;
- }
-#endif
- return true;
- }
-
-
- //private class SbnBinEnumerator : IEnumerator
- //{
- // private readonly SbnTree _tree;
- // private SbnNode _currentNode;
- // private SbnBin _currentBin, _lastBin;
- // private bool _finished;
-
- // public SbnBinEnumerator(SbnTree tree)
- // {
- // _tree = tree;
- // }
-
- // public void Dispose()
- // {
- // }
-
- // public bool MoveNext()
- // {
- // if (_finished)
- // return false;
-
- // _lastBin = _currentBin;
- // var res = SeekNextBin(1);
- // Debug.Assert(!ReferenceEquals(_lastBin, _currentBin));
- // return res;
- // }
-
- // bool SeekNextBin(int depth)
- // {
- // Debug.Assert(depth < 1000);
-
- // if (_currentNode == null)
- // {
- // _currentNode = _tree.Nodes[1];
- // if (_currentNode.FirstBin == null)
- // return SeekNextBin(depth+1);
- // }
-
- // if (_currentBin == null)
- // {
- // _currentBin = _currentNode.FirstBin;
- // if (_currentBin == null)
- // {
- // if (_currentNode.Nid == _tree.LastLeafNodeId)
- // {
- // _finished = true;
- // return false;
- // }
- // _currentNode = _tree.Nodes[_currentNode.Nid + 1];
- // return SeekNextBin(depth + 1);
- // }
- // return true;
-
-
- // }
-
- // _currentBin = _currentBin.Next;
- // if (_currentBin == null)
- // {
- // if (_currentNode.Nid == _tree.LastLeafNodeId)
- // {
- // _finished = true;
- // return false;
- // }
-
- // _currentNode = _tree.Nodes[_currentNode.Nid + 1];
- // return SeekNextBin(depth + 1);
- // }
-
- // return true;
- // }
-
- // public void Reset()
- // {
- // _currentNode = null;
-
- // _currentBin = null;
- // _finished = false;
- // }
-
- // public SbnBin Current { get { return _currentBin; } }
-
- // object IEnumerator.Current
- // {
- // get { return Current; }
- // }
- //}
-
- ///
- /// Method to compute the number of features in a given level
- ///
- /// The level
- /// The number of features
- public int FeaturesInLevel(int level)
- {
- // return the number of features in a level
- var start = (int)Math.Pow(2, level - 1);
- var end = 2 * start - 1;
- var featureCount = 0;
- foreach (var n in SbnEnumerable.GetRange(Nodes, start, end - start + 1))
- featureCount += n.FeatureCount;
- return featureCount;
- }
-
- ///
- /// Method to describe the tree
- ///
- /// The textwriter to use
- public void DescribeTree(TextWriter @out)
- {
-#if VERBOSE
- if (@out == null)
- throw new ArgumentNullException("out");
-
- @out.WriteLine("#Description");
- @out.WriteLine("# f=full [0, 1]");
- @out.WriteLine("# sf=features on seam");
- @out.WriteLine("# h=holdfeatures");
- @out.WriteLine("#level node f sf h");
- for (var i = 1; i <= NumLevels; i++)
- {
- var nodes = GetNodesOfLevel(i);
- foreach (var node in nodes)
- {
- @out.WriteLine("{0,5} {1,5}", i, node.ToStringVerbose());
- }
- }
-#else
- //We are not verbose so we don't do anything
-#endif
- }
-
- ///
- /// Method to create an from an id and an envelope
- ///
- /// The feature's id
- /// The geometry
- /// A sbnfeature
- private SbnFeature ToSbnFeature(uint fid, Envelope envelope)
- {
- return new SbnFeature(_header.Extent, fid, envelope);
- }
-
- ///
- /// Method to query the ids of features that intersect with
- ///
- /// The extent
- /// An enumeration of feature ids
- public IEnumerable QueryFids(Envelope extent)
- {
- var res = new List();
-
- Monitor.Enter(_syncRoot);
-
- extent = _header.Extent.Intersection(extent);
- byte minx, miny, maxx, maxy;
- ClampUtility.Clamp(_header.Extent, extent,
- out minx, out miny, out maxx, out maxy);
-
- Root.QueryFids(minx, miny, maxx, maxy, res);
-
- Monitor.Exit(_syncRoot);
-
- res.Sort();
-
- return res;
- }
-
- ///
- /// Method to get the nodes of a specic level
- ///
- ///
- ///
- public IList GetNodesOfLevel(int level)
- {
- if (level < 1 || level > NumLevels)
- throw new ArgumentOutOfRangeException("level");
-
- var start = (int)Math.Pow(2, level - 1);
- var end = 2 * start - 1;
- return NumPySlicing.GetRange(Nodes, start, end, 1);
- }
-
- ///
- /// Method to create an from a collection of (id, geometry) tuples
- ///
- /// The (id, geometry) tuples
- /// The newly created tree
- public static SbnTree Create(ICollection> boxedFeatures, Interval? zRange = null, Interval? mRange = null)
- {
- Interval x, y, z, m;
- GetIntervals(boxedFeatures, out x, out y, out z, out m);
- if (zRange.HasValue) z = z.ExpandedByInterval(zRange.Value);
- if (mRange.HasValue) m = m.ExpandedByInterval(mRange.Value);
-
- var tree = new SbnTree(new SbnHeader(boxedFeatures.Count, x, y, z, m));
- foreach (var boxedFeature in boxedFeatures)
- {
- tree.Insert(tree.ToSbnFeature(boxedFeature.Item1, boxedFeature.Item2));
- }
-
- tree.CompactSeamFeatures();
- return tree;
- }
-
- ///
- /// Method to get some of the shapefile header values.
- ///
- /// An enumeration of (id, geometry) tuples
- /// The x-extent
- /// The y-extent
- /// The z-extent
- /// The m-extent
- private static void GetIntervals(IEnumerable> geoms, out Interval xrange, out Interval yrange,
- out Interval zrange, out Interval mrange)
- {
- xrange = Interval.Create();
- yrange = Interval.Create();
- zrange = Interval.Create();
- mrange = Interval.Create();
-
- foreach (var tuple in geoms)
- {
- Interval x2Range, y2Range, z2Range, m2Range;
- GeometryMetricExtensions.GetMetric(tuple.Item2, out x2Range, out y2Range, out z2Range, out m2Range);
- xrange = xrange.ExpandedByInterval(x2Range);
- yrange = yrange.ExpandedByInterval(y2Range);
- zrange = zrange.ExpandedByInterval(z2Range);
- mrange = mrange.ExpandedByInterval(m2Range);
- }
- }
-
-#if UseGeoAPI
- ///
- /// Method to create an from a collection of (id, geometry) tuples
- ///
- /// The (id, geometry) tuples
- /// The newly created tree
- public static SbnTree Create(ICollection> boxedFeatures, Interval? zRange = null, Interval? mRange = null)
- {
- Interval x, y, z, m;
- GetIntervals(boxedFeatures, out x, out y, out z, out m);
- if (zRange.HasValue) z = z.ExpandedByInterval(zRange.Value);
- if (mRange.HasValue) m = m.ExpandedByInterval(mRange.Value);
-
- var tree = new SbnTree(new SbnHeader(boxedFeatures.Count, x, y, z, m));
- foreach (var boxedFeature in boxedFeatures)
- {
- tree.Insert(tree.ToSbnFeature(boxedFeature.Item1, boxedFeature.Item2));
- }
-
- tree.CompactSeamFeatures();
- return tree;
- }
-
- ///
- /// Method to get some of the shapefile header values.
- ///
- /// An enumeration of (id, geometry) tuples
- /// The x-extent
- /// The y-extent
- /// The z-extent
- /// The m-extent
- private static void GetIntervals(IEnumerable> geoms, out Interval xrange, out Interval yrange,
- out Interval zrange, out Interval mrange)
- {
- xrange = Interval.Create();
- yrange = Interval.Create();
- zrange = Interval.Create();
- mrange = Interval.Create();
-
- foreach (var tuple in geoms)
- {
- Interval x2Range, y2Range, z2Range, m2Range;
- GeometryMetricExtensions.GetMetric(tuple.Item2, out x2Range, out y2Range, out z2Range, out m2Range);
- xrange = xrange.ExpandedByInterval(x2Range);
- yrange = yrange.ExpandedByInterval(y2Range);
- zrange = zrange.ExpandedByInterval(z2Range);
- mrange = mrange.ExpandedByInterval(m2Range);
- }
- }
-#endif
- ///
- /// Method to compact this .
- ///
- private void CompactSeamFeatures()
- {
- // the mystery algorithm - compaction? optimization? obfuscation?
- if (NumLevels < 4)
- return;
-
- var start = FirstLeafNodeId/2 - 1;
- if (start < 3) start = 3;
-
- var end = start / 8;
- if (end < 1) end = 1;
-
- foreach (var node in NumPySlicing.GetRange(Nodes, start, end, -1))
- {
- var id = node.Nid;
- var children = SbnEnumerable.GetRange(Nodes, id * 2, 2);
- foreach (var child in children)
- {
- // There are no items to pull up
- if (child.FeatureCount == 0) continue;
-
- var cid = child.Nid;
- var grandchildren = SbnEnumerable.GetRange(Nodes, cid * 2, 2);
- var gccount = 0;
- foreach (var gcnode in grandchildren)
- gccount += gcnode.FeatureCount;
-
- //Debug.WriteLine("Node {0} has {1} GC", id, gccount);
- if (gccount == 0)
- {
- //Debug.WriteLine("Slurping {0} features from node {1}", child.AllFeatures().Count, child.id);
- //node.features.AddRange(child.features);
-
- // this is weird but it works
- if (child.FeatureCount < 4)
- {
- if (node.FirstBin == null)
- node.FirstBin = new SbnBin();
-
- //for (var i = 0; i < child.FeatureCount; i++)
- //{
- // //node.LastBin.AddFeature(child.FirstBin[i]);
- // node.LastBin.AddFeature(child.RemoveAt(0));
- //}
-
- while (child.FeatureCount > 0)
- { node.LastBin.AddFeature(child.RemoveAt(0)); }
- //Debug.Assert(child.FeatureCount == 0);
- //child.FirstBin = null;
- }
- }
- }
- }
- Built = true;
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+//#if !(NET40 || NET45 || PCL)
+//using FrameworkReplacemets;
+//#if !NET35
+//using FrameworkReplacemets.Linq;
+//#endif
+//using Enumerable = System.Linq.Enumerable;
+//#else
+//using System.Linq;
+//#endif
+#if !NET35
+using Enumerable = FrameworkReplacements.Linq.Enumerable;
+#else
+using Enumerable = System.Linq.Enumerable;
+#endif
+#if !(NET40 || NET45 || PCL)
+using FrameworkReplacements;
+#endif
+#if UseGeoAPI
+using Interval = GeoAPI.DataStructures.Interval;
+using GeoAPI.Geometries;
+#else
+using Interval = SharpSbn.DataStructures.Interval;
+using Envelope = SharpSbn.DataStructures.Envelope;
+#endif
+using System.Threading;
+using SbnEnumerable = FrameworkReplacements.Linq.Enumerable;
+
+namespace SharpSbn
+{
+ ///
+ /// A Sbn spatial tree
+ ///
+ public class SbnTree
+ {
+ ///
+ /// Property to test if GeoAPI is used or not!
+ ///
+ public static bool HasGeoAPISupport
+ {
+ get
+ {
+ return
+#if UseGeoAPI
+ true;
+#else
+ false;
+#endif
+ }
+ }
+
+#if !PCL
+ ///
+ /// Method to describe the tree's content
+ ///
+ ///
+ ///
+ public static void SbnToText(string sbnTree, TextWriter writer)
+ {
+ using (var br = new BinaryReader(File.OpenRead(sbnTree)))
+ {
+ // header
+ var header = new SbnHeader();
+ header.Read(br);
+ writer.WriteLine(header.ToString());
+
+ // Bin header
+ writer.WriteLine("[BinHeader]");
+
+ if (BinaryIOExtensions.ReadUInt32BE(br) != 1)
+ throw new SbnException("Invalid format, expecting 1");
+
+ var maxNodeId = BinaryIOExtensions.ReadInt32BE(br) / 4;
+ writer.WriteLine("#1, {0} => MaxNodeId = {1}", maxNodeId * 4, maxNodeId);
+
+ var ms = new MemoryStream(br.ReadBytes(maxNodeId * 8));
+
+ using (var msReader = new BinaryReader(ms))
+ {
+ var index = 2;
+ while (msReader.BaseStream.Position < msReader.BaseStream.Length)
+ {
+ writer.WriteLine("#{2}, Index {0}, NumFeatures={1}",
+ BinaryIOExtensions.ReadInt32BE(msReader), BinaryIOExtensions.ReadInt32BE(msReader), index++);
+ }
+ }
+
+ writer.WriteLine("[Bins]");
+ while (br.BaseStream.Position < br.BaseStream.Length)
+ {
+ var bin = new SbnBin();
+ var binId = bin.Read(br);
+ writer.Write("[SbnBin {0}: {1}]\n", binId, bin.NumFeatures);
+ for (var i = 0; i < bin.NumFeatures;i++)
+ writer.WriteLine(" "+ bin[i]);
+ }
+
+ }
+ writer.Flush();
+ }
+#endif
+
+#if !PCL
+
+ ///
+ /// Method to load an SBN index from a file
+ ///
+ /// The filename
+ /// The SBN index
+ public static SbnTree Load(string sbnFilename)
+ {
+ if (string.IsNullOrEmpty(sbnFilename))
+ throw new ArgumentNullException("sbnFilename");
+
+ if (!File.Exists(sbnFilename))
+ throw new FileNotFoundException("File not found", sbnFilename);
+
+ using (var stream = new FileStream(sbnFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ return Load(stream);
+ }
+ }
+#endif
+
+ ///
+ /// Method to load an SBN index from a stream
+ ///
+ /// The stream
+ /// The SBN index
+ public static SbnTree Load(Stream stream)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ using (var reader = new BinaryReader(stream))
+ {
+ return new SbnTree(reader);
+ }
+ }
+
+ private readonly SbnHeader _header = new SbnHeader();
+ internal SbnNode[] Nodes;
+#if NET40 || NET45
+ private readonly HashSet _featureIds = new HashSet();
+#else
+ private readonly Dictionary _featureIds = new Dictionary();
+#endif
+ private readonly object _syncRoot = new object();
+
+ ///
+ /// Creates the tree reading data from the
+ ///
+ /// The reader to use
+ private SbnTree(BinaryReader reader)
+ {
+ Monitor.Enter(_syncRoot);
+
+ _header = new SbnHeader();
+ _header.Read(reader);
+
+ BuildTree(_header.NumRecords);
+
+ if (BinaryIOExtensions.ReadUInt32BE(reader) != 1)
+ throw new SbnException("Invalid format, expecting 1");
+
+ var maxNodeId = BinaryIOExtensions.ReadInt32BE(reader) / 4;
+ var ms = new MemoryStream(reader.ReadBytes(maxNodeId * 8));
+ using (var msReader = new BinaryReader(ms))
+ {
+ var indexNodeId = 1;
+ while (msReader.BaseStream.Position < msReader.BaseStream.Length)
+ {
+ var nid = BinaryIOExtensions.ReadInt32BE(msReader);
+ var featureCount = BinaryIOExtensions.ReadInt32BE(msReader);
+
+ if (nid > 1)
+ {
+ var node = Nodes[indexNodeId];
+ while (node.FeatureCount < featureCount)
+ {
+ var bin = new SbnBin();
+ bin.Read(reader);
+ node.AddBin(bin, true);
+ }
+ Debug.Assert(node.VerifyBins());
+ }
+ indexNodeId++;
+ }
+ }
+
+ //Gather all feature ids
+ GatherFids();
+
+ //Assertions
+ Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length);
+ Debug.Assert(_featureIds.Count == _header.NumRecords);
+
+
+ Monitor.Exit(_syncRoot);
+ }
+
+ ///
+ /// Creates an instance of this class using the provided header
+ ///
+ /// The header
+ public SbnTree(SbnHeader header)
+ {
+ _header = header;
+ BuildTree(_header.NumRecords);
+ }
+
+ ///
+ /// Gets a value Synchronization object
+ ///
+ public object SyncRoot { get { return _syncRoot; } }
+
+ ///
+ /// Get a value indicating if this tree is synchronized
+ ///
+ public bool IsSynchronized
+ {
+ get
+ {
+ if (!Monitor.TryEnter(_syncRoot))
+ return false;
+ Monitor.Exit(_syncRoot);
+ return true;
+ }
+ }
+
+ ///
+ /// Method to collect all feature ids in the tree
+ ///
+ private void GatherFids()
+ {
+ foreach (SbnNode sbnNode in Enumerable.Skip(Nodes, 1))
+ {
+ if (sbnNode == null) continue;
+
+ foreach (var feature in sbnNode)
+#if NET40 || NET45
+ _featureIds.Add(feature.Fid);
+#else
+ _featureIds.Add(feature.Fid, 0);
+#endif
+ }
+ }
+
+ ///
+ /// Method to build the tree
+ ///
+ /// The number of features in the tree
+ private void BuildTree(int numFeatures)
+ {
+ Built = false;
+ NumLevels = GetNumberOfLevels(numFeatures);
+ FirstLeafNodeId = (int)Math.Pow(2, NumLevels - 1);
+ CreateNodes((int)Math.Pow(2, NumLevels));
+ }
+
+ ///
+ /// Gets a value indicating the 2d extent of the tree
+ ///
+ public Envelope Extent { get { return _header.Extent; }}
+
+ ///
+ /// Gets a value indicating the range of the z-ordinates
+ ///
+ public Interval ZRange { get { return _header.ZRange; }}
+
+ ///
+ /// Gets a value indicating the range of the m-ordinates
+ ///
+ public Interval MRange { get { return _header.MRange; } }
+
+ ///
+ /// Event raised when a rebuild of the tree is required,
+ /// that requires access to all feature information.
+ ///
+ public event EventHandler RebuildRequried;
+
+ ///
+ /// Event invoker for the event
+ ///
+ ///
+ protected virtual void OnRebuildRequired(SbnTreeRebuildRequiredEventArgs e)
+ {
+ var handler = RebuildRequried;
+ if (handler != null)
+ handler(this, e);
+ }
+
+#if UseGeoAPI
+ ///
+ /// Method to insert a new feature to the tree
+ ///
+ /// The feature's id
+ /// The feature's geometry
+ [CLSCompliant(false)]
+ public void Insert(uint fid, GeoAPI.Geometries.IGeometry geometry)
+ {
+ Interval x, y, z, m;
+ GeometryMetricExtensions.GetMetric(geometry, out x, out y, out z, out m);
+ Insert(fid, geometry.EnvelopeInternal, z, m);
+
+ }
+
+ ///
+ /// Method to create an from an id and a geometry
+ ///
+ /// The feature's id
+ /// The geometry
+ /// A sbnfeature
+ private SbnFeature ToSbnFeature(uint fid, GeoAPI.Geometries.IGeometry geometry)
+ {
+ return new SbnFeature(_header.Extent, fid, geometry.EnvelopeInternal);
+ }
+
+#endif
+
+ ///
+ /// Method to insert a new feature to the tree
+ ///
+ /// The feature's id
+ /// The feature's geometry
+ /// The z-ordinate extent
+ /// The m-ordinate extent
+#pragma warning disable 3001
+ public void Insert(uint fid, Envelope envelope, Interval? zRange = null, Interval? mRange = null)
+#pragma warning restore 3001
+ {
+ // lock the tree
+ Monitor.Enter(_syncRoot);
+
+ // Convert to an sbnfeature
+ var sbnFeature = ToSbnFeature(fid, envelope);
+
+ var inserted = false;
+ // Has the tree already been built?
+ if (Built)
+ {
+ // Does the feature fit into the current tree, signal that
+ // the tree needs to be recreated in order to function properly.
+ if (!_header.Extent.Contains(envelope))
+ {
+ OnRebuildRequired(new SbnTreeRebuildRequiredEventArgs(fid, envelope, zRange, mRange));
+ Monitor.Exit(_syncRoot);
+ return;
+ }
+
+ // Compute number of features in tree
+ var featureCount = FeatureCount + 1;
+
+ // Does the new number of features require more levels?
+ if (GetNumberOfLevels(featureCount) != NumLevels)
+ {
+ // This can be done inplace.
+ RebuildTree(featureCount, sbnFeature);
+ inserted = true;
+ }
+ }
+
+ //Insert the feature
+ if (!inserted) Insert(sbnFeature);
+
+ // Update the header metrics
+ _header.AddFeature(fid, envelope, zRange ?? Interval.Create(), mRange ?? Interval.Create());
+
+ // unlock the tree
+ Monitor.Exit(_syncRoot);
+
+ }
+
+ ///
+ /// Method to -inplace- rebuild the tree
+ ///
+ /// The number of features for the tree
+ /// The new feature to add
+ private void RebuildTree(int featureCount, SbnFeature newFeature)
+ {
+ var nodes = Nodes;
+ _featureIds.Clear();
+
+ BuildTree(featureCount);
+
+ for (var i = 1; i < nodes.Length; i++)
+ {
+ foreach (var feature in nodes[i])
+ Insert(feature);
+ }
+ Insert(newFeature);
+
+ CompactSeamFeatures();
+ }
+
+ ///
+ /// Method to remove the feature
+ ///
+ /// The id of the feature
+ /// The envelope in which to search for the feature
+#pragma warning disable 3001
+ public void Remove(uint fid, Envelope envelope = null)
+#pragma warning restore 3001
+ {
+ Monitor.Enter(_syncRoot);
+
+ envelope = envelope ?? _header.Extent;
+ var searchFeature = new SbnFeature(_header, fid, envelope);
+ Root.Remove(searchFeature);
+
+ Monitor.Exit(_syncRoot);
+ }
+
+ ///
+ /// Method to insert a feature to the tree
+ ///
+ ///
+ internal void Insert(SbnFeature feature)
+ {
+ // Insert a feature into the tree
+ Root.Insert(feature);
+#if (NET40 || NET45)
+ _featureIds.Add(feature.Fid);
+#else
+ _featureIds.Add(feature.Fid, 0);
+#endif
+ }
+
+ ///
+ /// Gets a value indicating that that the tree has been built.
+ ///
+ internal bool Built { get; set; }
+
+ ///
+ /// Gets a value indicating the number of levels in this tree
+ ///
+ public int NumLevels { get; private set; }
+
+ ///
+ /// Get a value indicating the number of features in the index
+ ///
+ public int FeatureCount { get { return _header.NumRecords; /*Root.CountAllFeatures();*/ } }
+
+ ///
+ /// Gets a value indicating the id of the first leaf
+ ///
+ internal int FirstLeafNodeId { get; private set; }
+
+ ///
+ /// Gets the id of the last leaf node
+ ///
+ internal int LastLeafNodeId { get { return FirstLeafNodeId * 2 - 1; } }
+
+ ///
+ /// Method to create the nodes for this tree
+ ///
+ /// The number of nodes
+ private void CreateNodes(int numNodes)
+ {
+ Nodes = new SbnNode[numNodes];
+ Nodes[1] = new SbnNode(this, 1, 0, 0, 255, 255);
+ Nodes[1].AddChildren();
+ }
+
+ ///
+ /// Function to compute the number of levels required for the number of features to add
+ ///
+ /// The number of features a tree should take
+ /// The number of levels
+ private static int GetNumberOfLevels(int featureCount)
+ {
+ var levels = (int)Math.Log(((featureCount - 1) / 8.0 + 1), 2) + 1;
+ if (levels < 2) levels = 2;
+ if (levels > 24) levels = 24;
+
+ return levels;
+ }
+
+ ///
+ /// The root node
+ ///
+ internal SbnNode Root { get { return Nodes[1]; } }
+
+#if !PCL
+ ///
+ /// Method to save the tree to a file
+ ///
+ /// The filename
+ public void Save(string sbnName)
+ {
+ Monitor.Enter(_syncRoot);
+
+ if (string.IsNullOrEmpty(sbnName))
+ throw new ArgumentNullException("sbnName");
+
+ var sbxName = Path.ChangeExtension(sbnName, "sbx");
+
+ if (File.Exists(sbnName)) File.Delete(sbnName);
+ if (File.Exists(sbxName)) File.Delete(sbxName);
+
+ var sbnStream = new FileStream(sbnName, FileMode.Create, FileAccess.Write, FileShare.None);
+ var sbxStream = new FileStream(sbxName, FileMode.Create, FileAccess.Write, FileShare.None);
+ using (var sbnWriter = new BinaryWriter(sbnStream))
+ using (var sbxWriter = new BinaryWriter(sbxStream))
+ Write(sbnWriter, sbxWriter);
+
+ Monitor.Exit(_syncRoot);
+ }
+#endif
+
+ ///
+ /// Method to get header values for the shapefile header record
+ ///
+ /// The number of bins
+ /// The index of the last bin that contains features
+ private void GetHeaderValues(out int numBins, out int lastBinIndex)
+ {
+ numBins = 0;
+ lastBinIndex = 0;
+ for (var i = 1; i < Nodes.Length; i++)
+ {
+ if (Nodes[i].FeatureCount > 0)
+ {
+ var numBinsForNode = (int)Math.Ceiling(Nodes[i].FeatureCount/100d);
+ lastBinIndex = i;
+ numBins += numBinsForNode;
+ }
+ }
+ }
+
+ ///
+ /// Method to write the tree
+ ///
+ /// A writer for the sbn stream
+ /// A writer for the sbx stream
+ private void Write(BinaryWriter sbnsw, BinaryWriter sbxsw)
+ {
+ // Gather header data
+ int numBins, lastBinIndex;
+ GetHeaderValues(out numBins, out lastBinIndex);
+
+ // we have one additional bin
+ numBins++;
+
+ // first bin descriptors
+ var numBinHeaderRecords = lastBinIndex;
+ var binHeaderSize = (numBinHeaderRecords) * 8;
+
+ // then bins with features
+ var usedBinSize = numBins * 8;
+
+ var sbxSize = 100 + usedBinSize;
+ var sbnSize = 100 + binHeaderSize + usedBinSize + FeatureCount*8;
+
+ // Write headers
+ _header.Write(sbnsw, sbnSize);
+ _header.Write(sbxsw, sbxSize);
+
+ // sbn and sbx records
+ // first create bin descriptors record
+ var recLen = (numBinHeaderRecords) * 4;
+ BinaryIOExtensions.WriteBE(sbnsw, 1);
+ BinaryIOExtensions.WriteBE(sbnsw, recLen);
+
+ BinaryIOExtensions.WriteBE(sbxsw, 50);
+ BinaryIOExtensions.WriteBE(sbxsw, recLen);
+
+ WriteBinHeader(sbnsw, lastBinIndex);
+
+ WriteBins(sbnsw, sbxsw);
+ }
+
+ ///
+ /// Method to write the bin header to the sbn file
+ ///
+ ///
+ ///
+ private void WriteBinHeader(BinaryWriter sbnsw, int lastBinIndex)
+ {
+ var binIndex = 2;
+ for (var i = 1; i <= lastBinIndex; i++)
+ {
+ if (Nodes[i].FeatureCount > 0)
+ {
+ BinaryIOExtensions.WriteBE(sbnsw, binIndex);
+ BinaryIOExtensions.WriteBE(sbnsw, Nodes[i].FeatureCount);
+ binIndex += (int) Math.Ceiling(Nodes[i].FeatureCount/100d);
+ }
+ else
+ {
+ BinaryIOExtensions.WriteBE(sbnsw, -1);
+ BinaryIOExtensions.WriteBE(sbnsw, 0);
+ }
+ }
+ }
+
+ ///
+ /// Method to write the bins
+ ///
+ /// The writer for the sbn file
+ /// The writer for the sbx file
+ private void WriteBins(BinaryWriter sbnWriter, BinaryWriter sbxWriter)
+ {
+ var binid = 2;
+ for (var i = 1; i < Nodes.Length; i++)
+ {
+ if (Nodes[i].FirstBin != null)
+ Nodes[i].FirstBin.Write(ref binid, sbnWriter, sbxWriter);
+ }
+
+ /*
+ using (var binIt = new SbnBinEnumerator(this))
+ while (binIt.MoveNext())
+ {
+ binIt.Current.Write(ref binid, sbnWriter, sbxWriter);
+ }*/
+ }
+
+ ///
+ /// Method to verify the nodes
+ ///
+ /// true if all nodes are correct
+ public bool VerifyNodes()
+ {
+#if DEBUG
+ foreach (var node in Enumerable.Skip(Nodes, 1))
+ {
+ if (!node.VerifyBins())
+ return false;
+ }
+#endif
+ return true;
+ }
+
+
+ //private class SbnBinEnumerator : IEnumerator
+ //{
+ // private readonly SbnTree _tree;
+ // private SbnNode _currentNode;
+ // private SbnBin _currentBin, _lastBin;
+ // private bool _finished;
+
+ // public SbnBinEnumerator(SbnTree tree)
+ // {
+ // _tree = tree;
+ // }
+
+ // public void Dispose()
+ // {
+ // }
+
+ // public bool MoveNext()
+ // {
+ // if (_finished)
+ // return false;
+
+ // _lastBin = _currentBin;
+ // var res = SeekNextBin(1);
+ // Debug.Assert(!ReferenceEquals(_lastBin, _currentBin));
+ // return res;
+ // }
+
+ // bool SeekNextBin(int depth)
+ // {
+ // Debug.Assert(depth < 1000);
+
+ // if (_currentNode == null)
+ // {
+ // _currentNode = _tree.Nodes[1];
+ // if (_currentNode.FirstBin == null)
+ // return SeekNextBin(depth+1);
+ // }
+
+ // if (_currentBin == null)
+ // {
+ // _currentBin = _currentNode.FirstBin;
+ // if (_currentBin == null)
+ // {
+ // if (_currentNode.Nid == _tree.LastLeafNodeId)
+ // {
+ // _finished = true;
+ // return false;
+ // }
+ // _currentNode = _tree.Nodes[_currentNode.Nid + 1];
+ // return SeekNextBin(depth + 1);
+ // }
+ // return true;
+
+
+ // }
+
+ // _currentBin = _currentBin.Next;
+ // if (_currentBin == null)
+ // {
+ // if (_currentNode.Nid == _tree.LastLeafNodeId)
+ // {
+ // _finished = true;
+ // return false;
+ // }
+
+ // _currentNode = _tree.Nodes[_currentNode.Nid + 1];
+ // return SeekNextBin(depth + 1);
+ // }
+
+ // return true;
+ // }
+
+ // public void Reset()
+ // {
+ // _currentNode = null;
+
+ // _currentBin = null;
+ // _finished = false;
+ // }
+
+ // public SbnBin Current { get { return _currentBin; } }
+
+ // object IEnumerator.Current
+ // {
+ // get { return Current; }
+ // }
+ //}
+
+ ///
+ /// Method to compute the number of features in a given level
+ ///
+ /// The level
+ /// The number of features
+ public int FeaturesInLevel(int level)
+ {
+ // return the number of features in a level
+ var start = (int)Math.Pow(2, level - 1);
+ var end = 2 * start - 1;
+ var featureCount = 0;
+ foreach (var n in SbnEnumerable.GetRange(Nodes, start, end - start + 1))
+ featureCount += n.FeatureCount;
+ return featureCount;
+ }
+
+ ///
+ /// Method to describe the tree
+ ///
+ /// The textwriter to use
+ public void DescribeTree(TextWriter @out)
+ {
+#if VERBOSE
+ if (@out == null)
+ throw new ArgumentNullException("out");
+
+ @out.WriteLine("#Description");
+ @out.WriteLine("# f=full [0, 1]");
+ @out.WriteLine("# sf=features on seam");
+ @out.WriteLine("# h=holdfeatures");
+ @out.WriteLine("#level node f sf h");
+ for (var i = 1; i <= NumLevels; i++)
+ {
+ var nodes = GetNodesOfLevel(i);
+ foreach (var node in nodes)
+ {
+ @out.WriteLine("{0,5} {1,5}", i, node.ToStringVerbose());
+ }
+ }
+#else
+ //We are not verbose so we don't do anything
+#endif
+ }
+
+ ///
+ /// Method to create an from an id and an envelope
+ ///
+ /// The feature's id
+ /// The geometry
+ /// A sbnfeature
+ private SbnFeature ToSbnFeature(uint fid, Envelope envelope)
+ {
+ return new SbnFeature(_header.Extent, fid, envelope);
+ }
+
+ ///
+ /// Method to query the ids of features that intersect with
+ ///
+ /// The extent
+ /// An enumeration of feature ids
+ public IEnumerable QueryFids(Envelope extent)
+ {
+ var res = new List();
+
+ Monitor.Enter(_syncRoot);
+
+ extent = _header.Extent.Intersection(extent);
+ byte minx, miny, maxx, maxy;
+ ClampUtility.Clamp(_header.Extent, extent,
+ out minx, out miny, out maxx, out maxy);
+
+ Root.QueryFids(minx, miny, maxx, maxy, res);
+
+ Monitor.Exit(_syncRoot);
+
+ res.Sort();
+
+ return res;
+ }
+
+ ///
+ /// Method to get the nodes of a specic level
+ ///
+ ///
+ ///
+ public IList GetNodesOfLevel(int level)
+ {
+ if (level < 1 || level > NumLevels)
+ throw new ArgumentOutOfRangeException("level");
+
+ var start = (int)Math.Pow(2, level - 1);
+ var end = 2 * start - 1;
+ return NumPySlicing.GetRange(Nodes, start, end, 1);
+ }
+
+ ///
+ /// Method to create an from a collection of (id, geometry) tuples
+ ///
+ /// The (id, geometry) tuples
+ /// The z-ordinate extent
+ /// The m-ordinate extent
+ /// The newly created tree
+ public static SbnTree Create(ICollection> boxedFeatures, Interval? zRange = null, Interval? mRange = null)
+ {
+ Interval x, y, z, m;
+ GetIntervals(boxedFeatures, out x, out y, out z, out m);
+ if (zRange.HasValue) z = z.ExpandedByInterval(zRange.Value);
+ if (mRange.HasValue) m = m.ExpandedByInterval(mRange.Value);
+
+ var tree = new SbnTree(new SbnHeader(boxedFeatures.Count, x, y, z, m));
+ foreach (var boxedFeature in boxedFeatures)
+ {
+ tree.Insert(tree.ToSbnFeature(boxedFeature.Item1, boxedFeature.Item2));
+ }
+
+ tree.CompactSeamFeatures();
+ return tree;
+ }
+
+ ///
+ /// Method to get some of the shapefile header values.
+ ///
+ /// An enumeration of (id, geometry) tuples
+ /// The x-extent
+ /// The y-extent
+ /// The z-extent
+ /// The m-extent
+ private static void GetIntervals(IEnumerable> geoms, out Interval xrange, out Interval yrange,
+ out Interval zrange, out Interval mrange)
+ {
+ xrange = Interval.Create();
+ yrange = Interval.Create();
+ zrange = Interval.Create();
+ mrange = Interval.Create();
+
+ foreach (var tuple in geoms)
+ {
+ Interval x2Range, y2Range, z2Range, m2Range;
+ GeometryMetricExtensions.GetMetric(tuple.Item2, out x2Range, out y2Range, out z2Range, out m2Range);
+ xrange = xrange.ExpandedByInterval(x2Range);
+ yrange = yrange.ExpandedByInterval(y2Range);
+ zrange = zrange.ExpandedByInterval(z2Range);
+ mrange = mrange.ExpandedByInterval(m2Range);
+ }
+ }
+
+#if UseGeoAPI
+ ///
+ /// Method to create an from a collection of (id, geometry) tuples
+ ///
+ /// The (id, geometry) tuples
+ /// The z-ordinate extent
+ /// The m-ordinate extent
+ /// The newly created tree
+ public static SbnTree Create(ICollection> boxedFeatures, Interval? zRange = null, Interval? mRange = null)
+ {
+ Interval x, y, z, m;
+ GetIntervals(boxedFeatures, out x, out y, out z, out m);
+ if (zRange.HasValue) z = z.ExpandedByInterval(zRange.Value);
+ if (mRange.HasValue) m = m.ExpandedByInterval(mRange.Value);
+
+ var tree = new SbnTree(new SbnHeader(boxedFeatures.Count, x, y, z, m));
+ foreach (var boxedFeature in boxedFeatures)
+ {
+ tree.Insert(tree.ToSbnFeature(boxedFeature.Item1, boxedFeature.Item2));
+ }
+
+ tree.CompactSeamFeatures();
+ return tree;
+ }
+
+ ///
+ /// Method to get some of the shapefile header values.
+ ///
+ /// An enumeration of (id, geometry) tuples
+ /// The x-extent
+ /// The y-extent
+ /// The z-extent
+ /// The m-extent
+ private static void GetIntervals(IEnumerable> geoms, out Interval xrange, out Interval yrange,
+ out Interval zrange, out Interval mrange)
+ {
+ xrange = Interval.Create();
+ yrange = Interval.Create();
+ zrange = Interval.Create();
+ mrange = Interval.Create();
+
+ foreach (var tuple in geoms)
+ {
+ Interval x2Range, y2Range, z2Range, m2Range;
+ GeometryMetricExtensions.GetMetric(tuple.Item2, out x2Range, out y2Range, out z2Range, out m2Range);
+ xrange = xrange.ExpandedByInterval(x2Range);
+ yrange = yrange.ExpandedByInterval(y2Range);
+ zrange = zrange.ExpandedByInterval(z2Range);
+ mrange = mrange.ExpandedByInterval(m2Range);
+ }
+ }
+#endif
+ ///
+ /// Method to compact this .
+ ///
+ private void CompactSeamFeatures()
+ {
+ // the mystery algorithm - compaction? optimization? obfuscation?
+ if (NumLevels < 4)
+ return;
+
+ var start = FirstLeafNodeId/2 - 1;
+ if (start < 3) start = 3;
+
+ var end = start / 8;
+ if (end < 1) end = 1;
+
+ foreach (var node in NumPySlicing.GetRange(Nodes, start, end, -1))
+ {
+ var id = node.Nid;
+ var children = SbnEnumerable.GetRange(Nodes, id * 2, 2);
+ foreach (var child in children)
+ {
+ // There are no items to pull up
+ if (child.FeatureCount == 0) continue;
+
+ var cid = child.Nid;
+ var grandchildren = SbnEnumerable.GetRange(Nodes, cid * 2, 2);
+ var gccount = 0;
+ foreach (var gcnode in grandchildren)
+ gccount += gcnode.FeatureCount;
+
+ //Debug.WriteLine("Node {0} has {1} GC", id, gccount);
+ if (gccount == 0)
+ {
+ //Debug.WriteLine("Slurping {0} features from node {1}", child.AllFeatures().Count, child.id);
+ //node.features.AddRange(child.features);
+
+ // this is weird but it works
+ if (child.FeatureCount < 4)
+ {
+ if (node.FirstBin == null)
+ node.FirstBin = new SbnBin();
+
+ //for (var i = 0; i < child.FeatureCount; i++)
+ //{
+ // //node.LastBin.AddFeature(child.FirstBin[i]);
+ // node.LastBin.AddFeature(child.RemoveAt(0));
+ //}
+
+ while (child.FeatureCount > 0)
+ { node.LastBin.AddFeature(child.RemoveAt(0)); }
+ //Debug.Assert(child.FeatureCount == 0);
+ //child.FirstBin = null;
+ }
+ }
+ }
+ }
+ Built = true;
+ }
+ }
}
\ No newline at end of file
diff --git a/SharpSbn/SbnTreeRebuildRequiredEventArgs.cs b/SharpSbn/SbnTreeRebuildRequiredEventArgs.cs
index 267acf6..1927fa0 100644
--- a/SharpSbn/SbnTreeRebuildRequiredEventArgs.cs
+++ b/SharpSbn/SbnTreeRebuildRequiredEventArgs.cs
@@ -1,53 +1,54 @@
-using System;
-#if (UseGeoAPI)
-using Envelope = GeoAPI.Geometries.Envelope;
-using Interval = GeoAPI.DataStructures.Interval;
-#else
-using Envelope = SharpSbn.DataStructures.Envelope;
-using Interval = SharpSbn.DataStructures.Interval;
-#endif
-namespace SharpSbn
-{
- ///
- /// A class containing the id and the geometry of a feature that causes the rebuild required event
- ///
- public class SbnTreeRebuildRequiredEventArgs : EventArgs
- {
- ///
- /// Creates an instance of this class
- ///
- /// The feature's id
- /// The features geometry
- /// An optional value for the z-Range
- /// An optional value for the m-Range
- [CLSCompliant(false)]
- public SbnTreeRebuildRequiredEventArgs(uint fid, Envelope geometry, Interval? zRange, Interval? mRange)
- {
- Fid = fid;
- Geometry = geometry;
- ZRange = zRange;
- MRange = mRange;
- }
-
- ///
- /// The feature's id
- ///
- [CLSCompliant(false)]
- public uint Fid { get; private set; }
-
- ///
- /// Gets a value indicating the geometry's
- ///
- public Envelope Geometry { get; private set; }
-
- ///
- /// Gets a value indicating the geometry's
- ///
- public Interval? ZRange { get; private set; }
-
- ///
- /// Gets a value indicating the geometry's
- ///
- public Interval? MRange { get; private set; }
- }
+using System;
+#if (UseGeoAPI)
+using Envelope = GeoAPI.Geometries.Envelope;
+using Interval = GeoAPI.DataStructures.Interval;
+#else
+using Envelope = SharpSbn.DataStructures.Envelope;
+using Interval = SharpSbn.DataStructures.Interval;
+#endif
+namespace SharpSbn
+{
+ ///
+ /// A class containing the id and the geometry of a feature that causes the rebuild required event
+ ///
+ public class SbnTreeRebuildRequiredEventArgs : EventArgs
+ {
+ ///
+ /// Creates an instance of this class
+ ///
+ /// The feature's id
+ /// The features geometry
+ /// An optional value for the z-Range
+ /// An optional value for the m-Range
+#pragma warning disable 3001
+ public SbnTreeRebuildRequiredEventArgs(uint fid, Envelope geometry, Interval? zRange, Interval? mRange)
+#pragma warning restore 3001
+ {
+ Fid = fid;
+ Geometry = geometry;
+ ZRange = zRange;
+ MRange = mRange;
+ }
+
+ ///
+ /// The feature's id
+ ///
+ [CLSCompliant(false)]
+ public uint Fid { get; private set; }
+
+ ///
+ /// Gets a value indicating the geometry's
+ ///
+ public Envelope Geometry { get; private set; }
+
+ ///
+ /// Gets a value indicating the geometry's
+ ///
+ public Interval? ZRange { get; private set; }
+
+ ///
+ /// Gets a value indicating the geometry's
+ ///
+ public Interval? MRange { get; private set; }
+ }
}
\ No newline at end of file