163 lines
4.7 KiB
JavaScript
Executable File
163 lines
4.7 KiB
JavaScript
Executable File
import { geomEach, coordEach } from '@turf/meta';
|
|
import { multiPolygon, polygon, featureCollection } from '@turf/helpers';
|
|
|
|
/**
|
|
* Smooths a {@link Polygon} or {@link MultiPolygon}. Based on [Chaikin's algorithm](http://graphics.cs.ucdavis.edu/education/CAGDNotes/Chaikins-Algorithm/Chaikins-Algorithm.html).
|
|
* Warning: may create degenerate polygons.
|
|
*
|
|
* @name polygonSmooth
|
|
* @param {FeatureCollection|Feature<Polygon|MultiPolygon>} inputPolys (Multi)Polygon(s) to smooth
|
|
* @param {Object} [options={}] Optional parameters
|
|
* @param {string} [options.iterations=1] THe number of times to smooth the polygon. A higher value means a smoother polygon.
|
|
* @returns {FeatureCollection<Polygon>} FeatureCollection containing the smoothed polygon/poylgons
|
|
* @example
|
|
* var polygon = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]);
|
|
*
|
|
* var smoothed = turf.polygonSmooth(polygon, {iterations: 3})
|
|
*
|
|
* //addToMap
|
|
* var addToMap = [smoothed, polygon];
|
|
*/
|
|
function polygonSmooth(inputPolys, options) {
|
|
var outPolys = [];
|
|
// Optional parameters
|
|
var iterations = options.iterations || 1;
|
|
if (!inputPolys) throw new Error("inputPolys is required");
|
|
|
|
geomEach(inputPolys, function (geom, geomIndex, properties) {
|
|
var outCoords;
|
|
var poly;
|
|
var tempOutput;
|
|
|
|
switch (geom.type) {
|
|
case "Polygon":
|
|
outCoords = [[]];
|
|
for (var i = 0; i < iterations; i++) {
|
|
tempOutput = [[]];
|
|
poly = geom;
|
|
if (i > 0) poly = polygon(outCoords).geometry;
|
|
processPolygon(poly, tempOutput);
|
|
outCoords = tempOutput.slice(0);
|
|
}
|
|
outPolys.push(polygon(outCoords, properties));
|
|
break;
|
|
case "MultiPolygon":
|
|
outCoords = [[[]]];
|
|
for (var y = 0; y < iterations; y++) {
|
|
tempOutput = [[[]]];
|
|
poly = geom;
|
|
if (y > 0) poly = multiPolygon(outCoords).geometry;
|
|
processMultiPolygon(poly, tempOutput);
|
|
outCoords = tempOutput.slice(0);
|
|
}
|
|
outPolys.push(multiPolygon(outCoords, properties));
|
|
break;
|
|
default:
|
|
throw new Error("geometry is invalid, must be Polygon or MultiPolygon");
|
|
}
|
|
});
|
|
return featureCollection(outPolys);
|
|
}
|
|
|
|
/**
|
|
* @param {poly} poly to process
|
|
* @param {poly} tempOutput to place the results in
|
|
* @private
|
|
*/
|
|
function processPolygon(poly, tempOutput) {
|
|
var prevGeomIndex = 0;
|
|
var subtractCoordIndex = 0;
|
|
|
|
coordEach(
|
|
poly,
|
|
function (
|
|
currentCoord,
|
|
coordIndex,
|
|
featureIndex,
|
|
multiFeatureIndex,
|
|
geometryIndex
|
|
) {
|
|
if (geometryIndex > prevGeomIndex) {
|
|
prevGeomIndex = geometryIndex;
|
|
subtractCoordIndex = coordIndex;
|
|
tempOutput.push([]);
|
|
}
|
|
var realCoordIndex = coordIndex - subtractCoordIndex;
|
|
var p1 = poly.coordinates[geometryIndex][realCoordIndex + 1];
|
|
var p0x = currentCoord[0];
|
|
var p0y = currentCoord[1];
|
|
var p1x = p1[0];
|
|
var p1y = p1[1];
|
|
tempOutput[geometryIndex].push([
|
|
0.75 * p0x + 0.25 * p1x,
|
|
0.75 * p0y + 0.25 * p1y,
|
|
]);
|
|
tempOutput[geometryIndex].push([
|
|
0.25 * p0x + 0.75 * p1x,
|
|
0.25 * p0y + 0.75 * p1y,
|
|
]);
|
|
},
|
|
true
|
|
);
|
|
tempOutput.forEach(function (ring) {
|
|
ring.push(ring[0]);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {poly} poly to process
|
|
* @param {poly} tempOutput to place the results in
|
|
* @private
|
|
*/
|
|
function processMultiPolygon(poly, tempOutput) {
|
|
var prevGeomIndex = 0;
|
|
var subtractCoordIndex = 0;
|
|
var prevMultiIndex = 0;
|
|
|
|
coordEach(
|
|
poly,
|
|
function (
|
|
currentCoord,
|
|
coordIndex,
|
|
featureIndex,
|
|
multiFeatureIndex,
|
|
geometryIndex
|
|
) {
|
|
if (multiFeatureIndex > prevMultiIndex) {
|
|
prevMultiIndex = multiFeatureIndex;
|
|
subtractCoordIndex = coordIndex;
|
|
tempOutput.push([[]]);
|
|
}
|
|
if (geometryIndex > prevGeomIndex) {
|
|
prevGeomIndex = geometryIndex;
|
|
subtractCoordIndex = coordIndex;
|
|
tempOutput[multiFeatureIndex].push([]);
|
|
}
|
|
var realCoordIndex = coordIndex - subtractCoordIndex;
|
|
var p1 =
|
|
poly.coordinates[multiFeatureIndex][geometryIndex][realCoordIndex + 1];
|
|
var p0x = currentCoord[0];
|
|
var p0y = currentCoord[1];
|
|
var p1x = p1[0];
|
|
var p1y = p1[1];
|
|
tempOutput[multiFeatureIndex][geometryIndex].push([
|
|
0.75 * p0x + 0.25 * p1x,
|
|
0.75 * p0y + 0.25 * p1y,
|
|
]);
|
|
tempOutput[multiFeatureIndex][geometryIndex].push([
|
|
0.25 * p0x + 0.75 * p1x,
|
|
0.25 * p0y + 0.75 * p1y,
|
|
]);
|
|
},
|
|
true
|
|
);
|
|
|
|
tempOutput.forEach(function (poly) {
|
|
poly.forEach(function (ring) {
|
|
ring.push(ring[0]);
|
|
});
|
|
});
|
|
}
|
|
|
|
export default polygonSmooth;
|