From 4e971b604169e4216dbdc2bc2b173532b9769484 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Feb 2013 16:42:55 +0100 Subject: [PATCH] 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. --- src/ol/source/vectorsource.js | 83 +++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index d9ac0832d2..9898c1c7fc 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -27,16 +27,16 @@ ol.source.FeatureCache = function() { this.idLookup_; /** - * @type {Object.} + * @type {Object.} * @private */ this.geometryTypeIndex_; /** - * @type {ol.structs.RTree} + * @type {Object.} * @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;