185 lines
5.2 KiB
JavaScript
185 lines
5.2 KiB
JavaScript
import ol_geom_LineString from 'ol/geom/LineString.js'
|
||
import { ol_coordinate_equal } from './GeomUtils';
|
||
|
||
(function () {
|
||
|
||
/**
|
||
* Visvalingam polyline simplification algorithm, adapted from http://bost.ocks.org/mike/simplify/simplify.js
|
||
* This uses the [Visvalingam–Whyatt](https://en.wikipedia.org/wiki/Visvalingam%E2%80%93Whyatt_algorithm) algorithm.
|
||
* @param {Object} options
|
||
* @param {number} [area] the tolerance area for simplification
|
||
* @param {number} [dist] a tolerance distance for simplification
|
||
* @param {number} [ratio=.8] a ratio of points to keep
|
||
* @param {number} [minPoints=2] minimum number of points to keep
|
||
* @param {boolean} [keepEnds] keep line ends
|
||
* @return { LineString } A new, simplified version of the original geometry.
|
||
* @api
|
||
*/
|
||
ol_geom_LineString.prototype.simplifyVisvalingam = function (options) {
|
||
var points = this.getCoordinates();
|
||
if (options.minPoints && options.minPoints >= points.length) {
|
||
return new ol_geom_LineString(points);
|
||
}
|
||
var heap = minHeap(),
|
||
maxArea = 0,
|
||
triangle,
|
||
triangles = [];
|
||
points = points.map(function (d) { return d.slice(0,2); });
|
||
for (var i = 1, n = points.length - 1; i < n; ++i) {
|
||
triangle = points.slice(i - 1, i + 2);
|
||
if (triangle[1][2] = area(triangle)) {
|
||
triangles.push(triangle);
|
||
heap.push(triangle);
|
||
}
|
||
}
|
||
|
||
for (i = 0, n = triangles.length; i < n; ++i) {
|
||
triangle = triangles[i];
|
||
triangle.previous = triangles[i - 1];
|
||
triangle.next = triangles[i + 1];
|
||
}
|
||
|
||
while (triangle = heap.pop()) {
|
||
// If the area of the current point is less than that of the previous point
|
||
// to be eliminated, use the latters area instead. This ensures that the
|
||
// current point cannot be eliminated without eliminating previously-
|
||
// eliminated points.
|
||
if (triangle[1][2] < maxArea) triangle[1][2] = maxArea;
|
||
else maxArea = triangle[1][2];
|
||
|
||
if (triangle.previous) {
|
||
triangle.previous.next = triangle.next;
|
||
triangle.previous[2] = triangle[2];
|
||
update(triangle.previous);
|
||
} else {
|
||
triangle[0][2] = triangle[1][2];
|
||
}
|
||
|
||
if (triangle.next) {
|
||
triangle.next.previous = triangle.previous;
|
||
triangle.next[0] = triangle[0];
|
||
update(triangle.next);
|
||
} else {
|
||
triangle[2][2] = triangle[1][2];
|
||
}
|
||
}
|
||
|
||
function update(triangle) {
|
||
heap.remove(triangle);
|
||
triangle[1][2] = area(triangle);
|
||
heap.push(triangle);
|
||
}
|
||
|
||
// Get area to remove
|
||
var w = options.area;
|
||
if (options.dist) w = options.dist * options.dist / 2;
|
||
// If no area
|
||
if (w === undefined || options.minPoints) {
|
||
// Get ordered weights
|
||
var weights = points.map(function (d) { return d.length < 3 ? Infinity : d[2] += Math.random(); /* break ties */ });
|
||
weights.sort(function (a, b) {
|
||
return b - a;
|
||
});
|
||
if (w) {
|
||
// Check min points
|
||
if (weights[options.minPoints] < w) {
|
||
w = weights[options.minPoints]
|
||
}
|
||
} else {
|
||
var pointsToKeep = options.minPoints;
|
||
// Calculate ratio
|
||
if (!pointsToKeep) {
|
||
var ratio = options.ratio || .8
|
||
pointsToKeep = Math.round(points.length * ratio);
|
||
}
|
||
pointsToKeep = Math.min(pointsToKeep, weights.length -1);
|
||
w = weights[pointsToKeep]
|
||
}
|
||
}
|
||
|
||
var result = points.filter(function (d) {
|
||
return d[2] > w;
|
||
});
|
||
if (options.keepEnds) {
|
||
if (!ol_coordinate_equal(result[0], points[0])) result.unshift(points[0]);
|
||
if (!ol_coordinate_equal(result[result.length-1], points[points.length-1])) result.push(points[points.length-1]);
|
||
}
|
||
|
||
return new ol_geom_LineString(result);
|
||
};
|
||
|
||
function compare(a, b) {
|
||
return a[1][2] - b[1][2];
|
||
}
|
||
|
||
function area(t) {
|
||
return Math.abs((t[0][0] - t[2][0]) * (t[1][1] - t[0][1]) - (t[0][0] - t[1][0]) * (t[2][1] - t[0][1]));
|
||
}
|
||
|
||
function minHeap() {
|
||
var heap = {},
|
||
array = [];
|
||
|
||
heap.push = function() {
|
||
for (var i = 0, n = arguments.length; i < n; ++i) {
|
||
var object = arguments[i];
|
||
up(object.index = array.push(object) - 1);
|
||
}
|
||
return array.length;
|
||
};
|
||
|
||
heap.pop = function() {
|
||
var removed = array[0],
|
||
object = array.pop();
|
||
if (array.length) {
|
||
array[object.index = 0] = object;
|
||
down(0);
|
||
}
|
||
return removed;
|
||
};
|
||
|
||
heap.size = function () {
|
||
return array.length;
|
||
};
|
||
|
||
heap.remove = function(removed) {
|
||
var i = removed.index,
|
||
object = array.pop();
|
||
if (i !== array.length) {
|
||
array[object.index = i] = object;
|
||
(compare(object, removed) < 0 ? up : down)(i);
|
||
}
|
||
return i;
|
||
};
|
||
|
||
function up(i) {
|
||
var object = array[i];
|
||
while (i > 0) {
|
||
var up = ((i + 1) >> 1) - 1,
|
||
parent = array[up];
|
||
if (compare(object, parent) >= 0) break;
|
||
array[parent.index = i] = parent;
|
||
array[object.index = i = up] = object;
|
||
}
|
||
}
|
||
|
||
function down(i) {
|
||
var object = array[i];
|
||
for (;;) {
|
||
var right = (i + 1) * 2,
|
||
left = right - 1,
|
||
down = i,
|
||
child = array[down];
|
||
if (left < array.length && compare(array[left], child) < 0) child = array[down = left];
|
||
if (right < array.length && compare(array[right], child) < 0) child = array[down = right];
|
||
if (down === i) break;
|
||
array[child.index = i] = child;
|
||
array[object.index = i = down] = object;
|
||
}
|
||
}
|
||
|
||
return heap;
|
||
}
|
||
})();
|
||
|
||
export default ol_geom_LineString |