예제 #1
0
        internal NodeInternal splitNode(RTree rTree, ref Rectangle r, NodeBase childNode)
        {
            // [Pick first entry for each group] Apply algorithm pickSeeds to
            // choose two entries to be the first elements of the groups. Assign
            // each to a group.

            // debug code

            /*double initialArea = 0;
             *   if (log.isDebugEnabled())
             *   {
             *     double unionMinX = Math.Min(n.mbrMinX, newRectMinX);
             *     double unionMinY = Math.Min(n.mbrMinY, newRectMinY);
             *     double unionMaxX = Math.Max(n.mbrMaxX, newRectMaxX);
             *     double unionMaxY = Math.Max(n.mbrMaxY, newRectMaxY);
             *
             *     initialArea = (unionMaxX - unionMinX) * (unionMaxY - unionMinY);
             *   }*/

            System.Array.Copy(rTree.initialEntryStatus, 0, rTree.entryStatus, 0, rTree.maxNodeEntries);

            NodeInternal newNode = null;

            newNode = new NodeInternal(level, rTree.maxNodeEntries);
            Update();
            pickSeeds(rTree, ref r, newNode, childNode); // this also sets the entryCount to 1

            // [Check if done] If all entries have been assigned, stop. If one group has so few entries that all the rest must be assigned to it in
            // order for it to have the minimum number m, assign them and stop.
            while (entryCount + newNode.entryCount < rTree.maxNodeEntries + 1)
            {
                if (rTree.maxNodeEntries + 1 - newNode.entryCount == rTree.minNodeEntries)
                {
                    // assign all remaining entries to original node
                    for (int i = 0; i < rTree.maxNodeEntries; i++)
                    {
                        if (rTree.entryStatus[i] == ((byte)RTree.EntryStatus.unassigned))
                        {
                            rTree.entryStatus[i] = ((byte)RTree.EntryStatus.assigned);

                            if (entries[i].Value.MinX < minimumBoundingRectangle.MinX)
                            {
                                minimumBoundingRectangle.MinX = entries[i].Value.MinX;
                            }
                            if (entries[i].Value.MinY < minimumBoundingRectangle.MinY)
                            {
                                minimumBoundingRectangle.MinY = entries[i].Value.MinY;
                            }
                            if (entries[i].Value.MaxX > minimumBoundingRectangle.MaxX)
                            {
                                minimumBoundingRectangle.MaxX = entries[i].Value.MaxX;
                            }
                            if (entries[i].Value.MaxY > minimumBoundingRectangle.MaxY)
                            {
                                minimumBoundingRectangle.MaxY = entries[i].Value.MaxY;
                            }
                            entryCount++;
                        }
                    }
                    break;
                }
                if (rTree.maxNodeEntries + 1 - entryCount == rTree.minNodeEntries)
                {
                    // assign all remaining entries to new node
                    for (int i = 0; i < rTree.maxNodeEntries; i++)
                    {
                        if (rTree.entryStatus[i] == ((byte)RTree.EntryStatus.unassigned))
                        {
                            rTree.entryStatus[i] = ((byte)RTree.EntryStatus.assigned);
                            Rectangle entriesR = entries[i].Value;
                            newNode.addEntry(ref entriesR, childNodes[i]);
                            entries[i]    = null;
                            childNodes[i] = null;
                        }
                    }
                    break;
                }

                // [Select entry to assign] Invoke algorithm pickNext to choose the next entry to assign. Add it to the group whose covering rectangle
                // will have to be enlarged least to accommodate it. Resolve ties by adding the entry to the group with smaller area, then to the
                // the one with fewer entries, then to either. Repeat from S2
                pickNext(rTree, newNode);
            }

            reorganize(rTree);

            // check that the MBR stored for each node is correct.
#if RtreeCheck
            if (!minimumBoundingRectangle.Equals(calculateMBR()))
            {
                throw new UnexpectedException("Error: splitNode old node MBR wrong");
            }
            if (!newNode.minimumBoundingRectangle.Equals(newNode.calculateMBR()))
            {
                throw new UnexpectedException("Error: splitNode new node MBR wrong");
            }
#endif

#if RtreeCheck
            double newArea            = minimumBoundingRectangle.Area + newNode.minimumBoundingRectangle.Area;
            double percentageIncrease = (100 * (newArea - initialArea)) / initialArea;
            Console.WriteLine("Node " + this + " split. New area increased by " + percentageIncrease + "%");
#endif

            return(newNode);
        }
