Merge pull request #1550 from twpayne/closest-point-interpolation

Preserve extra dimensions in closest point calculations
This commit is contained in:
Tom Payne
2014-01-20 04:10:02 -08:00
11 changed files with 151 additions and 48 deletions
+6 -3
View File
@@ -85,7 +85,6 @@ var displaySnap = function(coordinate) {
line = null; line = null;
info.innerHTML = ' '; info.innerHTML = ' ';
} else { } else {
info.innerHTML = closestFeature.get('PLT');
var geometry = closestFeature.getGeometry(); var geometry = closestFeature.getGeometry();
var closestPoint = geometry.getClosestPoint(coordinate); var closestPoint = geometry.getClosestPoint(coordinate);
if (point === null) { if (point === null) {
@@ -93,10 +92,14 @@ var displaySnap = function(coordinate) {
} else { } else {
point.setCoordinates(closestPoint); point.setCoordinates(closestPoint);
} }
var date = new Date(closestPoint[2] * 1000);
info.innerHTML =
closestFeature.get('PLT') + ' (' + date.toUTCString() + ')';
var coordinates = [coordinate, [closestPoint[0], closestPoint[1]]];
if (line === null) { if (line === null) {
line = new ol.geom.LineString([coordinate, closestPoint]); line = new ol.geom.LineString(coordinates);
} else { } else {
line.setCoordinates([coordinate, closestPoint]); line.setCoordinates(coordinates);
} }
} }
map.requestRenderFrame(); map.requestRenderFrame();
+2 -2
View File
@@ -94,7 +94,7 @@ ol.format.IGC.prototype.readFeatureFromText = function(text) {
var properties = {}; var properties = {};
var flatCoordinates = []; var flatCoordinates = [];
var year = 2000; var year = 2000;
var month = 1; var month = 0;
var day = 1; var day = 1;
var i, ii; var i, ii;
for (i = 0, ii = lines.length; i < ii; ++i) { for (i = 0, ii = lines.length; i < ii; ++i) {
@@ -134,7 +134,7 @@ ol.format.IGC.prototype.readFeatureFromText = function(text) {
m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line); m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line);
if (m) { if (m) {
day = parseInt(m[1], 10); day = parseInt(m[1], 10);
month = parseInt(m[2], 10); month = parseInt(m[2], 10) - 1;
year = 2000 + parseInt(m[3], 10); year = 2000 + parseInt(m[3], 10);
} else { } else {
m = ol.format.IGC.H_RECORD_RE_.exec(line); m = ol.format.IGC.H_RECORD_RE_.exec(line);
+11 -8
View File
@@ -39,21 +39,24 @@ ol.geom.Circle.prototype.clone = function() {
ol.geom.Circle.prototype.closestPointXY = ol.geom.Circle.prototype.closestPointXY =
function(x, y, closestPoint, minSquaredDistance) { function(x, y, closestPoint, minSquaredDistance) {
var flatCoordinates = this.flatCoordinates; var flatCoordinates = this.flatCoordinates;
var radius = flatCoordinates[this.stride] - flatCoordinates[0];
var dx = x - flatCoordinates[0]; var dx = x - flatCoordinates[0];
var dy = y - flatCoordinates[1]; var dy = y - flatCoordinates[1];
var d = Math.sqrt(dx * dx + dy * dy); var squaredDistance = dx * dx + dy * dy;
var distance = Math.max(d, 0);
var squaredDistance = distance * distance;
if (squaredDistance < minSquaredDistance) { if (squaredDistance < minSquaredDistance) {
if (d === 0) { var i;
closestPoint[0] = flatCoordinates[0]; if (squaredDistance === 0) {
closestPoint[1] = flatCoordinates[1]; for (i = 0; i < this.stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
} else { } else {
var delta = radius / d; var delta = this.getRadius() / Math.sqrt(squaredDistance);
closestPoint[0] = flatCoordinates[0] + delta * dx; closestPoint[0] = flatCoordinates[0] + delta * dx;
closestPoint[1] = flatCoordinates[1] + delta * dy; closestPoint[1] = flatCoordinates[1] + delta * dy;
for (i = 2; i < this.stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
} }
closestPoint.length = this.stride;
return squaredDistance; return squaredDistance;
} else { } else {
return minSquaredDistance; return minSquaredDistance;
+17 -13
View File
@@ -95,14 +95,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride,
if (offset == end) { if (offset == end) {
return minSquaredDistance; return minSquaredDistance;
} }
var squaredDistance; var i, squaredDistance;
if (maxDelta === 0) { if (maxDelta === 0) {
// All points are identical, so just test the first point. // All points are identical, so just test the first point.
squaredDistance = ol.geom.flat.squaredDistance( squaredDistance = ol.geom.flat.squaredDistance(
x, y, flatCoordinates[offset], flatCoordinates[offset + 1]); x, y, flatCoordinates[offset], flatCoordinates[offset + 1]);
if (squaredDistance < minSquaredDistance) { if (squaredDistance < minSquaredDistance) {
closestPoint[0] = flatCoordinates[offset]; for (i = 0; i < stride; ++i) {
closestPoint[1] = flatCoordinates[offset + 1]; closestPoint[i] = flatCoordinates[offset + i];
}
closestPoint.length = stride;
return squaredDistance; return squaredDistance;
} else { } else {
return minSquaredDistance; return minSquaredDistance;
@@ -112,15 +114,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride,
var tmpPoint = goog.isDef(opt_tmpPoint) ? opt_tmpPoint : [NaN, NaN]; var tmpPoint = goog.isDef(opt_tmpPoint) ? opt_tmpPoint : [NaN, NaN];
var index = offset + stride; var index = offset + stride;
while (index < end) { while (index < end) {
ol.geom.flat.closestPoint(x, y, ol.geom.flat.closestPoint(
flatCoordinates[index - stride], flatCoordinates[index - stride + 1], flatCoordinates, index - stride, index, stride, x, y, tmpPoint);
flatCoordinates[index], flatCoordinates[index + 1], tmpPoint);
squaredDistance = ol.geom.flat.squaredDistance( squaredDistance = ol.geom.flat.squaredDistance(
x, y, tmpPoint[0], tmpPoint[1]); x, y, tmpPoint[0], tmpPoint[1]);
if (squaredDistance < minSquaredDistance) { if (squaredDistance < minSquaredDistance) {
minSquaredDistance = squaredDistance; minSquaredDistance = squaredDistance;
closestPoint[0] = tmpPoint[0]; for (i = 0; i < stride; ++i) {
closestPoint[1] = tmpPoint[1]; closestPoint[i] = tmpPoint[i];
}
closestPoint.length = stride;
index += stride; index += stride;
} else { } else {
// Skip ahead multiple points, because we know that all the skipped // Skip ahead multiple points, because we know that all the skipped
@@ -140,15 +143,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride,
} }
if (isRing) { if (isRing) {
// Check the closing segment. // Check the closing segment.
ol.geom.flat.closestPoint(x, y, ol.geom.flat.closestPoint(
flatCoordinates[end - stride], flatCoordinates[end - stride + 1], flatCoordinates, end - stride, offset, stride, x, y, tmpPoint);
flatCoordinates[offset], flatCoordinates[offset + 1], tmpPoint);
squaredDistance = ol.geom.flat.squaredDistance( squaredDistance = ol.geom.flat.squaredDistance(
x, y, tmpPoint[0], tmpPoint[1]); x, y, tmpPoint[0], tmpPoint[1]);
if (squaredDistance < minSquaredDistance) { if (squaredDistance < minSquaredDistance) {
minSquaredDistance = squaredDistance; minSquaredDistance = squaredDistance;
closestPoint[0] = tmpPoint[0]; for (i = 0; i < stride; ++i) {
closestPoint[1] = tmpPoint[1]; closestPoint[i] = tmpPoint[i];
}
closestPoint.length = stride;
} }
} }
return minSquaredDistance; return minSquaredDistance;
+28 -17
View File
@@ -2,39 +2,50 @@ goog.provide('ol.geom.flat');
goog.require('goog.array'); goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.math');
goog.require('goog.vec.Mat4'); goog.require('goog.vec.Mat4');
/** /**
* Returns the point on the line segment (x1, y1) to (x2, y2) that is closest to * Returns the point on the 2D line segment flatCoordinates[offset1] to
* the point (x, y). * flatCoordinates[offset2] that is closest to the point (x, y). Extra
* dimensions are linearly interpolated.
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset1 Offset 1.
* @param {number} offset2 Offset 2.
* @param {number} stride Stride.
* @param {number} x X. * @param {number} x X.
* @param {number} y Y. * @param {number} y Y.
* @param {number} x1 X1.
* @param {number} y1 Y1.
* @param {number} x2 X2.
* @param {number} y2 Y2.
* @param {Array.<number>} closestPoint Closest point. * @param {Array.<number>} closestPoint Closest point.
*/ */
ol.geom.flat.closestPoint = function(x, y, x1, y1, x2, y2, closestPoint) { ol.geom.flat.closestPoint =
var dx = x2 - x1; function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) {
var dy = y2 - y1; var x1 = flatCoordinates[offset1];
var y1 = flatCoordinates[offset1 + 1];
var dx = flatCoordinates[offset2] - x1;
var dy = flatCoordinates[offset2 + 1] - y1;
var i, offset;
if (dx === 0 && dy === 0) { if (dx === 0 && dy === 0) {
closestPoint[0] = x1; offset = offset1;
closestPoint[1] = y1;
} else { } else {
var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
if (t > 1) { if (t > 1) {
closestPoint[0] = x2; offset = offset2;
closestPoint[1] = y2;
} else if (t > 0) { } else if (t > 0) {
closestPoint[0] = x1 + dx * t; for (i = 0; i < stride; ++i) {
closestPoint[1] = y1 + dy * t; closestPoint[i] = goog.math.lerp(flatCoordinates[offset1 + i],
flatCoordinates[offset2 + i], t);
}
closestPoint.length = stride;
return;
} else { } else {
closestPoint[0] = x1; offset = offset1;
closestPoint[1] = y1;
} }
} }
for (i = 0; i < stride; ++i) {
closestPoint[i] = flatCoordinates[offset + i];
}
closestPoint.length = stride;
}; };
+5 -3
View File
@@ -42,14 +42,16 @@ ol.geom.MultiPoint.prototype.closestPointXY =
} }
var flatCoordinates = this.flatCoordinates; var flatCoordinates = this.flatCoordinates;
var stride = this.stride; var stride = this.stride;
var i, ii; var i, ii, j;
for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
var squaredDistance = ol.geom.flat.squaredDistance( var squaredDistance = ol.geom.flat.squaredDistance(
x, y, flatCoordinates[i], flatCoordinates[i + 1]); x, y, flatCoordinates[i], flatCoordinates[i + 1]);
if (squaredDistance < minSquaredDistance) { if (squaredDistance < minSquaredDistance) {
minSquaredDistance = squaredDistance; minSquaredDistance = squaredDistance;
closestPoint[0] = flatCoordinates[i]; for (j = 0; j < stride; ++j) {
closestPoint[1] = flatCoordinates[i + 1]; closestPoint[j] = flatCoordinates[i + j];
}
closestPoint.length = stride;
} }
} }
return minSquaredDistance; return minSquaredDistance;
+6 -2
View File
@@ -40,8 +40,12 @@ ol.geom.Point.prototype.closestPointXY =
var squaredDistance = ol.geom.flat.squaredDistance( var squaredDistance = ol.geom.flat.squaredDistance(
x, y, flatCoordinates[0], flatCoordinates[1]); x, y, flatCoordinates[0], flatCoordinates[1]);
if (squaredDistance < minSquaredDistance) { if (squaredDistance < minSquaredDistance) {
closestPoint[0] = flatCoordinates[0]; var stride = this.stride;
closestPoint[1] = flatCoordinates[1]; var i;
for (i = 0; i < stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
closestPoint.length = stride;
return squaredDistance; return squaredDistance;
} else { } else {
return minSquaredDistance; return minSquaredDistance;
+31
View File
@@ -89,6 +89,37 @@ describe('ol.geom.Circle', function() {
expect(closestPoint[1]).to.roughlyEqual(-Math.sqrt(0.5), 1e-15); expect(closestPoint[1]).to.roughlyEqual(-Math.sqrt(0.5), 1e-15);
}); });
it('maintains Z coordinates', function() {
var circle = new ol.geom.Circle([0, 0, 1], 1);
expect(circle.getLayout()).to.be(ol.geom.GeometryLayout.XYZ);
var closestPoint = circle.getClosestPoint([2, 0]);
expect(closestPoint).to.have.length(3);
expect(closestPoint[0]).to.roughlyEqual(1, 1e-15);
expect(closestPoint[1]).to.roughlyEqual(0, 1e-15);
expect(closestPoint[2]).to.be(1);
});
it('maintains M coordinates', function() {
var circle = new ol.geom.Circle([0, 0, 2], 1,
ol.geom.GeometryLayout.XYM);
var closestPoint = circle.getClosestPoint([2, 0]);
expect(closestPoint).to.have.length(3);
expect(closestPoint[0]).to.roughlyEqual(1, 1e-15);
expect(closestPoint[1]).to.roughlyEqual(0, 1e-15);
expect(closestPoint[2]).to.be(2);
});
it('maintains Z and M coordinates', function() {
var circle = new ol.geom.Circle([0, 0, 1, 2], 1);
expect(circle.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
var closestPoint = circle.getClosestPoint([2, 0]);
expect(closestPoint).to.have.length(4);
expect(closestPoint[0]).to.roughlyEqual(1, 1e-15);
expect(closestPoint[1]).to.roughlyEqual(0, 1e-15);
expect(closestPoint[2]).to.be(1);
expect(closestPoint[3]).to.be(2);
});
}); });
describe('#getExtent', function() { describe('#getExtent', function() {
+27
View File
@@ -113,6 +113,33 @@ describe('ol.geom.closest', function() {
}); });
describe('with multi-dimensional data', function() {
var flatCoordinates = [0, 0, 10, -10, 2, 2, 30, -20];
var stride = 4;
describe('ol.geom.closest.getClosestPoint', function() {
it('interpolates M coordinates', function() {
var maxDelta = Math.sqrt(ol.geom.closest.getMaxSquaredDelta(
flatCoordinates, 0, flatCoordinates.length, stride, 0));
expect(maxDelta).to.roughlyEqual(Math.sqrt(8), 1e-9);
var closestPoint = [NaN, NaN];
expect(ol.geom.closest.getClosestPoint(
flatCoordinates, 0, flatCoordinates.length, stride,
maxDelta, false, 1, 1, closestPoint, Infinity)).
to.roughlyEqual(0, 1e-9);
expect(closestPoint).to.have.length(stride);
expect(closestPoint[0]).to.be(1);
expect(closestPoint[1]).to.be(1);
expect(closestPoint[2]).to.be(20);
expect(closestPoint[3]).to.be(-15);
});
});
});
}); });
+9
View File
@@ -154,6 +154,15 @@ describe('ol.geom.MultiPoint', function() {
expect(multiPoint.getStride()).to.be(4); expect(multiPoint.getStride()).to.be(4);
}); });
describe('#getClosestPoint', function() {
it('preserves extra dimensions', function() {
var closestPoint = multiPoint.getClosestPoint([6, 6]);
expect(closestPoint).to.eql([5, 6, 7, 8]);
});
});
}); });
}); });
+9
View File
@@ -95,6 +95,15 @@ describe('ol.geom.Point', function() {
expect(point.getStride()).to.be(4); expect(point.getStride()).to.be(4);
}); });
describe('#getClosestPoint', function() {
it('preseves extra dimensions', function() {
var closestPoint = point.getClosestPoint([0, 0]);
expect(closestPoint).to.eql([1, 2, 3, 4]);
});
});
}); });
}); });