123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- import { feature } from "@turf/helpers";
- import { getCoords, getType } from "@turf/invariant";
- // To-Do => Improve Typescript GeoJSON handling
- /**
- * Removes redundant coordinates from any GeoJSON Geometry.
- *
- * @name cleanCoords
- * @param {Geometry|Feature} geojson Feature or Geometry
- * @param {Object} [options={}] Optional parameters
- * @param {boolean} [options.mutate=false] allows GeoJSON input to be mutated
- * @returns {Geometry|Feature} the cleaned input Feature/Geometry
- * @example
- * var line = turf.lineString([[0, 0], [0, 2], [0, 5], [0, 8], [0, 8], [0, 10]]);
- * var multiPoint = turf.multiPoint([[0, 0], [0, 0], [2, 2]]);
- *
- * turf.cleanCoords(line).geometry.coordinates;
- * //= [[0, 0], [0, 10]]
- *
- * turf.cleanCoords(multiPoint).geometry.coordinates;
- * //= [[0, 0], [2, 2]]
- */
- function cleanCoords(geojson, options) {
- if (options === void 0) { options = {}; }
- // Backwards compatible with v4.0
- var mutate = typeof options === "object" ? options.mutate : options;
- if (!geojson)
- throw new Error("geojson is required");
- var type = getType(geojson);
- // Store new "clean" points in this Array
- var newCoords = [];
- switch (type) {
- case "LineString":
- newCoords = cleanLine(geojson);
- break;
- case "MultiLineString":
- case "Polygon":
- getCoords(geojson).forEach(function (line) {
- newCoords.push(cleanLine(line));
- });
- break;
- case "MultiPolygon":
- getCoords(geojson).forEach(function (polygons) {
- var polyPoints = [];
- polygons.forEach(function (ring) {
- polyPoints.push(cleanLine(ring));
- });
- newCoords.push(polyPoints);
- });
- break;
- case "Point":
- return geojson;
- case "MultiPoint":
- var existing = {};
- getCoords(geojson).forEach(function (coord) {
- var key = coord.join("-");
- if (!Object.prototype.hasOwnProperty.call(existing, key)) {
- newCoords.push(coord);
- existing[key] = true;
- }
- });
- break;
- default:
- throw new Error(type + " geometry not supported");
- }
- // Support input mutation
- if (geojson.coordinates) {
- if (mutate === true) {
- geojson.coordinates = newCoords;
- return geojson;
- }
- return { type: type, coordinates: newCoords };
- }
- else {
- if (mutate === true) {
- geojson.geometry.coordinates = newCoords;
- return geojson;
- }
- return feature({ type: type, coordinates: newCoords }, geojson.properties, {
- bbox: geojson.bbox,
- id: geojson.id,
- });
- }
- }
- /**
- * Clean Coords
- *
- * @private
- * @param {Array<number>|LineString} line Line
- * @returns {Array<number>} Cleaned coordinates
- */
- function cleanLine(line) {
- var points = getCoords(line);
- // handle "clean" segment
- if (points.length === 2 && !equals(points[0], points[1]))
- return points;
- var newPoints = [];
- var secondToLast = points.length - 1;
- var newPointsLength = newPoints.length;
- newPoints.push(points[0]);
- for (var i = 1; i < secondToLast; i++) {
- var prevAddedPoint = newPoints[newPoints.length - 1];
- if (points[i][0] === prevAddedPoint[0] &&
- points[i][1] === prevAddedPoint[1])
- continue;
- else {
- newPoints.push(points[i]);
- newPointsLength = newPoints.length;
- if (newPointsLength > 2) {
- if (isPointOnLineSegment(newPoints[newPointsLength - 3], newPoints[newPointsLength - 1], newPoints[newPointsLength - 2]))
- newPoints.splice(newPoints.length - 2, 1);
- }
- }
- }
- newPoints.push(points[points.length - 1]);
- newPointsLength = newPoints.length;
- if (equals(points[0], points[points.length - 1]) && newPointsLength < 4)
- throw new Error("invalid polygon");
- if (isPointOnLineSegment(newPoints[newPointsLength - 3], newPoints[newPointsLength - 1], newPoints[newPointsLength - 2]))
- newPoints.splice(newPoints.length - 2, 1);
- return newPoints;
- }
- /**
- * Compares two points and returns if they are equals
- *
- * @private
- * @param {Position} pt1 point
- * @param {Position} pt2 point
- * @returns {boolean} true if they are equals
- */
- function equals(pt1, pt2) {
- return pt1[0] === pt2[0] && pt1[1] === pt2[1];
- }
- /**
- * Returns if `point` is on the segment between `start` and `end`.
- * Borrowed from `@turf/boolean-point-on-line` to speed up the evaluation (instead of using the module as dependency)
- *
- * @private
- * @param {Position} start coord pair of start of line
- * @param {Position} end coord pair of end of line
- * @param {Position} point coord pair of point to check
- * @returns {boolean} true/false
- */
- function isPointOnLineSegment(start, end, point) {
- var x = point[0], y = point[1];
- var startX = start[0], startY = start[1];
- var endX = end[0], endY = end[1];
- var dxc = x - startX;
- var dyc = y - startY;
- var dxl = endX - startX;
- var dyl = endY - startY;
- var cross = dxc * dyl - dyc * dxl;
- if (cross !== 0)
- return false;
- else if (Math.abs(dxl) >= Math.abs(dyl))
- return dxl > 0 ? startX <= x && x <= endX : endX <= x && x <= startX;
- else
- return dyl > 0 ? startY <= y && y <= endY : endY <= y && y <= startY;
- }
- export default cleanCoords;
|