From 263a5b9b5fbb59c2637fabd1b406004197986743 Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Wed, 20 Jul 2011 13:23:04 +0700 Subject: [PATCH] Add method Polyline.closestLayerPoint Also fix method L.LineUtil.simplify for empty geometry --- spec/runner.html | 1 + spec/suites/SpecHelper.js | 23 ++++++++++++ .../layer/vector/PolylineGeometrySpec.js | 35 ++++++++++++++++++ src/geometry/LineUtil.js | 36 +++++++++++++------ src/layer/vector/Polyline.js | 19 ++++++++++ 5 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 spec/suites/layer/vector/PolylineGeometrySpec.js diff --git a/spec/runner.html b/spec/runner.html index 9990855c..3013985b 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -41,6 +41,7 @@ + diff --git a/spec/suites/SpecHelper.js b/spec/suites/SpecHelper.js index e013917a..fb88857f 100644 --- a/spec/suites/SpecHelper.js +++ b/spec/suites/SpecHelper.js @@ -2,4 +2,27 @@ function noSpecs() { xit('should have specs', function() { expect('specs').toBe(); }); +} + +if (!Array.prototype.map) { + Array.prototype.map = function(fun /*, thisp */) { + "use strict"; + + if (this === void 0 || this === null) + throw new TypeError(); + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") + throw new TypeError(); + + var res = new Array(len); + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) + res[i] = fun.call(thisp, t[i], i, t); + } + + return res; + }; } \ No newline at end of file diff --git a/spec/suites/layer/vector/PolylineGeometrySpec.js b/spec/suites/layer/vector/PolylineGeometrySpec.js new file mode 100644 index 00000000..9aec2cfe --- /dev/null +++ b/spec/suites/layer/vector/PolylineGeometrySpec.js @@ -0,0 +1,35 @@ +describe('PolylineGeometry', function() { + + var c = document.createElement('div'); + c.style.width = '400px'; + c.style.height = '400px'; + var map = new L.Map(c); + map.setView(new L.LatLng(55.8, 37.6), 6); + + describe("#distanceTo", function() { + it("should calculate correct distances to points", function() { + var p1 = map.latLngToLayerPoint(new L.LatLng(55.8, 37.6)); + var p2 = map.latLngToLayerPoint(new L.LatLng(57.123076977278, 44.861962891635)); + var latlngs = [[56.485503424111, 35.545556640339], [55.972522915346, 36.116845702918], [55.502459116923, 34.930322265253], [55.31534617509, 38.973291015816]] + .map(function(ll) { + return new L.LatLng(ll[0], ll[1]); + }); + var polyline = new L.Polyline([], { + 'noClip': true + }); + map.addLayer(polyline); + + expect(polyline.closestLayerPoint(p1)).toEqual(null); + + polyline.setLatLngs(latlngs); + var point = polyline.closestLayerPoint(p1); + expect(point).not.toEqual(null); + expect(point.distance).not.toEqual(Infinity); + expect(point.distance).not.toEqual(NaN); + + var point2 = polyline.closestLayerPoint(p2); + + expect(point.distance).toBeLessThan(point2.distance); + }); + }); +}); diff --git a/src/geometry/LineUtil.js b/src/geometry/LineUtil.js index 72a80855..36a5bddd 100644 --- a/src/geometry/LineUtil.js +++ b/src/geometry/LineUtil.js @@ -9,7 +9,7 @@ L.LineUtil = { * Improves rendering performance dramatically by lessening the number of points to draw. */ simplify: function(/*Point[]*/ points, /*Number*/ tolerance) { - if (!tolerance) return points.slice(); + if (!tolerance || !points.length) return points.slice(); // stage 1: vertex reduction points = this.reducePoints(points, tolerance); @@ -22,7 +22,13 @@ L.LineUtil = { // distance from a point to a segment between two points pointToSegmentDistance: function(/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { - return Math.sqrt(this._sqPointToSegmentDist(p, p1, p2)); + return Math.sqrt(this._sqPointToSegmentDist(p, p1, p2)); + }, + + closestPointOnSegment: function(/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + var point = this._sqClosestPointOnSegment(p, p1, p2); + point.distance = Math.sqrt(point._sqDist); + return point; }, // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm @@ -140,8 +146,10 @@ L.LineUtil = { return dx * dx + dy * dy; }, - // square distance from point to a segment - _sqPointToSegmentDist: function(p, p1, p2) { + /** + * @return L.Point point on segment with attribute _sqDist - square distance to segment + */ + _sqClosestPointOnSegment: function(p, p1, p2) { var x2 = p2.x - p1.x, y2 = p2.y - p1.y; @@ -150,10 +158,18 @@ L.LineUtil = { var dot = (p.x - p1.x) * x2 + (p.y - p1.y) * y2, t = dot / this._sqDist(p1, p2); - if (t < 0) return this._sqDist(p, p1); - if (t > 1) return this._sqDist(p, p2); - - var proj = new L.Point(p1.x + x2 * t, p1.y + y2 * t); - return this._sqDist(p, proj); - } + var apoint = p1; + if (t > 1) { + apoint = p2; + } else if (t > 0) { + apoint = new L.Point(p1.x + x2 * t, p1.y + y2 * t); + } + apoint._sqDist = this._sqDist(p, apoint); + return apoint; + }, + + // distance from a point to a segment between two points + _sqPointToSegmentDist: function(p, p1, p2) { + return this._sqClosestPointOnSegment(p, p1, p2)._sqDist; + } }; \ No newline at end of file diff --git a/src/layer/vector/Polyline.js b/src/layer/vector/Polyline.js index 606d7d71..3eb43a9d 100644 --- a/src/layer/vector/Polyline.js +++ b/src/layer/vector/Polyline.js @@ -51,6 +51,25 @@ L.Polyline = L.Path.extend({ return removed; }, + closestLayerPoint: function(p) { + var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null; + + for (var j = 0, jLen = parts.length; j < jLen; j++) { + var points = parts[j]; + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i-1]; + p2 = points[i]; + var point = L.LineUtil._sqClosestPointOnSegment(p, p1, p2); + if (point._sqDist < minDistance) { + minDistance = point._sqDist; + minPoint = point; + } + } + } + if (minPoint) minPoint.distance = Math.sqrt(minDistance); + return minPoint; + }, + _getPathPartStr: function(points) { var round = L.Path.VML;