예제 #2
0
        private void pickSeeds(RTree rTree, ref Rectangle r, NodeInternal newNode, NodeBase childNode)
        {
            // Find extreme rectangles along all dimension. Along each dimension, find the entry whose rectangle has the highest low side, and the one
            // with the lowest high side. Record the separation.
            double maxNormalizedSeparation = -1; // initialize to -1 so that even overlapping rectangles will be considered for the seeds
            int    highestLowIndex         = -1;
            int    lowestHighIndex         = -1;

            Update();
            // for the purposes of picking seeds, take the MBR of the node to include the new rectangle aswell.
            if (r.MinX < minimumBoundingRectangle.MinX)
            {
                minimumBoundingRectangle.MinX = r.MinX;
            }
            if (r.MinY < minimumBoundingRectangle.MinY)
            {
                minimumBoundingRectangle.MinY = r.MinY;
            }
            if (r.MaxX > minimumBoundingRectangle.MaxX)
            {
                minimumBoundingRectangle.MaxX = r.MaxX;
            }
            if (r.MaxY > minimumBoundingRectangle.MaxY)
            {
                minimumBoundingRectangle.MaxY = r.MaxY;
            }

            double mbrLenX = minimumBoundingRectangle.MaxX - minimumBoundingRectangle.MinX;
            double mbrLenY = minimumBoundingRectangle.MaxY - minimumBoundingRectangle.MinY;

#if RtreeCheck
            Console.WriteLine("pickSeeds(): NodeI = " + this);
#endif

            double tempHighestLow      = r.MinX;
            int    tempHighestLowIndex = -1; // -1 indicates the new rectangle is the seed

            double tempLowestHigh      = r.MaxX;
            int    tempLowestHighIndex = -1; // -1 indicates the new rectangle is the seed

            for (int i = 0; i < entryCount; i++)
            {
                double tempLow = entries[i].Value.MinX;
                if (tempLow >= tempHighestLow)
                {
                    tempHighestLow      = tempLow;
                    tempHighestLowIndex = i;
                } // ensure that the same index cannot be both lowestHigh and highestLow
                else
                {
                    double tempHigh = entries[i].Value.MaxX;
                    if (tempHigh <= tempLowestHigh)
                    {
                        tempLowestHigh      = tempHigh;
                        tempLowestHighIndex = i;
                    }
                }

                // PS2 [Adjust for shape of the rectangle cluster] Normalize the separations by dividing by the widths of the entire set along the corresponding dimension
                double normalizedSeparation = mbrLenX == 0 ? 1 : (tempHighestLow - tempLowestHigh) / mbrLenX;
                if (normalizedSeparation > 1 || normalizedSeparation < -1)
                {
                    Console.WriteLine("Invalid normalized separation X");
                }

#if RtreeCheck
                Console.WriteLine("Entry " + i + ", dimension X: HighestLow = " + tempHighestLow + " (index " + tempHighestLowIndex + ")" + ", LowestHigh = " + tempLowestHigh + " (index " + tempLowestHighIndex + ", NormalizedSeparation = " + normalizedSeparation);
#endif
                // PS3 [Select the most extreme pair] Choose the pair with the greatest normalized separation along any dimension.
                // Note that if negative it means the rectangles overlapped. However still include overlapping rectangles if that is the only choice available.
                if (normalizedSeparation >= maxNormalizedSeparation)
                {
                    highestLowIndex         = tempHighestLowIndex;
                    lowestHighIndex         = tempLowestHighIndex;
                    maxNormalizedSeparation = normalizedSeparation;
                }
            }

            // Repeat for the Y dimension
            tempHighestLow      = r.MinY;
            tempHighestLowIndex = -1; // -1 indicates the new rectangle is the seed

            tempLowestHigh      = r.MaxY;
            tempLowestHighIndex = -1; // -1 indicates the new rectangle is the seed

            for (int i = 0; i < entryCount; i++)
            {
                double tempLow = entries[i].Value.MinY;
                if (tempLow >= tempHighestLow)
                {
                    tempHighestLow      = tempLow;
                    tempHighestLowIndex = i;
                } // ensure that the same index cannot be both lowestHigh and highestLow
                else
                {
                    double tempHigh = entries[i].Value.MaxY;
                    if (tempHigh <= tempLowestHigh)
                    {
                        tempLowestHigh      = tempHigh;
                        tempLowestHighIndex = i;
                    }
                }

                // PS2 [Adjust for shape of the rectangle cluster] Normalize the separations by dividing by the widths of the entire set along the corresponding dimension
                double normalizedSeparation = mbrLenY == 0 ? 1 : (tempHighestLow - tempLowestHigh) / mbrLenY;
                if (normalizedSeparation > 1 || normalizedSeparation < -1)
                {
                    throw new UnexpectedException("Invalid normalized separation Y");
                }
#if RtreeCheck
                Console.WriteLine("Entry " + i + ", dimension Y: HighestLow = " + tempHighestLow + " (index " + tempHighestLowIndex + ")" + ", LowestHigh = " + tempLowestHigh + " (index " + tempLowestHighIndex + ", NormalizedSeparation = " + normalizedSeparation);
#endif
                // PS3 [Select the most extreme pair] Choose the pair with the greatest normalized separation along any dimension.
                // Note that if negative it means the rectangles overlapped. However still include overlapping rectangles if that is the only choice available.
                if (normalizedSeparation >= maxNormalizedSeparation)
                {
                    highestLowIndex         = tempHighestLowIndex;
                    lowestHighIndex         = tempLowestHighIndex;
                    maxNormalizedSeparation = normalizedSeparation;
                }
            }

            // At this point it is possible that the new rectangle is both highestLow and lowestHigh. This can happen if all rectangles in the node overlap the new rectangle.
            // Resolve this by declaring that the highestLowIndex is the lowest Y and, the lowestHighIndex is the largest X (but always a different rectangle)
            if (highestLowIndex == lowestHighIndex)
            {
                highestLowIndex = -1;
                double tempMinY = r.MinY;
                lowestHighIndex = 0;
                double tempMaxX = entries[0].Value.MaxX;

                for (int i = 1; i < entryCount; i++)
                {
                    if (entries[i].Value.MinY < tempMinY)
                    {
                        tempMinY        = entries[i].Value.MinY;
                        highestLowIndex = i;
                    }
                    else if (entries[i].Value.MaxX > tempMaxX)
                    {
                        tempMaxX        = entries[i].Value.MaxX;
                        lowestHighIndex = i;
                    }
                }
            }

            // highestLowIndex is the seed for the new node.
            if (highestLowIndex == -1)
            {
                newNode.addEntry(ref r, childNode);
            }
            else
            {
                Rectangle entriesR = entries[highestLowIndex].Value;
                newNode.addEntry(ref entriesR, childNodes[highestLowIndex]);
                entries[highestLowIndex]    = r; // move the new rectangle into the space vacated by the seed for the new node
                childNodes[highestLowIndex] = childNode;
            }

            // lowestHighIndex is the seed for the original node.
            if (lowestHighIndex == -1)
            {
                lowestHighIndex = highestLowIndex;
            }

            rTree.entryStatus[lowestHighIndex] = ((byte)RTree.EntryStatus.assigned);
            entryCount = 1;
            minimumBoundingRectangle = entries[lowestHighIndex].Value;
        }
