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);
}
}
}