Significantly improved line simplification performance
This commit is contained in:
parent
5d9c4f08fc
commit
1cc885056b
@ -18,6 +18,7 @@ Leaflet Changelog
|
||||
* Map now preserves its center after resize.
|
||||
* When panning to another copy of the world (that's infinite horizontally), map overlays now jump to corresponding positions. [#273](https://github.com/CloudMade/Leaflet/issues/273)
|
||||
* Limited maximum zoom change on a single mouse wheel movement (so you won't zoom across the whole zoom range in one scroll). [#149](https://github.com/CloudMade/Leaflet/issues/149)
|
||||
* Significantly improved line simplification performance (noticeable when rendering polylines/polygons with tens of thousands of points)
|
||||
* Improved circles performance by not drawing them if they're off the clip region.
|
||||
|
||||
#### API improvements
|
||||
|
@ -13,72 +13,80 @@ L.LineUtil = {
|
||||
return points.slice();
|
||||
}
|
||||
|
||||
var sqTolerance = tolerance * tolerance;
|
||||
|
||||
// stage 1: vertex reduction
|
||||
points = this.reducePoints(points, tolerance);
|
||||
points = this._reducePoints(points, sqTolerance);
|
||||
|
||||
// stage 2: Douglas-Peucker simplification
|
||||
points = this.simplifyDP(points, tolerance);
|
||||
points = this._simplifyDP(points, sqTolerance);
|
||||
|
||||
return points;
|
||||
},
|
||||
|
||||
// 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._sqClosestPointOnSegment(p, p1, p2, true));
|
||||
},
|
||||
|
||||
closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
|
||||
var point = this._sqClosestPointOnSegment(p, p1, p2);
|
||||
point.distance = Math.sqrt(point._sqDist);
|
||||
return point;
|
||||
return this._sqClosestPointOnSegment(p, p1, p2);
|
||||
},
|
||||
|
||||
// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
|
||||
simplifyDP: function (points, tol) {
|
||||
var maxDist2 = 0,
|
||||
index = 0,
|
||||
t2 = tol * tol,
|
||||
len = points.length,
|
||||
i, dist2;
|
||||
_simplifyDP: function (points, sqTolerance) {
|
||||
|
||||
if (len < 3) {
|
||||
return points;
|
||||
}
|
||||
var len = points.length,
|
||||
ArrayConstructor = typeof Uint8Array !== 'undefined' ? Uint8Array : Array,
|
||||
markers = new ArrayConstructor(len);
|
||||
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
dist2 = this._sqPointToSegmentDist(points[i], points[0], points[len - 1]);
|
||||
if (dist2 > maxDist2) {
|
||||
index = i;
|
||||
maxDist2 = dist2;
|
||||
markers[0] = markers[len - 1] = 1;
|
||||
|
||||
this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
|
||||
|
||||
var i,
|
||||
newPoints = [];
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (markers[i]) {
|
||||
newPoints.push(points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var part1, part2;
|
||||
return newPoints;
|
||||
},
|
||||
|
||||
if (maxDist2 >= t2) {
|
||||
part1 = points.slice(0, index);
|
||||
part2 = points.slice(index);
|
||||
_simplifyDPStep: function (points, markers, sqTolerance, first, last) {
|
||||
|
||||
part1 = this.simplifyDP(part1, tol);
|
||||
part2 = this.simplifyDP(part2, tol);
|
||||
var maxSqDist = 0,
|
||||
index, i, sqDist;
|
||||
|
||||
return part1.concat(part2);
|
||||
} else {
|
||||
return [points[0], points[len - 1]];
|
||||
for (i = first + 1; i <= last - 1; i++) {
|
||||
sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
|
||||
|
||||
if (sqDist > maxSqDist) {
|
||||
index = i;
|
||||
maxSqDist = sqDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxSqDist > sqTolerance) {
|
||||
markers[index] = 1;
|
||||
|
||||
this._simplifyDPStep(points, markers, sqTolerance, first, index);
|
||||
this._simplifyDPStep(points, markers, sqTolerance, index, last);
|
||||
}
|
||||
},
|
||||
|
||||
// reduce points that are too close to each other to a single point
|
||||
reducePoints: function (points, tol) {
|
||||
var reducedPoints = [points[0]],
|
||||
t2 = tol * tol;
|
||||
_reducePoints: function (points, sqTolerance) {
|
||||
var reducedPoints = [points[0]];
|
||||
|
||||
for (var i = 1, prev = 0, len = points.length; i < len; i++) {
|
||||
if (this._sqDist(points[i], points[prev]) < t2) {
|
||||
continue;
|
||||
if (this._sqDist(points[i], points[prev]) > sqTolerance) {
|
||||
reducedPoints.push(points[i]);
|
||||
prev = i;
|
||||
}
|
||||
reducedPoints.push(points[i]);
|
||||
prev = i;
|
||||
}
|
||||
if (prev < len - 1) {
|
||||
reducedPoints.push(points[len - 1]);
|
||||
@ -168,27 +176,30 @@ L.LineUtil = {
|
||||
return dx * dx + dy * dy;
|
||||
},
|
||||
|
||||
// return closest 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,
|
||||
apoint = p1;
|
||||
if (x2 || y2) {
|
||||
var dot = (p.x - p1.x) * x2 + (p.y - p1.y) * y2,
|
||||
t = dot / this._sqDist(p1, p2);
|
||||
// return closest point on segment or distance to that point
|
||||
_sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
|
||||
var x = p1.x,
|
||||
y = p1.y,
|
||||
dx = p2.x - x,
|
||||
dy = p2.y - y,
|
||||
dot = dx * dx + dy * dy,
|
||||
t;
|
||||
|
||||
if (dot > 0) {
|
||||
t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
|
||||
|
||||
if (t > 1) {
|
||||
apoint = p2;
|
||||
x = p2.x;
|
||||
y = p2.y;
|
||||
} else if (t > 0) {
|
||||
apoint = new L.Point(p1.x + x2 * t, p1.y + y2 * t);
|
||||
x += dx * t;
|
||||
y += dy * 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;
|
||||
dx = p.x - x;
|
||||
dy = p.y - y;
|
||||
|
||||
return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user