From d7591594ca5cfedba2faab0541f7b656e38548b4 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 2 Dec 2013 16:24:11 +0100 Subject: [PATCH] Allow forEachFeatureAtPixel callback to break out of loop --- src/ol/map.js | 9 ++-- src/ol/render/canvas/canvasreplay.js | 54 +++++++++++++------ .../canvas/canvasvectorlayerrenderer.js | 10 ++-- src/ol/renderer/layerrenderer.js | 5 +- src/ol/renderer/maprenderer.js | 27 ++++++++-- 5 files changed, 77 insertions(+), 28 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 18e7818346..c783f06fb1 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -457,10 +457,13 @@ ol.Map.prototype.disposeInternal = function() { /** * @param {ol.Pixel} pixel Pixel. - * @param {function(ol.Feature)} callback Feature callback. + * @param {function(this: S, ol.Feature): T} callback Feature callback. + * @param {S=} opt_obj Scope. + * @return {T|undefined} Callback result. + * @template S,T */ -ol.Map.prototype.forEachFeatureAtPixel = function(pixel, callback) { - this.renderer_.forEachFeatureAtPixel(pixel, callback); +ol.Map.prototype.forEachFeatureAtPixel = function(pixel, callback, opt_obj) { + return this.renderer_.forEachFeatureAtPixel(pixel, callback, opt_obj); }; diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index 2b5be51b49..375067ffec 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -120,7 +120,10 @@ ol.render.canvas.Replay.prototype.beginGeometry = function(geometry) { * @param {goog.vec.Mat4.AnyType} transform Transform. * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render * geometry function. - * @param {function(ol.geom.Geometry, Object)=} opt_callback Geometry callback. + * @param {function(ol.geom.Geometry, Object): T=} opt_callback + * Geometry callback. + * @return {T|undefined} Callback result. + * @template T */ ol.render.canvas.Replay.prototype.replay = function(context, transform, renderGeometryFunction, opt_callback) { @@ -189,7 +192,10 @@ ol.render.canvas.Replay.prototype.replay = goog.asserts.assert(goog.isDef(opt_callback)); geometry = /** @type {ol.geom.Geometry} */ (instruction[1]); var data = /** @type {Object} */ (instruction[2]); - opt_callback(geometry, data); + var result = opt_callback(geometry, data); + if (result) { + return result; + } } ++i; } else if (type == ol.render.canvas.Instruction.FILL) { @@ -240,6 +246,7 @@ ol.render.canvas.Replay.prototype.replay = goog.asserts.assert(d == pixelCoordinates.length); // assert that all instructions were consumed goog.asserts.assert(i == instructions.length); + return undefined; }; @@ -958,15 +965,18 @@ ol.render.canvas.ReplayGroup = function() { * @param {goog.vec.Mat4.AnyType} transform Transform. * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render * geometry function. - * @param {function(ol.geom.Geometry, Object)=} opt_callback Geometry callback. + * @param {function(ol.geom.Geometry, Object): T=} opt_callback Geometry + * callback. + * @return {T|undefined} Callback result. + * @template T */ ol.render.canvas.ReplayGroup.prototype.replay = function(context, extent, transform, renderGeometryFunction, opt_callback) { /** @type {Array.} */ var zs = goog.array.map(goog.object.getKeys(this.replayesByZIndex_), Number); goog.array.sort(zs); - this.replay_(zs, context, extent, transform, renderGeometryFunction, - opt_callback); + return this.replay_( + zs, context, extent, transform, renderGeometryFunction, opt_callback); }; @@ -978,11 +988,13 @@ ol.render.canvas.ReplayGroup.prototype.replay = * @param {goog.vec.Mat4.AnyType} transform Transform. * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render * geometry function. - * @param {function(ol.geom.Geometry, Object)=} opt_callback Geometry callback. + * @param {function(ol.geom.Geometry, Object): T=} opt_callback Geometry + * callback. + * @return {T|undefined} Callback result. + * @template T */ -ol.render.canvas.ReplayGroup.prototype.replay_ = - function(zs, context, extent, transform, renderGeometryFunction, - opt_callback) { +ol.render.canvas.ReplayGroup.prototype.replay_ = function(zs, context, extent, + transform, renderGeometryFunction, opt_callback) { var i, ii; for (i = 0, ii = zs.length; i < ii; ++i) { var replayes = this.replayesByZIndex_[zs[i].toString()]; @@ -990,10 +1002,15 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = for (replayType in replayes) { var replay = replayes[replayType]; if (ol.extent.intersects(extent, replay.getExtent())) { - replay.replay(context, transform, renderGeometryFunction, opt_callback); + var result = replay.replay( + context, transform, renderGeometryFunction, opt_callback); + if (result) { + return result; + } } } } + return undefined; }; @@ -1003,10 +1020,12 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = * @param {ol.Coordinate} coordinate Coordinate. * @param {function(ol.geom.Geometry): boolean} renderGeometryFunction Render * geometry function. - * @param {function(ol.geom.Geometry, Object)} callback Geometry callback. + * @param {function(ol.geom.Geometry, Object): T} callback Geometry callback. + * @return {T|undefined} Callback result. + * @template T */ -ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtCoordinate = - function(extent, resolution, coordinate, renderGeometryFunction, callback) { +ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtCoordinate = function( + extent, resolution, coordinate, renderGeometryFunction, callback) { var transform = this.hitDetectionTransform_; ol.vec.Mat4.makeTransform2D(transform, 0.5, 0.5, @@ -1017,16 +1036,21 @@ ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtCoordinate = goog.array.sort(zs, function(a, b) { return b - a; }); var context = this.hitDetectionContext_; + context.clearRect(0, 0, 1, 1); - this.replay_(zs, context, extent, transform, renderGeometryFunction, + return this.replay_(zs, context, extent, transform, renderGeometryFunction, /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Opaque data object. + * @return {?} Callback result. */ function(geometry, data) { var imageData = context.getImageData(0, 0, 1, 1).data; if (imageData[3] > 0) { - callback(geometry, data); + var result = callback(geometry, data); + if (result) { + return result; + } context.clearRect(0, 0, 1, 1); } }); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 4fca4bdfe0..7a57f556ca 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -85,14 +85,16 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = * @inheritDoc */ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = - function(pixel, callback) { - if (!goog.isNull(this.replayGroup_)) { + function(pixel, callback, opt_obj) { + if (goog.isNull(this.replayGroup_)) { + return undefined; + } else { goog.asserts.assert(!ol.extent.isEmpty(this.renderedExtent_)); goog.asserts.assert(!isNaN(this.renderedResolution_)); var coordinate = this.getMap().getCoordinateFromPixel(pixel); var renderGeometryFunction = this.getRenderGeometryFunction_(); goog.asserts.assert(goog.isFunction(renderGeometryFunction)); - this.replayGroup_.forEachGeometryAtCoordinate(this.renderedExtent_, + return this.replayGroup_.forEachGeometryAtCoordinate(this.renderedExtent_, this.renderedResolution_, coordinate, renderGeometryFunction, /** * @param {ol.geom.Geometry} geometry Geometry. @@ -101,7 +103,7 @@ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = function(geometry, data) { var feature = /** @type {ol.Feature} */ (data); goog.asserts.assert(goog.isDef(feature)); - callback(feature); + return callback.call(opt_obj, feature); }); } }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 110e294e72..23a2bf1d8d 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -44,7 +44,10 @@ goog.inherits(ol.renderer.Layer, goog.Disposable); /** * @param {ol.Pixel} pixel Pixel. - * @param {function(ol.Feature)} callback Feature callback. + * @param {function(this: S, ol.Feature): T} callback Feature callback. + * @param {S=} opt_obj Scope. + * @return {T|undefined} Callback result. + * @template S,T */ ol.renderer.Layer.prototype.forEachFeatureAtPixel = goog.nullFunction; diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 627d98066c..ec57481bf0 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -80,13 +80,30 @@ ol.renderer.Map.prototype.disposeInternal = function() { /** * @param {ol.Pixel} pixel Pixel. - * @param {function(ol.Feature)} callback Feature callback. + * @param {function(this: S, ol.Feature): T} callback Feature callback. + * @param {S=} opt_obj Scope. + * @return {T|undefined} Callback result. + * @template S,T */ ol.renderer.Map.prototype.forEachFeatureAtPixel = - function(pixel, callback) { - goog.object.forEach(this.layerRenderers_, function(layerRenderer) { - layerRenderer.forEachFeatureAtPixel(pixel, callback); - }); + function(pixel, callback, opt_obj) { + var layers = this.map_.getLayers(); + if (goog.isDef(layers)) { + var layersArray = layers.getArray(); + var i; + for (i = layersArray.length - 1; i >= 0; --i) { + var layer = layersArray[i]; + if (layer.getVisible()) { + var layerRenderer = this.getLayerRenderer(layer); + var result = + layerRenderer.forEachFeatureAtPixel(pixel, callback, opt_obj); + if (result) { + return result; + } + } + } + } + return undefined; };