Upgrade rbush to 2.x

This commit is contained in:
Bryan Housel
2016-07-01 23:12:51 -04:00
parent 4b5389c310
commit 197b936d50
8 changed files with 619 additions and 587 deletions
+211 -199
View File
@@ -61,6 +61,10 @@
return [this[0][0], this[0][1], this[1][0], this[1][1]];
},
bbox: function() {
return { minX: this[0][0], minY: this[0][1], maxX: this[1][0], maxY: this[1][1] };
},
polygon: function() {
return [
[this[0][0], this[0][1]],
@@ -2168,16 +2172,78 @@
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var rbush = createCommonjsModule(function (module) {
/*
(c) 2015, Vladimir Agafonkin
RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
https://github.com/mourner/rbush
*/
(function () {
var index$1 = createCommonjsModule(function (module) {
'use strict';
module.exports = partialSort;
// Floyd-Rivest selection algorithm:
// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
// The k-th element will have the (k - left + 1)th smallest value in [left, right]
function partialSort(arr, k, left, right, compare) {
left = left || 0;
right = right || (arr.length - 1);
compare = compare || defaultCompare;
while (right > left) {
if (right - left > 600) {
var n = right - left + 1;
var m = k - left + 1;
var z = Math.log(n);
var s = 0.5 * Math.exp(2 * z / 3);
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
partialSort(arr, k, newLeft, newRight, compare);
}
var t = arr[k];
var i = left;
var j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
});
var require$$0 = (index$1 && typeof index$1 === 'object' && 'default' in index$1 ? index$1['default'] : index$1);
var index = createCommonjsModule(function (module) {
'use strict';
module.exports = rbush;
var quickselect = require$$0;
function rbush(maxEntries, format) {
if (!(this instanceof rbush)) return new rbush(maxEntries, format);
@@ -2204,7 +2270,7 @@
result = [],
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return result;
if (!intersects(bbox, node)) return result;
var nodesToSearch = [],
i, len, child, childBBox;
@@ -2213,7 +2279,7 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
childBBox = node.leaf ? toBBox(child) : child;
if (intersects(bbox, childBBox)) {
if (node.leaf) result.push(child);
@@ -2232,7 +2298,7 @@
var node = this.data,
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return false;
if (!intersects(bbox, node)) return false;
var nodesToSearch = [],
i, len, child, childBBox;
@@ -2241,7 +2307,7 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
childBBox = node.leaf ? toBBox(child) : child;
if (intersects(bbox, childBBox)) {
if (node.leaf || contains(bbox, childBBox)) return true;
@@ -2296,16 +2362,11 @@
},
clear: function () {
this.data = {
children: [],
height: 1,
bbox: empty(),
leaf: true
};
this.data = createNode([]);
return this;
},
remove: function (item) {
remove: function (item, equalsFn) {
if (!item) return this;
var node = this.data,
@@ -2325,7 +2386,7 @@
}
if (node.leaf) { // check current node
index = node.children.indexOf(item);
index = findItem(item, node.children, equalsFn);
if (index !== -1) {
// item found, remove the item and condense tree upwards
@@ -2336,7 +2397,7 @@
}
}
if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
path.push(node);
indexes.push(i);
i = 0;
@@ -2356,8 +2417,8 @@
toBBox: function (item) { return item; },
compareMinX: function (a, b) { return a[0] - b[0]; },
compareMinY: function (a, b) { return a[1] - b[1]; },
compareMinX: compareNodeMinX,
compareMinY: compareNodeMinY,
toJSON: function () { return this.data; },
@@ -2385,12 +2446,7 @@
if (N <= M) {
// reached leaf level; return leaf
node = {
children: items.slice(left, right + 1),
height: 1,
bbox: null,
leaf: true
};
node = createNode(items.slice(left, right + 1));
calcBBox(node, this.toBBox);
return node;
}
@@ -2403,12 +2459,9 @@
M = Math.ceil(N / Math.pow(M, height - 1));
}
node = {
children: [],
height: height,
bbox: null,
leaf: false
};
node = createNode([]);
node.leaf = false;
node.height = height;
// split the items into M mostly square tiles
@@ -2451,8 +2504,8 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
area = bboxArea(child.bbox);
enlargement = enlargedArea(bbox, child.bbox) - area;
area = bboxArea(child);
enlargement = enlargedArea(bbox, child) - area;
// choose entry with the least area enlargement
if (enlargement < minEnlargement) {
@@ -2478,7 +2531,7 @@
_insert: function (item, level, isNode) {
var toBBox = this.toBBox,
bbox = isNode ? item.bbox : toBBox(item),
bbox = isNode ? item : toBBox(item),
insertPath = [];
// find the best node for accommodating the item, saving all nodes along the path too
@@ -2486,7 +2539,7 @@
// put the item into the node
node.children.push(item);
extend(node.bbox, bbox);
extend(node, bbox);
// split on node overflow; propagate upwards if necessary
while (level >= 0) {
@@ -2511,14 +2564,9 @@
var splitIndex = this._chooseSplitIndex(node, m, M);
var newNode = {
children: node.children.splice(splitIndex, node.children.length - splitIndex),
height: node.height,
bbox: null,
leaf: false
};
if (node.leaf) newNode.leaf = true;
var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
newNode.height = node.height;
newNode.leaf = node.leaf;
calcBBox(node, this.toBBox);
calcBBox(newNode, this.toBBox);
@@ -2529,12 +2577,9 @@
_splitRoot: function (node, newNode) {
// split root node
this.data = {
children: [node, newNode],
height: node.height + 1,
bbox: null,
leaf: false
};
this.data = createNode([node, newNode]);
this.data.height = node.height + 1;
this.data.leaf = false;
calcBBox(this.data, this.toBBox);
},
@@ -2596,13 +2641,13 @@
for (i = m; i < M - m; i++) {
child = node.children[i];
extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
extend(leftBBox, node.leaf ? toBBox(child) : child);
margin += bboxMargin(leftBBox);
}
for (i = M - m - 1; i >= m; i--) {
child = node.children[i];
extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
extend(rightBBox, node.leaf ? toBBox(child) : child);
margin += bboxMargin(rightBBox);
}
@@ -2612,7 +2657,7 @@
_adjustParentBBoxes: function (bbox, path, level) {
// adjust bboxes along the given tree path
for (var i = level; i >= 0; i--) {
extend(path[i].bbox, bbox);
extend(path[i], bbox);
}
},
@@ -2642,71 +2687,97 @@
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
this.toBBox = new Function('a',
'return {minX: a' + format[0] +
', minY: a' + format[1] +
', maxX: a' + format[2] +
', maxY: a' + format[3] + '};');
}
};
function findItem(item, items, equalsFn) {
if (!equalsFn) return items.indexOf(item);
for (var i = 0; i < items.length; i++) {
if (equalsFn(item, items[i])) return i;
}
return -1;
}
// calculate node's bbox from bboxes of its children
function calcBBox(node, toBBox) {
node.bbox = distBBox(node, 0, node.children.length, toBBox);
distBBox(node, 0, node.children.length, toBBox, node);
}
// min bounding rectangle of node children from k to p-1
function distBBox(node, k, p, toBBox) {
var bbox = empty();
function distBBox(node, k, p, toBBox, destNode) {
if (!destNode) destNode = createNode(null);
destNode.minX = Infinity;
destNode.minY = Infinity;
destNode.maxX = -Infinity;
destNode.maxY = -Infinity;
for (var i = k, child; i < p; i++) {
child = node.children[i];
extend(bbox, node.leaf ? toBBox(child) : child.bbox);
extend(destNode, node.leaf ? toBBox(child) : child);
}
return bbox;
return destNode;
}
function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
function extend(a, b) {
a[0] = Math.min(a[0], b[0]);
a[1] = Math.min(a[1], b[1]);
a[2] = Math.max(a[2], b[2]);
a[3] = Math.max(a[3], b[3]);
a.minX = Math.min(a.minX, b.minX);
a.minY = Math.min(a.minY, b.minY);
a.maxX = Math.max(a.maxX, b.maxX);
a.maxY = Math.max(a.maxY, b.maxY);
return a;
}
function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
function compareNodeMinX(a, b) { return a.minX - b.minX; }
function compareNodeMinY(a, b) { return a.minY - b.minY; }
function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
function enlargedArea(a, b) {
return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
(Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
}
function intersectionArea(a, b) {
var minX = Math.max(a[0], b[0]),
minY = Math.max(a[1], b[1]),
maxX = Math.min(a[2], b[2]),
maxY = Math.min(a[3], b[3]);
var minX = Math.max(a.minX, b.minX),
minY = Math.max(a.minY, b.minY),
maxX = Math.min(a.maxX, b.maxX),
maxY = Math.min(a.maxY, b.maxY);
return Math.max(0, maxX - minX) *
Math.max(0, maxY - minY);
}
function contains(a, b) {
return a[0] <= b[0] &&
a[1] <= b[1] &&
b[2] <= a[2] &&
b[3] <= a[3];
return a.minX <= b.minX &&
a.minY <= b.minY &&
b.maxX <= a.maxX &&
b.maxY <= a.maxY;
}
function intersects(a, b) {
return b[0] <= a[2] &&
b[1] <= a[3] &&
b[2] >= a[0] &&
b[3] >= a[1];
return b.minX <= a.maxX &&
b.minY <= a.maxY &&
b.maxX >= a.minX &&
b.maxY >= a.minY;
}
function createNode(children) {
return {
children: children,
height: 1,
leaf: true,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
}
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
@@ -2723,88 +2794,30 @@
if (right - left <= n) continue;
mid = left + Math.ceil((right - left) / n / 2) * n;
select(arr, left, right, mid, compare);
quickselect(arr, mid, left, right, compare);
stack.push(left, mid, mid, right);
}
}
// Floyd-Rivest selection algorithm:
// sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
function select(arr, left, right, k, compare) {
var n, i, z, s, sd, newLeft, newRight, t, j;
while (right > left) {
if (right - left > 600) {
n = right - left + 1;
i = k - left + 1;
z = Math.log(n);
s = 0.5 * Math.exp(2 * z / 3);
sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
select(arr, newLeft, newRight, k, compare);
}
t = arr[k];
i = left;
j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// export as AMD/CommonJS module or global variable
if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
else if (typeof module !== 'undefined') module.exports = rbush;
else if (typeof self !== 'undefined') self.rbush = rbush;
else window.rbush = rbush;
})();
});
var rbush$1 = (rbush && typeof rbush === 'object' && 'default' in rbush ? rbush['default'] : rbush);
var rbush = (index && typeof index === 'object' && 'default' in index ? index['default'] : index);
function Tree(head) {
var rtree = rbush$1(),
rectangles = {};
var rtree = rbush(),
bboxes = {};
function entityRectangle(entity) {
var rect = entity.extent(head).rectangle();
rect.id = entity.id;
rectangles[entity.id] = rect;
return rect;
function entityBBox(entity) {
var bbox = entity.extent(head).bbox();
bbox.id = entity.id;
bboxes[entity.id] = bbox;
return bbox;
}
function updateParents(entity, insertions, memo) {
head.parentWays(entity).forEach(function(way) {
if (rectangles[way.id]) {
rtree.remove(rectangles[way.id]);
if (bboxes[way.id]) {
rtree.remove(bboxes[way.id]);
insertions[way.id] = way;
}
updateParents(way, insertions, memo);
@@ -2813,8 +2826,8 @@
head.parentRelations(entity).forEach(function(relation) {
if (memo[entity.id]) return;
memo[entity.id] = true;
if (rectangles[relation.id]) {
rtree.remove(rectangles[relation.id]);
if (bboxes[relation.id]) {
rtree.remove(bboxes[relation.id]);
insertions[relation.id] = relation;
}
updateParents(relation, insertions, memo);
@@ -2832,11 +2845,11 @@
if (!entity.visible)
continue;
if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
if (head.entities.hasOwnProperty(entity.id) || bboxes[entity.id]) {
if (!force) {
continue;
} else if (rectangles[entity.id]) {
rtree.remove(rectangles[entity.id]);
} else if (bboxes[entity.id]) {
rtree.remove(bboxes[entity.id]);
}
}
@@ -2844,7 +2857,7 @@
updateParents(entity, insertions, {});
}
rtree.load(_.map(insertions, entityRectangle));
rtree.load(_.map(insertions, entityBBox));
return tree;
};
@@ -2857,12 +2870,12 @@
head = graph;
diff.deleted().forEach(function(entity) {
rtree.remove(rectangles[entity.id]);
delete rectangles[entity.id];
rtree.remove(bboxes[entity.id]);
delete bboxes[entity.id];
});
diff.modified().forEach(function(entity) {
rtree.remove(rectangles[entity.id]);
rtree.remove(bboxes[entity.id]);
insertions[entity.id] = entity;
updateParents(entity, insertions, {});
});
@@ -2871,11 +2884,11 @@
insertions[entity.id] = entity;
});
rtree.load(_.map(insertions, entityRectangle));
rtree.load(_.map(insertions, entityBBox));
}
return rtree.search(extent.rectangle()).map(function(rect) {
return head.entity(rect.id);
return rtree.search(extent.bbox()).map(function(bbox) {
return head.entity(bbox.id);
});
};
@@ -3527,7 +3540,7 @@
};
}
var index = createCommonjsModule(function (module) {
var index$2 = createCommonjsModule(function (module) {
module.exports = element;
module.exports.pair = pair;
module.exports.format = format;
@@ -12503,16 +12516,16 @@
var mouse = context.mouse(),
pad = 50,
rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
ids = _.map(rtree.search(rect), 'id');
bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad },
ids = _.map(rtree.search(bbox), 'id');
if (!ids.length) return;
layers.selectAll('.' + ids.join(', .'))
.classed('proximate', true);
}
var rtree = rbush$1(),
rectangles = {};
var rtree = rbush(),
bboxes = {};
function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
var hidePoints = !surface.selectAll('.node.point').node();
@@ -12522,10 +12535,10 @@
if (fullRedraw) {
rtree.clear();
rectangles = {};
bboxes = {};
} else {
for (i = 0; i < entities.length; i++) {
rtree.remove(rectangles[entities[i].id]);
rtree.remove(bboxes[entities[i].id]);
}
}
@@ -12599,8 +12612,8 @@
y: coord[1] + offset[1],
textAnchor: offset[2]
};
var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
if (tryInsert(rect, entity.id)) return p;
var bbox = { minX: p.x - m, minY: p.y - m, maxX: p.x + width + m, maxY: p.y + height + m };
if (tryInsert(bbox, entity.id)) return p;
}
@@ -12616,14 +12629,14 @@
if (start < 0 || start + width > length) continue;
var sub = subpath(nodes, start, start + width),
rev = reverse(sub),
rect = [
Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
];
bbox = {
minX: Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
minY: Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
maxX: Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
maxY: Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
};
if (rev) sub = sub.reverse();
if (tryInsert(rect, entity.id)) return {
if (tryInsert(bbox, entity.id)) return {
'font-size': height + 2,
lineString: lineString(sub),
startOffset: offset + '%'
@@ -12635,7 +12648,7 @@
var centroid = path.centroid(entity.asGeoJSON(graph, true)),
extent = entity.extent(graph),
entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
rect;
bbox;
if (isNaN(centroid[0]) || entitywidth < 20) return;
@@ -12652,24 +12665,23 @@
p.y = centroid[1] + textOffset;
p.textAnchor = 'middle';
p.height = height;
rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
bbox = { minX: p.x - width/2, minY: p.y, maxX: p.x + width/2, maxY: p.y + height + textOffset };
} else {
rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
bbox = { minX: iconX, minY: iconY, maxX: iconX + iconSize, maxY: iconY + iconSize };
}
if (tryInsert(rect, entity.id)) return p;
if (tryInsert(bbox, entity.id)) return p;
}
function tryInsert(rect, id) {
function tryInsert(bbox, id) {
// Check that label is visible
if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
rect[3] > dimensions[1]) return false;
var v = rtree.search(rect).length === 0;
if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) return false;
var v = rtree.search(bbox).length === 0;
if (v) {
rect.id = id;
rtree.insert(rect);
rectangles[id] = rect;
bbox.id = id;
rtree.insert(bbox);
bboxes[id] = bbox;
}
return v;
}
@@ -12706,11 +12718,11 @@
if (showDebug) {
var gj = rtree.all().map(function(d) {
return { type: 'Polygon', coordinates: [[
[d[0], d[1]],
[d[2], d[1]],
[d[2], d[3]],
[d[0], d[3]],
[d[0], d[1]]
[d.minX, d.minY],
[d.maxX, d.minY],
[d.maxX, d.maxY],
[d.minX, d.maxY],
[d.minX, d.minY]
]]};
});
+164 -155
View File
@@ -8,16 +8,78 @@
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var rbush = createCommonjsModule(function (module) {
/*
(c) 2015, Vladimir Agafonkin
RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
https://github.com/mourner/rbush
*/
(function () {
var index$1 = createCommonjsModule(function (module) {
'use strict';
module.exports = partialSort;
// Floyd-Rivest selection algorithm:
// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
// The k-th element will have the (k - left + 1)th smallest value in [left, right]
function partialSort(arr, k, left, right, compare) {
left = left || 0;
right = right || (arr.length - 1);
compare = compare || defaultCompare;
while (right > left) {
if (right - left > 600) {
var n = right - left + 1;
var m = k - left + 1;
var z = Math.log(n);
var s = 0.5 * Math.exp(2 * z / 3);
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
partialSort(arr, k, newLeft, newRight, compare);
}
var t = arr[k];
var i = left;
var j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
});
var require$$0 = (index$1 && typeof index$1 === 'object' && 'default' in index$1 ? index$1['default'] : index$1);
var index = createCommonjsModule(function (module) {
'use strict';
module.exports = rbush;
var quickselect = require$$0;
function rbush(maxEntries, format) {
if (!(this instanceof rbush)) return new rbush(maxEntries, format);
@@ -44,7 +106,7 @@
result = [],
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return result;
if (!intersects(bbox, node)) return result;
var nodesToSearch = [],
i, len, child, childBBox;
@@ -53,7 +115,7 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
childBBox = node.leaf ? toBBox(child) : child;
if (intersects(bbox, childBBox)) {
if (node.leaf) result.push(child);
@@ -72,7 +134,7 @@
var node = this.data,
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return false;
if (!intersects(bbox, node)) return false;
var nodesToSearch = [],
i, len, child, childBBox;
@@ -81,7 +143,7 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
childBBox = node.leaf ? toBBox(child) : child;
if (intersects(bbox, childBBox)) {
if (node.leaf || contains(bbox, childBBox)) return true;
@@ -136,16 +198,11 @@
},
clear: function () {
this.data = {
children: [],
height: 1,
bbox: empty(),
leaf: true
};
this.data = createNode([]);
return this;
},
remove: function (item) {
remove: function (item, equalsFn) {
if (!item) return this;
var node = this.data,
@@ -165,7 +222,7 @@
}
if (node.leaf) { // check current node
index = node.children.indexOf(item);
index = findItem(item, node.children, equalsFn);
if (index !== -1) {
// item found, remove the item and condense tree upwards
@@ -176,7 +233,7 @@
}
}
if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
path.push(node);
indexes.push(i);
i = 0;
@@ -196,8 +253,8 @@
toBBox: function (item) { return item; },
compareMinX: function (a, b) { return a[0] - b[0]; },
compareMinY: function (a, b) { return a[1] - b[1]; },
compareMinX: compareNodeMinX,
compareMinY: compareNodeMinY,
toJSON: function () { return this.data; },
@@ -225,12 +282,7 @@
if (N <= M) {
// reached leaf level; return leaf
node = {
children: items.slice(left, right + 1),
height: 1,
bbox: null,
leaf: true
};
node = createNode(items.slice(left, right + 1));
calcBBox(node, this.toBBox);
return node;
}
@@ -243,12 +295,9 @@
M = Math.ceil(N / Math.pow(M, height - 1));
}
node = {
children: [],
height: height,
bbox: null,
leaf: false
};
node = createNode([]);
node.leaf = false;
node.height = height;
// split the items into M mostly square tiles
@@ -291,8 +340,8 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
area = bboxArea(child.bbox);
enlargement = enlargedArea(bbox, child.bbox) - area;
area = bboxArea(child);
enlargement = enlargedArea(bbox, child) - area;
// choose entry with the least area enlargement
if (enlargement < minEnlargement) {
@@ -318,7 +367,7 @@
_insert: function (item, level, isNode) {
var toBBox = this.toBBox,
bbox = isNode ? item.bbox : toBBox(item),
bbox = isNode ? item : toBBox(item),
insertPath = [];
// find the best node for accommodating the item, saving all nodes along the path too
@@ -326,7 +375,7 @@
// put the item into the node
node.children.push(item);
extend(node.bbox, bbox);
extend(node, bbox);
// split on node overflow; propagate upwards if necessary
while (level >= 0) {
@@ -351,14 +400,9 @@
var splitIndex = this._chooseSplitIndex(node, m, M);
var newNode = {
children: node.children.splice(splitIndex, node.children.length - splitIndex),
height: node.height,
bbox: null,
leaf: false
};
if (node.leaf) newNode.leaf = true;
var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
newNode.height = node.height;
newNode.leaf = node.leaf;
calcBBox(node, this.toBBox);
calcBBox(newNode, this.toBBox);
@@ -369,12 +413,9 @@
_splitRoot: function (node, newNode) {
// split root node
this.data = {
children: [node, newNode],
height: node.height + 1,
bbox: null,
leaf: false
};
this.data = createNode([node, newNode]);
this.data.height = node.height + 1;
this.data.leaf = false;
calcBBox(this.data, this.toBBox);
},
@@ -436,13 +477,13 @@
for (i = m; i < M - m; i++) {
child = node.children[i];
extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
extend(leftBBox, node.leaf ? toBBox(child) : child);
margin += bboxMargin(leftBBox);
}
for (i = M - m - 1; i >= m; i--) {
child = node.children[i];
extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
extend(rightBBox, node.leaf ? toBBox(child) : child);
margin += bboxMargin(rightBBox);
}
@@ -452,7 +493,7 @@
_adjustParentBBoxes: function (bbox, path, level) {
// adjust bboxes along the given tree path
for (var i = level; i >= 0; i--) {
extend(path[i].bbox, bbox);
extend(path[i], bbox);
}
},
@@ -482,71 +523,97 @@
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
this.toBBox = new Function('a',
'return {minX: a' + format[0] +
', minY: a' + format[1] +
', maxX: a' + format[2] +
', maxY: a' + format[3] + '};');
}
};
function findItem(item, items, equalsFn) {
if (!equalsFn) return items.indexOf(item);
for (var i = 0; i < items.length; i++) {
if (equalsFn(item, items[i])) return i;
}
return -1;
}
// calculate node's bbox from bboxes of its children
function calcBBox(node, toBBox) {
node.bbox = distBBox(node, 0, node.children.length, toBBox);
distBBox(node, 0, node.children.length, toBBox, node);
}
// min bounding rectangle of node children from k to p-1
function distBBox(node, k, p, toBBox) {
var bbox = empty();
function distBBox(node, k, p, toBBox, destNode) {
if (!destNode) destNode = createNode(null);
destNode.minX = Infinity;
destNode.minY = Infinity;
destNode.maxX = -Infinity;
destNode.maxY = -Infinity;
for (var i = k, child; i < p; i++) {
child = node.children[i];
extend(bbox, node.leaf ? toBBox(child) : child.bbox);
extend(destNode, node.leaf ? toBBox(child) : child);
}
return bbox;
return destNode;
}
function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
function extend(a, b) {
a[0] = Math.min(a[0], b[0]);
a[1] = Math.min(a[1], b[1]);
a[2] = Math.max(a[2], b[2]);
a[3] = Math.max(a[3], b[3]);
a.minX = Math.min(a.minX, b.minX);
a.minY = Math.min(a.minY, b.minY);
a.maxX = Math.max(a.maxX, b.maxX);
a.maxY = Math.max(a.maxY, b.maxY);
return a;
}
function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
function compareNodeMinX(a, b) { return a.minX - b.minX; }
function compareNodeMinY(a, b) { return a.minY - b.minY; }
function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
function enlargedArea(a, b) {
return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
(Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
}
function intersectionArea(a, b) {
var minX = Math.max(a[0], b[0]),
minY = Math.max(a[1], b[1]),
maxX = Math.min(a[2], b[2]),
maxY = Math.min(a[3], b[3]);
var minX = Math.max(a.minX, b.minX),
minY = Math.max(a.minY, b.minY),
maxX = Math.min(a.maxX, b.maxX),
maxY = Math.min(a.maxY, b.maxY);
return Math.max(0, maxX - minX) *
Math.max(0, maxY - minY);
}
function contains(a, b) {
return a[0] <= b[0] &&
a[1] <= b[1] &&
b[2] <= a[2] &&
b[3] <= a[3];
return a.minX <= b.minX &&
a.minY <= b.minY &&
b.maxX <= a.maxX &&
b.maxY <= a.maxY;
}
function intersects(a, b) {
return b[0] <= a[2] &&
b[1] <= a[3] &&
b[2] >= a[0] &&
b[3] >= a[1];
return b.minX <= a.maxX &&
b.minY <= a.maxY &&
b.maxX >= a.minX &&
b.maxY >= a.minY;
}
function createNode(children) {
return {
children: children,
height: 1,
leaf: true,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
}
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
@@ -563,72 +630,14 @@
if (right - left <= n) continue;
mid = left + Math.ceil((right - left) / n / 2) * n;
select(arr, left, right, mid, compare);
quickselect(arr, mid, left, right, compare);
stack.push(left, mid, mid, right);
}
}
// Floyd-Rivest selection algorithm:
// sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
function select(arr, left, right, k, compare) {
var n, i, z, s, sd, newLeft, newRight, t, j;
while (right > left) {
if (right - left > 600) {
n = right - left + 1;
i = k - left + 1;
z = Math.log(n);
s = 0.5 * Math.exp(2 * z / 3);
sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
select(arr, newLeft, newRight, k, compare);
}
t = arr[k];
i = left;
j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// export as AMD/CommonJS module or global variable
if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
else if (typeof module !== 'undefined') module.exports = rbush;
else if (typeof self !== 'undefined') self.rbush = rbush;
else window.rbush = rbush;
})();
});
var rbush$1 = (rbush && typeof rbush === 'object' && 'default' in rbush ? rbush['default'] : rbush);
var rbush = (index && typeof index === 'object' && 'default' in index ? index['default'] : index);
function mapillary() {
var mapillary = {},
@@ -828,7 +837,7 @@
if (which === 'images') d.ca = feature.properties.ca;
if (which === 'signs') d.signs = feature.properties.rects;
features.push([loc[0], loc[1], loc[0], loc[1], d]);
features.push({minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d});
}
cache.rtree.load(features);
@@ -885,9 +894,9 @@
var partitions = partitionViewport(psize, projection, dimensions);
return _.flatten(_.compact(_.map(partitions, function(extent) {
return rtree.search(extent.rectangle())
return rtree.search(extent.bbox())
.slice(0, limit)
.map(function(d) { return d[4]; });
.map(function(d) { return d.data; });
})));
}
@@ -1009,8 +1018,8 @@
}
iD.services.mapillary.cache = {
images: { inflight: {}, loaded: {}, rtree: rbush$1() },
signs: { inflight: {}, loaded: {}, rtree: rbush$1() }
images: { inflight: {}, loaded: {}, rtree: rbush() },
signs: { inflight: {}, loaded: {}, rtree: rbush() }
};
iD.services.mapillary.image = null;
@@ -1034,10 +1043,10 @@
nominatim.countryCode = function(location, callback) {
var cache = iD.services.nominatim.cache,
countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
countryCodes = cache.search({ minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] });
if (countryCodes.length > 0)
return callback(null, countryCodes[0][4]);
return callback(null, countryCodes[0].data);
d3.json(endpoint +
iD.util.qsString({
@@ -1053,14 +1062,14 @@
var extent = iD.geo.Extent(location).padByMeters(1000);
cache.insert(extent.rectangle().concat(result.address.country_code));
cache.insert(Object.assign(extent.bbox(), { data: result.address.country_code }));
callback(null, result.address.country_code);
});
};
nominatim.reset = function() {
iD.services.nominatim.cache = rbush$1();
iD.services.nominatim.cache = rbush();
return this;
};
+185 -177
View File
@@ -569,16 +569,78 @@
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var rbush = createCommonjsModule(function (module) {
/*
(c) 2015, Vladimir Agafonkin
RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
https://github.com/mourner/rbush
*/
(function () {
var index$1 = createCommonjsModule(function (module) {
'use strict';
module.exports = partialSort;
// Floyd-Rivest selection algorithm:
// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
// The k-th element will have the (k - left + 1)th smallest value in [left, right]
function partialSort(arr, k, left, right, compare) {
left = left || 0;
right = right || (arr.length - 1);
compare = compare || defaultCompare;
while (right > left) {
if (right - left > 600) {
var n = right - left + 1;
var m = k - left + 1;
var z = Math.log(n);
var s = 0.5 * Math.exp(2 * z / 3);
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
partialSort(arr, k, newLeft, newRight, compare);
}
var t = arr[k];
var i = left;
var j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
});
var require$$0 = (index$1 && typeof index$1 === 'object' && 'default' in index$1 ? index$1['default'] : index$1);
var index = createCommonjsModule(function (module) {
'use strict';
module.exports = rbush;
var quickselect = require$$0;
function rbush(maxEntries, format) {
if (!(this instanceof rbush)) return new rbush(maxEntries, format);
@@ -605,7 +667,7 @@
result = [],
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return result;
if (!intersects(bbox, node)) return result;
var nodesToSearch = [],
i, len, child, childBBox;
@@ -614,7 +676,7 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
childBBox = node.leaf ? toBBox(child) : child;
if (intersects(bbox, childBBox)) {
if (node.leaf) result.push(child);
@@ -633,7 +695,7 @@
var node = this.data,
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return false;
if (!intersects(bbox, node)) return false;
var nodesToSearch = [],
i, len, child, childBBox;
@@ -642,7 +704,7 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
childBBox = node.leaf ? toBBox(child) : child;
if (intersects(bbox, childBBox)) {
if (node.leaf || contains(bbox, childBBox)) return true;
@@ -697,16 +759,11 @@
},
clear: function () {
this.data = {
children: [],
height: 1,
bbox: empty(),
leaf: true
};
this.data = createNode([]);
return this;
},
remove: function (item) {
remove: function (item, equalsFn) {
if (!item) return this;
var node = this.data,
@@ -726,7 +783,7 @@
}
if (node.leaf) { // check current node
index = node.children.indexOf(item);
index = findItem(item, node.children, equalsFn);
if (index !== -1) {
// item found, remove the item and condense tree upwards
@@ -737,7 +794,7 @@
}
}
if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
path.push(node);
indexes.push(i);
i = 0;
@@ -757,8 +814,8 @@
toBBox: function (item) { return item; },
compareMinX: function (a, b) { return a[0] - b[0]; },
compareMinY: function (a, b) { return a[1] - b[1]; },
compareMinX: compareNodeMinX,
compareMinY: compareNodeMinY,
toJSON: function () { return this.data; },
@@ -786,12 +843,7 @@
if (N <= M) {
// reached leaf level; return leaf
node = {
children: items.slice(left, right + 1),
height: 1,
bbox: null,
leaf: true
};
node = createNode(items.slice(left, right + 1));
calcBBox(node, this.toBBox);
return node;
}
@@ -804,12 +856,9 @@
M = Math.ceil(N / Math.pow(M, height - 1));
}
node = {
children: [],
height: height,
bbox: null,
leaf: false
};
node = createNode([]);
node.leaf = false;
node.height = height;
// split the items into M mostly square tiles
@@ -852,8 +901,8 @@
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
area = bboxArea(child.bbox);
enlargement = enlargedArea(bbox, child.bbox) - area;
area = bboxArea(child);
enlargement = enlargedArea(bbox, child) - area;
// choose entry with the least area enlargement
if (enlargement < minEnlargement) {
@@ -879,7 +928,7 @@
_insert: function (item, level, isNode) {
var toBBox = this.toBBox,
bbox = isNode ? item.bbox : toBBox(item),
bbox = isNode ? item : toBBox(item),
insertPath = [];
// find the best node for accommodating the item, saving all nodes along the path too
@@ -887,7 +936,7 @@
// put the item into the node
node.children.push(item);
extend(node.bbox, bbox);
extend(node, bbox);
// split on node overflow; propagate upwards if necessary
while (level >= 0) {
@@ -912,14 +961,9 @@
var splitIndex = this._chooseSplitIndex(node, m, M);
var newNode = {
children: node.children.splice(splitIndex, node.children.length - splitIndex),
height: node.height,
bbox: null,
leaf: false
};
if (node.leaf) newNode.leaf = true;
var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
newNode.height = node.height;
newNode.leaf = node.leaf;
calcBBox(node, this.toBBox);
calcBBox(newNode, this.toBBox);
@@ -930,12 +974,9 @@
_splitRoot: function (node, newNode) {
// split root node
this.data = {
children: [node, newNode],
height: node.height + 1,
bbox: null,
leaf: false
};
this.data = createNode([node, newNode]);
this.data.height = node.height + 1;
this.data.leaf = false;
calcBBox(this.data, this.toBBox);
},
@@ -997,13 +1038,13 @@
for (i = m; i < M - m; i++) {
child = node.children[i];
extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
extend(leftBBox, node.leaf ? toBBox(child) : child);
margin += bboxMargin(leftBBox);
}
for (i = M - m - 1; i >= m; i--) {
child = node.children[i];
extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
extend(rightBBox, node.leaf ? toBBox(child) : child);
margin += bboxMargin(rightBBox);
}
@@ -1013,7 +1054,7 @@
_adjustParentBBoxes: function (bbox, path, level) {
// adjust bboxes along the given tree path
for (var i = level; i >= 0; i--) {
extend(path[i].bbox, bbox);
extend(path[i], bbox);
}
},
@@ -1043,71 +1084,97 @@
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
this.toBBox = new Function('a',
'return {minX: a' + format[0] +
', minY: a' + format[1] +
', maxX: a' + format[2] +
', maxY: a' + format[3] + '};');
}
};
function findItem(item, items, equalsFn) {
if (!equalsFn) return items.indexOf(item);
for (var i = 0; i < items.length; i++) {
if (equalsFn(item, items[i])) return i;
}
return -1;
}
// calculate node's bbox from bboxes of its children
function calcBBox(node, toBBox) {
node.bbox = distBBox(node, 0, node.children.length, toBBox);
distBBox(node, 0, node.children.length, toBBox, node);
}
// min bounding rectangle of node children from k to p-1
function distBBox(node, k, p, toBBox) {
var bbox = empty();
function distBBox(node, k, p, toBBox, destNode) {
if (!destNode) destNode = createNode(null);
destNode.minX = Infinity;
destNode.minY = Infinity;
destNode.maxX = -Infinity;
destNode.maxY = -Infinity;
for (var i = k, child; i < p; i++) {
child = node.children[i];
extend(bbox, node.leaf ? toBBox(child) : child.bbox);
extend(destNode, node.leaf ? toBBox(child) : child);
}
return bbox;
return destNode;
}
function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
function extend(a, b) {
a[0] = Math.min(a[0], b[0]);
a[1] = Math.min(a[1], b[1]);
a[2] = Math.max(a[2], b[2]);
a[3] = Math.max(a[3], b[3]);
a.minX = Math.min(a.minX, b.minX);
a.minY = Math.min(a.minY, b.minY);
a.maxX = Math.max(a.maxX, b.maxX);
a.maxY = Math.max(a.maxY, b.maxY);
return a;
}
function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
function compareNodeMinX(a, b) { return a.minX - b.minX; }
function compareNodeMinY(a, b) { return a.minY - b.minY; }
function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
function enlargedArea(a, b) {
return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
(Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
}
function intersectionArea(a, b) {
var minX = Math.max(a[0], b[0]),
minY = Math.max(a[1], b[1]),
maxX = Math.min(a[2], b[2]),
maxY = Math.min(a[3], b[3]);
var minX = Math.max(a.minX, b.minX),
minY = Math.max(a.minY, b.minY),
maxX = Math.min(a.maxX, b.maxX),
maxY = Math.min(a.maxY, b.maxY);
return Math.max(0, maxX - minX) *
Math.max(0, maxY - minY);
}
function contains(a, b) {
return a[0] <= b[0] &&
a[1] <= b[1] &&
b[2] <= a[2] &&
b[3] <= a[3];
return a.minX <= b.minX &&
a.minY <= b.minY &&
b.maxX <= a.maxX &&
b.maxY <= a.maxY;
}
function intersects(a, b) {
return b[0] <= a[2] &&
b[1] <= a[3] &&
b[2] >= a[0] &&
b[3] >= a[1];
return b.minX <= a.maxX &&
b.minY <= a.maxY &&
b.maxX >= a.minX &&
b.maxY >= a.minY;
}
function createNode(children) {
return {
children: children,
height: 1,
leaf: true,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
}
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
@@ -1124,72 +1191,14 @@
if (right - left <= n) continue;
mid = left + Math.ceil((right - left) / n / 2) * n;
select(arr, left, right, mid, compare);
quickselect(arr, mid, left, right, compare);
stack.push(left, mid, mid, right);
}
}
// Floyd-Rivest selection algorithm:
// sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
function select(arr, left, right, k, compare) {
var n, i, z, s, sd, newLeft, newRight, t, j;
while (right > left) {
if (right - left > 600) {
n = right - left + 1;
i = k - left + 1;
z = Math.log(n);
s = 0.5 * Math.exp(2 * z / 3);
sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
select(arr, newLeft, newRight, k, compare);
}
t = arr[k];
i = left;
j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// export as AMD/CommonJS module or global variable
if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
else if (typeof module !== 'undefined') module.exports = rbush;
else if (typeof self !== 'undefined') self.rbush = rbush;
else window.rbush = rbush;
})();
});
var rbush$1 = (rbush && typeof rbush === 'object' && 'default' in rbush ? rbush['default'] : rbush);
var rbush = (index && typeof index === 'object' && 'default' in index ? index['default'] : index);
function Labels(projection, context) {
var path = d3.geo.path().projection(projection);
@@ -1429,16 +1438,16 @@
var mouse = context.mouse(),
pad = 50,
rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
ids = _.map(rtree.search(rect), 'id');
bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad },
ids = _.map(rtree.search(bbox), 'id');
if (!ids.length) return;
layers.selectAll('.' + ids.join(', .'))
.classed('proximate', true);
}
var rtree = rbush$1(),
rectangles = {};
var rtree = rbush(),
bboxes = {};
function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
var hidePoints = !surface.selectAll('.node.point').node();
@@ -1448,10 +1457,10 @@
if (fullRedraw) {
rtree.clear();
rectangles = {};
bboxes = {};
} else {
for (i = 0; i < entities.length; i++) {
rtree.remove(rectangles[entities[i].id]);
rtree.remove(bboxes[entities[i].id]);
}
}
@@ -1525,8 +1534,8 @@
y: coord[1] + offset[1],
textAnchor: offset[2]
};
var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
if (tryInsert(rect, entity.id)) return p;
var bbox = { minX: p.x - m, minY: p.y - m, maxX: p.x + width + m, maxY: p.y + height + m };
if (tryInsert(bbox, entity.id)) return p;
}
@@ -1542,14 +1551,14 @@
if (start < 0 || start + width > length) continue;
var sub = subpath(nodes, start, start + width),
rev = reverse(sub),
rect = [
Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
];
bbox = {
minX: Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
minY: Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
maxX: Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
maxY: Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
};
if (rev) sub = sub.reverse();
if (tryInsert(rect, entity.id)) return {
if (tryInsert(bbox, entity.id)) return {
'font-size': height + 2,
lineString: lineString(sub),
startOffset: offset + '%'
@@ -1561,7 +1570,7 @@
var centroid = path.centroid(entity.asGeoJSON(graph, true)),
extent = entity.extent(graph),
entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
rect;
bbox;
if (isNaN(centroid[0]) || entitywidth < 20) return;
@@ -1578,24 +1587,23 @@
p.y = centroid[1] + textOffset;
p.textAnchor = 'middle';
p.height = height;
rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
bbox = { minX: p.x - width/2, minY: p.y, maxX: p.x + width/2, maxY: p.y + height + textOffset };
} else {
rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
bbox = { minX: iconX, minY: iconY, maxX: iconX + iconSize, maxY: iconY + iconSize };
}
if (tryInsert(rect, entity.id)) return p;
if (tryInsert(bbox, entity.id)) return p;
}
function tryInsert(rect, id) {
function tryInsert(bbox, id) {
// Check that label is visible
if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
rect[3] > dimensions[1]) return false;
var v = rtree.search(rect).length === 0;
if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) return false;
var v = rtree.search(bbox).length === 0;
if (v) {
rect.id = id;
rtree.insert(rect);
rectangles[id] = rect;
bbox.id = id;
rtree.insert(bbox);
bboxes[id] = bbox;
}
return v;
}
@@ -1632,11 +1640,11 @@
if (showDebug) {
var gj = rtree.all().map(function(d) {
return { type: 'Polygon', coordinates: [[
[d[0], d[1]],
[d[2], d[1]],
[d[2], d[3]],
[d[0], d[3]],
[d[0], d[1]]
[d.minX, d.minY],
[d.maxX, d.minY],
[d.maxX, d.maxY],
[d.minX, d.maxY],
[d.minX, d.minY]
]]};
});
+20 -20
View File
@@ -3,19 +3,19 @@ import rbush from 'rbush';
export function Tree(head) {
var rtree = rbush(),
rectangles = {};
bboxes = {};
function entityRectangle(entity) {
var rect = entity.extent(head).rectangle();
rect.id = entity.id;
rectangles[entity.id] = rect;
return rect;
function entityBBox(entity) {
var bbox = entity.extent(head).bbox();
bbox.id = entity.id;
bboxes[entity.id] = bbox;
return bbox;
}
function updateParents(entity, insertions, memo) {
head.parentWays(entity).forEach(function(way) {
if (rectangles[way.id]) {
rtree.remove(rectangles[way.id]);
if (bboxes[way.id]) {
rtree.remove(bboxes[way.id]);
insertions[way.id] = way;
}
updateParents(way, insertions, memo);
@@ -24,8 +24,8 @@ export function Tree(head) {
head.parentRelations(entity).forEach(function(relation) {
if (memo[entity.id]) return;
memo[entity.id] = true;
if (rectangles[relation.id]) {
rtree.remove(rectangles[relation.id]);
if (bboxes[relation.id]) {
rtree.remove(bboxes[relation.id]);
insertions[relation.id] = relation;
}
updateParents(relation, insertions, memo);
@@ -43,11 +43,11 @@ export function Tree(head) {
if (!entity.visible)
continue;
if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
if (head.entities.hasOwnProperty(entity.id) || bboxes[entity.id]) {
if (!force) {
continue;
} else if (rectangles[entity.id]) {
rtree.remove(rectangles[entity.id]);
} else if (bboxes[entity.id]) {
rtree.remove(bboxes[entity.id]);
}
}
@@ -55,7 +55,7 @@ export function Tree(head) {
updateParents(entity, insertions, {});
}
rtree.load(_.map(insertions, entityRectangle));
rtree.load(_.map(insertions, entityBBox));
return tree;
};
@@ -68,12 +68,12 @@ export function Tree(head) {
head = graph;
diff.deleted().forEach(function(entity) {
rtree.remove(rectangles[entity.id]);
delete rectangles[entity.id];
rtree.remove(bboxes[entity.id]);
delete bboxes[entity.id];
});
diff.modified().forEach(function(entity) {
rtree.remove(rectangles[entity.id]);
rtree.remove(bboxes[entity.id]);
insertions[entity.id] = entity;
updateParents(entity, insertions, {});
});
@@ -82,11 +82,11 @@ export function Tree(head) {
insertions[entity.id] = entity;
});
rtree.load(_.map(insertions, entityRectangle));
rtree.load(_.map(insertions, entityBBox));
}
return rtree.search(extent.rectangle()).map(function(rect) {
return head.entity(rect.id);
return rtree.search(extent.bbox()).map(function(bbox) {
return head.entity(bbox.id);
});
};
+4
View File
@@ -50,6 +50,10 @@ _.extend(Extent.prototype, {
return [this[0][0], this[0][1], this[1][0], this[1][1]];
},
bbox: function() {
return { minX: this[0][0], minY: this[0][1], maxX: this[1][0], maxY: this[1][1] };
},
polygon: function() {
return [
[this[0][0], this[0][1]],
+3 -3
View File
@@ -198,7 +198,7 @@ export function mapillary() {
if (which === 'images') d.ca = feature.properties.ca;
if (which === 'signs') d.signs = feature.properties.rects;
features.push([loc[0], loc[1], loc[0], loc[1], d]);
features.push({minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d});
}
cache.rtree.load(features);
@@ -255,9 +255,9 @@ export function mapillary() {
var partitions = partitionViewport(psize, projection, dimensions);
return _.flatten(_.compact(_.map(partitions, function(extent) {
return rtree.search(extent.rectangle())
return rtree.search(extent.bbox())
.slice(0, limit)
.map(function(d) { return d[4]; });
.map(function(d) { return d.data; });
})));
}
+3 -3
View File
@@ -7,10 +7,10 @@ export function nominatim() {
nominatim.countryCode = function(location, callback) {
var cache = iD.services.nominatim.cache,
countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
countryCodes = cache.search({ minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] });
if (countryCodes.length > 0)
return callback(null, countryCodes[0][4]);
return callback(null, countryCodes[0].data);
d3.json(endpoint +
iD.util.qsString({
@@ -26,7 +26,7 @@ export function nominatim() {
var extent = iD.geo.Extent(location).padByMeters(1000);
cache.insert(extent.rectangle().concat(result.address.country_code));
cache.insert(Object.assign(extent.bbox(), { data: result.address.country_code }));
callback(null, result.address.country_code);
});
+29 -30
View File
@@ -238,8 +238,8 @@ export function Labels(projection, context) {
var mouse = context.mouse(),
pad = 50,
rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
ids = _.map(rtree.search(rect), 'id');
bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad },
ids = _.map(rtree.search(bbox), 'id');
if (!ids.length) return;
layers.selectAll('.' + ids.join(', .'))
@@ -247,7 +247,7 @@ export function Labels(projection, context) {
}
var rtree = rbush(),
rectangles = {};
bboxes = {};
function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
var hidePoints = !surface.selectAll('.node.point').node();
@@ -257,10 +257,10 @@ export function Labels(projection, context) {
if (fullRedraw) {
rtree.clear();
rectangles = {};
bboxes = {};
} else {
for (i = 0; i < entities.length; i++) {
rtree.remove(rectangles[entities[i].id]);
rtree.remove(bboxes[entities[i].id]);
}
}
@@ -334,8 +334,8 @@ export function Labels(projection, context) {
y: coord[1] + offset[1],
textAnchor: offset[2]
};
var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
if (tryInsert(rect, entity.id)) return p;
var bbox = { minX: p.x - m, minY: p.y - m, maxX: p.x + width + m, maxY: p.y + height + m };
if (tryInsert(bbox, entity.id)) return p;
}
@@ -351,14 +351,14 @@ export function Labels(projection, context) {
if (start < 0 || start + width > length) continue;
var sub = subpath(nodes, start, start + width),
rev = reverse(sub),
rect = [
Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
];
bbox = {
minX: Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
minY: Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
maxX: Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
maxY: Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
};
if (rev) sub = sub.reverse();
if (tryInsert(rect, entity.id)) return {
if (tryInsert(bbox, entity.id)) return {
'font-size': height + 2,
lineString: lineString(sub),
startOffset: offset + '%'
@@ -370,7 +370,7 @@ export function Labels(projection, context) {
var centroid = path.centroid(entity.asGeoJSON(graph, true)),
extent = entity.extent(graph),
entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
rect;
bbox;
if (isNaN(centroid[0]) || entitywidth < 20) return;
@@ -387,24 +387,23 @@ export function Labels(projection, context) {
p.y = centroid[1] + textOffset;
p.textAnchor = 'middle';
p.height = height;
rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
bbox = { minX: p.x - width/2, minY: p.y, maxX: p.x + width/2, maxY: p.y + height + textOffset };
} else {
rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
bbox = { minX: iconX, minY: iconY, maxX: iconX + iconSize, maxY: iconY + iconSize };
}
if (tryInsert(rect, entity.id)) return p;
if (tryInsert(bbox, entity.id)) return p;
}
function tryInsert(rect, id) {
function tryInsert(bbox, id) {
// Check that label is visible
if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
rect[3] > dimensions[1]) return false;
var v = rtree.search(rect).length === 0;
if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) return false;
var v = rtree.search(bbox).length === 0;
if (v) {
rect.id = id;
rtree.insert(rect);
rectangles[id] = rect;
bbox.id = id;
rtree.insert(bbox);
bboxes[id] = bbox;
}
return v;
}
@@ -441,11 +440,11 @@ export function Labels(projection, context) {
if (showDebug) {
var gj = rtree.all().map(function(d) {
return { type: 'Polygon', coordinates: [[
[d[0], d[1]],
[d[2], d[1]],
[d[2], d[3]],
[d[0], d[3]],
[d[0], d[1]]
[d.minX, d.minY],
[d.maxX, d.minY],
[d.maxX, d.maxY],
[d.minX, d.maxY],
[d.minX, d.minY]
]]};
});