using System.Collections; using GeoAPI.Geometries; namespace GisSharpBlog.NetTopologySuite.Index.Quadtree { /// /// The base class for nodes in a Quadtree. /// public abstract class NodeBase { /// /// Returns the index of the subquad that wholly contains the given envelope. /// If none does, returns -1. /// /// /// public static int GetSubnodeIndex(IEnvelope env, ICoordinate centre) { int subnodeIndex = -1; if (env.MinX >= centre.X) { if (env.MinY >= centre.Y) subnodeIndex = 3; if (env.MaxY <= centre.Y) subnodeIndex = 1; } if (env.MaxX <= centre.X) { if (env.MinY >= centre.Y) subnodeIndex = 2; if (env.MaxY <= centre.Y) subnodeIndex = 0; } return subnodeIndex; } /// /// /// protected IList items = new ArrayList(); /// /// subquads are numbered as follows: /// 2 | 3 /// --+-- /// 0 | 1 /// protected Node[] subnode = new Node[4]; /// /// /// public NodeBase() { } /// /// /// public IList Items { get { return items; } } /// /// /// public bool HasItems { get { // return !items.IsEmpty; if (items.Count == 0) return false; return true; } } /// /// /// /// public void Add(object item) { items.Add(item); } /// /// Removes a single item from this subtree. /// /// The envelope containing the item. /// The item to remove. /// true if the item was found and removed. public bool Remove(IEnvelope itemEnv, object item) { // use envelope to restrict nodes scanned if (!IsSearchMatch(itemEnv)) return false; bool found = false; for (int i = 0; i < 4; i++) { if (subnode[i] != null) { found = subnode[i].Remove(itemEnv, item); if (found) { // trim subtree if empty if (subnode[i].IsPrunable) subnode[i] = null; break; } } } // if item was found lower down, don't need to search for it here if (found) return found; // otherwise, try and remove the item from the list of items in this node if(items.Contains(item)) { items.Remove(item); found = true; } return found; } /// /// /// public bool IsPrunable { get { return !(HasChildren || HasItems); } } /// /// /// public bool HasChildren { get { for (int i = 0; i < 4; i++) { if (subnode[i] != null) return true; } return false; } } /// /// /// public bool IsEmpty { get { bool isEmpty = true; if(items.Count != 0) isEmpty = false; for (int i = 0; i < 4; i++) if (subnode[i] != null) if (!subnode[i].IsEmpty) isEmpty = false; return isEmpty; } } /// /// Insert items in this into the parameter! /// /// IList for adding items. /// Parameter IList with this items. public IList AddAllItems(ref IList resultItems) { // this node may have items as well as subnodes (since items may not // be wholely contained in any single subnode // resultItems.addAll(this.items); foreach (object o in this.items) resultItems.Add(o); for (int i = 0; i < 4; i++) if (subnode[i] != null) subnode[i].AddAllItems(ref resultItems); return resultItems; } /// /// /// /// /// protected abstract bool IsSearchMatch(IEnvelope searchEnv); /// /// /// /// /// public void AddAllItemsFromOverlapping(IEnvelope searchEnv, ref IList resultItems) { if (!IsSearchMatch(searchEnv)) return; // this node may have items as well as subnodes (since items may not // be wholely contained in any single subnode foreach (object o in this.items) resultItems.Add(o); for (int i = 0; i < 4; i++) if (subnode[i] != null) subnode[i].AddAllItemsFromOverlapping(searchEnv, ref resultItems); } /// /// /// /// /// public void Visit(IEnvelope searchEnv, IItemVisitor visitor) { if (!IsSearchMatch(searchEnv)) return; // this node may have items as well as subnodes (since items may not // be wholely contained in any single subnode VisitItems(searchEnv, visitor); for (int i = 0; i < 4; i++) if (subnode[i] != null) subnode[i].Visit(searchEnv, visitor); } /// /// /// /// /// private void VisitItems(IEnvelope searchEnv, IItemVisitor visitor) { // would be nice to filter items based on search envelope, but can't until they contain an envelope for (IEnumerator i = items.GetEnumerator(); i.MoveNext(); ) visitor.VisitItem(i.Current); } /// /// /// public int Depth { get { int maxSubDepth = 0; for (int i = 0; i < 4; i++) { if (subnode[i] != null) { int sqd = subnode[i].Depth; if (sqd > maxSubDepth) maxSubDepth = sqd; } } return maxSubDepth + 1; } } /// /// /// public int Count { get { int subSize = 0; for (int i = 0; i < 4; i++) if (subnode[i] != null) subSize += subnode[i].Count; return subSize + items.Count; } } /// /// /// public int NodeCount { get { int subSize = 0; for (int i = 0; i < 4; i++) if (subnode[i] != null) subSize += subnode[i].Count; return subSize + 1; } } } }