예제 #3
0
        private PriorityQueueRTree createNearestNDistanceQueue(Point p, UInt32 count, double furthestDistance)
        {
            PriorityQueueRTree distanceQueue = new PriorityQueueRTree();

            //  return immediately if given an invalid "count" parameter
            if (count == 0)
            {
                return(distanceQueue);
            }

            parents.Clear();
            parents.Push(rootNode);

            parentsEntry.Clear();
            parentsEntry.Push(-1);

            // TODO: possible shortcut here - could test for intersection with the MBR of the root node. If no intersection, return immediately.
            double furthestDistanceSq = furthestDistance * furthestDistance;

            while (parents.Count > 0)
            {
                NodeBase n          = parents.Peek();
                int      startIndex = parentsEntry.Peek() + 1;

                if (!n.IsLeaf)
                {
                    // go through every entry in the index node to check if it could contain an entry closer than the farthest entry currently stored.
                    bool         near         = false;
                    NodeInternal nodeInternal = n as NodeInternal;
                    for (int i = startIndex; i < n.entryCount; i++)
                    {
                        if (n.entries[i].Value.distanceSq(p.x, p.y) <= furthestDistanceSq)
                        {
                            parents.Push(nodeInternal.childNodes[i]);
                            parentsEntry.Pop();
                            parentsEntry.Push(i); // this becomes the start index when the child has been searched
                            parentsEntry.Push(-1);
                            near = true;
                            break; // ie go to next iteration of while()
                        }
                    }
                    if (near)
                    {
                        continue;
                    }
                }
                else
                {
                    // go through every entry in the leaf to check if it is currently one of the nearest N entries.
                    for (int i = 0; i < n.entryCount; i++)
                    {
                        double entryDistanceSq = n.entries[i].Value.distanceSq(p.x, p.y);

                        if (entryDistanceSq <= furthestDistanceSq)
                        {
                            distanceQueue.Insert(n.entries[i].Value, entryDistanceSq);

                            while (distanceQueue.Count > count)
                            {
                                // normal case - we can simply remove the lowest priority (highest distance) entry
                                Rectangle value      = distanceQueue.ValuePeek;
                                double    distanceSq = distanceQueue.PriorityPeek;
                                distanceQueue.Pop();

                                // rare case - multiple items of the same priority (distance)
                                if (distanceSq == distanceQueue.PriorityPeek)
                                {
                                    savedValues.Add(value);
                                    savedPriority = distanceSq;
                                }
                                else
                                {
                                    savedValues.Clear();
                                }
                            }

                            // if the saved values have the same distance as the next one in the tree, add them back in.
                            if (savedValues.Count > 0 && savedPriority == distanceQueue.PriorityPeek)
                            {
                                for (int svi = 0; svi < savedValues.Count; svi++)
                                {
                                    distanceQueue.Insert(savedValues[svi], savedPriority);
                                }
                                savedValues.Clear();
                            }

                            // narrow the search, if we have already found N items
                            if (distanceQueue.PriorityPeek < furthestDistanceSq && distanceQueue.Count >= count)
                            {
                                furthestDistanceSq = distanceQueue.PriorityPeek;
                            }
                        }
                    }
                }
                parents.Pop();
                parentsEntry.Pop();
            }
            return(distanceQueue);
        }
