openlayers
Version:
Build tools and sources for developing OpenLayers based mapping applications
269 lines (233 loc) • 6.71 kB
JavaScript
goog.provide('ol.structs.RBush');
goog.require('ol');
goog.require('ol.ext.rbush');
goog.require('ol.extent');
goog.require('ol.obj');
/**
* Wrapper around the RBush by Vladimir Agafonkin.
*
* @constructor
* @param {number=} opt_maxEntries Max entries.
* @see https://github.com/mourner/rbush
* @struct
* @template T
*/
ol.structs.RBush = function(opt_maxEntries) {
/**
* @private
*/
this.rbush_ = ol.ext.rbush(opt_maxEntries);
/**
* A mapping between the objects added to this rbush wrapper
* and the objects that are actually added to the internal rbush.
* @private
* @type {Object.<number, ol.RBushEntry>}
*/
this.items_ = {};
if (ol.DEBUG) {
/**
* @private
* @type {number}
*/
this.readers_ = 0;
}
};
/**
* Insert a value into the RBush.
* @param {ol.Extent} extent Extent.
* @param {T} value Value.
*/
ol.structs.RBush.prototype.insert = function(extent, value) {
if (ol.DEBUG && this.readers_) {
throw new Error('Can not insert value while reading');
}
/** @type {ol.RBushEntry} */
var item = {
minX: extent[0],
minY: extent[1],
maxX: extent[2],
maxY: extent[3],
value: value
};
this.rbush_.insert(item);
// remember the object that was added to the internal rbush
ol.DEBUG && console.assert(!(ol.getUid(value) in this.items_),
'uid (%s) of value (%s) already exists', ol.getUid(value), value);
this.items_[ol.getUid(value)] = item;
};
/**
* Bulk-insert values into the RBush.
* @param {Array.<ol.Extent>} extents Extents.
* @param {Array.<T>} values Values.
*/
ol.structs.RBush.prototype.load = function(extents, values) {
if (ol.DEBUG && this.readers_) {
throw new Error('Can not insert values while reading');
}
ol.DEBUG && console.assert(extents.length === values.length,
'extens and values must have same length (%s === %s)',
extents.length, values.length);
var items = new Array(values.length);
for (var i = 0, l = values.length; i < l; i++) {
var extent = extents[i];
var value = values[i];
/** @type {ol.RBushEntry} */
var item = {
minX: extent[0],
minY: extent[1],
maxX: extent[2],
maxY: extent[3],
value: value
};
items[i] = item;
ol.DEBUG && console.assert(!(ol.getUid(value) in this.items_),
'uid (%s) of value (%s) already exists', ol.getUid(value), value);
this.items_[ol.getUid(value)] = item;
}
this.rbush_.load(items);
};
/**
* Remove a value from the RBush.
* @param {T} value Value.
* @return {boolean} Removed.
*/
ol.structs.RBush.prototype.remove = function(value) {
if (ol.DEBUG && this.readers_) {
throw new Error('Can not remove value while reading');
}
var uid = ol.getUid(value);
ol.DEBUG && console.assert(uid in this.items_,
'uid (%s) of value (%s) does not exist', uid, value);
// get the object in which the value was wrapped when adding to the
// internal rbush. then use that object to do the removal.
var item = this.items_[uid];
delete this.items_[uid];
return this.rbush_.remove(item) !== null;
};
/**
* Update the extent of a value in the RBush.
* @param {ol.Extent} extent Extent.
* @param {T} value Value.
*/
ol.structs.RBush.prototype.update = function(extent, value) {
ol.DEBUG && console.assert(ol.getUid(value) in this.items_,
'uid (%s) of value (%s) does not exist', ol.getUid(value), value);
var item = this.items_[ol.getUid(value)];
var bbox = [item.minX, item.minY, item.maxX, item.maxY];
if (!ol.extent.equals(bbox, extent)) {
if (ol.DEBUG && this.readers_) {
throw new Error('Can not update extent while reading');
}
this.remove(value);
this.insert(extent, value);
}
};
/**
* Return all values in the RBush.
* @return {Array.<T>} All.
*/
ol.structs.RBush.prototype.getAll = function() {
var items = this.rbush_.all();
return items.map(function(item) {
return item.value;
});
};
/**
* Return all values in the given extent.
* @param {ol.Extent} extent Extent.
* @return {Array.<T>} All in extent.
*/
ol.structs.RBush.prototype.getInExtent = function(extent) {
/** @type {ol.RBushEntry} */
var bbox = {
minX: extent[0],
minY: extent[1],
maxX: extent[2],
maxY: extent[3]
};
var items = this.rbush_.search(bbox);
return items.map(function(item) {
return item.value;
});
};
/**
* Calls a callback function with each value in the tree.
* If the callback returns a truthy value, this value is returned without
* checking the rest of the tree.
* @param {function(this: S, T): *} callback Callback.
* @param {S=} opt_this The object to use as `this` in `callback`.
* @return {*} Callback return value.
* @template S
*/
ol.structs.RBush.prototype.forEach = function(callback, opt_this) {
if (ol.DEBUG) {
++this.readers_;
try {
return this.forEach_(this.getAll(), callback, opt_this);
} finally {
--this.readers_;
}
} else {
return this.forEach_(this.getAll(), callback, opt_this);
}
};
/**
* Calls a callback function with each value in the provided extent.
* @param {ol.Extent} extent Extent.
* @param {function(this: S, T): *} callback Callback.
* @param {S=} opt_this The object to use as `this` in `callback`.
* @return {*} Callback return value.
* @template S
*/
ol.structs.RBush.prototype.forEachInExtent = function(extent, callback, opt_this) {
if (ol.DEBUG) {
++this.readers_;
try {
return this.forEach_(this.getInExtent(extent), callback, opt_this);
} finally {
--this.readers_;
}
} else {
return this.forEach_(this.getInExtent(extent), callback, opt_this);
}
};
/**
* @param {Array.<T>} values Values.
* @param {function(this: S, T): *} callback Callback.
* @param {S=} opt_this The object to use as `this` in `callback`.
* @private
* @return {*} Callback return value.
* @template S
*/
ol.structs.RBush.prototype.forEach_ = function(values, callback, opt_this) {
var result;
for (var i = 0, l = values.length; i < l; i++) {
result = callback.call(opt_this, values[i]);
if (result) {
return result;
}
}
return result;
};
/**
* @return {boolean} Is empty.
*/
ol.structs.RBush.prototype.isEmpty = function() {
return ol.obj.isEmpty(this.items_);
};
/**
* Remove all values from the RBush.
*/
ol.structs.RBush.prototype.clear = function() {
this.rbush_.clear();
this.items_ = {};
};
/**
* @param {ol.Extent=} opt_extent Extent.
* @return {!ol.Extent} Extent.
*/
ol.structs.RBush.prototype.getExtent = function(opt_extent) {
// FIXME add getExtent() to rbush
var data = this.rbush_.data;
return [data.minX, data.minY, data.maxX, data.maxY];
};