Move more functions into private methods

This commit is contained in:
Tom Payne
2013-05-25 19:24:45 -05:00
parent 3cdb9f130b
commit 32c663cf2c
+379 -366
View File
@@ -69,116 +69,139 @@ ol.structs.RTree = function(opt_maxWidth) {
({extent: [0, 0, 0, 0], nodes: []}); ({extent: [0, 0, 0, 0], nodes: []});
/** /**
* Find the best specific node(s) for object to be deleted from. * Non-recursive search function
* *
* @param {ol.structs.RTreeNode} rect Rectangle. * @param {ol.Extent} extent Extent.
* @param {Object} obj Object. * @param {string=} opt_type Optional type of the objects we want to find.
* @param {ol.structs.RTreeNode} root Root to start search. * @return {Array} Result.
* @return {Array} Leaf node parent. * @this {ol.structs.RTree}
*/ */
var removeSubtree = function(rect, obj, root) { this.search = function(extent, opt_type) {
var hitStack = []; // Contains the elements that overlap var rect = /** @type {ol.structs.RTreeNode} */ ({extent: extent});
var countStack = []; // Contains the elements that overlap return /** @type {Array} */ (that.searchSubtree_.apply(this,
var returnArray = []; [rect, false, [], rootTree, opt_type]));
var currentDepth = 1;
if (!rect || !ol.extent.intersects(rect.extent, root.extent)) {
return returnArray;
}
/** @type {ol.structs.RTreeNode} */
var workingObject = /** @type {ol.structs.RTreeNode} */
({extent: rect.extent.concat(), target: obj});
countStack.push(root.nodes.length);
hitStack.push(root);
do {
var tree = hitStack.pop();
var i = countStack.pop() - 1;
if (goog.isDef(workingObject.target)) {
// We are searching for a target
while (i >= 0) {
var lTree = tree.nodes[i];
if (ol.extent.intersects(workingObject.extent, lTree.extent)) {
if ((workingObject.target && goog.isDef(lTree.leaf) &&
lTree.leaf === workingObject.target) ||
(!workingObject.target && (goog.isDef(lTree.leaf) ||
ol.extent.containsExtent(workingObject.extent, lTree.extent))))
{ // A Match !!
// Yup we found a match...
// we can cancel search and start walking up the list
if (goog.isDef(lTree.nodes)) {
// If we are deleting a node not a leaf...
returnArray = searchSubtree(lTree, true, [], lTree);
tree.nodes.splice(i, 1);
} else {
returnArray = tree.nodes.splice(i, 1);
}
// Resize MBR down...
ol.structs.RTree.makeMBR_(tree.nodes, tree);
workingObject.target = undefined;
if (tree.nodes.length < that.minWidth_) { // Underflow
workingObject.nodes = /** @type {Array} */
(searchSubtree(tree, true, [], tree));
}
break;
} else if (goog.isDef(lTree.nodes)) {
// Not a Leaf
currentDepth += 1;
countStack.push(i);
hitStack.push(tree);
tree = lTree;
i = lTree.nodes.length;
}
}
i -= 1;
}
} else if (goog.isDef(workingObject.nodes)) {
// We are unsplitting
tree.nodes.splice(i + 1, 1); // Remove unsplit node
// workingObject.nodes contains a list of elements removed from the
// tree so far
if (tree.nodes.length > 0) {
ol.structs.RTree.makeMBR_(tree.nodes, tree);
}
for (var t = 0, tt = workingObject.nodes.length; t < tt; ++t) {
insertSubtree(workingObject.nodes[t], tree);
}
workingObject.nodes.length = 0;
if (hitStack.length === 0 && tree.nodes.length <= 1) {
// Underflow..on root!
workingObject.nodes = /** @type {Array} */
(searchSubtree(tree, true, workingObject.nodes, tree));
tree.nodes.length = 0;
hitStack.push(tree);
countStack.push(1);
} else if (hitStack.length > 0 && tree.nodes.length < that.minWidth_) {
// Underflow..AGAIN!
workingObject.nodes = /** @type {Array} */
(searchSubtree(tree, true, workingObject.nodes, tree));
tree.nodes.length = 0;
} else {
workingObject.nodes = undefined; // Just start resizing
}
} else { // we are just resizing
ol.structs.RTree.makeMBR_(tree.nodes, tree);
}
currentDepth -= 1;
} while (hitStack.length > 0);
return returnArray;
}; };
/**
* Non-recursive search function
*
* @param {ol.Extent} extent Extent.
* @param {string=} opt_type Optional type of the objects we want to find.
* @return {Object} Result. Keys are UIDs of the values.
* @this {ol.structs.RTree}
*/
this.searchReturningObject = function(extent, opt_type) {
var rect = /** @type {ol.structs.RTreeNode} */ ({extent: extent});
return /** @type {Object} */ (that.searchSubtree_.apply(this,
[rect, false, [], rootTree, opt_type, true]));
};
/**
* Non-recursive function that deletes a specific region.
*
* @param {ol.Extent} extent Extent.
* @param {Object=} opt_obj Object.
* @return {Array} Result.
* @this {ol.structs.RTree}
*/
this.remove = function(extent, opt_obj) {
arguments[0] = /** @type {ol.structs.RTreeNode} */ ({extent: extent});
switch (arguments.length) {
case 1:
arguments[1] = false; // opt_obj == false for conditionals
case 2:
arguments[2] = rootTree; // Add root node to end of argument list
default:
arguments.length = 3;
}
if (arguments[1] === false) { // Do area-wide †
var numberDeleted = 0;
var result = [];
do {
numberDeleted = result.length;
result = result.concat(this.removeSubtree_.apply(this, arguments));
} while (numberDeleted != result.length);
return result;
} else { // Delete a specific item
return this.removeSubtree_.apply(this, arguments);
}
};
/**
* Non-recursive insert function.
*
* @param {ol.Extent} extent Extent.
* @param {Object} obj Object to insert.
* @param {string=} opt_type Optional type to store along with the object.
*/
this.insert = function(extent, obj, opt_type) {
var node = /** @type {ol.structs.RTreeNode} */
({extent: extent, leaf: obj});
if (goog.isDef(opt_type)) {
node.type = opt_type;
}
that.insertSubtree_(node, rootTree);
};
//End of RTree
};
/**
* Generates a minimally bounding rectangle for all rectangles in
* array "nodes". `rect` is modified into the MBR.
*
* @param {Array} nodes Nodes.
* @param {ol.structs.RTreeNode} rect Rectangle.
* @private
* @return {ol.structs.RTreeNode} Rectangle.
*/
ol.structs.RTree.makeMBR_ = function(nodes, rect) {
if (nodes.length < 1) {
return {extent: [0, 0, 0, 0]};
}
rect.extent = nodes[0].extent.concat();
for (var i = nodes.length - 1; i > 0; --i) {
ol.extent.extend(rect.extent, nodes[i].extent);
}
return rect;
};
/**
* This is Jon-Carlos Rivera's special addition to the world of r-trees.
* Every other (simple) method he found produced poor trees.
* This skews insertions to prefering squarer and emptier nodes.
*
* @param {number} l L.
* @param {number} w W.
* @param {number} fill Fill.
* @private
* @return {number} Squarified ratio.
*/
ol.structs.RTree.squarifiedRatio_ = function(l, w, fill) {
// Area of new enlarged rectangle
var peri = (l + w) / 2; // Average size of a side of the new rectangle
var area = l * w; // Area of new rectangle
// return the ratio of the perimeter to the area - the closer to 1 we are,
// the more "square" a rectangle is. conversly, when approaching zero the
// more elongated a rectangle is
var geo = area / (peri * peri);
return area * fill / geo;
};
/** /**
* Choose the best damn node for rectangle to be inserted into. * Choose the best damn node for rectangle to be inserted into.
* *
* @param {ol.structs.RTreeNode} rect Rectangle. * @param {ol.structs.RTreeNode} rect Rectangle.
* @param {ol.structs.RTreeNode} root Root to start search. * @param {ol.structs.RTreeNode} root Root to start search.
* @private
* @return {Array} Leaf node parent. * @return {Array} Leaf node parent.
*/ */
var chooseLeafSubtree = function(rect, root) { ol.structs.RTree.prototype.chooseLeafSubtree_ = function(rect, root) {
var bestChoiceIndex = -1; var bestChoiceIndex = -1;
var bestChoiceStack = []; var bestChoiceStack = [];
var bestChoiceArea; var bestChoiceArea;
@@ -231,92 +254,94 @@ ol.structs.RTree = function(opt_maxWidth) {
return bestChoiceStack; return bestChoiceStack;
}; };
/**
* Split a set of nodes into two roughly equally-filled nodes.
*
* @param {Array.<ol.structs.RTreeNode>} nodes Array of nodes.
* @return {Array.<Array.<ol.structs.RTreeNode>>} An array of two new arrays
* of nodes.
*/
var linearSplit = function(nodes) {
var n = pickLinear(nodes);
while (nodes.length > 0) {
pickNext(nodes, n[0], n[1]);
}
return n;
};
/** /**
* Insert the best source rectangle into the best fitting parent node: a or b. * Non-recursive internal insert function.
* *
* @param {Array.<ol.structs.RTreeNode>} nodes Source node array. * @param {ol.structs.RTreeNode} node Node to insert.
* @param {ol.structs.RTreeNode} a Target node array a. * @param {ol.structs.RTreeNode} root Root to begin insertion at.
* @param {ol.structs.RTreeNode} b Target node array b. * @private
*/ */
var pickNext = function(nodes, a, b) { ol.structs.RTree.prototype.insertSubtree_ = function(node, root) {
// Area of new enlarged rectangle var bc; // Best Current node
var areaA = ol.structs.RTree.squarifiedRatio_(a.extent[1] - a.extent[0], // Initial insertion is special because we resize the Tree and we don't
a.extent[3] - a.extent[2], a.nodes.length + 1); // care about any overflow (seriously, how can the first object overflow?)
var areaB = ol.structs.RTree.squarifiedRatio_(b.extent[1] - b.extent[0], if (root.nodes.length === 0) {
b.extent[3] - b.extent[2], b.nodes.length + 1); root.extent = node.extent.concat();
var highAreaDelta; root.nodes.push(node);
var highAreaNode; return;
var lowestGrowthGroup; }
for (var i = nodes.length - 1; i >= 0; --i) { // Find the best fitting leaf node
var l = nodes[i]; // chooseLeaf returns an array of all tree levels (including root)
// that were traversed while trying to find the leaf
var treeStack = this.chooseLeafSubtree_(node, root);
var workingObject = node;
var newAreaA = [ // Walk back up the tree resizing and inserting as needed
a.extent[0] < l.extent[0] ? a.extent[0] : l.extent[0], do {
a.extent[1] > l.extent[1] ? a.extent[1] : l.extent[1], //handle the case of an empty node (from a split)
a.extent[2] < l.extent[2] ? a.extent[2] : l.extent[2], if (bc && goog.isDef(bc.nodes) && bc.nodes.length === 0) {
a.extent[3] > l.extent[3] ? a.extent[3] : l.extent[3] var pbc = bc; // Past bc
]; bc = treeStack.pop();
var changeNewAreaA = Math.abs(ol.structs.RTree.squarifiedRatio_( for (var t = 0, tt = bc.nodes.length; t < tt; ++t) {
newAreaA[1] - newAreaA[0], if (bc.nodes[t] === pbc || bc.nodes[t].nodes.length === 0) {
newAreaA[3] - newAreaA[2], a.nodes.length + 2) - areaA); bc.nodes.splice(t, 1);
break;
var newAreaB = [
b.extent[0] < l.extent[0] ? b.extent[0] : l.extent[0],
b.extent[1] > l.extent[1] ? b.extent[1] : l.extent[1],
b.extent[2] < l.extent[2] ? b.extent[2] : l.extent[2],
b.extent[3] > l.extent[3] ? b.extent[3] : l.extent[3]
];
var changeNewAreaB = Math.abs(ol.structs.RTree.squarifiedRatio_(
newAreaB[1] - newAreaB[0], newAreaB[3] - newAreaB[2],
b.nodes.length + 2) - areaB);
var changeNewAreaDelta = Math.abs(changeNewAreaB - changeNewAreaA);
if (!highAreaNode || !highAreaDelta ||
changeNewAreaDelta < highAreaDelta) {
highAreaNode = i;
highAreaDelta = changeNewAreaDelta;
lowestGrowthGroup = changeNewAreaB < changeNewAreaA ? b : a;
} }
} }
var tempNode = nodes.splice(highAreaNode, 1)[0]; } else {
if (a.nodes.length + nodes.length + 1 <= that.minWidth_) { bc = treeStack.pop();
a.nodes.push(tempNode);
ol.extent.extend(a.extent, tempNode.extent);
} else if (b.nodes.length + nodes.length + 1 <= that.minWidth_) {
b.nodes.push(tempNode);
ol.extent.extend(b.extent, tempNode.extent);
} }
else {
lowestGrowthGroup.nodes.push(tempNode); // If there is data attached to this workingObject
ol.extent.extend(lowestGrowthGroup.extent, tempNode.extent); var isArray = goog.isArray(workingObject);
if (goog.isDef(workingObject.leaf) ||
goog.isDef(workingObject.nodes) || isArray) {
// Do Insert
if (isArray) {
for (var ai = 0, aii = workingObject.length; ai < aii; ++ai) {
ol.extent.extend(bc.extent, workingObject[ai].extent);
} }
bc.nodes = bc.nodes.concat(workingObject);
} else {
ol.extent.extend(bc.extent, workingObject.extent);
bc.nodes.push(workingObject); // Do Insert
}
if (bc.nodes.length <= this.maxWidth_) { // Start Resizeing Up the Tree
workingObject = {extent: bc.extent.concat()};
} else { // Otherwise Split this Node
// linearSplit_() returns an array containing two new nodes
// formed from the split of the previous node's overflow
var a = this.linearSplit_(bc.nodes);
workingObject = a;//[1];
if (treeStack.length < 1) { // If are splitting the root..
bc.nodes.push(a[0]);
treeStack.push(bc); // Reconsider the root element
workingObject = a[1];
}
}
} else { // Otherwise Do Resize
//Just keep applying the new bounding rectangle to the parents..
ol.extent.extend(bc.extent, workingObject.extent);
workingObject = ({extent: bc.extent.concat()});
}
} while (treeStack.length > 0);
}; };
/** /**
* Pick the "best" two starter nodes to use as seeds using the "linear" * Pick the "best" two starter nodes to use as seeds using the "linear"
* criteria. * criteria.
* *
* @param {Array.<ol.structs.RTreeNode>} nodes Array of source nodes. * @param {Array.<ol.structs.RTreeNode>} nodes Array of source nodes.
* @private
* @return {Array.<ol.structs.RTreeNode>} An array of two new arrays * @return {Array.<ol.structs.RTreeNode>} An array of two new arrays
* of nodes. * of nodes.
*/ */
var pickLinear = function(nodes) { ol.structs.RTree.prototype.pickLinear_ = function(nodes) {
var lowestHighX = nodes.length - 1; var lowestHighX = nodes.length - 1;
var highestLowX = 0; var highestLowX = 0;
var lowestHighY = nodes.length - 1; var lowestHighY = nodes.length - 1;
@@ -366,6 +391,192 @@ ol.structs.RTree = function(opt_maxWidth) {
}; };
/**
* Insert the best source rectangle into the best fitting parent node: a or b.
*
* @param {Array.<ol.structs.RTreeNode>} nodes Source node array.
* @param {ol.structs.RTreeNode} a Target node array a.
* @param {ol.structs.RTreeNode} b Target node array b.
* @private
*/
ol.structs.RTree.prototype.pickNext_ = function(nodes, a, b) {
// Area of new enlarged rectangle
var areaA = ol.structs.RTree.squarifiedRatio_(a.extent[1] - a.extent[0],
a.extent[3] - a.extent[2], a.nodes.length + 1);
var areaB = ol.structs.RTree.squarifiedRatio_(b.extent[1] - b.extent[0],
b.extent[3] - b.extent[2], b.nodes.length + 1);
var highAreaDelta;
var highAreaNode;
var lowestGrowthGroup;
for (var i = nodes.length - 1; i >= 0; --i) {
var l = nodes[i];
var newAreaA = [
a.extent[0] < l.extent[0] ? a.extent[0] : l.extent[0],
a.extent[1] > l.extent[1] ? a.extent[1] : l.extent[1],
a.extent[2] < l.extent[2] ? a.extent[2] : l.extent[2],
a.extent[3] > l.extent[3] ? a.extent[3] : l.extent[3]
];
var changeNewAreaA = Math.abs(ol.structs.RTree.squarifiedRatio_(
newAreaA[1] - newAreaA[0],
newAreaA[3] - newAreaA[2], a.nodes.length + 2) - areaA);
var newAreaB = [
b.extent[0] < l.extent[0] ? b.extent[0] : l.extent[0],
b.extent[1] > l.extent[1] ? b.extent[1] : l.extent[1],
b.extent[2] < l.extent[2] ? b.extent[2] : l.extent[2],
b.extent[3] > l.extent[3] ? b.extent[3] : l.extent[3]
];
var changeNewAreaB = Math.abs(ol.structs.RTree.squarifiedRatio_(
newAreaB[1] - newAreaB[0], newAreaB[3] - newAreaB[2],
b.nodes.length + 2) - areaB);
var changeNewAreaDelta = Math.abs(changeNewAreaB - changeNewAreaA);
if (!highAreaNode || !highAreaDelta ||
changeNewAreaDelta < highAreaDelta) {
highAreaNode = i;
highAreaDelta = changeNewAreaDelta;
lowestGrowthGroup = changeNewAreaB < changeNewAreaA ? b : a;
}
}
var tempNode = nodes.splice(highAreaNode, 1)[0];
if (a.nodes.length + nodes.length + 1 <= this.minWidth_) {
a.nodes.push(tempNode);
ol.extent.extend(a.extent, tempNode.extent);
} else if (b.nodes.length + nodes.length + 1 <= this.minWidth_) {
b.nodes.push(tempNode);
ol.extent.extend(b.extent, tempNode.extent);
}
else {
lowestGrowthGroup.nodes.push(tempNode);
ol.extent.extend(lowestGrowthGroup.extent, tempNode.extent);
}
};
/**
* Find the best specific node(s) for object to be deleted from.
*
* @param {ol.structs.RTreeNode} rect Rectangle.
* @param {Object} obj Object.
* @param {ol.structs.RTreeNode} root Root to start search.
* @private
* @return {Array} Leaf node parent.
*/
ol.structs.RTree.prototype.removeSubtree_ = function(rect, obj, root) {
var hitStack = []; // Contains the elements that overlap
var countStack = []; // Contains the elements that overlap
var returnArray = [];
var currentDepth = 1;
if (!rect || !ol.extent.intersects(rect.extent, root.extent)) {
return returnArray;
}
/** @type {ol.structs.RTreeNode} */
var workingObject = /** @type {ol.structs.RTreeNode} */
({extent: rect.extent.concat(), target: obj});
countStack.push(root.nodes.length);
hitStack.push(root);
do {
var tree = hitStack.pop();
var i = countStack.pop() - 1;
if (goog.isDef(workingObject.target)) {
// We are searching for a target
while (i >= 0) {
var lTree = tree.nodes[i];
if (ol.extent.intersects(workingObject.extent, lTree.extent)) {
if ((workingObject.target && goog.isDef(lTree.leaf) &&
lTree.leaf === workingObject.target) ||
(!workingObject.target && (goog.isDef(lTree.leaf) ||
ol.extent.containsExtent(workingObject.extent, lTree.extent))))
{ // A Match !!
// Yup we found a match...
// we can cancel search and start walking up the list
if (goog.isDef(lTree.nodes)) {
// If we are deleting a node not a leaf...
returnArray = this.searchSubtree_(lTree, true, [], lTree);
tree.nodes.splice(i, 1);
} else {
returnArray = tree.nodes.splice(i, 1);
}
// Resize MBR down...
ol.structs.RTree.makeMBR_(tree.nodes, tree);
workingObject.target = undefined;
if (tree.nodes.length < this.minWidth_) { // Underflow
workingObject.nodes = /** @type {Array} */
(this.searchSubtree_(tree, true, [], tree));
}
break;
} else if (goog.isDef(lTree.nodes)) {
// Not a Leaf
currentDepth += 1;
countStack.push(i);
hitStack.push(tree);
tree = lTree;
i = lTree.nodes.length;
}
}
i -= 1;
}
} else if (goog.isDef(workingObject.nodes)) {
// We are unsplitting
tree.nodes.splice(i + 1, 1); // Remove unsplit node
// workingObject.nodes contains a list of elements removed from the
// tree so far
if (tree.nodes.length > 0) {
ol.structs.RTree.makeMBR_(tree.nodes, tree);
}
for (var t = 0, tt = workingObject.nodes.length; t < tt; ++t) {
this.insertSubtree_(workingObject.nodes[t], tree);
}
workingObject.nodes.length = 0;
if (hitStack.length === 0 && tree.nodes.length <= 1) {
// Underflow..on root!
workingObject.nodes = /** @type {Array} */
(this.searchSubtree_(tree, true, workingObject.nodes, tree));
tree.nodes.length = 0;
hitStack.push(tree);
countStack.push(1);
} else if (hitStack.length > 0 && tree.nodes.length < this.minWidth_) {
// Underflow..AGAIN!
workingObject.nodes = /** @type {Array} */
(this.searchSubtree_(tree, true, workingObject.nodes, tree));
tree.nodes.length = 0;
} else {
workingObject.nodes = undefined; // Just start resizing
}
} else { // we are just resizing
ol.structs.RTree.makeMBR_(tree.nodes, tree);
}
currentDepth -= 1;
} while (hitStack.length > 0);
return returnArray;
};
/**
* Split a set of nodes into two roughly equally-filled nodes.
*
* @param {Array.<ol.structs.RTreeNode>} nodes Array of nodes.
* @private
* @return {Array.<Array.<ol.structs.RTreeNode>>} An array of two new arrays
* of nodes.
*/
ol.structs.RTree.prototype.linearSplit_ = function(nodes) {
var n = this.pickLinear_(nodes);
while (nodes.length > 0) {
this.pickNext_(nodes, n[0], n[1]);
}
return n;
};
/** /**
* Non-recursive internal search function * Non-recursive internal search function
* *
@@ -376,10 +587,11 @@ ol.structs.RTree = function(opt_maxWidth) {
* @param {string=} opt_type Optional type to search for. * @param {string=} opt_type Optional type to search for.
* @param {boolean=} opt_resultAsObject If set, result will be an object keyed * @param {boolean=} opt_resultAsObject If set, result will be an object keyed
* by UID. * by UID.
* @private
* @return {Array|Object} Result. * @return {Array|Object} Result.
*/ */
var searchSubtree = function(rect, returnNode, result, root, opt_type, ol.structs.RTree.prototype.searchSubtree_ = function(
opt_resultAsObject) { rect, returnNode, result, root, opt_type, opt_resultAsObject) {
var resultObject = {}; var resultObject = {};
var hitStack = []; // Contains the elements that overlap var hitStack = []; // Contains the elements that overlap
@@ -423,202 +635,3 @@ ol.structs.RTree = function(opt_maxWidth) {
return result; return result;
} }
}; };
/**
* Non-recursive internal insert function.
*
* @param {ol.structs.RTreeNode} node Node to insert.
* @param {ol.structs.RTreeNode} root Root to begin insertion at.
*/
var insertSubtree = function(node, root) {
var bc; // Best Current node
// Initial insertion is special because we resize the Tree and we don't
// care about any overflow (seriously, how can the first object overflow?)
if (root.nodes.length === 0) {
root.extent = node.extent.concat();
root.nodes.push(node);
return;
}
// Find the best fitting leaf node
// chooseLeaf returns an array of all tree levels (including root)
// that were traversed while trying to find the leaf
var treeStack = chooseLeafSubtree(node, root);
var workingObject = node;
// Walk back up the tree resizing and inserting as needed
do {
//handle the case of an empty node (from a split)
if (bc && goog.isDef(bc.nodes) && bc.nodes.length === 0) {
var pbc = bc; // Past bc
bc = treeStack.pop();
for (var t = 0, tt = bc.nodes.length; t < tt; ++t) {
if (bc.nodes[t] === pbc || bc.nodes[t].nodes.length === 0) {
bc.nodes.splice(t, 1);
break;
}
}
} else {
bc = treeStack.pop();
}
// If there is data attached to this workingObject
var isArray = goog.isArray(workingObject);
if (goog.isDef(workingObject.leaf) ||
goog.isDef(workingObject.nodes) || isArray) {
// Do Insert
if (isArray) {
for (var ai = 0, aii = workingObject.length; ai < aii; ++ai) {
ol.extent.extend(bc.extent, workingObject[ai].extent);
}
bc.nodes = bc.nodes.concat(workingObject);
} else {
ol.extent.extend(bc.extent, workingObject.extent);
bc.nodes.push(workingObject); // Do Insert
}
if (bc.nodes.length <= that.maxWidth_) { // Start Resizeing Up the Tree
workingObject = {extent: bc.extent.concat()};
} else { // Otherwise Split this Node
// linearSplit() returns an array containing two new nodes
// formed from the split of the previous node's overflow
var a = linearSplit(bc.nodes);
workingObject = a;//[1];
if (treeStack.length < 1) { // If are splitting the root..
bc.nodes.push(a[0]);
treeStack.push(bc); // Reconsider the root element
workingObject = a[1];
}
}
} else { // Otherwise Do Resize
//Just keep applying the new bounding rectangle to the parents..
ol.extent.extend(bc.extent, workingObject.extent);
workingObject = ({extent: bc.extent.concat()});
}
} while (treeStack.length > 0);
};
/**
* Non-recursive search function
*
* @param {ol.Extent} extent Extent.
* @param {string=} opt_type Optional type of the objects we want to find.
* @return {Array} Result.
* @this {ol.structs.RTree}
*/
this.search = function(extent, opt_type) {
var rect = /** @type {ol.structs.RTreeNode} */ ({extent: extent});
return /** @type {Array} */ (searchSubtree.apply(this, [rect, false, [],
rootTree, opt_type]));
};
/**
* Non-recursive search function
*
* @param {ol.Extent} extent Extent.
* @param {string=} opt_type Optional type of the objects we want to find.
* @return {Object} Result. Keys are UIDs of the values.
* @this {ol.structs.RTree}
*/
this.searchReturningObject = function(extent, opt_type) {
var rect = /** @type {ol.structs.RTreeNode} */ ({extent: extent});
return /** @type {Object} */ (searchSubtree.apply(this, [rect, false, [],
rootTree, opt_type, true]));
};
/**
* Non-recursive function that deletes a specific region.
*
* @param {ol.Extent} extent Extent.
* @param {Object=} opt_obj Object.
* @return {Array} Result.
* @this {ol.structs.RTree}
*/
this.remove = function(extent, opt_obj) {
arguments[0] = /** @type {ol.structs.RTreeNode} */ ({extent: extent});
switch (arguments.length) {
case 1:
arguments[1] = false; // opt_obj == false for conditionals
case 2:
arguments[2] = rootTree; // Add root node to end of argument list
default:
arguments.length = 3;
}
if (arguments[1] === false) { // Do area-wide †
var numberDeleted = 0;
var result = [];
do {
numberDeleted = result.length;
result = result.concat(removeSubtree.apply(this, arguments));
} while (numberDeleted != result.length);
return result;
} else { // Delete a specific item
return removeSubtree.apply(this, arguments);
}
};
/**
* Non-recursive insert function.
*
* @param {ol.Extent} extent Extent.
* @param {Object} obj Object to insert.
* @param {string=} opt_type Optional type to store along with the object.
*/
this.insert = function(extent, obj, opt_type) {
var node = /** @type {ol.structs.RTreeNode} */
({extent: extent, leaf: obj});
if (goog.isDef(opt_type)) {
node.type = opt_type;
}
insertSubtree(node, rootTree);
};
//End of RTree
};
/**
* Generates a minimally bounding rectangle for all rectangles in
* array "nodes". `rect` is modified into the MBR.
*
* @param {Array} nodes Nodes.
* @param {ol.structs.RTreeNode} rect Rectangle.
* @private
* @return {ol.structs.RTreeNode} Rectangle.
*/
ol.structs.RTree.makeMBR_ = function(nodes, rect) {
if (nodes.length < 1) {
return {extent: [0, 0, 0, 0]};
}
rect.extent = nodes[0].extent.concat();
for (var i = nodes.length - 1; i > 0; --i) {
ol.extent.extend(rect.extent, nodes[i].extent);
}
return rect;
};
/**
* This is Jon-Carlos Rivera's special addition to the world of r-trees.
* Every other (simple) method he found produced poor trees.
* This skews insertions to prefering squarer and emptier nodes.
*
* @param {number} l L.
* @param {number} w W.
* @param {number} fill Fill.
* @private
* @return {number} Squarified ratio.
*/
ol.structs.RTree.squarifiedRatio_ = function(l, w, fill) {
// Area of new enlarged rectangle
var peri = (l + w) / 2; // Average size of a side of the new rectangle
var area = l * w; // Area of new rectangle
// return the ratio of the perimeter to the area - the closer to 1 we are,
// the more "square" a rectangle is. conversly, when approaching zero the
// more elongated a rectangle is
var geo = area / (peri * peri);
return area * fill / geo;
};