예제 #4
0
        private bool checkConsistency(NodeBase n, int expectedLevel, Rectangle?expectedMBR)
        {
            // go through the tree, and check that the internal data structures of the tree are not corrupted.

            if (n == null)
            {
                throw new UnexpectedException($"Error: Could not read node {this}");
            }

            // if tree is empty, then there should be exactly one node, at level 1
            // TODO: also check the MBR is as for a new node
            if (n == rootNode && Count == 0)
            {
                if (n.level != 1)
                {
                    throw new UnexpectedException("Error: tree is empty but root node is not at level 1");
                }
            }

            if (n.level != expectedLevel)
            {
                throw new UnexpectedException("Error: Node " + this + ", expected level " + expectedLevel + ", actual level " + n.level);
            }

            Rectangle calculatedMBR = n.calculateMinimumBoundingRectangle();
            Rectangle actualMBR     = n.minimumBoundingRectangle;

            if (!actualMBR.Equals(calculatedMBR))
            {
                if (actualMBR.MinX != n.minimumBoundingRectangle.MinX)
                {
                    throw new UnexpectedException($"  actualMinX={actualMBR.MinX}, calc={calculatedMBR.MinX}");
                }
                if (actualMBR.MinY != n.minimumBoundingRectangle.MinY)
                {
                    throw new UnexpectedException($"  actualMinY={actualMBR.MinY}, calc={calculatedMBR.MinY}");
                }
                if (actualMBR.MaxX != n.minimumBoundingRectangle.MaxX)
                {
                    throw new UnexpectedException($"  actualMaxX={actualMBR.MaxX}, calc={calculatedMBR.MaxX}");
                }
                if (actualMBR.MaxY != n.minimumBoundingRectangle.MaxY)
                {
                    throw new UnexpectedException($"  actualMaxY={actualMBR.MaxY}, calc={calculatedMBR.MaxY}");
                }
                throw new UnexpectedException("Error: Node " + this + ", calculated MBR does not equal stored MBR");
            }

            if (expectedMBR != null && !actualMBR.Equals(expectedMBR))
            {
                throw new UnexpectedException("Error: Node " + this + ", expected MBR (from parent) does not equal stored MBR");
            }

            for (int i = 0; i < n.entryCount; i++)
            {
                if (n.level > 1) // if not a leaf
                {
                    NodeInternal nodeInternal = n as NodeInternal;
                    if (nodeInternal.childNodes[i] == null)
                    {
                        throw new UnexpectedException("Error: Node " + this + ", Entry " + i + " is null");
                    }
                    if (!checkConsistency(nodeInternal.childNodes[i], n.level - 1, n.entries[i]))
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
예제 #5
0
        /// <summary>
        /// Removes a rectangle from the Rtree
        /// </summary>
        /// <param name="r">the rectangle to delete</param>
        /// <returns>true if rectangle deleted otherwise false</returns>
        public bool Remove(Rectangle r)
        {
            // FindLeaf algorithm inlined here. Note the "official" algorithm searches all overlapping entries. This seems inefficient,
            // as an entry is only worth searching if it contains (NOT overlaps) the rectangle we are searching for.

            // FL1 [Search subtrees] If root is not a leaf, check each entry to determine if it contains r. For each entry found, invoke
            // findLeaf on the node pointed to by the entry, until r is found or all entries have been checked.
            parents.Clear();
            parents.Push(rootNode);

            parentsEntry.Clear();
            parentsEntry.Push(-1);
            NodeBase n          = null;
            int      foundIndex = -1; // index of entry to be deleted in leaf

            while (foundIndex == -1 && parents.Count > 0)
            {
                n = parents.Peek();
                int startIndex = parentsEntry.Peek() + 1;

                if (!n.IsLeaf)
                {
                    NodeInternal internalNode = n as NodeInternal;
                    bool         Contains     = false;
                    for (int i = startIndex; i < n.entryCount; i++)
                    {
                        if (n.entries[i].Value.Contains(r))
                        {
                            parents.Push(internalNode.childNodes[i]);
                            parentsEntry.Pop();
                            parentsEntry.Push(i); // this becomes the start index when the child has been searched
                            parentsEntry.Push(-1);
                            Contains = true;
                            break; // ie go to next iteration of while()
                        }
                    }
                    if (Contains)
                    {
                        continue;
                    }
                }
                else
                {
                    NodeLeaf leaf = n as NodeLeaf;
                    foundIndex = leaf.findEntry(ref r);
                }

                parents.Pop();
                parentsEntry.Pop();
            } // while not found

            if (foundIndex != -1)
            {
                NodeLeaf leaf = n as NodeLeaf;
                leaf.deleteEntry(foundIndex);
                leaf.condenseTree(this);
                size--;
            }

            // shrink the tree if possible (i.e. if root node has exactly one entry, and that entry is not a leaf node, delete the root (it's entry becomes the new root)
            NodeBase root = rootNode;

            while (root.entryCount == 1 && treeHeight > 1)
            {
                NodeInternal rootInternal = root as NodeInternal;
                root.entryCount = 0;
                rootNode        = rootInternal.childNodes[0];
                treeHeight--;
            }

            // if the tree is now empty, then set the MBR of the root node back to it's original state (this is only needed when the tree is empty,
            // as this is the only state where an empty node is not eliminated)
            if (size == 0)
            {
                rootNode.minimumBoundingRectangle = new Rectangle(true);
            }

#if RtreeCheck
            checkConsistency();
#endif

            return(foundIndex != -1);
        }