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); +}); diff --git a/src/ol/map.js b/src/ol/map.js index 0a26e32f98..d7b3a4e048 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -498,9 +498,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 25d263dbc2..693cc9f974 100644 --- a/src/ol/render/canvas/canvasreplay.js +++ b/src/ol/render/canvas/canvasreplay.js @@ -1329,7 +1329,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/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 5e004f6b96..7f17c14ee3 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -41,6 +41,28 @@ 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. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(opt_obj, feature, this); + }); +}; + + /** * @inheritDoc */ diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index d48be3110a..d1c973e293 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,23 +89,22 @@ 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. + * @return {?} Callback result. */ function(geometry, data) { var feature = /** @type {ol.Feature} */ (data); @@ -183,8 +170,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 +210,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/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index 05e7a0d792..62637b0548 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -46,6 +46,28 @@ 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. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(opt_obj, feature, this); + }); +}; + + /** * @inheritDoc */ diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 7206d83a5b..dd7b5af273 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; } diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index 4336ea8e37..466fa0f8ed 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -72,6 +72,28 @@ 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. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(opt_obj, feature, this); + }); +}; + + /** * @inheritDoc */ diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js index e5ed007814..81a790bf89 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,35 @@ 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. + * @return {?} Callback result. + */ + function(geometry, data) { + var feature = /** @type {ol.Feature} */ (data); + return callback(feature); + }); + } +}; + + /** * @param {ol.Coordinate} center Center. * @param {number} resolution Resolution. 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. */