Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[WIP] Post compose hit detection #1765

Closed
wants to merge 4 commits into from

2 participants

Éric Lemoine Tom Payne
Éric Lemoine
Owner

This PR addresses #1591. This is still work in progress.

Tom Payne

@elemoine, this all looks very sensible to me. Are there are areas that you think need more work?

Éric Lemoine
Owner

Are there are areas that you think need more work?

I do not see any right now. I just need to clean up the commit history, and possibly add a few tests where it makes sense. Thanks for taking a look at it.

Éric Lemoine
Owner
Éric Lemoine
Owner

I don't like this patch. I'd like to think more about the problem and discuss it with others. Closing.

Éric Lemoine elemoine closed this
Éric Lemoine elemoine deleted the branch
Éric Lemoine elemoine restored the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
78 src/ol/render/canvas/canvasimmediate.js
View
@@ -34,9 +34,10 @@ ol.render.canvas.Immediate =
/**
* @private
* @type {Object.<string,
- * Array.<function(ol.render.canvas.Immediate)>>}
+ * Array.<{feature: ol.Feature,
+ * callback: function(ol.render.canvas.Immediate)}>>}
*/
- this.callbacksByZIndex_ = {};
+ this.callbackObjectsByZIndex_ = {};
/**
* @private
@@ -218,6 +219,12 @@ ol.render.canvas.Immediate =
*/
this.tmpLocalTransform_ = goog.vec.Mat4.createNumber();
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.hitDetectionMode_ = false;
+
};
@@ -382,13 +389,16 @@ ol.render.canvas.Immediate.prototype.drawRings_ =
* @param {function(ol.render.canvas.Immediate)} callback Callback.
* @todo api
*/
-ol.render.canvas.Immediate.prototype.drawAsync = function(zIndex, callback) {
+ol.render.canvas.Immediate.prototype.drawAsync =
+ function(zIndex, callback, opt_feature) {
+ var feature = goog.isDef(opt_feature) ? opt_feature : null;
var zIndexKey = zIndex.toString();
- var callbacks = this.callbacksByZIndex_[zIndexKey];
- if (goog.isDef(callbacks)) {
- callbacks.push(callback);
+ var callbackObjects = this.callbackObjectsByZIndex_[zIndexKey];
+ if (goog.isDef(callbackObjects)) {
+ callbackObjects.push({callback: callback, feature: feature});
} else {
- this.callbacksByZIndex_[zIndexKey] = [callback];
+ this.callbackObjectsByZIndex_[zIndexKey] =
+ [{callback: callback, feature: feature}];
}
};
@@ -464,7 +474,7 @@ ol.render.canvas.Immediate.prototype.drawFeature = function(feature, style) {
ol.render.canvas.Immediate.GEOMETRY_RENDERES_[geometry.getType()];
goog.asserts.assert(goog.isDef(renderGeometry));
renderGeometry.call(render, geometry, null);
- });
+ }, feature);
};
@@ -694,15 +704,46 @@ ol.render.canvas.Immediate.prototype.drawText = goog.abstractMethod;
*/
ol.render.canvas.Immediate.prototype.flush = function() {
/** @type {Array.<number>} */
- var zs = goog.array.map(goog.object.getKeys(this.callbacksByZIndex_), Number);
+ var zs = goog.array.map(
+ goog.object.getKeys(this.callbackObjectsByZIndex_), Number);
goog.array.sort(zs);
- var i, ii, callbacks, j, jj;
+ var i, ii, callbackObjects, j, jj;
+ for (i = 0, ii = zs.length; i < ii; ++i) {
+ callbackObjects = this.callbackObjectsByZIndex_[zs[i].toString()];
+ for (j = 0, jj = callbackObjects.length; j < jj; ++j) {
+ callbackObjects[j].callback(this);
+ }
+ }
+};
+
+
+/**
+ * @param {function(ol.Feature): T} callback Feature callback.
+ * @return {T} Callback result.
+ * @template T
+ */
+ol.render.canvas.Immediate.prototype.flushHitDetection = function(callback) {
+ this.hitDetectionMode_ = true;
+ /** @type {Array.<number>} */
+ var zs = goog.array.map(
+ goog.object.getKeys(this.callbackObjectsByZIndex_), Number);
+ goog.array.sort(zs, function(a, b) { return b - a; });
+ var i, ii, callbackObject, callbackObjects, feature, j, result;
for (i = 0, ii = zs.length; i < ii; ++i) {
- callbacks = this.callbacksByZIndex_[zs[i].toString()];
- for (j = 0, jj = callbacks.length; j < jj; ++j) {
- callbacks[j](this);
+ callbackObjects = this.callbackObjectsByZIndex_[zs[i].toString()];
+ for (j = callbackObjects.length - 1; j >= 0; --j) {
+ callbackObject = callbackObjects[j];
+ feature = callbackObject.feature;
+ if (!goog.isNull(feature)) {
+ callbackObject.callback(this);
+ result = callback(feature);
+ if (result) {
+ return result;
+ }
+ }
}
}
+ return undefined;
};
@@ -823,7 +864,13 @@ ol.render.canvas.Immediate.prototype.setContextTextState_ =
ol.render.canvas.Immediate.prototype.setFillStrokeStyle =
function(fillStyle, strokeStyle) {
if (goog.isNull(fillStyle)) {
- this.fillState_ = null;
+ if (this.hitDetectionMode_) {
+ this.fillState_ = {
+ fillStyle: ol.color.asString(ol.render.canvas.defaultFillStyle)
+ };
+ } else {
+ this.fillState_ = null;
+ }
} else {
var fillStyleColor = fillStyle.getColor();
this.fillState_ = {
@@ -871,7 +918,8 @@ ol.render.canvas.Immediate.prototype.setImageStyle = function(imageStyle) {
} else {
var imageAnchor = imageStyle.getAnchor();
// FIXME pixel ratio
- var imageImage = imageStyle.getImage(1);
+ var imageImage = this.hitDetectionMode_ ?
+ imageStyle.getHitDetectionImage(1) : imageStyle.getImage(1);
var imageOpacity = imageStyle.getOpacity();
var imageRotateWithView = imageStyle.getRotateWithView();
var imageRotation = imageStyle.getRotation();
4 src/ol/render/ivectorcontext.js
View
@@ -16,8 +16,10 @@ ol.render.IVectorContext = function() {
/**
* @param {number} zIndex Z index.
* @param {function(ol.render.canvas.Immediate)} callback Callback.
+ * @param {ol.Feature=} opt_feature Feature.
*/
-ol.render.IVectorContext.prototype.drawAsync = function(zIndex, callback) {
+ol.render.IVectorContext.prototype.drawAsync =
+ function(zIndex, callback, opt_feature) {
};
87 src/ol/renderer/canvas/canvasmaprenderer.js
View
@@ -63,6 +63,27 @@ ol.renderer.canvas.Map = function(container, map) {
*/
this.transform_ = goog.vec.Mat4.createNumber();
+ /**
+ * @type {HTMLCanvasElement}
+ */
+ var hitDetectionCanvas = /** @type {HTMLCanvasElement} */
+ (goog.dom.createElement(goog.dom.TagName.CANVAS));
+ hitDetectionCanvas.width = 1;
+ hitDetectionCanvas.height = 1;
+
+ /**
+ * @private
+ * @type {CanvasRenderingContext2D}
+ */
+ this.hitDetectionContext_ = /** @type {CanvasRenderingContext2D} */
+ (hitDetectionCanvas.getContext('2d'));
+
+ /**
+ * @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.hitDetectionTransform_ = goog.vec.Mat4.createNumber();
+
};
goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map);
@@ -114,6 +135,72 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ =
/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @param {ol.FrameState} frameState Frame state.
+ * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Callback.
+ * @param {S} thisArg Value to use as `this` when executing `callback`.
+ * @return {T} Callback result.
+ * @template S, T
+ * @private
+ */
+ol.renderer.canvas.Map.prototype.postComposeHitDetection_ =
+ function(coordinate, frameState, callback, thisArg) {
+ var map = this.getMap();
+ if (map.hasListener(ol.render.EventType.POSTCOMPOSE)) {
+ var context = this.hitDetectionContext_;
+ var view2DState = frameState.view2DState;
+ var pixelRatio = frameState.pixelRatio;
+ var transform = this.hitDetectionTransform_;
+ ol.vec.Mat4.makeTransform2D(transform,
+ 0.5, 0.5,
+ pixelRatio / view2DState.resolution,
+ -pixelRatio / view2DState.resolution,
+ -view2DState.rotation,
+ -coordinate[0], -coordinate[1]);
+ var render = new ol.render.canvas.Immediate(context, pixelRatio,
+ frameState.extent, transform, view2DState.rotation);
+ var composeEvent = new ol.render.Event(ol.render.EventType.POSTCOMPOSE,
+ map, render, frameState, context, null);
+ map.dispatchEvent(composeEvent);
+ context.clearRect(0, 0, 1, 1);
+ return render.flushHitDetection(
+ /**
+ * @param {ol.Feature} feature Feature.
+ * @return {T} Callback result.
+ * @template T
+ */
+ function(feature) {
+ var imageData = context.getImageData(0, 0, 1, 1).data;
+ if (imageData[3] > 0) {
+ var result = callback.call(thisArg, feature, null);
+ if (result) {
+ return result;
+ }
+ context.clearRect(0, 0, 1, 1);
+ }
+ });
+ }
+ return undefined;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.Map.prototype.forEachFeatureAtPixel =
+ function(coordinate, frameState, callback, thisArg,
+ layerFilter, thisArg2) {
+ var result = this.postComposeHitDetection_(
+ coordinate, frameState, callback, thisArg);
+ if (result) {
+ return result;
+ }
+ return goog.base(this, 'forEachFeatureAtPixel', coordinate, frameState,
+ callback, thisArg, layerFilter, thisArg2);
+};
+
+
+/**
* @param {ol.layer.Layer} layer Layer.
* @return {ol.renderer.canvas.Layer} Canvas layer renderer.
*/
Something went wrong with that request. Please try again.