diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 9fe1f28092..e5bb858d38 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,18 +1,15 @@ goog.provide('ol.layer.Vector'); +goog.require('goog.array'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); goog.require('goog.object'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEventType'); goog.require('ol.feature'); goog.require('ol.layer.Layer'); -/** - * @enum {string} - */ -ol.layer.VectorProperty = { - RENDER_GEOMETRY_FUNCTIONS: 'renderGeometryFunctions' -}; - - /** * @constructor @@ -48,24 +45,29 @@ ol.layer.Vector = function(opt_options) { this.setStyle(options.style); } + /** + * Collection of Features to skip drawing. + * @type {ol.Collection} + * @private + */ + this.skippedFeatures_ = new ol.Collection(); + + /** + * Array of Feature ids to skip drawing. + * @type {Array.} + * @private + */ + this.skippedFeaturesIds_ = []; + + goog.events.listen(this.skippedFeatures_, [ + ol.CollectionEventType.REMOVE, + ol.CollectionEventType.ADD + ], this.updateSkippedFeaturesArray_, false, this); + }; goog.inherits(ol.layer.Vector, ol.layer.Layer); -/** - * @return {ol.Collection|undefined} Render geometry functions. - * @todo stability experimental - */ -ol.layer.Vector.prototype.getRenderGeometryFunctions = function() { - return /** @type {ol.Collection|undefined} */ ( - this.get(ol.layer.VectorProperty.RENDER_GEOMETRY_FUNCTIONS)); -}; -goog.exportProperty( - ol.layer.Vector.prototype, - 'getRenderGeometryFunctions', - ol.layer.Vector.prototype.getRenderGeometryFunctions); - - /** * Get the style for features. This returns whatever was passed to the `style` * option at construction or to the `setStyle` method. @@ -87,22 +89,6 @@ ol.layer.Vector.prototype.getStyleFunction = function() { }; -/** - * @param {ol.Collection|undefined} renderGeometryFunctions Render geometry - * functions. - * @todo stability experimental - */ -ol.layer.Vector.prototype.setRenderGeometryFunctions = - function(renderGeometryFunctions) { - this.set(ol.layer.VectorProperty.RENDER_GEOMETRY_FUNCTIONS, - renderGeometryFunctions); -}; -goog.exportProperty( - ol.layer.Vector.prototype, - 'setRenderGeometryFunctions', - ol.layer.Vector.prototype.setRenderGeometryFunctions); - - /** * Set the style for features. This can be a single style object, an array * of styles, or a function that takes a feature and resolution and returns @@ -116,3 +102,36 @@ ol.layer.Vector.prototype.setStyle = function(style) { this.styleFunction_ = ol.feature.createStyleFunction(style); this.dispatchChangeEvent(); }; + + +/** + * Update Features Ids internal array. + * @private + */ +ol.layer.Vector.prototype.updateSkippedFeaturesArray_ = function() { + this.skippedFeaturesIds_ = goog.array.map( + this.skippedFeatures_.getArray(), goog.getUid); + // Don’t use dispatchChangeEvent here because we don’t want the batch + // to be re-created, just replayed. + this.dispatchEvent(goog.events.EventType.CHANGE); +}; + + +/** + * Get the collection of features to be skipped. + * @return {ol.Collection} Features collection. + * @todo stability experimental + */ +ol.layer.Vector.prototype.getSkippedFeatures = function() { + return this.skippedFeatures_; +}; + + +/** + * Get the feature’s ids to be skipped. + * @return {Array.} Array of features Ids + * @todo stability experimental + */ +ol.layer.Vector.prototype.getSkippedFeaturesIds = function() { + return this.skippedFeaturesIds_; +}; diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 257324cab1..0d2787ce55 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -134,6 +134,12 @@ ol.render.canvas.Replay = function(tolerance, maxExtent, resolution) { */ this.tmpLocalTransform_ = goog.vec.Mat4.createNumber(); + /** + * @private + * @type {Object} + */ + this.instructionIndices_ = {}; + }; @@ -197,11 +203,13 @@ ol.render.canvas.Replay.prototype.appendFlatCoordinates = /** * @protected * @param {ol.geom.Geometry} geometry Geometry. + * @param {number} uid Data uid */ -ol.render.canvas.Replay.prototype.beginGeometry = function(geometry) { +ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, uid) { this.beginGeometryInstruction1_ = [ol.render.canvas.Instruction.BEGIN_GEOMETRY, geometry, 0]; this.instructions.push(this.beginGeometryInstruction1_); + this.instructionIndices_[this.instructions.length - 1] = uid; this.beginGeometryInstruction2_ = [ol.render.canvas.Instruction.BEGIN_GEOMETRY, geometry, 0]; this.hitDetectionInstructions.push(this.beginGeometryInstruction2_); @@ -214,8 +222,7 @@ ol.render.canvas.Replay.prototype.beginGeometry = function(geometry) { * @param {number} pixelRatio Pixel ratio. * @param {goog.vec.Mat4.Number} transform Transform. * @param {number} viewRotation View rotation. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip. * @param {Array.<*>} instructions Instructions array. * @param {function(ol.geom.Geometry, Object): T|undefined} geometryCallback * Geometry callback. @@ -223,7 +230,7 @@ ol.render.canvas.Replay.prototype.beginGeometry = function(geometry) { * @template T */ ol.render.canvas.Replay.prototype.replay_ = function( - context, pixelRatio, transform, viewRotation, renderGeometryFunction, + context, pixelRatio, transform, viewRotation, skippedFeaturesIds, instructions, geometryCallback) { /** @type {Array.} */ var pixelCoordinates; @@ -247,7 +254,8 @@ ol.render.canvas.Replay.prototype.replay_ = function( switch (type) { case ol.render.canvas.Instruction.BEGIN_GEOMETRY: geometry = /** @type {ol.geom.Geometry} */ (instruction[1]); - if (renderGeometryFunction(geometry)) { + if (!goog.array.contains(skippedFeaturesIds, + this.instructionIndices_[i])) { ++i; } else { i = /** @type {number} */ (instruction[2]); @@ -453,16 +461,15 @@ ol.render.canvas.Replay.prototype.replay_ = function( * @param {number} pixelRatio Pixel ratio. * @param {goog.vec.Mat4.Number} transform Transform. * @param {number} viewRotation View rotation. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.Replay.prototype.replay = function( - context, pixelRatio, transform, viewRotation, renderGeometryFunction) { + context, pixelRatio, transform, viewRotation, skippedFeaturesIds) { var instructions = this.instructions; return this.replay_(context, pixelRatio, transform, viewRotation, - renderGeometryFunction, instructions, undefined); + skippedFeaturesIds, instructions, undefined); }; @@ -470,19 +477,18 @@ ol.render.canvas.Replay.prototype.replay = function( * @param {CanvasRenderingContext2D} context Context. * @param {goog.vec.Mat4.Number} transform Transform. * @param {number} viewRotation View rotation. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip * @param {function(ol.geom.Geometry, Object): T=} opt_geometryCallback * Geometry callback. * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.Replay.prototype.replayHitDetection = function( - context, transform, viewRotation, renderGeometryFunction, + context, transform, viewRotation, skippedFeaturesIds, opt_geometryCallback) { var instructions = this.hitDetectionInstructions; return this.replay_(context, 1, transform, viewRotation, - renderGeometryFunction, instructions, opt_geometryCallback); + skippedFeaturesIds, instructions, opt_geometryCallback); }; @@ -764,7 +770,7 @@ ol.render.canvas.ImageReplay.prototype.drawPointGeometry = goog.asserts.assert(goog.isDef(this.scale_)); goog.asserts.assert(goog.isDef(this.width_)); ol.extent.extend(this.extent_, pointGeometry.getExtent()); - this.beginGeometry(pointGeometry); + this.beginGeometry(pointGeometry, goog.getUid(data)); var flatCoordinates = pointGeometry.getFlatCoordinates(); var stride = pointGeometry.getStride(); var myBegin = this.coordinates.length; @@ -806,7 +812,7 @@ ol.render.canvas.ImageReplay.prototype.drawMultiPointGeometry = goog.asserts.assert(goog.isDef(this.scale_)); goog.asserts.assert(goog.isDef(this.width_)); ol.extent.extend(this.extent_, multiPointGeometry.getExtent()); - this.beginGeometry(multiPointGeometry); + this.beginGeometry(multiPointGeometry, goog.getUid(data)); var flatCoordinates = multiPointGeometry.getFlatCoordinates(); var stride = multiPointGeometry.getStride(); var myBegin = this.coordinates.length; @@ -1018,7 +1024,7 @@ ol.render.canvas.LineStringReplay.prototype.drawLineStringGeometry = } ol.extent.extend(this.extent_, lineStringGeometry.getExtent()); this.setStrokeStyle_(); - this.beginGeometry(lineStringGeometry); + this.beginGeometry(lineStringGeometry, goog.getUid(data)); this.hitDetectionInstructions.push( [ol.render.canvas.Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, @@ -1047,7 +1053,7 @@ ol.render.canvas.LineStringReplay.prototype.drawMultiLineStringGeometry = } ol.extent.extend(this.extent_, multiLineStringGeometry.getExtent()); this.setStrokeStyle_(); - this.beginGeometry(multiLineStringGeometry); + this.beginGeometry(multiLineStringGeometry, goog.getUid(data)); this.hitDetectionInstructions.push( [ol.render.canvas.Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, @@ -1225,7 +1231,7 @@ ol.render.canvas.PolygonReplay.prototype.drawCircleGeometry = } ol.extent.extend(this.extent_, circleGeometry.getExtent()); this.setFillStrokeStyles_(); - this.beginGeometry(circleGeometry); + this.beginGeometry(circleGeometry, goog.getUid(data)); // always fill the circle for hit detection this.hitDetectionInstructions.push( [ol.render.canvas.Instruction.SET_FILL_STYLE, @@ -1276,7 +1282,7 @@ ol.render.canvas.PolygonReplay.prototype.drawPolygonGeometry = } ol.extent.extend(this.extent_, polygonGeometry.getExtent()); this.setFillStrokeStyles_(); - this.beginGeometry(polygonGeometry); + this.beginGeometry(polygonGeometry, goog.getUid(data)); // always fill the polygon for hit detection this.hitDetectionInstructions.push( [ol.render.canvas.Instruction.SET_FILL_STYLE, @@ -1312,7 +1318,7 @@ ol.render.canvas.PolygonReplay.prototype.drawMultiPolygonGeometry = } ol.extent.extend(this.extent_, multiPolygonGeometry.getExtent()); this.setFillStrokeStyles_(); - this.beginGeometry(multiPolygonGeometry); + this.beginGeometry(multiPolygonGeometry, goog.getUid(data)); // always fill the multi-polygon for hit detection this.hitDetectionInstructions.push( [ol.render.canvas.Instruction.SET_FILL_STYLE, @@ -1564,7 +1570,7 @@ ol.render.canvas.TextReplay.prototype.drawText = this.setReplayStrokeState_(this.textStrokeState_); } this.setReplayTextState_(this.textState_); - this.beginGeometry(geometry); + this.beginGeometry(geometry, goog.getUid(data)); var myBegin = this.coordinates.length; var myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false); @@ -1844,18 +1850,17 @@ ol.render.canvas.ReplayGroup = function(tolerance, maxExtent, resolution) { * @param {number} pixelRatio Pixel ratio. * @param {goog.vec.Mat4.Number} transform Transform. * @param {number} viewRotation View rotation. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.ReplayGroup.prototype.replay = function(context, extent, - pixelRatio, transform, viewRotation, renderGeometryFunction) { + pixelRatio, transform, viewRotation, skippedFeaturesIds) { /** @type {Array.} */ var zs = goog.array.map(goog.object.getKeys(this.replaysByZIndex_), Number); goog.array.sort(zs); return this.replay_(zs, context, extent, pixelRatio, transform, - viewRotation, renderGeometryFunction); + viewRotation, skippedFeaturesIds); }; @@ -1866,15 +1871,14 @@ ol.render.canvas.ReplayGroup.prototype.replay = function(context, extent, * @param {ol.Extent} extent Extent. * @param {goog.vec.Mat4.Number} transform Transform. * @param {number} viewRotation View rotation. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip * @param {function(ol.geom.Geometry, Object): T} geometryCallback Geometry * callback. * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( - zs, context, extent, transform, viewRotation, renderGeometryFunction, + zs, context, extent, transform, viewRotation, skippedFeaturesIds, geometryCallback) { var i, ii, replays, replayType, replay, result; for (i = 0, ii = zs.length; i < ii; ++i) { @@ -1883,7 +1887,7 @@ ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( replay = replays[replayType]; if (ol.extent.intersects(extent, replay.getExtent())) { result = replay.replayHitDetection(context, transform, viewRotation, - renderGeometryFunction, geometryCallback); + skippedFeaturesIds, geometryCallback); if (result) { return result; } @@ -1902,14 +1906,13 @@ ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( * @param {number} pixelRatio Pixel ratio. * @param {goog.vec.Mat4.Number} transform Transform. * @param {number} viewRotation View rotation. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.ReplayGroup.prototype.replay_ = function( zs, context, extent, pixelRatio, transform, viewRotation, - renderGeometryFunction) { + skippedFeaturesIds) { var maxExtent = this.maxExtent_; var minX = maxExtent[0]; @@ -1935,7 +1938,7 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = function( if (goog.isDef(replay) && ol.extent.intersects(extent, replay.getExtent())) { result = replay.replay(context, pixelRatio, transform, viewRotation, - renderGeometryFunction); + skippedFeaturesIds); if (result) { return result; } @@ -1953,15 +1956,14 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = function( * @param {number} resolution Resolution. * @param {number} rotation Rotation. * @param {ol.Coordinate} coordinate Coordinate. - * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render - * geometry function. + * @param {Array.} skippedFeaturesIds Ids of features to skip * @param {function(ol.geom.Geometry, Object): T} callback Geometry callback. * @return {T|undefined} Callback result. * @template T */ ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function( extent, resolution, rotation, coordinate, - renderGeometryFunction, callback) { + skippedFeaturesIds, callback) { var transform = this.hitDetectionTransform_; ol.vec.Mat4.makeTransform2D(transform, 0.5, 0.5, @@ -1976,7 +1978,7 @@ ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function( context.clearRect(0, 0, 1, 1); return this.replayHitDetection_(zs, context, extent, transform, - rotation, renderGeometryFunction, + rotation, skippedFeaturesIds, /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Opaque data object. diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 5b0f97c226..30cb9f3130 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -4,7 +4,6 @@ goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); -goog.require('goog.functions'); goog.require('ol.ViewHint'); goog.require('ol.extent'); goog.require('ol.feature'); @@ -96,12 +95,13 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = } else { replayContext = context; } - var renderGeometryFunction = this.getRenderGeometryFunction_(); - goog.asserts.assert(goog.isFunction(renderGeometryFunction)); + goog.asserts.assertInstanceof(layer, ol.layer.Vector); + var skippedFeaturesIds = layer.getSkippedFeaturesIds(); + goog.asserts.assert(goog.isArray(skippedFeaturesIds)); replayContext.globalAlpha = layerState.opacity; replayGroup.replay( replayContext, frameState.extent, frameState.pixelRatio, transform, - frameState.view2DState.rotation, renderGeometryFunction); + frameState.view2DState.rotation, skippedFeaturesIds); if (replayContext != context) { this.dispatchRenderEvent(replayContext, frameState, transform); @@ -126,10 +126,11 @@ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = var resolution = frameState.view2DState.resolution; var rotation = frameState.view2DState.rotation; var layer = this.getLayer(); - var renderGeometryFunction = this.getRenderGeometryFunction_(); - goog.asserts.assert(goog.isFunction(renderGeometryFunction)); + goog.asserts.assertInstanceof(layer, ol.layer.Vector); + var skippedFeaturesIds = layer.getSkippedFeaturesIds(); + goog.asserts.assert(goog.isArray(skippedFeaturesIds)); return this.replayGroup_.forEachGeometryAtPixel(extent, resolution, - rotation, coordinate, renderGeometryFunction, + rotation, coordinate, skippedFeaturesIds, /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Data. @@ -144,43 +145,6 @@ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = }; -/** - * @private - * @return {function(ol.geom.Geometry): boolean} Render geometry function. - */ -ol.renderer.canvas.VectorLayer.prototype.getRenderGeometryFunction_ = - function() { - var vectorLayer = this.getLayer(); - goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector); - var renderGeometryFunctions = vectorLayer.getRenderGeometryFunctions(); - if (!goog.isDef(renderGeometryFunctions)) { - return goog.functions.TRUE; - } - var renderGeometryFunctionsArray = renderGeometryFunctions.getArray(); - switch (renderGeometryFunctionsArray.length) { - case 0: - return goog.functions.TRUE; - case 1: - return renderGeometryFunctionsArray[0]; - default: - return ( - /** - * @param {ol.geom.Geometry} geometry Geometry. - * @return {boolean} Render geometry. - */ - function(geometry) { - var i, ii; - for (i = 0, ii = renderGeometryFunctionsArray.length; i < ii; ++i) { - if (!renderGeometryFunctionsArray[i](geometry)) { - return false; - } - } - return true; - }); - } -}; - - /** * Handle changes in image style state. * @param {goog.events.Event} event Image style change event. diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index 07c8196ff8..f4a436ee17 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -141,7 +141,7 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ = var transform = this.getTransform_(ol.extent.getCenter(extent), resolution, pixelRatio, size); replayGroup.replay(this.canvasContext_, extent, pixelRatio, transform, 0, - goog.functions.TRUE); + []); this.replayGroup_ = replayGroup; @@ -158,7 +158,7 @@ ol.source.ImageVector.prototype.forEachFeatureAtPixel = return undefined; } else { return this.replayGroup_.forEachGeometryAtPixel( - extent, resolution, 0, coordinate, goog.functions.TRUE, + extent, resolution, 0, coordinate, [], /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Data.