Merge pull request #1504 from elemoine/vector-api-image-vector-hit-detection

[vector-api] Vector image hit detection
This commit is contained in:
Éric Lemoine
2014-01-14 03:33:47 -08:00
12 changed files with 196 additions and 35 deletions
+8 -2
View File
@@ -30,22 +30,28 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="span12"> <div class="span8">
<h4 id="title">Image vector example</h4> <h4 id="title">Image vector example</h4>
<p id="shortdesc">Example of an image vector layer.</p> <p id="shortdesc">Example of an image vector layer.</p>
<div id="docs"> <div id="docs">
<p>This example uses a <code>ol.source.ImageVector</code> source. That source gets vector features from the <p>This example uses a <code>ol.source.ImageVector</code> source. That source gets vector features from the
<code>ol.source.Vector</code> it's configured with and draw these features to an HTML5 canvas element that <code>ol.source.Vector</code> it's configured with, and draw these features to an HTML5 canvas element that
is then used as the image of an image layer.</p> is then used as the image of an image layer.</p>
<p>See the <a href="image-vector-layer.js" target="_blank">image-vector-layer.js source</a> to see how this is done.</p> <p>See the <a href="image-vector-layer.js" target="_blank">image-vector-layer.js source</a> to see how this is done.</p>
</div> </div>
<div id="tags">vector, image</div> <div id="tags">vector, image</div>
</div> </div>
<div class="span4">
<div id="info" class="alert alert-success">
&nbsp;
</div>
</div>
</div> </div>
</div> </div>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="loader.js?id=image-vector-layer" type="text/javascript"></script> <script src="loader.js?id=image-vector-layer" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script> <script src="../resources/example-behaviour.js" type="text/javascript"></script>
+56 -2
View File
@@ -1,8 +1,9 @@
goog.require('ol.Map'); goog.require('ol.Map');
goog.require('ol.RendererHints'); goog.require('ol.RendererHint');
goog.require('ol.View2D'); goog.require('ol.View2D');
goog.require('ol.layer.Image'); goog.require('ol.layer.Image');
goog.require('ol.layer.Tile'); goog.require('ol.layer.Tile');
goog.require('ol.render.FeaturesOverlay');
goog.require('ol.source.GeoJSON'); goog.require('ol.source.GeoJSON');
goog.require('ol.source.ImageVector'); goog.require('ol.source.ImageVector');
goog.require('ol.source.MapQuest'); goog.require('ol.source.MapQuest');
@@ -37,10 +38,63 @@ var map = new ol.Map({
}) })
}) })
], ],
renderers: ol.RendererHints.createFromQueryData(), renderer: ol.RendererHint.CANVAS,
target: 'map', target: 'map',
view: new ol.View2D({ view: new ol.View2D({
center: [0, 0], center: [0, 0],
zoom: 1 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 = '&nbsp;';
}
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);
});
+6 -2
View File
@@ -498,9 +498,13 @@ ol.Map.prototype.disposeInternal = function() {
*/ */
ol.Map.prototype.forEachFeatureAtPixel = ol.Map.prototype.forEachFeatureAtPixel =
function(pixel, callback, opt_obj, opt_layerFunction, opt_obj2) { 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( return this.renderer_.forEachFeatureAtPixel(
pixel, callback, opt_obj, opt_layerFunction, opt_obj2); coordinate, this.frameState_, callback, opt_obj,
opt_layerFunction, opt_obj2);
}; };
+1 -1
View File
@@ -1329,7 +1329,7 @@ ol.render.canvas.ReplayGroup.prototype.replay_ =
* @return {T|undefined} Callback result. * @return {T|undefined} Callback result.
* @template T * @template T
*/ */
ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtCoordinate = function( ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function(
extent, resolution, rotation, coordinate, extent, resolution, rotation, coordinate,
renderGeometryFunction, callback) { renderGeometryFunction, callback) {
@@ -41,6 +41,28 @@ ol.renderer.canvas.ImageLayer = function(mapRenderer, imageLayer) {
goog.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.Layer); 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 * @inheritDoc
*/ */
@@ -32,12 +32,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, vectorLayer) {
*/ */
this.dirty_ = false; this.dirty_ = false;
/**
* @private
* @type {ol.Extent}
*/
this.frameStateExtent_ = ol.extent.createEmpty();
/** /**
* @private * @private
* @type {number} * @type {number}
@@ -50,12 +44,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, vectorLayer) {
*/ */
this.renderedResolution_ = NaN; this.renderedResolution_ = NaN;
/**
* @private
* @type {number}
*/
this.renderedRotation_ = NaN;
/** /**
* @private * @private
* @type {ol.Extent} * @type {ol.Extent}
@@ -101,23 +89,22 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame =
* @inheritDoc * @inheritDoc
*/ */
ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel = ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtPixel =
function(pixel, callback, opt_obj) { function(coordinate, frameState, callback, opt_obj) {
if (goog.isNull(this.replayGroup_)) { if (goog.isNull(this.replayGroup_)) {
return undefined; return undefined;
} else { } else {
goog.asserts.assert(!ol.extent.isEmpty(this.frameStateExtent_)); var extent = frameState.extent;
goog.asserts.assert(!isNaN(this.renderedResolution_)); var resolution = frameState.view2DState.resolution;
goog.asserts.assert(!isNaN(this.renderedRotation_)); var rotation = frameState.view2DState.rotation;
var coordinate = this.getMap().getCoordinateFromPixel(pixel);
var layer = this.getLayer(); var layer = this.getLayer();
var renderGeometryFunction = this.getRenderGeometryFunction_(); var renderGeometryFunction = this.getRenderGeometryFunction_();
goog.asserts.assert(goog.isFunction(renderGeometryFunction)); goog.asserts.assert(goog.isFunction(renderGeometryFunction));
return this.replayGroup_.forEachGeometryAtCoordinate(this.frameStateExtent_, return this.replayGroup_.forEachGeometryAtPixel(extent, resolution,
this.renderedResolution_, this.renderedRotation_, coordinate, rotation, coordinate, renderGeometryFunction,
renderGeometryFunction,
/** /**
* @param {ol.geom.Geometry} geometry Geometry. * @param {ol.geom.Geometry} geometry Geometry.
* @param {Object} data Data. * @param {Object} data Data.
* @return {?} Callback result.
*/ */
function(geometry, data) { function(geometry, data) {
var feature = /** @type {ol.Feature} */ (data); var feature = /** @type {ol.Feature} */ (data);
@@ -183,8 +170,6 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
var frameStateResolution = frameState.view2DState.resolution; var frameStateResolution = frameState.view2DState.resolution;
var pixelRatio = frameState.devicePixelRatio; var pixelRatio = frameState.devicePixelRatio;
this.frameStateExtent_ = frameStateExtent;
if (!this.dirty_ && if (!this.dirty_ &&
this.renderedResolution_ == frameStateResolution && this.renderedResolution_ == frameStateResolution &&
this.renderedRevision_ == vectorSource.getRevision() && this.renderedRevision_ == vectorSource.getRevision() &&
@@ -225,7 +210,6 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
this.renderedResolution_ = frameStateResolution; this.renderedResolution_ = frameStateResolution;
this.renderedRevision_ = vectorSource.getRevision(); this.renderedRevision_ = vectorSource.getRevision();
this.renderedRotation_ = frameState.view2DState.rotation;
if (!replayGroup.isEmpty()) { if (!replayGroup.isEmpty()) {
this.replayGroup_ = replayGroup; this.replayGroup_ = replayGroup;
} }
@@ -46,6 +46,28 @@ ol.renderer.dom.ImageLayer = function(mapRenderer, imageLayer) {
goog.inherits(ol.renderer.dom.ImageLayer, ol.renderer.dom.Layer); 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 * @inheritDoc
*/ */
+2 -1
View File
@@ -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 * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature
* callback. * callback.
* @param {S=} opt_obj Scope. * @param {S=} opt_obj Scope.
+6 -4
View File
@@ -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 * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature
* callback. * callback.
* @param {S=} opt_obj Scope for feature callback. * @param {S=} opt_obj Scope for feature callback.
@@ -92,7 +93,8 @@ ol.renderer.Map.prototype.disposeInternal = function() {
* @template S,T,U * @template S,T,U
*/ */
ol.renderer.Map.prototype.forEachFeatureAtPixel = 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) ? var layerFunction = goog.isDef(opt_layerFunction) ?
opt_layerFunction : goog.functions.TRUE; opt_layerFunction : goog.functions.TRUE;
var layersArray = this.map_.getLayerGroup().getLayersArray(); var layersArray = this.map_.getLayerGroup().getLayersArray();
@@ -101,8 +103,8 @@ ol.renderer.Map.prototype.forEachFeatureAtPixel =
var layer = layersArray[i]; var layer = layersArray[i];
if (layer.getVisible() && layerFunction.call(opt_obj2, layer)) { if (layer.getVisible() && layerFunction.call(opt_obj2, layer)) {
var layerRenderer = this.getLayerRenderer(layer); var layerRenderer = this.getLayerRenderer(layer);
var result = var result = layerRenderer.forEachFeatureAtPixel(
layerRenderer.forEachFeatureAtPixel(pixel, callback, opt_obj); coordinate, frameState, callback, opt_obj);
if (result) { if (result) {
return result; return result;
} }
@@ -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 * @inheritDoc
*/ */
+31
View File
@@ -73,6 +73,12 @@ ol.source.ImageVector = function(options) {
*/ */
this.canvasSize_ = [0, 0]; this.canvasSize_ = [0, 0];
/**
* @private
* @type {ol.render.canvas.ReplayGroup}
*/
this.replayGroup_ = null;
goog.base(this, { goog.base(this, {
attributions: options.attributions, attributions: options.attributions,
canvasFunction: goog.bind(this.canvasFunctionInternal_, this), canvasFunction: goog.bind(this.canvasFunctionInternal_, this),
@@ -135,10 +141,35 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ =
replayGroup.replay(this.canvasContext_, extent, pixelRatio, transform, replayGroup.replay(this.canvasContext_, extent, pixelRatio, transform,
goog.functions.TRUE); goog.functions.TRUE);
this.replayGroup_ = replayGroup;
return this.canvasElement_; 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 {ol.Coordinate} center Center.
* @param {number} resolution Resolution. * @param {number} resolution Resolution.
+13
View File
@@ -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.<ol.Attribution>} Attributions. * @return {Array.<ol.Attribution>} Attributions.
*/ */