From 1670b311428095d8f9527f060f47cd3c9dd8a105 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 4 Nov 2013 19:19:54 +0100 Subject: [PATCH 1/3] Moving squaredDistanceToSegment to the coordinate package --- src/ol/coordinate.js | 51 +++++++++++++++++++++++++++++++++++++++ src/ol/geom/base.js | 31 ------------------------ src/ol/geom/linestring.js | 3 ++- 3 files changed, 53 insertions(+), 32 deletions(-) delete mode 100644 src/ol/geom/base.js diff --git a/src/ol/coordinate.js b/src/ol/coordinate.js index 85f0b5318e..8616c6ba06 100644 --- a/src/ol/coordinate.js +++ b/src/ol/coordinate.js @@ -42,6 +42,45 @@ ol.coordinate.add = function(coordinate, delta) { }; +/** + * @param {ol.Coordinate} coordinate The coordinate. + * @param {Array.} segment The two coordinates of the segment. + * @return {Array} An array compatible with ol.Coordinate, but with 4 vaules: + * [0] x-coordinate of the point closest to the given point on the segment; + * [1] y-coordinate of the point closest to the given point on the segment; + * [2] squared distance between given point and segment; + * [3] describes how far between the two segment points the given point is. + */ +ol.coordinate.closestOnSegment = function(coordinate, segment) { + var x0 = coordinate[0]; + var y0 = coordinate[1]; + var start = segment[0]; + var end = segment[1]; + var x1 = start[0]; + var y1 = start[1]; + var x2 = end[0]; + var y2 = end[1]; + var dx = x2 - x1; + var dy = y2 - y1; + var along = (dx == 0 && dy == 0) ? 0 : + ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0); + var x, y; + if (along <= 0) { + x = x1; + y = y1; + } else if (along >= 1) { + x = x2; + y = y2; + } else { + x = x1 + along * dx; + y = y1 + along * dy; + } + var xDist = x - x0; + var yDist = y - y0; + return [x, y, xDist * xDist + yDist * yDist, along]; +}; + + /** * @param {number=} opt_fractionDigits The number of digits to include * after the decimal point. Default is `0`. @@ -136,6 +175,18 @@ ol.coordinate.squaredDistance = function(coord1, coord2) { }; +/** + * Calculate the squared distance from a coordinate to a line segment. + * + * @param {ol.Coordinate} coordinate Coordinate of the point. + * @param {Array.} segment Line segment (2 coordinates). + * @return {number} Squared distance from the point to the line segment. + */ +ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) { + return ol.coordinate.closestOnSegment(coordinate, segment)[2]; +}; + + /** * @param {ol.Coordinate|undefined} coordinate Coordinate. * @return {string} Hemisphere, degrees, minutes and seconds. diff --git a/src/ol/geom/base.js b/src/ol/geom/base.js deleted file mode 100644 index 9950aab807..0000000000 --- a/src/ol/geom/base.js +++ /dev/null @@ -1,31 +0,0 @@ -goog.provide('ol.geom'); - -goog.require('ol.coordinate'); - - -/** - * Calculate the squared distance from a point to a line segment. - * - * @param {ol.Coordinate} coordinate Coordinate of the point. - * @param {Array.} segment Line segment (2 coordinates). - * @return {number} Squared distance from the point to the line segment. - */ -ol.geom.squaredDistanceToSegment = function(coordinate, segment) { - // http://de.softuses.com/103478, Kommentar #1 - var v = segment[0]; - var w = segment[1]; - var l2 = ol.coordinate.squaredDistance(v, w); - if (l2 === 0) { - return ol.coordinate.squaredDistance(coordinate, v); - } - var t = ((coordinate[0] - v[0]) * (w[0] - v[0]) + - (coordinate[1] - v[1]) * (w[1] - v[1])) / l2; - if (t < 0) { - return ol.coordinate.squaredDistance(coordinate, v); - } - if (t > 1) { - return ol.coordinate.squaredDistance(coordinate, w); - } - return ol.coordinate.squaredDistance(coordinate, - [v[0] + t * (w[0] - v[0]), v[1] + t * (w[1] - v[1])]); -}; diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 11b2fd3e05..a4f8d261dc 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -3,6 +3,7 @@ goog.provide('ol.geom.LineString'); goog.require('goog.asserts'); goog.require('goog.events.EventType'); goog.require('ol.CoordinateArray'); +goog.require('ol.coordinate'); goog.require('ol.extent'); goog.require('ol.geom'); goog.require('ol.geom.Geometry'); @@ -104,7 +105,7 @@ ol.geom.LineString.prototype.distanceFromCoordinate = function(coordinate) { var coordinates = this.getCoordinates(); var dist2 = Infinity; for (var i = 0, j = 1, len = coordinates.length; j < len; i = j++) { - dist2 = Math.min(dist2, ol.geom.squaredDistanceToSegment(coordinate, + dist2 = Math.min(dist2, ol.coordinate.squaredDistanceToSegment(coordinate, [coordinates[i], coordinates[j]])); } return Math.sqrt(dist2); From e9b934d041c89675ddc790a10db7659e4f59d1ca Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 4 Nov 2013 20:54:07 +0100 Subject: [PATCH 2/3] Let closestOnSegment return nothing more than a coordinate Since we do not use the 'along' property anywhere, and the resulting array returned by closestOnSegment could cause trouble when working with 3d coodinates, now only the closest point is returned, and the squared distance to the segment is calculated by squaredDistanceToSegment instead. --- src/ol/coordinate.js | 19 ++++++++++--------- src/ol/geom/linestring.js | 1 - 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ol/coordinate.js b/src/ol/coordinate.js index 8616c6ba06..a0eebcde74 100644 --- a/src/ol/coordinate.js +++ b/src/ol/coordinate.js @@ -43,13 +43,15 @@ ol.coordinate.add = function(coordinate, delta) { /** + * Calculates the point closest to the passed coordinate on the passed segment. + * This is the foot of the perpendicular of the coordinate to the segment when + * the foot is on the segment, or the closest segment coordinate when the foot + * is outside the segment. + * * @param {ol.Coordinate} coordinate The coordinate. * @param {Array.} segment The two coordinates of the segment. - * @return {Array} An array compatible with ol.Coordinate, but with 4 vaules: - * [0] x-coordinate of the point closest to the given point on the segment; - * [1] y-coordinate of the point closest to the given point on the segment; - * [2] squared distance between given point and segment; - * [3] describes how far between the two segment points the given point is. + * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to + * the segment. */ ol.coordinate.closestOnSegment = function(coordinate, segment) { var x0 = coordinate[0]; @@ -75,9 +77,7 @@ ol.coordinate.closestOnSegment = function(coordinate, segment) { x = x1 + along * dx; y = y1 + along * dy; } - var xDist = x - x0; - var yDist = y - y0; - return [x, y, xDist * xDist + yDist * yDist, along]; + return [x, y]; }; @@ -183,7 +183,8 @@ ol.coordinate.squaredDistance = function(coord1, coord2) { * @return {number} Squared distance from the point to the line segment. */ ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) { - return ol.coordinate.closestOnSegment(coordinate, segment)[2]; + return ol.coordinate.squaredDistance(coordinate, + ol.coordinate.closestOnSegment(coordinate, segment)); }; diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index a4f8d261dc..e7ed07843b 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -5,7 +5,6 @@ goog.require('goog.events.EventType'); goog.require('ol.CoordinateArray'); goog.require('ol.coordinate'); goog.require('ol.extent'); -goog.require('ol.geom'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryEvent'); goog.require('ol.geom.GeometryType'); From f3170165059c9013c140b3911ce94ca7942798a2 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 4 Nov 2013 22:14:34 +0100 Subject: [PATCH 3/3] Add tests --- test/spec/ol/coordinate.test.js | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/spec/ol/coordinate.test.js diff --git a/test/spec/ol/coordinate.test.js b/test/spec/ol/coordinate.test.js new file mode 100644 index 0000000000..baee06d42d --- /dev/null +++ b/test/spec/ol/coordinate.test.js @@ -0,0 +1,42 @@ +goog.provide('ol.test.coordinate'); + +describe.only('ol.coordinate', function() { + + describe('#closestOnSegment', function() { + it('can handle points where the foot of the perpendicular is closest', + function() { + var point = [2, 5]; + var segment = [[-5, 0], [10, 0]]; + expect(ol.coordinate.closestOnSegment(point, segment)) + .to.eql([2, 0]); + }); + it('can handle points where the foot of the perpendicular is not closest', + function() { + var point = [0, -6]; + var segment = [[-5, 0], [0, -1]]; + expect(ol.coordinate.closestOnSegment(point, segment)) + .to.eql([0, -1]); + }); + }); + + describe('#squaredDistanceToSegment', function() { + it('can handle points where the foot of the perpendicular is closest', + function() { + var point = [2, 5]; + var segment = [[-5, 0], [10, 0]]; + expect(ol.coordinate.squaredDistanceToSegment(point, segment)) + .to.eql(25); + }); + it('can handle points where the foot of the perpendicular is not closest', + function() { + var point = [0, -6]; + var segment = [[-5, 0], [0, -1]]; + expect(ol.coordinate.squaredDistanceToSegment(point, segment)) + .to.eql(25); + }); + + }); + +}); + +goog.require('ol.coordinate');