Optimize hit detection by rendering features in a limited extent

This commit is contained in:
Tim Schaub
2014-12-20 12:49:17 -07:00
parent 3be6a84de6
commit 1ee03625e9
3 changed files with 39 additions and 12 deletions

View File

@@ -210,12 +210,14 @@ ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) {
* @param {Object} skippedFeaturesHash Ids of features to skip. * @param {Object} skippedFeaturesHash Ids of features to skip.
* @param {Array.<*>} instructions Instructions array. * @param {Array.<*>} instructions Instructions array.
* @param {function(ol.Feature): T|undefined} featureCallback Feature callback. * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
* @param {ol.Extent=} opt_hitExtent Only check features that intersect this
* extent.
* @return {T|undefined} Callback result. * @return {T|undefined} Callback result.
* @template T * @template T
*/ */
ol.render.canvas.Replay.prototype.replay_ = function( ol.render.canvas.Replay.prototype.replay_ = function(
context, pixelRatio, transform, viewRotation, skippedFeaturesHash, context, pixelRatio, transform, viewRotation, skippedFeaturesHash,
instructions, featureCallback) { instructions, featureCallback, opt_hitExtent) {
/** @type {Array.<number>} */ /** @type {Array.<number>} */
var pixelCoordinates; var pixelCoordinates;
if (ol.vec.Mat4.equals2D(transform, this.renderedTransform_)) { if (ol.vec.Mat4.equals2D(transform, this.renderedTransform_)) {
@@ -240,10 +242,13 @@ ol.render.canvas.Replay.prototype.replay_ = function(
case ol.render.canvas.Instruction.BEGIN_GEOMETRY: case ol.render.canvas.Instruction.BEGIN_GEOMETRY:
feature = /** @type {ol.Feature} */ (instruction[1]); feature = /** @type {ol.Feature} */ (instruction[1]);
var featureUid = goog.getUid(feature).toString(); var featureUid = goog.getUid(feature).toString();
if (!goog.isDef(skippedFeaturesHash[featureUid])) { if (goog.isDef(skippedFeaturesHash[featureUid])) {
++i;
} else {
i = /** @type {number} */ (instruction[2]); i = /** @type {number} */ (instruction[2]);
} else if (goog.isDef(opt_hitExtent) && !ol.extent.intersects(
opt_hitExtent, feature.getGeometry().getExtent())) {
i = /** @type {number} */ (instruction[2]);
} else {
++i;
} }
break; break;
case ol.render.canvas.Instruction.BEGIN_PATH: case ol.render.canvas.Instruction.BEGIN_PATH:
@@ -467,15 +472,17 @@ ol.render.canvas.Replay.prototype.replay = function(
* @param {number} viewRotation View rotation. * @param {number} viewRotation View rotation.
* @param {Object} skippedFeaturesHash Ids of features to skip * @param {Object} skippedFeaturesHash Ids of features to skip
* @param {function(ol.Feature): T=} opt_featureCallback Feature callback. * @param {function(ol.Feature): T=} opt_featureCallback Feature callback.
* @param {ol.Extent=} opt_hitExtent Only check features that intersect this
* extent.
* @return {T|undefined} Callback result. * @return {T|undefined} Callback result.
* @template T * @template T
*/ */
ol.render.canvas.Replay.prototype.replayHitDetection = function( ol.render.canvas.Replay.prototype.replayHitDetection = function(
context, transform, viewRotation, skippedFeaturesHash, context, transform, viewRotation, skippedFeaturesHash,
opt_featureCallback) { opt_featureCallback, opt_hitExtent) {
var instructions = this.hitDetectionInstructions; var instructions = this.hitDetectionInstructions;
return this.replay_(context, 1, transform, viewRotation, return this.replay_(context, 1, transform, viewRotation,
skippedFeaturesHash, instructions, opt_featureCallback); skippedFeaturesHash, instructions, opt_featureCallback, opt_hitExtent);
}; };
@@ -1783,9 +1790,11 @@ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) {
* @param {number} tolerance Tolerance. * @param {number} tolerance Tolerance.
* @param {ol.Extent} maxExtent Max extent. * @param {ol.Extent} maxExtent Max extent.
* @param {number} resolution Resolution. * @param {number} resolution Resolution.
* @param {number=} opt_renderBuffer Optional rendering buffer.
* @struct * @struct
*/ */
ol.render.canvas.ReplayGroup = function(tolerance, maxExtent, resolution) { ol.render.canvas.ReplayGroup = function(
tolerance, maxExtent, resolution, opt_renderBuffer) {
/** /**
* @private * @private
@@ -1805,6 +1814,12 @@ ol.render.canvas.ReplayGroup = function(tolerance, maxExtent, resolution) {
*/ */
this.resolution_ = resolution; this.resolution_ = resolution;
/**
* @private
* @type {number|undefined}
*/
this.renderBuffer_ = opt_renderBuffer;
/** /**
* @private * @private
* @type {Object.<string, * @type {Object.<string,
@@ -1862,6 +1877,16 @@ ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function(
var context = this.hitDetectionContext_; var context = this.hitDetectionContext_;
context.clearRect(0, 0, 1, 1); context.clearRect(0, 0, 1, 1);
/**
* @type {ol.Extent}
*/
var hitExtent;
if (goog.isDef(this.renderBuffer_)) {
hitExtent = ol.extent.createEmpty();
ol.extent.extendCoordinate(hitExtent, coordinate);
ol.extent.buffer(hitExtent, resolution * this.renderBuffer_, hitExtent);
}
return this.replayHitDetection_(context, transform, rotation, return this.replayHitDetection_(context, transform, rotation,
skippedFeaturesHash, skippedFeaturesHash,
/** /**
@@ -1877,7 +1902,7 @@ ol.render.canvas.ReplayGroup.prototype.forEachGeometryAtPixel = function(
} }
context.clearRect(0, 0, 1, 1); context.clearRect(0, 0, 1, 1);
} }
}); }, hitExtent);
}; };
@@ -1966,12 +1991,14 @@ ol.render.canvas.ReplayGroup.prototype.replay = function(
* @param {number} viewRotation View rotation. * @param {number} viewRotation View rotation.
* @param {Object} skippedFeaturesHash Ids of features to skip * @param {Object} skippedFeaturesHash Ids of features to skip
* @param {function(ol.Feature): T} featureCallback Feature callback. * @param {function(ol.Feature): T} featureCallback Feature callback.
* @param {ol.Extent=} opt_hitExtent Only check features that intersect this
* extent.
* @return {T|undefined} Callback result. * @return {T|undefined} Callback result.
* @template T * @template T
*/ */
ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function(
context, transform, viewRotation, skippedFeaturesHash, context, transform, viewRotation, skippedFeaturesHash,
featureCallback) { featureCallback, opt_hitExtent) {
/** @type {Array.<number>} */ /** @type {Array.<number>} */
var zs = goog.array.map(goog.object.getKeys(this.replaysByZIndex_), Number); var zs = goog.array.map(goog.object.getKeys(this.replaysByZIndex_), Number);
goog.array.sort(zs, function(a, b) { return b - a; }); goog.array.sort(zs, function(a, b) { return b - a; });
@@ -1983,7 +2010,7 @@ ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function(
replay = replays[ol.render.REPLAY_ORDER[j]]; replay = replays[ol.render.REPLAY_ORDER[j]];
if (goog.isDef(replay)) { if (goog.isDef(replay)) {
result = replay.replayHitDetection(context, transform, viewRotation, result = replay.replayHitDetection(context, transform, viewRotation,
skippedFeaturesHash, featureCallback); skippedFeaturesHash, featureCallback, opt_hitExtent);
if (result) { if (result) {
return result; return result;
} }

View File

@@ -207,7 +207,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
var replayGroup = var replayGroup =
new ol.render.canvas.ReplayGroup( new ol.render.canvas.ReplayGroup(
ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
resolution); resolution, vectorLayer.getRenderBuffer());
vectorSource.loadFeatures(extent, resolution, projection); vectorSource.loadFeatures(extent, resolution, projection);
var renderFeature = var renderFeature =
/** /**

View File

@@ -247,7 +247,7 @@ ol.renderer.dom.VectorLayer.prototype.prepareFrame =
var replayGroup = var replayGroup =
new ol.render.canvas.ReplayGroup( new ol.render.canvas.ReplayGroup(
ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
resolution); resolution, vectorLayer.getRenderBuffer());
vectorSource.loadFeatures(extent, resolution, projection); vectorSource.loadFeatures(extent, resolution, projection);
var renderFeature = var renderFeature =
/** /**