/// <summary> /// Handles the scenario where an overflow happens (the chosen leaf container is full). /// </summary> /// <remarks> /// It handles overflows by first trying to search for a suitable sibling under the same parent to insert the new leaf into. /// If this fails (either because there are no siblings, or because they are all full), it will split the node (or two /// nodes if there was at least one sibling) into 2 or 3 nodes, and distributes their children (and the new node) evenly amongst them. /// </remarks> /// <param name="node">The node where the overflow happened.</param> /// <param name="leaf">The leaf (or any new node if this is an internal level) to be inserted.</param> /// <returns>The newly created node if a split was inevitable.</returns> private HilbertNode HandleOverflow(HilbertNode node, HilbertNode leaf) { // check for a sibling node HilbertNode sibling = this.ChooseSibling(node); // then we redistribute these children evenly across the 2 or 3 nodes: // - between the original and its sibling if there was one, and it was not full // - between the original and a new node if there wasn't any sibling // - between the original, the sibling and the new node, if there was a sibling, but it was full // the original node is assumed to be always full here (hence the method name HandleOverflow) List <HilbertNode> distributionList = new List <HilbertNode>(); HilbertNode newNode = null; distributionList.Add(node); if (sibling == null || sibling.IsFull) { newNode = new HilbertNode((HilbertNode)node.Parent); distributionList.Add(newNode); } if (sibling != null) { distributionList.Insert(sibling.LargestHilbertValue < node.LargestHilbertValue ? 0 : 1, sibling); } this.RedistributeChildrenEvenly(distributionList, leaf); return(newNode); }
/// <summary> /// Adjusts the tree ascending from the leaf level up to the root. /// </summary> /// <param name="node">The node where the overflow happened.</param> /// <param name="newNode">The new node, if a split was inevitable in <see cref="HandleOverflow(HilbertNode, HilbertNode)"/>, otherwise it's <c>null</c>.</param> /// <returns>The new <see cref="HilbertNode"/> on the root level, if the root node had to be split.</returns> private HilbertNode AdjustTree(HilbertNode node, HilbertNode newNode = null) { while (node != this.Root) { // propagate node split upward HilbertNode nParent = (HilbertNode)node.Parent; HilbertNode pParent = null; if (newNode != null) { if (this.IsFull(nParent)) { pParent = this.HandleOverflow(nParent, newNode); } else { nParent.AddChild(newNode); } } // update MBR and LHV in parent level nParent.CorrectBounding(); nParent.Children?.Sort(this.comparer); nParent.LargestHilbertValue = nParent.ChildrenCount == 0 ? new BigInteger(-1) : ((HilbertNode)nParent.Children[nParent.ChildrenCount - 1]).LargestHilbertValue; node = nParent; newNode = pParent; } return(newNode); }
/// <summary> /// Chooses one the sibling for a node. /// </summary> /// <param name="node">The node.</param> /// <returns>A sibling node of <c>node</c>.</returns> private HilbertNode ChooseSibling(HilbertNode node) { Tuple <HilbertNode, HilbertNode> siblings = this.ChooseSiblings(node); // prefer the sibling to the right side return(siblings.Item2 == null || siblings.Item2.IsFull ? siblings.Item1 : siblings.Item2); }
/// <summary> /// Adds a geometry by creating a new leaf node. /// </summary> /// <param name="geometry">The geometry to be added.</param> protected override void AddGeometry(IBasicGeometry geometry) { HilbertNode leaf = new HilbertNode(geometry, this.GetHilbertValue(geometry)); HilbertNode leafContainer = this.ChooseLeafContainer(leaf); if (leafContainer == this.Root && leafContainer.ChildrenCount == 0) { this.Height = 1; } HilbertNode newNode = null; if (!this.IsFull(leafContainer)) { leafContainer.AddChild(leaf); } else { newNode = this.HandleOverflow(leafContainer, leaf); } HilbertNode rightRoot = this.AdjustTree(leafContainer, newNode); this.IncreaseHeight(rightRoot); }
/// <summary> /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. /// </summary> /// <param name="x">The first object to compare.</param> /// <param name="y">The second object to compare.</param> /// <returns> /// A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table.Value Meaning Less than zero<paramref name="x" /> is less than <paramref name="y" />.Zero<paramref name="x" /> equals <paramref name="y" />.Greater than zero<paramref name="x" /> is greater than <paramref name="y" />. /// </returns> public int Compare(Node x, Node y) { HilbertNode a = (HilbertNode)x; HilbertNode b = (HilbertNode)y; return(BigInteger.Compare(a.LargestHilbertValue, b.LargestHilbertValue)); }
/// <summary> /// Increases the height when a split has propagated to the root node (in other words, the root node had to be split). /// </summary> /// <param name="rightRoot">The newly created sibling for the root node.</param> private void IncreaseHeight(HilbertNode rightRoot) { if (rightRoot == null) { return; } HilbertNode newRoot = new HilbertNode(this.MaxChildren); newRoot.AddChild(this.Root); newRoot.AddChild(rightRoot); this.Root = newRoot; this.Height++; }
/// <summary> /// Chooses the appropriate leaf container for a new leaf. /// </summary> /// <param name="leaf">The new leaf which shall be inserted to the tree.</param> /// <returns>The appropriate leaf container in which the new leaf shall be inserted.</returns> private HilbertNode ChooseLeafContainer(HilbertNode leaf) { Node node = this.Root; while (!node.IsLeafContainer) { HilbertNode lastChild = (HilbertNode)node.Children[node.ChildrenCount - 1]; // select the child with the minimum LHV which is greater than "value" // if there are no such children, select the last child (the one with the greatest LHV) node = lastChild.LargestHilbertValue <= leaf.LargestHilbertValue ? lastChild : node.Children.Find(n => ((HilbertNode)n).LargestHilbertValue > leaf.LargestHilbertValue); } return((HilbertNode)node); }
/// <summary> /// Handles the scenario where underflow happens by trying to redistribute the children of the node /// involved node between him and his siblings. If this fails, and the node has two siblings, it merges /// the node with its two siblings. In other cases we must let the underflow to happen. /// </summary> /// <param name="node">The node.</param> private void HandleUnderflow(HilbertNode node) { if (node == this.Root) { return; } if (node.ChildrenCount == 0) { node.Parent.RemoveChild(node); this.HandleUnderflow((HilbertNode)node.Parent); return; } if (node.ChildrenCount < this.MinChildren) { // check for sibling nodes Tuple <HilbertNode, HilbertNode> siblings = this.ChooseSiblings(node); if (this.HasSpareChild(siblings.Item1) || this.HasSpareChild(siblings.Item2)) { // if there are at least one sibling with more than the minimum amount of children, we redistribute List <HilbertNode> distributionList = new List <HilbertNode>(); if (siblings.Item1 != null) { distributionList.Add(siblings.Item1); } distributionList.Add(node); if (siblings.Item2 != null) { distributionList.Add(siblings.Item2); } this.RedistributeChildrenEvenly(distributionList); } else if (siblings.Item1 != null && siblings.Item2 != null) { // if there are two siblings but both of the have the minimum number of children, we delete a node, then redistribute List <HilbertNode> distributionList = new List <HilbertNode>(); distributionList.Add(siblings.Item1); distributionList.Add(siblings.Item2); List <HilbertNode> additionalChildren = new List <HilbertNode>(); node.Children.ForEach(child => additionalChildren.Add((HilbertNode)child)); node.Parent.RemoveChild(node); this.RedistributeChildrenEvenly(distributionList, additionalChildren: additionalChildren); } } }
/// <summary> /// Redistributes the children of the nodes specified evenly. /// </summary> /// <param name="nodes">The nodes whose children shall be redistributed evenly.</param> /// <param name="additionalChild">An optional additional child to be added.</param> /// <param name="additionalChildren">Optional additional children to be added.</param> private void RedistributeChildrenEvenly(List <HilbertNode> nodes, HilbertNode additionalChild = null, List <HilbertNode> additionalChildren = null) { List <HilbertNode> children = new List <HilbertNode>(); nodes.ForEach(node => { node?.Children?.ForEach(child => children.Add((HilbertNode)child)); }); if (additionalChild != null) { children.Add(additionalChild); } if (additionalChildren != null) { children.AddRange(additionalChildren); } children.Sort(this.comparer); nodes.ForEach(n => n.ClearChildren()); Int32 distributionFactor = Convert.ToInt32((decimal)children.Count / nodes.Count); IEnumerator <HilbertNode> enumerator = children.GetEnumerator(); nodes.ForEach(container => { for (Int32 i = 0; i < distributionFactor; i++) { if (!enumerator.MoveNext()) { break; } container.AddChild(enumerator.Current); } }); if (enumerator.MoveNext()) { nodes[nodes.Count - 1].AddChild(enumerator.Current); } }
/// <summary> /// Chooses two siblings (left and right) for a node. /// </summary> /// <param name="node">The node.</param> /// <returns>A <see cref="Tuple"/> of two siblings (left and right) of <c>node</c>.</returns> private Tuple <HilbertNode, HilbertNode> ChooseSiblings(HilbertNode node) { if (node.Parent?.Children == null) { return(Tuple.Create <HilbertNode, HilbertNode>(null, null)); } List <Node> siblings = node.Parent.Children; Int32 index = siblings.IndexOf(node); HilbertNode left = null; HilbertNode right = null; if (index + 1 < siblings.Count) { right = (HilbertNode)siblings[index + 1]; } if (index > 0) { left = (HilbertNode)siblings[index - 1]; } return(Tuple.Create <HilbertNode, HilbertNode>(left, right)); }
/// <summary> /// Removes the specified geometry from the tree. /// </summary> /// <param name="geometry">The geometry.</param> /// <returns> /// <c>true</c>, if the tree contains the geometry, otherwise, <c>false</c>. /// </returns> protected override Boolean RemoveGeometry(IBasicGeometry geometry) { // search for the leaf container which contains the specified geometry, if not found, return false Node l = null; this.FindLeafContainer(geometry, this.Root, ref l); if (l == null) { return(false); } // if found, remove the leaf with the specified geometry HilbertNode leafContainer = (HilbertNode)l; leafContainer.RemoveChild( leafContainer.Children.Find(child => child.Geometry == geometry)); // then check for a potential underflow this.HandleUnderflow(leafContainer); // adjust parents this.AdjustTree(leafContainer); return(true); }
/// <summary> /// Utility function which determines whether the specified node is full. /// </summary> /// <remarks> /// It checks whether the node is the root node, because the root node can contain more children than all the other nodes. /// If its not the root node it just returns <c>node.IsFull</c>. /// </remarks> /// <param name="node">The node to be checked.</param> /// <returns> /// <c>true</c> if the specified node is full; otherwise, <c>false</c>. /// </returns> private Boolean IsFull(HilbertNode node) { return(node == this.Root ? node.ChildrenCount == this.MinChildren * 2 : node.IsFull); }
/// <summary> /// Determines whether the specified node has a spare child, that is, whether or not it has more children than the minimum allowed. /// </summary> /// <param name="node">The node.</param> /// <returns> /// <c>true</c> if the specified node has spare child, otherwise, <c>false</c> /// </returns> private Boolean HasSpareChild(HilbertNode node) { return(node != null && node.ChildrenCount > this.MinChildren); }
/// <summary> /// Initializes a new instance of the <see cref="HilbertNode"/> class. /// This constructor creates a new leaf node. /// </summary> /// <param name="geometry">The geometry which shall be contained within this leaf.</param> /// <param name="hilbertValue">The hilbert value of <c>geometry</c>.</param> /// <param name="parent"> /// The parent node of this node (in this case a leaf container). /// This is optional as the parent will be set when this node is added as a child to another <see cref="HilbertNode"/>. /// </param> public HilbertNode(IBasicGeometry geometry, BigInteger hilbertValue, HilbertNode parent = null) : base(geometry, parent) { this.LargestHilbertValue = hilbertValue; }
/// <summary> /// Initializes a new instance of the <see cref="HilbertNode"/> class. /// This constructor creates a new internal node (non-root, and non-leaf). /// </summary> /// <param name="parent">The parent node of this node.</param> public HilbertNode(HilbertNode parent) : base(parent) { this.LargestHilbertValue = new BigInteger(-1); }