diff --git a/examples/igc.js b/examples/igc.js index 0c7a65e076..7811f693ef 100644 --- a/examples/igc.js +++ b/examples/igc.js @@ -85,7 +85,6 @@ var displaySnap = function(coordinate) { line = null; info.innerHTML = ' '; } else { - info.innerHTML = closestFeature.get('PLT'); var geometry = closestFeature.getGeometry(); var closestPoint = geometry.getClosestPoint(coordinate); if (point === null) { @@ -93,10 +92,14 @@ var displaySnap = function(coordinate) { } else { 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) { - line = new ol.geom.LineString([coordinate, closestPoint]); + line = new ol.geom.LineString(coordinates); } else { - line.setCoordinates([coordinate, closestPoint]); + line.setCoordinates(coordinates); } } map.requestRenderFrame(); diff --git a/src/ol/format/igcformat.js b/src/ol/format/igcformat.js index 2100a10b33..ea4005822e 100644 --- a/src/ol/format/igcformat.js +++ b/src/ol/format/igcformat.js @@ -94,7 +94,7 @@ ol.format.IGC.prototype.readFeatureFromText = function(text) { var properties = {}; var flatCoordinates = []; var year = 2000; - var month = 1; + var month = 0; var day = 1; var i, ii; 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); if (m) { day = parseInt(m[1], 10); - month = parseInt(m[2], 10); + month = parseInt(m[2], 10) - 1; year = 2000 + parseInt(m[3], 10); } else { m = ol.format.IGC.H_RECORD_RE_.exec(line); diff --git a/src/ol/geom/circle.js b/src/ol/geom/circle.js index 00ab10d11e..fffb4399c0 100644 --- a/src/ol/geom/circle.js +++ b/src/ol/geom/circle.js @@ -39,21 +39,24 @@ ol.geom.Circle.prototype.clone = function() { ol.geom.Circle.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { var flatCoordinates = this.flatCoordinates; - var radius = flatCoordinates[this.stride] - flatCoordinates[0]; var dx = x - flatCoordinates[0]; var dy = y - flatCoordinates[1]; - var d = Math.sqrt(dx * dx + dy * dy); - var distance = Math.max(d, 0); - var squaredDistance = distance * distance; + var squaredDistance = dx * dx + dy * dy; if (squaredDistance < minSquaredDistance) { - if (d === 0) { - closestPoint[0] = flatCoordinates[0]; - closestPoint[1] = flatCoordinates[1]; + var i; + if (squaredDistance === 0) { + for (i = 0; i < this.stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } } else { - var delta = radius / d; + var delta = this.getRadius() / Math.sqrt(squaredDistance); closestPoint[0] = flatCoordinates[0] + delta * dx; closestPoint[1] = flatCoordinates[1] + delta * dy; + for (i = 2; i < this.stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } } + closestPoint.length = this.stride; return squaredDistance; } else { return minSquaredDistance; diff --git a/src/ol/geom/closestgeom.js b/src/ol/geom/closestgeom.js index 5fa7712359..0084a0d6f6 100644 --- a/src/ol/geom/closestgeom.js +++ b/src/ol/geom/closestgeom.js @@ -95,14 +95,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride, if (offset == end) { return minSquaredDistance; } - var squaredDistance; + var i, squaredDistance; if (maxDelta === 0) { // All points are identical, so just test the first point. squaredDistance = ol.geom.flat.squaredDistance( x, y, flatCoordinates[offset], flatCoordinates[offset + 1]); if (squaredDistance < minSquaredDistance) { - closestPoint[0] = flatCoordinates[offset]; - closestPoint[1] = flatCoordinates[offset + 1]; + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[offset + i]; + } + closestPoint.length = stride; return squaredDistance; } else { 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 index = offset + stride; while (index < end) { - ol.geom.flat.closestPoint(x, y, - flatCoordinates[index - stride], flatCoordinates[index - stride + 1], - flatCoordinates[index], flatCoordinates[index + 1], tmpPoint); + ol.geom.flat.closestPoint( + flatCoordinates, index - stride, index, stride, x, y, tmpPoint); squaredDistance = ol.geom.flat.squaredDistance( x, y, tmpPoint[0], tmpPoint[1]); if (squaredDistance < minSquaredDistance) { minSquaredDistance = squaredDistance; - closestPoint[0] = tmpPoint[0]; - closestPoint[1] = tmpPoint[1]; + for (i = 0; i < stride; ++i) { + closestPoint[i] = tmpPoint[i]; + } + closestPoint.length = stride; index += stride; } else { // 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) { // Check the closing segment. - ol.geom.flat.closestPoint(x, y, - flatCoordinates[end - stride], flatCoordinates[end - stride + 1], - flatCoordinates[offset], flatCoordinates[offset + 1], tmpPoint); + ol.geom.flat.closestPoint( + flatCoordinates, end - stride, offset, stride, x, y, tmpPoint); squaredDistance = ol.geom.flat.squaredDistance( x, y, tmpPoint[0], tmpPoint[1]); if (squaredDistance < minSquaredDistance) { minSquaredDistance = squaredDistance; - closestPoint[0] = tmpPoint[0]; - closestPoint[1] = tmpPoint[1]; + for (i = 0; i < stride; ++i) { + closestPoint[i] = tmpPoint[i]; + } + closestPoint.length = stride; } } return minSquaredDistance; diff --git a/src/ol/geom/flatgeom.js b/src/ol/geom/flatgeom.js index d6ba281505..88635b9100 100644 --- a/src/ol/geom/flatgeom.js +++ b/src/ol/geom/flatgeom.js @@ -2,39 +2,50 @@ goog.provide('ol.geom.flat'); goog.require('goog.array'); goog.require('goog.asserts'); +goog.require('goog.math'); goog.require('goog.vec.Mat4'); /** - * Returns the point on the line segment (x1, y1) to (x2, y2) that is closest to - * the point (x, y). + * Returns the point on the 2D line segment flatCoordinates[offset1] to + * flatCoordinates[offset2] that is closest to the point (x, y). Extra + * dimensions are linearly interpolated. + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset1 Offset 1. + * @param {number} offset2 Offset 2. + * @param {number} stride Stride. * @param {number} x X. * @param {number} y Y. - * @param {number} x1 X1. - * @param {number} y1 Y1. - * @param {number} x2 X2. - * @param {number} y2 Y2. * @param {Array.} closestPoint Closest point. */ -ol.geom.flat.closestPoint = function(x, y, x1, y1, x2, y2, closestPoint) { - var dx = x2 - x1; - var dy = y2 - y1; +ol.geom.flat.closestPoint = + function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) { + 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) { - closestPoint[0] = x1; - closestPoint[1] = y1; + offset = offset1; } else { var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); if (t > 1) { - closestPoint[0] = x2; - closestPoint[1] = y2; + offset = offset2; } else if (t > 0) { - closestPoint[0] = x1 + dx * t; - closestPoint[1] = y1 + dy * t; + for (i = 0; i < stride; ++i) { + closestPoint[i] = goog.math.lerp(flatCoordinates[offset1 + i], + flatCoordinates[offset2 + i], t); + } + closestPoint.length = stride; + return; } else { - closestPoint[0] = x1; - closestPoint[1] = y1; + offset = offset1; } } + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[offset + i]; + } + closestPoint.length = stride; }; diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js index 1cc711b710..142c6f4c92 100644 --- a/src/ol/geom/multipoint.js +++ b/src/ol/geom/multipoint.js @@ -42,14 +42,16 @@ ol.geom.MultiPoint.prototype.closestPointXY = } var flatCoordinates = this.flatCoordinates; var stride = this.stride; - var i, ii; + var i, ii, j; for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { var squaredDistance = ol.geom.flat.squaredDistance( x, y, flatCoordinates[i], flatCoordinates[i + 1]); if (squaredDistance < minSquaredDistance) { minSquaredDistance = squaredDistance; - closestPoint[0] = flatCoordinates[i]; - closestPoint[1] = flatCoordinates[i + 1]; + for (j = 0; j < stride; ++j) { + closestPoint[j] = flatCoordinates[i + j]; + } + closestPoint.length = stride; } } return minSquaredDistance; diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 4e357f7e5a..46c76b5a9a 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -40,8 +40,12 @@ ol.geom.Point.prototype.closestPointXY = var squaredDistance = ol.geom.flat.squaredDistance( x, y, flatCoordinates[0], flatCoordinates[1]); if (squaredDistance < minSquaredDistance) { - closestPoint[0] = flatCoordinates[0]; - closestPoint[1] = flatCoordinates[1]; + var stride = this.stride; + var i; + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + closestPoint.length = stride; return squaredDistance; } else { return minSquaredDistance; diff --git a/test/spec/ol/geom/circle.test.js b/test/spec/ol/geom/circle.test.js index e7e6fa119d..3ae6298440 100644 --- a/test/spec/ol/geom/circle.test.js +++ b/test/spec/ol/geom/circle.test.js @@ -89,6 +89,37 @@ describe('ol.geom.Circle', function() { 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() { diff --git a/test/spec/ol/geom/closestgeom.test.js b/test/spec/ol/geom/closestgeom.test.js index 102f924e4e..124b5afde6 100644 --- a/test/spec/ol/geom/closestgeom.test.js +++ b/test/spec/ol/geom/closestgeom.test.js @@ -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); + }); + + }); + + }); + }); diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js index f2ef5bef66..336b5558c9 100644 --- a/test/spec/ol/geom/multipoint.test.js +++ b/test/spec/ol/geom/multipoint.test.js @@ -154,6 +154,15 @@ describe('ol.geom.MultiPoint', function() { 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]); + }); + + }); + }); }); diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index 72389411ac..84b01276c3 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -95,6 +95,15 @@ describe('ol.geom.Point', function() { 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]); + }); + + }); + }); });