/// <summary> /// Insert an item into the tree this is the root of. /// </summary> /// <param name="itemInterval"></param> /// <param name="item"></param> public virtual void Insert(Interval itemInterval, object item) { int index = GetSubnodeIndex(itemInterval, ORIGIN); // if index is -1, itemEnv must contain the origin. if (index == -1) { Add(item); return; } /* * the item must be contained in one interval, so insert it into the * tree for that interval (which may not yet exist) */ Node node = Nodes[index]; /* * If the subnode doesn't exist or this item is not contained in it, * have to expand the tree upward to contain the item. */ if (node == null || !node.Interval.Contains(itemInterval)) { Node largerNode = Node.CreateExpanded(node, itemInterval); Nodes[index] = largerNode; } /* * At this point we have a subnode which exists and must contain * contains the env for the item. Insert the item into the tree. */ InsertContained(Nodes[index], itemInterval, item); }
/// <summary> /// /// </summary> /// <param name="itemInterval"></param> /// <returns></returns> public static Node CreateNode(Interval itemInterval) { Key key = new Key(itemInterval); Node node = new Node(key.Interval, key.Level); return node; }
/// <summary> /// /// </summary> /// <param name="node"></param> /// <param name="addInterval"></param> /// <returns></returns> public static Node CreateExpanded(Node node, Interval addInterval) { Interval expandInt = new Interval(addInterval); if (node != null) expandInt.ExpandToInclude(node._interval); Node largerNode = CreateNode(expandInt); if (node != null) largerNode.Insert(node); return largerNode; }
/// <summary> /// /// </summary> /// <param name="interval"></param> /// <param name="resultItems"></param> /// <returns></returns> public virtual IList AddAllItemsFromOverlapping(Interval interval, IList resultItems) { if (!IsSearchMatch(interval)) return _items; // resultItems.addAll(items); foreach (object o in _items) resultItems.Add(o); for (int i = 0; i < 2; i++) if (_subnode[i] != null) _subnode[i].AddAllItemsFromOverlapping(interval, resultItems); return _items; }
/// <summary> /// Insert an item which is known to be contained in the tree rooted at /// the given Node. Lower levels of the tree will be created /// if necessary to hold the item. /// </summary> /// <param name="tree"></param> /// <param name="itemInterval"></param> /// <param name="item"></param> private static void InsertContained(Node tree, Interval itemInterval, object item) { Assert.IsTrue(tree.Interval.Contains(itemInterval)); /* * Do NOT create a new node for zero-area intervals - this would lead * to infinite recursion. Instead, use a heuristic of simply returning * the smallest existing node containing the query */ bool isZeroArea = IntervalSize.IsZeroWidth(itemInterval.Min, itemInterval.Max); NodeBase node; if (isZeroArea) node = tree.Find(itemInterval); else node = tree.GetNode(itemInterval); node.Add(item); }
/// <summary> /// /// </summary> /// <param name="interval"></param> /// <param name="level"></param> public Node(Interval interval, int level) { _interval = interval; _level = level; _centre = (interval.Min + interval.Max) / 2; }
/// <summary> /// /// </summary> /// <param name="index"></param> /// <returns></returns> private Node CreateSubnode(int index) { // create a new subnode in the appropriate interval double min = 0.0; double max = 0.0; switch (index) { case 0: min = _interval.Min; max = _centre; break; case 1: min = _centre; max = _interval.Max; break; default: break; } Interval subInt = new Interval(min, max); Node node = new Node(subInt, _level - 1); return node; }
/// <summary> /// Returns the index of the subnode that wholely contains the given interval. /// If none does, returns -1. /// </summary> /// <param name="interval"></param> /// <param name="centre"></param> public static int GetSubnodeIndex(Interval interval, double centre) { int subnodeIndex = -1; if (interval.Min >= centre) subnodeIndex = 1; if (interval.Max <= centre) subnodeIndex = 0; return subnodeIndex; }
/// <summary> /// /// </summary> /// <param name="interval"></param> /// <param name="foundItems"></param> public virtual void Query(Interval interval, IList foundItems) { _root.AddAllItemsFromOverlapping(interval, foundItems); }
/// <summary> /// /// </summary> /// <param name="itemInterval"></param> /// <param name="item"></param> public virtual void Insert(Interval itemInterval, object item) { CollectStats(itemInterval); Interval insertInterval = EnsureExtent(itemInterval, _minExtent); _root.Insert(insertInterval, item); }
/// <summary> /// /// </summary> /// <param name="interval"></param> public Key(Interval interval) { ComputeKey(interval); }
/// <summary> /// /// </summary> /// <param name="level"></param> /// <param name="itemInterval"></param> private void ComputeInterval(int level, Interval itemInterval) { double size = DoubleBits.PowerOf2(level); _pt = Math.Floor(itemInterval.Min / size) * size; _interval.Init(_pt, _pt + size); }
/// <summary> /// Return a square envelope containing the argument envelope, /// whose extent is a power of two and which is based at a power of 2. /// </summary> /// <param name="itemInterval"></param> public void ComputeKey(Interval itemInterval) { _level = ComputeLevel(itemInterval); _interval = new Interval(); ComputeInterval(_level, itemInterval); // MD - would be nice to have a non-iterative form of this algorithm while (!_interval.Contains(itemInterval)) { _level += 1; ComputeInterval(_level, itemInterval); } }
/// <summary> /// The root node matches all searches. /// </summary> /// <param name="interval"></param> protected override bool IsSearchMatch(Interval interval) { return true; }
/// <summary> /// /// </summary> /// <param name="itemInterval"></param> /// <returns></returns> protected override bool IsSearchMatch(Interval itemInterval) { return itemInterval.Overlaps(_interval); }
/// <summary> /// /// </summary> /// <param name="interval"></param> /// <returns></returns> public static int ComputeLevel(Interval interval) { double dx = interval.Width; int level = DoubleBits.GetExponent(dx) + 1; return level; }
/// <summary> /// Ensure that the Interval for the inserted item has non-zero extents. /// Use the current minExtent to pad it, if necessary. /// </summary> public static Interval EnsureExtent(Interval itemInterval, double minExtent) { double min = itemInterval.Min; double max = itemInterval.Max; // has a non-zero extent if (min != max) return itemInterval; // pad extent if (min == max) { min = min - minExtent / 2.0; max = min + minExtent / 2.0; } return new Interval(min, max); }
/// <summary> /// Returns the subnode containing the envelope. /// Creates the node if /// it does not already exist. /// </summary> /// <param name="searchInterval"></param> public virtual Node GetNode(Interval searchInterval) { int subnodeIndex = GetSubnodeIndex(searchInterval, _centre); // if index is -1 searchEnv is not contained in a subnode if (subnodeIndex != -1) { // create the node if it does not exist Node node = GetSubnode(subnodeIndex); // recursively search the found/created node return node.GetNode(searchInterval); } return this; }
/// <summary> /// min and max may be the same value. /// </summary> /// <param name="interval"></param> public virtual IList Query(Interval interval) { /* * the items that are matched are all items in intervals * which overlap the query interval */ IList foundItems = new ArrayList(); Query(interval, foundItems); return foundItems; }
/// <summary> /// Returns the smallest existing /// node containing the envelope. /// </summary> /// <param name="searchInterval"></param> public virtual NodeBase Find(Interval searchInterval) { int subnodeIndex = GetSubnodeIndex(searchInterval, _centre); if (subnodeIndex == -1) return this; if (Nodes[subnodeIndex] != null) { // query lies in subnode, so search it Node node = Nodes[subnodeIndex]; return node.Find(searchInterval); } // no existing subnode, so return this one anyway return this; }
/// <summary> /// /// </summary> /// <param name="interval"></param> private void CollectStats(Interval interval) { double del = interval.Width; if (del < _minExtent && del > 0.0) _minExtent = del; }
/// <summary> /// /// </summary> /// <param name="interval"></param> /// <returns></returns> protected abstract bool IsSearchMatch(Interval interval);