Add method Polyline.closestLayerPoint

Also fix method L.LineUtil.simplify for empty geometry
This commit is contained in:
Andrey Rublev 2011-07-20 13:23:04 +07:00
parent cf83c2e146
commit 263a5b9b5f
5 changed files with 104 additions and 10 deletions

View File

@ -41,6 +41,7 @@
<!-- /layer -->
<script type="text/javascript" src="suites/layer/TileLayerSpec.js"></script>
<script type="text/javascript" src="suites/layer/vector/PolylineGeometrySpec.js"></script>
<!-- /map -->
<script type="text/javascript" src="suites/map/MapSpec.js"></script>

View File

@ -3,3 +3,26 @@ function noSpecs() {
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;
};
}

View File

@ -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);
});
});
});

View File

@ -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);
@ -25,6 +25,12 @@ L.LineUtil = {
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
simplifyDP: function(points, tol) {
var maxDist2 = 0,
@ -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 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;
},
var proj = new L.Point(p1.x + x2 * t, p1.y + y2 * t);
return this._sqDist(p, proj);
// distance from a point to a segment between two points
_sqPointToSegmentDist: function(p, p1, p2) {
return this._sqClosestPointOnSegment(p, p1, p2)._sqDist;
}
};

View File

@ -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;