using System; using System.Collections; using GeoAPI.Geometries; using GisSharpBlog.NetTopologySuite.Geometries; using GisSharpBlog.NetTopologySuite.Utilities; namespace GisSharpBlog.NetTopologySuite.Index.Strtree { /// /// A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. /// For two-dimensional spatial data. /// The STR packed R-tree is simple to implement and maximizes space /// utilization; that is, as many leaves as possible are filled to capacity. /// Overlap between nodes is far less than in a basic R-tree. However, once the /// tree has been built (explicitly or on the first call to #query), items may /// not be added or removed. /// Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With /// Application To GIS. Morgan Kaufmann, San Francisco, 2002. /// public class STRtree : AbstractSTRtree, ISpatialIndex { /// /// /// private class AnonymousXComparerImpl : IComparer { private STRtree container = null; /// /// /// /// public AnonymousXComparerImpl(STRtree container) { this.container = container; } /// /// /// /// /// /// public int Compare(object o1, object o2) { return container.CompareDoubles(container.CentreX((IEnvelope) ((IBoundable) o1).Bounds), container.CentreX((IEnvelope) ((IBoundable) o2).Bounds)); } } /// /// /// private class AnonymousYComparerImpl : IComparer { private STRtree container = null; /// /// /// /// public AnonymousYComparerImpl(STRtree container) { this.container = container; } /// /// /// /// /// /// public int Compare(object o1, object o2) { return container.CompareDoubles(container.CentreY((IEnvelope) ((IBoundable) o1).Bounds), container.CentreY((IEnvelope) ((IBoundable) o2).Bounds)); } } /// /// /// private class AnonymousAbstractNodeImpl : AbstractNode { /// /// /// /// public AnonymousAbstractNodeImpl(int nodeCapacity) : base(nodeCapacity) { } /// /// /// /// protected override object ComputeBounds() { IEnvelope bounds = null; for (IEnumerator i = ChildBoundables.GetEnumerator(); i.MoveNext(); ) { IBoundable childBoundable = (IBoundable) i.Current; if (bounds == null) bounds = new Envelope((IEnvelope) childBoundable.Bounds); else bounds.ExpandToInclude((IEnvelope) childBoundable.Bounds); } return bounds; } } /// /// /// private class AnonymousIntersectsOpImpl : IIntersectsOp { private STRtree container = null; /// /// /// /// public AnonymousIntersectsOpImpl(STRtree container) { this.container = container; } /// /// /// /// /// /// public bool Intersects(object aBounds, object bBounds) { return ((IEnvelope) aBounds).Intersects((IEnvelope) bBounds); } } /// /// Constructs an STRtree with the default (10) node capacity. /// public STRtree() : this(10) { } /// /// Constructs an STRtree with the given maximum number of child nodes that /// a node may have. /// public STRtree(int nodeCapacity) : base(nodeCapacity) { } /// /// /// /// /// /// private double Avg(double a, double b) { return (a + b) / 2d; } /// /// /// /// /// private double CentreX(IEnvelope e) { return Avg(e.MinX, e.MaxX); } /// /// /// /// /// private double CentreY(IEnvelope e) { return Avg(e.MinY, e.MaxY); } /// /// Creates the parent level for the given child level. First, orders the items /// by the x-values of the midpoints, and groups them into vertical slices. /// For each slice, orders the items by the y-values of the midpoints, and /// group them into runs of size M (the node capacity). For each run, creates /// a new (parent) node. /// /// /// protected override IList CreateParentBoundables(IList childBoundables, int newLevel) { Assert.IsTrue(childBoundables.Count != 0); int minLeafCount = (int)Math.Ceiling((childBoundables.Count / (double)NodeCapacity)); ArrayList sortedChildBoundables = new ArrayList(childBoundables); sortedChildBoundables.Sort(new AnonymousXComparerImpl(this)); IList[] verticalSlices = VerticalSlices(sortedChildBoundables, (int) Math.Ceiling(Math.Sqrt(minLeafCount))); IList tempList = CreateParentBoundablesFromVerticalSlices(verticalSlices, newLevel); return tempList; } /// /// /// /// /// /// private IList CreateParentBoundablesFromVerticalSlices(IList[] verticalSlices, int newLevel) { Assert.IsTrue(verticalSlices.Length > 0); IList parentBoundables = new ArrayList(); for (int i = 0; i < verticalSlices.Length; i++) { IList tempList = CreateParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel); foreach (object o in tempList) parentBoundables.Add(o); } return parentBoundables; } /// /// /// /// /// /// protected IList CreateParentBoundablesFromVerticalSlice(IList childBoundables, int newLevel) { return base.CreateParentBoundables(childBoundables, newLevel); } /// /// /// /// Must be sorted by the x-value of the envelope midpoints. /// protected IList[] VerticalSlices(IList childBoundables, int sliceCount) { int sliceCapacity = (int)Math.Ceiling(childBoundables.Count / (double)sliceCount); IList[] slices = new IList[sliceCount]; IEnumerator i = childBoundables.GetEnumerator(); for (int j = 0; j < sliceCount; j++) { slices[j] = new ArrayList(); int boundablesAddedToSlice = 0; /* * Diego Guidi says: * the line below introduce an error: * the first element at the iteration (not the first) is lost! * This is simply a different implementation of Iteration in .NET against Java */ // while (i.MoveNext() && boundablesAddedToSlice < sliceCapacity) while (boundablesAddedToSlice < sliceCapacity && i.MoveNext()) { IBoundable childBoundable = (IBoundable) i.Current; slices[j].Add(childBoundable); boundablesAddedToSlice++; } } return slices; } /// /// /// /// /// protected override AbstractNode CreateNode(int level) { return new AnonymousAbstractNodeImpl(level); } /// /// /// protected override IIntersectsOp IntersectsOp { get { return new AnonymousIntersectsOpImpl(this); } } /// /// Inserts an item having the given bounds into the tree. /// /// /// public void Insert(IEnvelope itemEnv, object item) { if (itemEnv.IsNull) return; base.Insert(itemEnv, item); } /// /// Returns items whose bounds intersect the given envelope. /// /// public IList Query(IEnvelope searchEnv) { //Yes this method does something. It specifies that the bounds is an //Envelope. super.query takes an object, not an Envelope. [Jon Aquino 10/24/2003] return base.Query(searchEnv); } /// /// Returns items whose bounds intersect the given envelope. /// /// /// public void Query(IEnvelope searchEnv, IItemVisitor visitor) { //Yes this method does something. It specifies that the bounds is an //Envelope. super.query takes an Object, not an Envelope. [Jon Aquino 10/24/2003] base.Query(searchEnv, visitor); } /// /// Removes a single item from the tree. /// /// The Envelope of the item to remove. /// The item to remove. /// true if the item was found. public bool Remove(IEnvelope itemEnv, object item) { return base.Remove(itemEnv, item); } /// /// /// /// protected override IComparer GetComparer() { return new AnonymousYComparerImpl(this); } } }