diff --git a/src/ol/map.js b/src/ol/map.js index c353275e55..11f38bba82 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -589,6 +589,44 @@ ol.Map.prototype.forEachFeatureAtPixel = }; +/** + * Detect layers that have a colour value at a pixel on the viewport, and + * execute a callback with each matching layer. Layers included in the + * detection can be configured through `opt_layerFilter`. Feature overlays will + * always be included in the detection. + * @param {ol.Pixel} pixel Pixel. + * @param {function(this: S, ol.layer.Layer): T} callback Layer + * callback. If the detected feature is not on a layer, but on a + * {@link ol.FeatureOverlay}, then the argument to this function will + * be `null`. To stop detection, callback functions can return a truthy + * value. + * @param {S=} opt_this Value to use as `this` when executing `callback`. + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer + * filter function, only layers which are visible and for which this + * function returns `true` will be tested for features. By default, all + * visible layers will be tested. Feature overlays will always be tested. + * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result, i.e. the return value of last + * callback execution, or the first truthy callback return value. + * @template S,T,U + * @api stable + */ +ol.Map.prototype.forEachLayerAtPixel = + function(pixel, callback, opt_this, opt_layerFilter, opt_this2) { + if (goog.isNull(this.frameState_)) { + return; + } + var coordinate = this.getCoordinateFromPixel(pixel); + var thisArg = goog.isDef(opt_this) ? opt_this : null; + var layerFilter = goog.isDef(opt_layerFilter) ? + opt_layerFilter : goog.functions.TRUE; + var thisArg2 = goog.isDef(opt_this2) ? opt_this2 : null; + return this.renderer_.forEachLayerAtPixel( + coordinate, this.frameState_, callback, thisArg, + layerFilter, thisArg2); +}; + + /** * Detect if features intersect a pixel on the viewport. Layers included in the * detection can be configured through `opt_layerFilter`. Feature overlays will diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 77b76f17f9..490751926e 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -56,6 +56,27 @@ goog.inherits(ol.renderer.Layer, goog.Disposable); ol.renderer.Layer.prototype.forEachFeatureAtPixel = goog.nullFunction; +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState Frame state. + * @param {function(this: S, ol.layer.Layer): T} callback Layer callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T + */ +ol.renderer.Layer.prototype.forEachLayerAtPixel = + function(coordinate, frameState, callback, thisArg) { + var hasFeature = this.forEachFeatureAtPixel( + coordinate, frameState, goog.functions.TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.layer_); + } else { + return undefined; + } +}; + + /** * @param {ol.Coordinate} coordinate Coordinate. * @param {olx.FrameState} frameState Frame state. diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 10b0429bd8..82dc358f2c 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -169,6 +169,59 @@ ol.renderer.Map.prototype.forEachFeatureAtPixel = }; +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState FrameState. + * @param {function(this: S, ol.layer.Layer): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ +ol.renderer.Map.prototype.forEachLayerAtPixel = + function(coordinate, frameState, callback, thisArg, + layerFilter, thisArg2) { + var result; + var viewState = frameState.viewState; + var viewResolution = viewState.resolution; + var viewRotation = viewState.rotation; + + if (!goog.isNull(this.replayGroup)) { + var hasFeature = this.replayGroup.forEachFeatureAtPixel(viewResolution, + viewRotation, coordinate, {}, goog.functions.TRUE); + + if (hasFeature) { + result = callback.call(thisArg, null); + if (result) { + return result; + } + } + } + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && + layerFilter.call(thisArg2, layer)) { + var layerRenderer = this.getLayerRenderer(layer); + result = layerRenderer.forEachLayerAtPixel( + coordinate, frameState, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; +}; + + /** * @param {ol.Coordinate} coordinate Coordinate. * @param {olx.FrameState} frameState FrameState.