diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 0d1147f1c7..2201146827 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -6,13 +6,7 @@ goog.require('goog.asserts'); goog.require('goog.events.EventType'); goog.require('goog.object'); goog.require('ol.Feature'); -goog.require('ol.expr'); -goog.require('ol.expr.Literal'); -goog.require('ol.expr.Logical'); -goog.require('ol.expr.LogicalOp'); -goog.require('ol.expr.functions'); goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Layer'); goog.require('ol.proj'); goog.require('ol.source.Vector'); @@ -34,12 +28,6 @@ ol.layer.FeatureCache = function() { */ this.idLookup_; - /** - * @type {Object.} - * @private - */ - this.geometryTypeIndex_; - /** * @type {ol.structs.RTree} * @private @@ -56,11 +44,6 @@ ol.layer.FeatureCache = function() { */ ol.layer.FeatureCache.prototype.clear = function() { this.idLookup_ = {}; - var geometryTypeIndex = {}; - for (var key in ol.geom.GeometryType) { - geometryTypeIndex[ol.geom.GeometryType[key]] = {}; - } - this.geometryTypeIndex_ = geometryTypeIndex; this.rTree_ = new ol.structs.RTree(); }; @@ -75,99 +58,18 @@ ol.layer.FeatureCache.prototype.add = function(feature) { this.idLookup_[id] = feature; - // index by geometry type and bounding box + // index by bounding box if (!goog.isNull(geometry)) { - var geometryType = geometry.getType(); - this.geometryTypeIndex_[geometryType][id] = feature; - this.rTree_.insert(geometry.getBounds(), - feature, geometryType); + this.rTree_.insert(geometry.getBounds(), feature); } }; /** - * @param {ol.expr.Expression=} opt_expr Expression for filtering. * @return {Object.} Object of features, keyed by id. */ -ol.layer.FeatureCache.prototype.getFeaturesObject = function(opt_expr) { - var features; - if (!goog.isDef(opt_expr)) { - features = this.idLookup_; - } else { - // check for geometryType or extent expression - var name = ol.expr.isLibCall(opt_expr); - if (name === ol.expr.functions.GEOMETRY_TYPE) { - var args = /** @type {ol.expr.Call} */ (opt_expr).getArgs(); - goog.asserts.assert(args.length === 1); - goog.asserts.assert(args[0] instanceof ol.expr.Literal); - var type = /** @type {ol.expr.Literal } */ (args[0]).evaluate(); - goog.asserts.assertString(type); - features = this.geometryTypeIndex_[type]; - } else if (name === ol.expr.functions.EXTENT) { - var args = /** @type {ol.expr.Call} */ (opt_expr).getArgs(); - goog.asserts.assert(args.length === 4); - for (var i = 0; i < 4; ++i) { - goog.asserts.assert(args[i] instanceof ol.expr.Literal); - } - var extent = [ - /** @type {ol.expr.Literal} */ (args[0]).evaluate(), - /** @type {ol.expr.Literal} */ (args[1]).evaluate(), - /** @type {ol.expr.Literal} */ (args[2]).evaluate(), - /** @type {ol.expr.Literal} */ (args[3]).evaluate() - ]; - features = this.rTree_.searchReturningObject(extent); - } else { - // not a call expression, check logical - if (opt_expr instanceof ol.expr.Logical) { - var op = /** @type {ol.expr.Logical} */ (opt_expr).getOperator(); - if (op === ol.expr.LogicalOp.AND) { - var expressions = [opt_expr.getLeft(), opt_expr.getRight()]; - var expr, args, type, extent; - for (var i = 0; i <= 1; ++i) { - expr = expressions[i]; - name = ol.expr.isLibCall(expr); - if (name === ol.expr.functions.GEOMETRY_TYPE) { - args = /** @type {ol.expr.Call} */ (expr).getArgs(); - goog.asserts.assert(args.length === 1); - goog.asserts.assert(args[0] instanceof ol.expr.Literal); - type = /** @type {ol.expr.Literal } */ (args[0]).evaluate(); - goog.asserts.assertString(type); - } else if (name === ol.expr.functions.EXTENT) { - args = /** @type {ol.expr.Call} */ (expr).getArgs(); - goog.asserts.assert(args.length === 4); - for (var j = 0; j < 4; ++j) { - goog.asserts.assert(args[j] instanceof ol.expr.Literal); - } - extent = [[ - /** @type {ol.expr.Literal} */ (args[0]).evaluate(), - /** @type {ol.expr.Literal} */ (args[1]).evaluate() - ], [ - /** @type {ol.expr.Literal} */ (args[2]).evaluate(), - /** @type {ol.expr.Literal} */ (args[3]).evaluate() - ]]; - } - } - if (type && extent) { - features = this.getFeaturesObjectForExtent(extent, - /** @type {ol.geom.GeometryType} */ (type)); - } - } - } - } - 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 (ol.expr.evaluateFeature(opt_expr, feature)) { - features[i] = feature; - } - } - } - } - return features; +ol.layer.FeatureCache.prototype.getFeaturesObject = function() { + return this.idLookup_; }; @@ -175,19 +77,10 @@ ol.layer.FeatureCache.prototype.getFeaturesObject = function(opt_expr) { * Get all features whose bounding box intersects the provided extent. * * @param {ol.Extent} extent Bounding extent. - * @param {ol.geom.GeometryType=} opt_type Optional geometry type. * @return {Object.} Features. */ -ol.layer.FeatureCache.prototype.getFeaturesObjectForExtent = function(extent, - opt_type) { - var features; - if (goog.isDef(opt_type) && - goog.object.isEmpty(this.geometryTypeIndex_[opt_type])) { - features = {}; - } else { - features = this.rTree_.searchReturningObject(extent, opt_type); - } - return features; +ol.layer.FeatureCache.prototype.getFeaturesObjectForExtent = function(extent) { + return this.rTree_.searchReturningObject(extent); }; @@ -228,10 +121,8 @@ ol.layer.FeatureCache.prototype.remove = function(feature) { delete this.idLookup_[id]; - // index by geometry type and bounding box + // index by bounding box if (!goog.isNull(geometry)) { - var geometryType = geometry.getType(); - delete this.geometryTypeIndex_[geometryType][id]; this.rTree_.remove(geometry.getBounds(), feature); } }; @@ -362,18 +253,17 @@ ol.layer.Vector.prototype.getStyle = function() { * * @param {ol.Extent} extent Bounding extent. * @param {ol.proj.Projection} projection Target projection. - * @param {ol.geom.GeometryType=} opt_type Optional geometry type. * @param {Function=} opt_callback Callback to call when data is parsed. * @return {Object.} Features or null if source is loading * data for `extent`. */ ol.layer.Vector.prototype.getFeaturesObjectForExtent = function(extent, - projection, opt_type, opt_callback) { + projection, opt_callback) { var source = this.getSource(); return source.prepareFeatures(this, extent, projection, opt_callback) == ol.source.VectorLoadState.LOADING ? null : - this.featureCache_.getFeaturesObjectForExtent(extent, opt_type); + this.featureCache_.getFeaturesObjectForExtent(extent); }; diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 5b9c8a4bb0..f8e63aa1ee 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -101,21 +101,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileArchetype_ = null; - /** - * Geometry types in rendering order. - * TODO: these will go away shortly (in favor of one call per symbolizer type) - * @private - * @type {Array.} - */ - this.geometryTypes_ = [ - ol.geom.GeometryType.POINT, - ol.geom.GeometryType.MULTIPOINT, - ol.geom.GeometryType.LINESTRING, - ol.geom.GeometryType.MULTILINESTRING, - ol.geom.GeometryType.POLYGON, - ol.geom.GeometryType.MULTIPOLYGON - ]; - /** * @private * @type {number} @@ -464,14 +449,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var tilesOnSketchCanvas = {}; // TODO make gutter configurable? var tileGutter = 15 * tileResolution; - var tile, tileCoord, key, x, y; - // render features by geometry type - var types = this.geometryTypes_, - numTypes = types.length, - deferred = false, - dirty = false, - i, type, tileExtent, - groups, group, j, numGroups, featuresObject, tileHasFeatures; + var tile, tileCoord, key, x, y, i, type; + var deferred = false; + var dirty = false; + var tileExtent, groups, group, j, numGroups, featuresObject, tileHasFeatures; fetchTileData: for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { @@ -486,21 +467,15 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tileExtent[1] -= tileGutter; tileExtent[3] += tileGutter; tileHasFeatures = false; - for (i = 0; i < numTypes; ++i) { - type = types[i]; - if (!goog.isDef(featuresToRender[type])) { - featuresToRender[type] = {}; - } - featuresObject = layer.getFeaturesObjectForExtent(tileExtent, - projection, type, this.requestMapRenderFrame_); - if (goog.isNull(featuresObject)) { - deferred = true; - break fetchTileData; - } - tileHasFeatures = tileHasFeatures || - !goog.object.isEmpty(featuresObject); - goog.object.extend(featuresToRender[type], featuresObject); + featuresObject = layer.getFeaturesObjectForExtent(tileExtent, + projection, this.requestMapRenderFrame_); + if (goog.isNull(featuresObject)) { + deferred = true; + break fetchTileData; } + tileHasFeatures = tileHasFeatures || + !goog.object.isEmpty(featuresObject); + goog.object.extend(featuresToRender, featuresObject); if (tileHasFeatures) { tilesOnSketchCanvas[key] = tileCoord; } @@ -511,19 +486,15 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = } this.dirty_ = dirty; - renderByGeometryType: - for (type in featuresToRender) { - groups = layer.groupFeaturesBySymbolizerLiteral( - featuresToRender[type], tileResolution); - numGroups = groups.length; - for (j = 0; j < numGroups; ++j) { - group = groups[j]; - deferred = sketchCanvasRenderer.renderFeaturesByGeometryType( - /** @type {ol.geom.GeometryType} */ (type), - group[0], group[1], group[2]); - if (deferred) { - break renderByGeometryType; - } + groups = layer.groupFeaturesBySymbolizerLiteral(featuresToRender, + tileResolution); + numGroups = groups.length; + for (j = 0; j < numGroups; ++j) { + group = groups[j]; + deferred = sketchCanvasRenderer.renderFeatures(group[0], group[1], + group[2]); + if (deferred) { + break; } } diff --git a/src/ol/renderer/canvas/canvasvectorrenderer.js b/src/ol/renderer/canvas/canvasvectorrenderer.js index 5cdcab0c83..25c746ec70 100644 --- a/src/ol/renderer/canvas/canvasvectorrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorrenderer.js @@ -114,42 +114,21 @@ ol.renderer.canvas.Vector.prototype.getMaxSymbolSize = function() { /** - * @param {ol.geom.GeometryType} type Geometry type. * @param {Array.} features Array of features. * @param {ol.style.Literal} symbolizer Symbolizer. * @param {Array} data Additional data. * @return {boolean} true if deferred, false if rendered. */ -ol.renderer.canvas.Vector.prototype.renderFeaturesByGeometryType = - function(type, features, symbolizer, data) { +ol.renderer.canvas.Vector.prototype.renderFeatures = + function(features, symbolizer, data) { var deferred = false; - if (!(symbolizer instanceof ol.style.TextLiteral)) { - switch (type) { - case ol.geom.GeometryType.POINT: - case ol.geom.GeometryType.MULTIPOINT: - goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral, - 'Expected point symbolizer: ' + symbolizer); - deferred = this.renderPointFeatures_( - features, /** @type {ol.style.PointLiteral} */ (symbolizer)); - break; - case ol.geom.GeometryType.LINESTRING: - case ol.geom.GeometryType.MULTILINESTRING: - goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral, - 'Expected line symbolizer: ' + symbolizer); - this.renderLineStringFeatures_( - features, /** @type {ol.style.LineLiteral} */ (symbolizer)); - break; - case ol.geom.GeometryType.POLYGON: - case ol.geom.GeometryType.MULTIPOLYGON: - goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral, - 'Expected polygon symbolizer: ' + symbolizer); - this.renderPolygonFeatures_( - features, /** @type {ol.style.PolygonLiteral} */ (symbolizer)); - break; - default: - throw new Error('Rendering not implemented for geometry type: ' + type); - } - } else { + if (symbolizer instanceof ol.style.PointLiteral) { + deferred = this.renderPointFeatures_(features, symbolizer); + } else if (symbolizer instanceof ol.style.LineLiteral) { + this.renderLineStringFeatures_(features, symbolizer); + } else if (symbolizer instanceof ol.style.PolygonLiteral) { + this.renderPolygonFeatures_(features, symbolizer); + } else if (symbolizer instanceof ol.style.TextLiteral) { this.renderText_(features, symbolizer, data); } return deferred; diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index d3c0ee3494..f20b90e9da 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -23,24 +23,6 @@ describe('ol.layer.Vector', function() { new ol.Feature({ g: new ol.geom.Point([16.0, 48.0]) }), - new ol.Feature({ - g: new ol.geom.Point([16.1, 48.1]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.2, 48.2]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.3, 48.3]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]]) - }), new ol.Feature({ g: new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]]) }) @@ -51,47 +33,9 @@ describe('ol.layer.Vector', function() { layer.addFeatures(features); }); - var geomFilter = ol.expr.parse('geometryType("linestring")'); - var extentFilter = ol.expr.parse('extent(16, 48, 16.3, 48.3)'); - - it('can filter by geometry type using its GeometryType index', function() { - sinon.spy(geomFilter, 'evaluate'); - var lineStrings = layer.featureCache_.getFeaturesObject(geomFilter); - expect(geomFilter.evaluate).to.not.be.called(); - expect(goog.object.getCount(lineStrings)).to.eql(4); - expect(goog.object.getValues(lineStrings)).to.contain(features[4]); - }); - - it('can filter by extent using its RTree', function() { - sinon.spy(extentFilter, 'evaluate'); - var subset = layer.featureCache_.getFeaturesObject(extentFilter); - expect(extentFilter.evaluate).to.not.be.called(); - expect(goog.object.getCount(subset)).to.eql(4); - expect(goog.object.getValues(subset)).not.to.contain(features[7]); - }); - - it('can filter by extent and geometry type using its index', function() { - var filter1 = new ol.expr.Logical( - ol.expr.LogicalOp.AND, geomFilter, extentFilter); - var filter2 = new ol.expr.Logical( - ol.expr.LogicalOp.AND, extentFilter, geomFilter); - sinon.spy(filter1, 'evaluate'); - sinon.spy(filter2, 'evaluate'); - var subset1 = layer.featureCache_.getFeaturesObject(filter1); - var subset2 = layer.featureCache_.getFeaturesObject(filter2); - expect(filter1.evaluate).to.not.be.called(); - expect(filter2.evaluate).to.not.be.called(); - expect(goog.object.getCount(subset1)).to.eql(0); - expect(goog.object.getCount(subset2)).to.eql(0); - }); - - it('can handle query using the filter\'s evaluate function', function() { - var filter = new ol.expr.Logical( - ol.expr.LogicalOp.OR, geomFilter, extentFilter); - sinon.spy(filter, 'evaluate'); - var subset = layer.featureCache_.getFeaturesObject(filter); - expect(filter.evaluate).to.be.called(); - expect(goog.object.getCount(subset)).to.eql(8); + it('returns the features in an object', function() { + var featuresObject = layer.featureCache_.getFeaturesObject(); + expect(goog.object.getCount(featuresObject)).to.eql(features.length); }); }); @@ -181,8 +125,6 @@ goog.require('goog.dispose'); goog.require('goog.object'); goog.require('ol.Feature'); goog.require('ol.expr'); -goog.require('ol.expr.Logical'); -goog.require('ol.expr.LogicalOp'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.proj');