Faster extent + geometry type queries

By maintaining separate R-Trees for each geometry type, we cover
the most critical query from the canvas renderer at higher
speeds. Using the filter's own evaluate method, we can now also
serve arbitrary queries, but they won't be fast.

Still needs unit tests.
This commit is contained in:
ahocevar
2013-02-06 16:42:55 +01:00
parent 619803cdeb
commit 4e971b6041

View File

@@ -27,16 +27,16 @@ ol.source.FeatureCache = function() {
this.idLookup_;
/**
* @type {Object.<ol.Feature>}
* @type {Object.<string, ol.Feature>}
* @private
*/
this.geometryTypeIndex_;
/**
* @type {ol.structs.RTree}
* @type {Object.<ol.geom.GeometryType, ol.structs.RTree>}
* @private
*/
this.rTree_;
this.boundsByGeometryType_;
this.clear();
@@ -49,13 +49,16 @@ ol.source.FeatureCache = function() {
ol.source.FeatureCache.prototype.clear = function() {
this.idLookup_ = {};
var geometryTypeIndex = {};
var geometryTypeIndex = {},
boundsByGeometryType = {},
geometryType;
for (var key in ol.geom.GeometryType) {
geometryTypeIndex[ol.geom.GeometryType[key]] = {};
geometryType = ol.geom.GeometryType[key];
geometryTypeIndex[geometryType] = {};
boundsByGeometryType[geometryType] = new ol.structs.RTree();
}
this.geometryTypeIndex_ = geometryTypeIndex;
this.rTree_ = new ol.structs.RTree();
this.boundsByGeometryType_ = boundsByGeometryType;
};
@@ -71,8 +74,10 @@ ol.source.FeatureCache.prototype.add = function(feature) {
// index by geometry type and bounding box
if (!goog.isNull(geometry)) {
this.geometryTypeIndex_[geometry.getType()][id] = feature;
this.rTree_.put(geometry.getBounds(), feature);
var geometryType = geometry.getType();
this.geometryTypeIndex_[geometryType][id] = feature;
this.boundsByGeometryType_[geometryType].put(geometry.getBounds(),
feature);
}
};
@@ -83,34 +88,48 @@ ol.source.FeatureCache.prototype.add = function(feature) {
* @private
*/
ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) {
var features;
var i, features;
if (!goog.isDef(opt_filter)) {
features = this.idLookup_;
} else {
if (opt_filter instanceof ol.filter.Logical) {
features = {};
var filters = opt_filter.getFilters(),
filterFeatures, key, or;
for (var i = filters.length - 1; i >= 0; --i) {
filterFeatures = this.getFeaturesObject_(filters[i]);
goog.object.extend(features, filterFeatures);
if (opt_filter.operator === ol.filter.LogicalOperator.AND) {
or = features;
features = {};
for (key in or) {
if (filterFeatures[key]) {
features[key] = or[key];
}
}
}
}
} else if (opt_filter instanceof ol.filter.Geometry) {
if (opt_filter instanceof ol.filter.Geometry) {
features = this.geometryTypeIndex_[opt_filter.getType()];
} else if (opt_filter instanceof ol.filter.Extent) {
features = this.rTree_.find(opt_filter.getExtent());
} else {
// TODO: support other filter types
throw new Error('Filter type not supported: ' + opt_filter);
var boundsByGeometryType = this.boundsByGeometryType_,
extent = opt_filter.getExtent();
features = {};
for (i in boundsByGeometryType) {
goog.object.extend(features, boundsByGeometryType[i].find(extent));
}
} else if (opt_filter instanceof ol.filter.Logical) {
var filters = opt_filter.getFilters();
if (filters.length === 2) {
var filter, geometryFilter, extentFilter;
for (i = 0; i <= 1; ++i) {
filter = filters[i];
if (filter instanceof ol.filter.Geometry) {
geometryFilter = filter;
} else if (filter instanceof ol.filter.Extent) {
extentFilter = filter;
}
}
if (extentFilter && geometryFilter) {
features = this.boundsByGeometryType_[geometryFilter.getType()]
.find(extentFilter.getExtent());
}
}
}
if (!goog.isDef(features)) {
// TODO: support fast lane for other filter types
var candidates = this.idLookup_,
feature;
features = {};
for (i in candidates) {
feature = candidates[i];
if (opt_filter.evaluate(feature) === true) {
features[i] = feature;
}
}
}
}
return features;