/// <summary> /// Returns the record in remaininglist with the largest affinity for /// one group over the other. /// </summary> /// <param name="remainingList"></param> /// <param name="groupOne"></param> /// <param name="groupTwo"></param> /// <param name="D1"></param> /// <param name="D2"></param> /// <returns></returns> private static IndexRecord <T> pickNext(List <IndexRecord <T> > remainingList, NodeRecord <T> groupOne, NodeRecord <T> groupTwo, out int D1, out int D2) { IndexRecord <T> nextRecord = null; int maxDiff = int.MinValue; D1 = 0; D2 = 0; int diff, d1, d2; foreach (var record in remainingList) { // PN1. d1 = RTree <T> .EnclosingArea(groupOne.BBox, record.BBox) - groupOne.BBox.GetArea(); d2 = RTree <T> .EnclosingArea(groupTwo.BBox, record.BBox) - groupTwo.BBox.GetArea(); diff = Math.Abs(d2 - d1); if (diff > maxDiff) { maxDiff = diff; // PN2. D1 = d1; D2 = d2; nextRecord = record; } } return(nextRecord); }
/// <summary> /// Returns the two index records in the remaininglist that, if put into a node, /// would have the largest bouding box of any two records. /// </summary> /// <param name="a"></param> /// <param name="b"></param> private void pickSeeds(List <IndexRecord <T> > remainingList, out IndexRecord <T> a, out IndexRecord <T> b) { int maxD = int.MinValue; a = null; b = null; foreach (var childNodeA in remainingList) { foreach (var childNodeB in remainingList) { if (childNodeA == childNodeB) { continue; } int D = RTree <T> .EnclosingArea(childNodeA.BBox, childNodeB.BBox); // PS1. // This is a measure inefficiency of grouping items together. D = D - childNodeA.BBox.GetArea() - childNodeB.BBox.GetArea(); if (D > maxD) { // PS2. maxD = D; a = childNodeA; b = childNodeB; } } } }
/// <summary> /// Splits this node into two based on the chosen algorithm. /// Returns a new node with some of the records. This node /// retains some of the records as well. /// </summary> /// <param name="item"></param> /// <returns></returns> public NodeRecord <T> Split(IndexRecord <T> item) { NodeRecord <T> newNode = Node.SplitQuadradic(item); ResizeBBox(); // newNode's bounding box should be the right size already. return(newNode); }
/// <summary> /// Attempts to insert a record. /// If this succeeds, it resizes the bouding box. /// Otherwise return false. /// </summary> /// <param name="item"></param> /// <returns></returns> public bool TryInsert(IndexRecord <T> item) { bool success = Node.TryInsert(item); if (success) { ResizeBBox(); } return(success); }
/// <summary> /// Attempts to insert an item into this's list of records. /// If it fails, returns false (the node needs to split). /// </summary> /// <param name="item"></param> /// <returns></returns> public bool TryInsert(IndexRecord <T> item) { if (records.Count < M) { records.Add(item); // Bounding box resizing must be handled by calling function. return(true); } else { // Need to split. return(false); } }
/// <summary> /// Utilizes the Quadradic split algorithm to determine where each /// of the records go - either to group one or group two. /// </summary> /// <param name="newItem"></param> /// <returns></returns> public NodeRecord <T> SplitQuadradic(IndexRecord <T> newItem) { List <IndexRecord <T> > itemsToBeAssigned = records; itemsToBeAssigned.Add(newItem); IndexRecord <T> A, B; // QS1. pickSeeds(itemsToBeAssigned, out A, out B); itemsToBeAssigned.Remove(A); itemsToBeAssigned.Remove(B); NodeRecord <T> groupOne = new NodeRecord <T>() { BBox = A.BBox, Node = new RTreeNode <T>(isLeaf) }; NodeRecord <T> groupTwo = new NodeRecord <T>() { BBox = B.BBox, Node = new RTreeNode <T>(isLeaf) }; groupOne.Node.TryInsert(A); groupTwo.Node.TryInsert(B); // QS2. while (itemsToBeAssigned.Count > 0) { // QS3. int d1, d2; var next = pickNext(itemsToBeAssigned, groupOne, groupTwo, out d1, out d2); if (d1 < d2) { groupOne.TryInsert(next); } else if (d2 < d1) { groupTwo.TryInsert(next); } else { // Insert to whichever is smaller. if (groupOne.Node.GetRecordCount() < groupTwo.Node.GetRecordCount()) { groupOne.TryInsert(next); } else { groupTwo.TryInsert(next); } } itemsToBeAssigned.Remove(next); // QS2. if (groupOne.Node.GetRecordCount() + itemsToBeAssigned.Count <= m) { while (itemsToBeAssigned.Count > 0) { groupOne.TryInsert(itemsToBeAssigned.First()); itemsToBeAssigned.Remove(itemsToBeAssigned.First()); } } // QS2. if (groupTwo.Node.GetRecordCount() + itemsToBeAssigned.Count <= m) { while (itemsToBeAssigned.Count > 0) { groupTwo.TryInsert(itemsToBeAssigned.First()); itemsToBeAssigned.Remove(itemsToBeAssigned.First()); } } } // Set this equal to group two. records = groupTwo.Node.records; return(groupOne); }