Fix and accelerate ol.source.Vector#getClosestFeatureToCoordinate
The previous implementation contained a bug (the reduced extent was calculated around the closest point found, it should have been calculated around the coordinate being searched for). This allows a speed-up that requires only a single traversal of the R-Tree (as opposed to many traversals).
This commit is contained in:
@@ -174,54 +174,39 @@ ol.source.Vector.prototype.getClosestFeatureToCoordinate =
|
|||||||
// Find the closest feature using branch and bound. We start searching an
|
// Find the closest feature using branch and bound. We start searching an
|
||||||
// infinite extent, and find the distance from the first feature found. This
|
// infinite extent, and find the distance from the first feature found. This
|
||||||
// becomes the closest feature. We then compute a smaller extent which any
|
// becomes the closest feature. We then compute a smaller extent which any
|
||||||
// closer feature must overlap. We search again with this smaller extent,
|
// closer feature must intersect. We continue searching with this smaller
|
||||||
// trying to find a closer feature. The loop continues until no closer
|
// extent, trying to find a closer feature. Every time we find a closer
|
||||||
// feature can be found.
|
// feature, we update the extent being searched so that any even closer
|
||||||
|
// feature must intersect it. We continue until we run out of features.
|
||||||
var x = coordinate[0];
|
var x = coordinate[0];
|
||||||
var y = coordinate[1];
|
var y = coordinate[1];
|
||||||
var closestFeature = null;
|
var closestFeature = null;
|
||||||
var closestPoint = [NaN, NaN];
|
var closestPoint = [NaN, NaN];
|
||||||
var minSquaredDistance = Infinity;
|
var minSquaredDistance = Infinity;
|
||||||
var extent = [-Infinity, -Infinity, Infinity, Infinity];
|
var extent = [-Infinity, -Infinity, Infinity, Infinity];
|
||||||
/** @type {Object.<string, boolean>} */
|
this.rBush_.forEachInExtent(extent,
|
||||||
var visitedFeatures = {};
|
|
||||||
var callback =
|
|
||||||
/**
|
/**
|
||||||
* @param {ol.Feature} feature Feature.
|
* @param {ol.Feature} feature Feature.
|
||||||
* @return {ol.Feature|undefined} Closer feature.
|
|
||||||
*/
|
*/
|
||||||
function(feature) {
|
function(feature) {
|
||||||
// Make sure that we only test against each feature once. This both avoids
|
var geometry = feature.getGeometry();
|
||||||
// unnecessary calculation and prevents an infinite loop which would occur
|
goog.asserts.assert(!goog.isNull(geometry));
|
||||||
// if the coordinate is exactly half way between two features.
|
var previousMinSquaredDistance = minSquaredDistance;
|
||||||
var featureKey = goog.getUid(feature).toString();
|
minSquaredDistance = geometry.closestPointXY(
|
||||||
if (visitedFeatures.hasOwnProperty(featureKey)) {
|
x, y, closestPoint, minSquaredDistance);
|
||||||
return undefined;
|
if (minSquaredDistance < previousMinSquaredDistance) {
|
||||||
} else {
|
closestFeature = feature;
|
||||||
visitedFeatures[featureKey] = true;
|
// This is sneaky. Reduce the extent that it is currently being
|
||||||
}
|
// searched while the R-Tree traversal using this same extent object
|
||||||
var geometry = feature.getGeometry();
|
// is still in progress. This is safe because the new extent is
|
||||||
if (goog.isNull(geometry)) {
|
// strictly contained by the old extent.
|
||||||
return undefined;
|
var minDistance = Math.sqrt(minSquaredDistance);
|
||||||
}
|
extent[0] = x - minDistance;
|
||||||
var previousMinSquaredDistance = minSquaredDistance;
|
extent[1] = y - minDistance;
|
||||||
minSquaredDistance = geometry.closestPointXY(
|
extent[2] = x + minDistance;
|
||||||
x, y, closestPoint, minSquaredDistance);
|
extent[3] = y + minDistance;
|
||||||
goog.asserts.assert(minSquaredDistance <= previousMinSquaredDistance);
|
}
|
||||||
if (minSquaredDistance < previousMinSquaredDistance) {
|
});
|
||||||
closestFeature = feature;
|
|
||||||
var minDistance = Math.sqrt(minSquaredDistance);
|
|
||||||
extent[0] = closestPoint[0] - minDistance;
|
|
||||||
extent[1] = closestPoint[1] - minDistance;
|
|
||||||
extent[2] = closestPoint[0] + minDistance;
|
|
||||||
extent[3] = closestPoint[1] + minDistance;
|
|
||||||
return closestFeature;
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
while (goog.isDef(this.rBush_.forEachInExtent(extent, callback))) {
|
|
||||||
}
|
|
||||||
return closestFeature;
|
return closestFeature;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user