From f7be1c155e835497c83366798e32864d563fe3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 10:45:51 +0100 Subject: [PATCH 1/8] Hit detection refactoring Get the frame state from the map instead of storing values in the layer renderers. --- src/ol/map.js | 8 +++-- src/ol/render/canvas/canvasreplay.js | 2 +- .../canvas/canvasvectorlayerrenderer.js | 29 ++++--------------- src/ol/renderer/layerrenderer.js | 3 +- src/ol/renderer/maprenderer.js | 10 ++++--- 5 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index db3bfcb476..282b83996d 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -457,9 +457,13 @@ ol.Map.prototype.disposeInternal = function() { */ ol.Map.prototype.forEachFeatureAtPixel = function(pixel, callback, opt_obj, opt_layerFunction, opt_obj2) { - // FIXME this function should probably take an options object + if (goog.isNull(this.frameState_)) { + return; + } + var coordinate = this.getCoordinateFromPixel(pixel); return this.renderer_.forEachFeatureAtPixel( - pixel, callback, opt_obj, opt_layerFunction, opt_obj2); + coordinate, this.frameState_, callback, opt_obj, + opt_layerFunction, opt_obj2); }; diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js index dfbb5cdd09..5a102d66a1 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -1317,7 +1317,7 @@ ol.render.canvas.ReplayGroup.prototype.replay_ = * @return {T|undefined} Callback result. * @template T */ -ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtCoordinate = function( +ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function( extent, resolution, rotation, coordinate, renderGeometryFunction, callback) { diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index d48be3110a..f0c6eb4df4 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -32,12 +32,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, vectorLayer) { */ this.dirty_ = false; - /** - * @private - * @type {ol.Extent} - */ - this.frameStateExtent_ = ol.extent.createEmpty(); - /** * @private * @type {number} @@ -50,12 +44,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, vectorLayer) { */ this.renderedResolution_ = NaN; - /** - * @private - * @type {number} - */ - this.renderedRotation_ = NaN; - /** * @private * @type {ol.Extent} @@ -101,20 +89,18 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = * @inheritDoc */ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = - function(pixel, callback, opt_obj) { + function(coordinate, frameState, callback, opt_obj) { if (goog.isNull(this.replayGroup_)) { return undefined; } else { - goog.asserts.assert(!ol.extent.isEmpty(this.frameStateExtent_)); - goog.asserts.assert(!isNaN(this.renderedResolution_)); - goog.asserts.assert(!isNaN(this.renderedRotation_)); - var coordinate = this.getMap().getCoordinateFromPixel(pixel); + var extent = frameState.extent; + var resolution = frameState.view2DState.resolution; + var rotation = frameState.view2DState.rotation; var layer = this.getLayer(); var renderGeometryFunction = this.getRenderGeometryFunction_(); goog.asserts.assert(goog.isFunction(renderGeometryFunction)); - return this.replayGroup_.forEachGeometryAtCoordinate(this.frameStateExtent_, - this.renderedResolution_, this.renderedRotation_, coordinate, - renderGeometryFunction, + return this.replayGroup_.forEachGeometryAtPixel(extent, resolution, + rotation, coordinate, renderGeometryFunction, /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Data. @@ -183,8 +169,6 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = var frameStateResolution = frameState.view2DState.resolution; var pixelRatio = frameState.devicePixelRatio; - this.frameStateExtent_ = frameStateExtent; - if (!this.dirty_ && this.renderedResolution_ == frameStateResolution && this.renderedRevision_ == vectorSource.getRevision() && @@ -225,7 +209,6 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = this.renderedResolution_ = frameStateResolution; this.renderedRevision_ = vectorSource.getRevision(); - this.renderedRotation_ = frameState.view2DState.rotation; if (!replayGroup.isEmpty()) { this.replayGroup_ = replayGroup; } diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 4365b61258..f626c16724 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -45,7 +45,8 @@ goog.inherits(ol.renderer.Layer, goog.Disposable); /** - * @param {ol.Pixel} pixel Pixel. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.FrameState} frameState Frame state. * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature * callback. * @param {S=} opt_obj Scope. diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index b460629aac..c46c63d067 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -81,7 +81,8 @@ ol.renderer.Map.prototype.disposeInternal = function() { /** - * @param {ol.Pixel} pixel Pixel. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.FrameState} frameState FrameState. * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature * callback. * @param {S=} opt_obj Scope for feature callback. @@ -92,7 +93,8 @@ ol.renderer.Map.prototype.disposeInternal = function() { * @template S,T,U */ ol.renderer.Map.prototype.forEachFeatureAtPixel = - function(pixel, callback, opt_obj, opt_layerFunction, opt_obj2) { + function(coordinate, frameState, callback, opt_obj, + opt_layerFunction, opt_obj2) { var layerFunction = goog.isDef(opt_layerFunction) ? opt_layerFunction : goog.functions.TRUE; var layersArray = this.map_.getLayerGroup().getLayersArray(); @@ -101,8 +103,8 @@ ol.renderer.Map.prototype.forEachFeatureAtPixel = var layer = layersArray[i]; if (layer.getVisible() && layerFunction.call(opt_obj2, layer)) { var layerRenderer = this.getLayerRenderer(layer); - var result = - layerRenderer.forEachFeatureAtPixel(pixel, callback, opt_obj); + var result = layerRenderer.forEachFeatureAtPixel( + coordinate, frameState, callback, opt_obj); if (result) { return result; } From 4cd2a75900d433545203f345d7ba553646f5aa51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 11:41:33 +0100 Subject: [PATCH 2/8] Add ol.source.Source#forEachFeatureAtPixel --- src/ol/source/source.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ol/source/source.js b/src/ol/source/source.js index 2e4b077a4f..d07f916bdb 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -93,6 +93,19 @@ ol.source.Source.prototype.dispatchChangeEvent = function() { }; +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {function(ol.Feature): T} callback Feature callback. + * @return {T|undefined} Callback result. + * @template T + */ +ol.source.Source.prototype.forEachFeatureAtPixel = + goog.nullFunction; + + /** * @return {Array.} Attributions. */ From 849e50517cb5fc281f4907e0115bb668979820b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 11:42:25 +0100 Subject: [PATCH 3/8] Add ol.source.ImageVector#forEachFeatureAtPixel --- src/ol/source/imagevectorsource.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index e5ed007814..28886bef58 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -73,6 +73,12 @@ ol.source.ImageVector = function(options) { */ this.canvasSize_ = [0, 0]; + /** + * @private + * @type {ol.render.canvas.ReplayGroup} + */ + this.replayGroup_ = null; + goog.base(this, { attributions: options.attributions, canvasFunction: goog.bind(this.canvasFunctionInternal_, this), @@ -135,10 +141,34 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ = replayGroup.replay(this.canvasContext_, extent, pixelRatio, transform, goog.functions.TRUE); + this.replayGroup_ = replayGroup; + return this.canvasElement_; }; +/** + * @inheritDoc + */ +ol.source.ImageVector.prototype.forEachFeatureAtPixel = + function(extent, resolution, rotation, coordinate, callback) { + if (goog.isNull(this.replayGroup_)) { + return undefined; + } else { + return this.replayGroup_.forEachGeometryAtPixel( + extent, resolution, 0, coordinate, goog.functions.TRUE, + /** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {Object} data Data. + */ + function(geometry, data) { + var feature = /** @type {ol.Feature} */ (data); + return callback(feature); + }); + } +}; + + /** * @param {ol.Coordinate} center Center. * @param {number} resolution Resolution. From ee17babc05c04205a7bcfd832d47d5151535ddc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 11:49:40 +0100 Subject: [PATCH 4/8] Add ol.renderer.canvas.ImageLayer#forEachFeatureAtPixel --- .../canvas/canvasimagelayerrenderer.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 5e004f6b96..683449b737 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -41,6 +41,27 @@ ol.renderer.canvas.ImageLayer = function(mapRenderer, imageLayer) { goog.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.Layer); +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtPixel = + function(coordinate, frameState, callback, opt_obj) { + var layer = this.getLayer(); + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.Image); + var extent = frameState.extent; + var resolution = frameState.view2DState.resolution; + var rotation = frameState.view2DState.rotation; + return source.forEachFeatureAtPixel(extent, resolution, rotation, coordinate, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + return callback.call(opt_obj, feature, this); + }); +}; + + /** * @inheritDoc */ From a15bacd963cf3135612eaeb514ef595904728f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 11:50:06 +0100 Subject: [PATCH 5/8] Add ol.renderer.dom.ImageLayer#forEachFeatureAtPixel --- src/ol/renderer/dom/domimagelayerrenderer.js | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index 05e7a0d792..9f2a0bd68a 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -46,6 +46,27 @@ ol.renderer.dom.ImageLayer = function(mapRenderer, imageLayer) { goog.inherits(ol.renderer.dom.ImageLayer, ol.renderer.dom.Layer); +/** + * @inheritDoc + */ +ol.renderer.dom.ImageLayer.prototype.forEachFeatureAtPixel = + function(coordinate, frameState, callback, opt_obj) { + var layer = this.getLayer(); + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.Image); + var extent = frameState.extent; + var resolution = frameState.view2DState.resolution; + var rotation = frameState.view2DState.rotation; + return source.forEachFeatureAtPixel(extent, resolution, rotation, coordinate, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + return callback.call(opt_obj, feature, this); + }); +}; + + /** * @inheritDoc */ From 6c30710d0c888a0e5818b2b2a4eab2b11e2a995c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 11:50:23 +0100 Subject: [PATCH 6/8] Add ol.renderer.webgl.ImageLayer#forEachFeatureAtPixel --- .../renderer/webgl/webglimagelayerrenderer.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index 4336ea8e37..a1588b1d28 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -72,6 +72,27 @@ ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) { }; +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtPixel = + function(coordinate, frameState, callback, opt_obj) { + var layer = this.getLayer(); + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.Image); + var extent = frameState.extent; + var resolution = frameState.view2DState.resolution; + var rotation = frameState.view2DState.rotation; + return source.forEachFeatureAtPixel(extent, resolution, rotation, coordinate, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + return callback.call(opt_obj, feature, this); + }); +}; + + /** * @inheritDoc */ From b52b2223a612fce88d28cba7da3ac4cff5bd3b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 11:50:41 +0100 Subject: [PATCH 7/8] Add hit detection to image-vector-layer example --- examples/image-vector-layer.html | 10 ++++-- examples/image-vector-layer.js | 58 ++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/examples/image-vector-layer.html b/examples/image-vector-layer.html index b8a37b6d5f..9531bbc7a4 100644 --- a/examples/image-vector-layer.html +++ b/examples/image-vector-layer.html @@ -30,22 +30,28 @@
-
+

Image vector example

Example of an image vector layer.

This example uses a ol.source.ImageVector source. That source gets vector features from the - ol.source.Vector it's configured with and draw these features to an HTML5 canvas element that + ol.source.Vector it's configured with, and draw these features to an HTML5 canvas element that is then used as the image of an image layer.

See the image-vector-layer.js source to see how this is done.

vector, image
+
+
+   +
+
+ diff --git a/examples/image-vector-layer.js b/examples/image-vector-layer.js index 76e6e83dae..279bb5f284 100644 --- a/examples/image-vector-layer.js +++ b/examples/image-vector-layer.js @@ -1,8 +1,9 @@ goog.require('ol.Map'); -goog.require('ol.RendererHints'); +goog.require('ol.RendererHint'); goog.require('ol.View2D'); goog.require('ol.layer.Image'); goog.require('ol.layer.Tile'); +goog.require('ol.render.FeaturesOverlay'); goog.require('ol.source.GeoJSON'); goog.require('ol.source.ImageVector'); goog.require('ol.source.MapQuest'); @@ -37,10 +38,63 @@ var map = new ol.Map({ }) }) ], - renderers: ol.RendererHints.createFromQueryData(), + renderer: ol.RendererHint.CANVAS, target: 'map', view: new ol.View2D({ center: [0, 0], zoom: 1 }) }); + +var highlightStyleArray = [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#f00', + width: 1 + }), + fill: new ol.style.Fill({ + color: 'rgba(255,0,0,0.1)' + }) +})]; + +var featuresOverlay = new ol.render.FeaturesOverlay({ + map: map, + styleFunction: function(feature, resolution) { + return highlightStyleArray; + } +}); + +var highlight; +var displayFeatureInfo = function(pixel) { + + var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) { + return feature; + }); + + var info = document.getElementById('info'); + if (feature) { + info.innerHTML = feature.getId() + ': ' + feature.get('name'); + } else { + info.innerHTML = ' '; + } + + if (feature !== highlight) { + if (highlight) { + featuresOverlay.removeFeature(highlight); + } + if (feature) { + featuresOverlay.addFeature(feature); + } + highlight = feature; + } + +}; + +$(map.getViewport()).on('mousemove', function(evt) { + var pixel = map.getEventPixel(evt.originalEvent); + displayFeatureInfo(pixel); +}); + +map.on('singleclick', function(evt) { + var pixel = evt.getPixel(); + displayFeatureInfo(pixel); +}); From 542cf80da989eaeb8c51caf231335803e698ae6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 13 Jan 2014 12:03:20 +0100 Subject: [PATCH 8/8] Better typing --- src/ol/renderer/canvas/canvasimagelayerrenderer.js | 1 + src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 1 + src/ol/renderer/dom/domimagelayerrenderer.js | 1 + src/ol/renderer/webgl/webglimagelayerrenderer.js | 1 + src/ol/source/imagevectorsource.js | 1 + 5 files changed, 5 insertions(+) diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 683449b737..7f17c14ee3 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -55,6 +55,7 @@ ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtPixel = return source.forEachFeatureAtPixel(extent, resolution, rotation, coordinate, /** * @param {ol.Feature} feature Feature. + * @return {?} Callback result. */ function(feature) { return callback.call(opt_obj, feature, this); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index f0c6eb4df4..d1c973e293 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -104,6 +104,7 @@ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Data. + * @return {?} Callback result. */ function(geometry, data) { var feature = /** @type {ol.Feature} */ (data); diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index 9f2a0bd68a..62637b0548 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -60,6 +60,7 @@ ol.renderer.dom.ImageLayer.prototype.forEachFeatureAtPixel = return source.forEachFeatureAtPixel(extent, resolution, rotation, coordinate, /** * @param {ol.Feature} feature Feature. + * @return {?} Callback result. */ function(feature) { return callback.call(opt_obj, feature, this); diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index a1588b1d28..466fa0f8ed 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -86,6 +86,7 @@ ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtPixel = return source.forEachFeatureAtPixel(extent, resolution, rotation, coordinate, /** * @param {ol.Feature} feature Feature. + * @return {?} Callback result. */ function(feature) { return callback.call(opt_obj, feature, this); diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index 28886bef58..81a790bf89 100644 --- a/src/ol/source/imagevectorsource.js +++ b/src/ol/source/imagevectorsource.js @@ -160,6 +160,7 @@ ol.source.ImageVector.prototype.forEachFeatureAtPixel = /** * @param {ol.geom.Geometry} geometry Geometry. * @param {Object} data Data. + * @return {?} Callback result. */ function(geometry, data) { var feature = /** @type {ol.Feature} */ (data);