From 404a8195a404f5e0e155ba91bca2c15aee150a47 Mon Sep 17 00:00:00 2001 From: James Beard Date: Wed, 20 Nov 2024 14:35:19 +1100 Subject: [PATCH 1/5] Implemented a different approach to cleanCoords that should work better with lines that retrace or pass back through their origin. Note depends on PR #2751 to be merged first as part of the problem was booleanPointOnLine misbehaving. --- packages/turf-clean-coords/index.ts | 120 +++++++++--------------- packages/turf-clean-coords/package.json | 1 + packages/turf-clean-coords/test.ts | 97 +++++++++++++++++++ pnpm-lock.yaml | 3 + 4 files changed, 143 insertions(+), 78 deletions(-) diff --git a/packages/turf-clean-coords/index.ts b/packages/turf-clean-coords/index.ts index 3be8ba3efd..972abe70eb 100644 --- a/packages/turf-clean-coords/index.ts +++ b/packages/turf-clean-coords/index.ts @@ -1,6 +1,8 @@ import { Position } from "geojson"; import { feature } from "@turf/helpers"; import { getCoords, getType } from "@turf/invariant"; +import { booleanPointOnLine } from "@turf/boolean-point-on-line"; +import { lineString } from "@turf/helpers"; // To-Do => Improve Typescript GeoJSON handling @@ -99,62 +101,54 @@ function cleanCoords( * @returns {Array} Cleaned coordinates */ function cleanLine(line: Position[], type: string) { - var points = getCoords(line); + const 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); - } + const newPoints = []; + + // Segments based approach. With initial segment a-b, keep comparing to a + // longer segment a-c and as long as b is still on a-c, b is a redundant + // point. + let a = 0, + b = 1, + c = 2; + + // Guaranteed we'll use the first point. + newPoints.push(points[a]); + // While there is still room to extend the segment ... + while (c < points.length) { + if (booleanPointOnLine(points[b], lineString([points[a], points[c]]))) { + // b is on a-c, so we can discard point b, and extend a-b to be the same + // as a-c as the basis for comparison during the next iteration. + b = c; + } else { + // b is NOT on a-c, suggesting a-c is not an extension of a-b. Commit a-b + // as a necessary segment. + newPoints.push(points[b]); + + // Make our a-b for the next iteration start from the end of the segment + // that was just locked in i.e. next a-b should be the current b-(b+1). + a = b; + b++; + c = b; } + // Plan to look at the next point during the next iteration. + c++; } - newPoints.push(points[points.length - 1]); - newPointsLength = newPoints.length; - - // (Multi)Polygons must have at least 4 points, but a closed LineString with only 3 points is acceptable - if ( - (type === "Polygon" || type === "MultiPolygon") && - equals(points[0], points[points.length - 1]) && - newPointsLength < 4 - ) { - throw new Error("invalid polygon"); - } + // No remaining points, so commit the current a-b segment. + newPoints.push(points[b]); - if (type === "LineString" && newPointsLength < 3) { - return newPoints; + // (Multi)Polygons must have at least 4 points and be closed. + if (type === "Polygon" || type === "MultiPolygon") { + if (!equals(points[0], points[points.length - 1])) { + throw new Error("invalid polygon, first and last points not equal"); + } + if (newPoints.length < 4) { + throw new Error("invalid polygon, fewer than 4 points"); + } } - if ( - isPointOnLineSegment( - newPoints[newPointsLength - 3], - newPoints[newPointsLength - 1], - newPoints[newPointsLength - 2] - ) - ) - newPoints.splice(newPoints.length - 2, 1); - return newPoints; } @@ -170,35 +164,5 @@ function equals(pt1: Position, pt2: Position) { 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: Position, end: Position, point: Position) { - 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 { cleanCoords }; export default cleanCoords; diff --git a/packages/turf-clean-coords/package.json b/packages/turf-clean-coords/package.json index 718bee991a..c4e3bb19d0 100644 --- a/packages/turf-clean-coords/package.json +++ b/packages/turf-clean-coords/package.json @@ -67,6 +67,7 @@ "write-json-file": "^5.0.0" }, "dependencies": { + "@turf/boolean-point-on-line": "workspace:^", "@turf/helpers": "workspace:^", "@turf/invariant": "workspace:^", "@types/geojson": "^7946.0.10", diff --git a/packages/turf-clean-coords/test.ts b/packages/turf-clean-coords/test.ts index 60f77f4cfd..4748281aec 100644 --- a/packages/turf-clean-coords/test.ts +++ b/packages/turf-clean-coords/test.ts @@ -151,3 +151,100 @@ test("turf-clean-coords -- prevent input mutation", (t) => { t.deepEqual(multiPolyBefore, multiPoly, "multiPolygon should NOT be mutated"); t.end(); }); + +test("turf-clean-coords -- issue 2305", (t) => { + // From https://github.com/Turfjs/turf/issues/2305#issue-1287442870 + t.deepEqual( + cleanCoords( + lineString([ + [0, 0], + [0, 1], + [0, 0], + ]) + ), + lineString([ + [0, 0], + [0, 1], + [0, 0], + ]) + ); + + // From https://github.com/Turfjs/turf/issues/2305#issue-1287442870 + t.deepEqual( + cleanCoords( + lineString([ + [0, 0], + [0, 0], + [0, 2], + [0, 2], + [0, 0], + ]) + ), + lineString([ + [0, 0], + [0, 2], + [0, 0], + ]) + ); + + t.end(); +}); + +test("turf-clean-coords -- issue 2740", (t) => { + // Issue 2740 is cleanCoords was too aggresive at removing points. + t.deepEqual( + cleanCoords( + lineString([ + [0, 0], + [0, 2], + [0, 0], + ]) + ), + lineString([ + [0, 0], + [0, 2], + [0, 0], + ]), + "#2740 north-south retraced line" + ); + + t.deepEqual( + cleanCoords( + lineString([ + [0, 0], + [0, 1], + [0, 2], + [0, 3], + [0, 0], + ]) + ), + lineString([ + [0, 0], + [0, 3], + [0, 0], + ]), + "#2740 north-south retraced line extra points" + ); + + t.deepEqual( + cleanCoords( + lineString([ + [0, 0], + [0, 1], + [0, 2], + [0, -2], + [0, -1], + [0, 0], + ]) + ), + lineString([ + [0, 0], + [0, 2], + [0, -2], + [0, 0], + ]), + "#2740 north-south retraced past origin and back to start" + ); + + t.end(); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ab4e90963..4448c6004b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2057,6 +2057,9 @@ importers: packages/turf-clean-coords: dependencies: + '@turf/boolean-point-on-line': + specifier: workspace:^ + version: link:../turf-boolean-point-on-line '@turf/helpers': specifier: workspace:^ version: link:../turf-helpers From 9f4eae629ef5a30533757d743dea6a490cef9414 Mon Sep 17 00:00:00 2001 From: James Beard Date: Wed, 20 Nov 2024 15:39:55 +1100 Subject: [PATCH 2/5] Updated cleanCoords to run twice on polygons in case the start point was one of the points that should be cleaned. --- packages/turf-clean-coords/index.ts | 34 ++++++++++++++++++++-- packages/turf-clean-coords/package.json | 1 + packages/turf-clean-coords/test.ts | 38 ++++++++++++++++++++++++- pnpm-lock.yaml | 3 ++ 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/packages/turf-clean-coords/index.ts b/packages/turf-clean-coords/index.ts index 972abe70eb..5d09349359 100644 --- a/packages/turf-clean-coords/index.ts +++ b/packages/turf-clean-coords/index.ts @@ -45,14 +45,24 @@ function cleanCoords( case "MultiLineString": case "Polygon": getCoords(geojson).forEach(function (line) { - newCoords.push(cleanLine(line, type)); + // Run cleanLine twice in case polygon start point is one of the points + // that ought to get cleaned. + // https://github.com/Turfjs/turf/issues/2406 + const firstPass = cleanLine(line, type); + const secondPass = cleanLine(rotatePoints(firstPass), type); + newCoords.push(secondPass); }); break; case "MultiPolygon": getCoords(geojson).forEach(function (polygons: any) { var polyPoints: Position[] = []; polygons.forEach(function (ring: Position[]) { - polyPoints.push(cleanLine(ring, type)); + // Run cleanLine twice in case polygon start point is one of the points + // that ought to get cleaned. + // https://github.com/Turfjs/turf/issues/2406 + const firstPass = cleanLine(ring, type); + const secondPass = cleanLine(rotatePoints(firstPass), type); + polyPoints.push(secondPass); }); newCoords.push(polyPoints); }); @@ -164,5 +174,25 @@ function equals(pt1: Position, pt2: Position) { return pt1[0] === pt2[0] && pt1[1] === pt2[1]; } +/** + * Rotates a set of points to alter the start point of the polygon. + * + * @private + * @param {Position[]} pts Points to rotate + * @returns {Position[]} Rotated points + */ +function rotatePoints(pts: Position[]) { + // Don't alter the input pts array. Avoid side effects. + const points = pts.slice(); + if (!equals(points[0], points[points.length - 1])) { + // Not a closed polygon so don't touch it. + return points; + } + points.pop(); // Discard duplicate closing point temporarily. + points.push(points.shift() as Position); // Shuffle points along a place. + points.push(points[0]); // Duplicate the new closing point to end of array. + return points; +} + export { cleanCoords }; export default cleanCoords; diff --git a/packages/turf-clean-coords/package.json b/packages/turf-clean-coords/package.json index c4e3bb19d0..759e2716a9 100644 --- a/packages/turf-clean-coords/package.json +++ b/packages/turf-clean-coords/package.json @@ -54,6 +54,7 @@ "test:types": "tsc --esModuleInterop --module node16 --moduleResolution node16 --noEmit --strict types.ts" }, "devDependencies": { + "@turf/boolean-equal": "workspace:^", "@turf/truncate": "workspace:^", "@types/benchmark": "^2.1.5", "@types/tape": "^4.13.4", diff --git a/packages/turf-clean-coords/test.ts b/packages/turf-clean-coords/test.ts index 4748281aec..899436b0e6 100644 --- a/packages/turf-clean-coords/test.ts +++ b/packages/turf-clean-coords/test.ts @@ -4,6 +4,7 @@ import path from "path"; import { fileURLToPath } from "url"; import { loadJsonFileSync } from "load-json-file"; import { truncate } from "@turf/truncate"; +import { booleanEqual } from "@turf/boolean-equal"; import { point, multiPoint, @@ -38,7 +39,10 @@ test("turf-clean-coords", (t) => { if (process.env.REGEN) writeJsonFileSync(directories.out + filename, results); - t.deepEqual(results, loadJsonFileSync(directories.out + filename), name); + t.true( + booleanEqual(results, loadJsonFileSync(directories.out + filename)), + name + ); }); t.end(); }); @@ -248,3 +252,35 @@ test("turf-clean-coords -- issue 2740", (t) => { t.end(); }); + +test("turf-clean-coords -- issue 2406", (t) => { + t.true( + booleanEqual( + cleanCoords( + polygon([ + [ + [1, 3], // a + [3, 3], // b + [3, 1], // c + [3, -3], // d + [-3, -3], // e + [-3, 3], // f + [1, 3], // a + ], + ]) + ), + polygon([ + [ + [-3, 3], // f + [3, 3], // b + [3, -3], // d + [-3, -3], // e + [-3, 3], // f + ], + ]) + ), + "#2406 polygon start point (a) should also be removed" + ); + + t.end(); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4448c6004b..e92dab0aea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2073,6 +2073,9 @@ importers: specifier: ^2.8.1 version: 2.8.1 devDependencies: + '@turf/boolean-equal': + specifier: workspace:^ + version: link:../turf-boolean-equal '@turf/truncate': specifier: workspace:^ version: link:../turf-truncate From e95daa9115d1d4ab1eb847de28636a10164daafc Mon Sep 17 00:00:00 2001 From: James Beard Date: Wed, 20 Nov 2024 22:42:46 +1100 Subject: [PATCH 3/5] Less wasteful way to check if start / end point should be removed as part of clean operation. --- packages/turf-clean-coords/index.ts | 55 +++++++++++------------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/packages/turf-clean-coords/index.ts b/packages/turf-clean-coords/index.ts index 5d09349359..8e02add71b 100644 --- a/packages/turf-clean-coords/index.ts +++ b/packages/turf-clean-coords/index.ts @@ -45,24 +45,14 @@ function cleanCoords( case "MultiLineString": case "Polygon": getCoords(geojson).forEach(function (line) { - // Run cleanLine twice in case polygon start point is one of the points - // that ought to get cleaned. - // https://github.com/Turfjs/turf/issues/2406 - const firstPass = cleanLine(line, type); - const secondPass = cleanLine(rotatePoints(firstPass), type); - newCoords.push(secondPass); + newCoords.push(cleanLine(line, type)); }); break; case "MultiPolygon": getCoords(geojson).forEach(function (polygons: any) { var polyPoints: Position[] = []; polygons.forEach(function (ring: Position[]) { - // Run cleanLine twice in case polygon start point is one of the points - // that ought to get cleaned. - // https://github.com/Turfjs/turf/issues/2406 - const firstPass = cleanLine(ring, type); - const secondPass = cleanLine(rotatePoints(firstPass), type); - polyPoints.push(secondPass); + polyPoints.push(cleanLine(ring, type)); }); newCoords.push(polyPoints); }); @@ -149,9 +139,26 @@ function cleanLine(line: Position[], type: string) { // No remaining points, so commit the current a-b segment. newPoints.push(points[b]); - // (Multi)Polygons must have at least 4 points and be closed. if (type === "Polygon" || type === "MultiPolygon") { - if (!equals(points[0], points[points.length - 1])) { + // For polygons need to make sure the start / end point wasn't one of the + // points that needed to be cleaned. + // https://github.com/Turfjs/turf/issues/2406 + // For points [a, b, c, ..., z, a] + // if a is on line b-z, it too can be removed. New array becomes + // [b, c, ..., z, b] + if ( + booleanPointOnLine( + newPoints[0], + lineString([newPoints[1], points[newPoints.length - 2]]) + ) + ) { + newPoints.shift(); // Discard starting point. + newPoints.pop(); // Discard closing point. + newPoints.push(newPoints[0]); // Duplicate the new closing point to end of array. + } + + // (Multi)Polygons must have at least 4 points and be closed. + if (!equals(newPoints[0], newPoints[newPoints.length - 1])) { throw new Error("invalid polygon, first and last points not equal"); } if (newPoints.length < 4) { @@ -174,25 +181,5 @@ function equals(pt1: Position, pt2: Position) { return pt1[0] === pt2[0] && pt1[1] === pt2[1]; } -/** - * Rotates a set of points to alter the start point of the polygon. - * - * @private - * @param {Position[]} pts Points to rotate - * @returns {Position[]} Rotated points - */ -function rotatePoints(pts: Position[]) { - // Don't alter the input pts array. Avoid side effects. - const points = pts.slice(); - if (!equals(points[0], points[points.length - 1])) { - // Not a closed polygon so don't touch it. - return points; - } - points.pop(); // Discard duplicate closing point temporarily. - points.push(points.shift() as Position); // Shuffle points along a place. - points.push(points[0]); // Duplicate the new closing point to end of array. - return points; -} - export { cleanCoords }; export default cleanCoords; From c454a03fcc10f40905a8e1bbab3f1c4596e0280d Mon Sep 17 00:00:00 2001 From: James Beard Date: Tue, 17 Dec 2024 07:22:58 +1100 Subject: [PATCH 4/5] Made unit test messages more descriptive and added to contributors. --- packages/turf-clean-coords/package.json | 3 ++- packages/turf-clean-coords/test.ts | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/turf-clean-coords/package.json b/packages/turf-clean-coords/package.json index 759e2716a9..3af6e79fb0 100644 --- a/packages/turf-clean-coords/package.json +++ b/packages/turf-clean-coords/package.json @@ -4,7 +4,8 @@ "description": "turf clean-coords module", "author": "Turf Authors", "contributors": [ - "Stefano Borghi <@stebogit>" + "Stefano Borghi <@stebogit>", + "James Beard <@smallsaucepan>" ], "license": "MIT", "bugs": { diff --git a/packages/turf-clean-coords/test.ts b/packages/turf-clean-coords/test.ts index 899436b0e6..8244d62c28 100644 --- a/packages/turf-clean-coords/test.ts +++ b/packages/turf-clean-coords/test.ts @@ -156,7 +156,7 @@ test("turf-clean-coords -- prevent input mutation", (t) => { t.end(); }); -test("turf-clean-coords -- issue 2305", (t) => { +test("turf-clean-coords - north south lines - issue 2305", (t) => { // From https://github.com/Turfjs/turf/issues/2305#issue-1287442870 t.deepEqual( cleanCoords( @@ -170,7 +170,8 @@ test("turf-clean-coords -- issue 2305", (t) => { [0, 0], [0, 1], [0, 0], - ]) + ]), + "northern turnaround point is kept" ); // From https://github.com/Turfjs/turf/issues/2305#issue-1287442870 @@ -188,13 +189,14 @@ test("turf-clean-coords -- issue 2305", (t) => { [0, 0], [0, 2], [0, 0], - ]) + ]), + "northern turnaround point is kept" ); t.end(); }); -test("turf-clean-coords -- issue 2740", (t) => { +test("turf-clean-coords - overly aggressive removal - issue 2740", (t) => { // Issue 2740 is cleanCoords was too aggresive at removing points. t.deepEqual( cleanCoords( @@ -209,7 +211,7 @@ test("turf-clean-coords -- issue 2740", (t) => { [0, 2], [0, 0], ]), - "#2740 north-south retraced line" + "north-south retraced line turnaround point kept" ); t.deepEqual( @@ -227,7 +229,7 @@ test("turf-clean-coords -- issue 2740", (t) => { [0, 3], [0, 0], ]), - "#2740 north-south retraced line extra points" + "north-south retraced line properly cleaned" ); t.deepEqual( @@ -247,13 +249,13 @@ test("turf-clean-coords -- issue 2740", (t) => { [0, -2], [0, 0], ]), - "#2740 north-south retraced past origin and back to start" + "north-south retraced past origin and back to start line properly cleaned" ); t.end(); }); -test("turf-clean-coords -- issue 2406", (t) => { +test("turf-clean-coords - start point protected - issue 2406", (t) => { t.true( booleanEqual( cleanCoords( @@ -279,7 +281,7 @@ test("turf-clean-coords -- issue 2406", (t) => { ], ]) ), - "#2406 polygon start point (a) should also be removed" + "polygon start point (a) was also removed" ); t.end(); From 3ed5b230649738fc70606f11f2ca2d930b21aa7d Mon Sep 17 00:00:00 2001 From: James Beard Date: Sat, 21 Dec 2024 18:13:02 +1100 Subject: [PATCH 5/5] Fixed issue where a polygon that got cleaned down to a single line segment was causing errors - started checking number of points before doing other tests. Replaced our own booleanEquals with external geojsonEquality. booleanEquals uses cleanCoords, which we're using to check ... cleanCoords. Fixed a couple of linting issues. --- packages/turf-boolean-disjoint/index.ts | 1 - packages/turf-clean-coords/index.ts | 8 +-- packages/turf-clean-coords/package.json | 2 +- packages/turf-clean-coords/test.ts | 38 ++++++++++++- packages/turf-meta/test.ts | 2 +- pnpm-lock.yaml | 74 +++++++++++++++++++++++-- 6 files changed, 110 insertions(+), 15 deletions(-) diff --git a/packages/turf-boolean-disjoint/index.ts b/packages/turf-boolean-disjoint/index.ts index 60e89c23ff..4bcc07c426 100644 --- a/packages/turf-boolean-disjoint/index.ts +++ b/packages/turf-boolean-disjoint/index.ts @@ -65,7 +65,6 @@ function booleanDisjoint( function disjoint(geom1: any, geom2: any, ignoreSelfIntersections: boolean) { switch (geom1.type) { case "Point": - /* eslint-disable @typescript-eslint/no-unused-vars */ switch (geom2.type) { case "Point": return !compareCoords(geom1.coordinates, geom2.coordinates); diff --git a/packages/turf-clean-coords/index.ts b/packages/turf-clean-coords/index.ts index 8e02add71b..9c8660252c 100644 --- a/packages/turf-clean-coords/index.ts +++ b/packages/turf-clean-coords/index.ts @@ -149,7 +149,7 @@ function cleanLine(line: Position[], type: string) { if ( booleanPointOnLine( newPoints[0], - lineString([newPoints[1], points[newPoints.length - 2]]) + lineString([newPoints[1], newPoints[newPoints.length - 2]]) ) ) { newPoints.shift(); // Discard starting point. @@ -158,12 +158,12 @@ function cleanLine(line: Position[], type: string) { } // (Multi)Polygons must have at least 4 points and be closed. - if (!equals(newPoints[0], newPoints[newPoints.length - 1])) { - throw new Error("invalid polygon, first and last points not equal"); - } if (newPoints.length < 4) { throw new Error("invalid polygon, fewer than 4 points"); } + if (!equals(newPoints[0], newPoints[newPoints.length - 1])) { + throw new Error("invalid polygon, first and last points not equal"); + } } return newPoints; diff --git a/packages/turf-clean-coords/package.json b/packages/turf-clean-coords/package.json index 3af6e79fb0..72f767e74b 100644 --- a/packages/turf-clean-coords/package.json +++ b/packages/turf-clean-coords/package.json @@ -55,11 +55,11 @@ "test:types": "tsc --esModuleInterop --module node16 --moduleResolution node16 --noEmit --strict types.ts" }, "devDependencies": { - "@turf/boolean-equal": "workspace:^", "@turf/truncate": "workspace:^", "@types/benchmark": "^2.1.5", "@types/tape": "^4.13.4", "benchmark": "^2.1.4", + "geojson-equality-ts": "^1.0.2", "load-json-file": "^7.0.1", "npm-run-all": "^4.1.5", "tape": "^5.9.0", diff --git a/packages/turf-clean-coords/test.ts b/packages/turf-clean-coords/test.ts index 8244d62c28..8bd99215f9 100644 --- a/packages/turf-clean-coords/test.ts +++ b/packages/turf-clean-coords/test.ts @@ -1,10 +1,10 @@ import fs from "fs"; import test from "tape"; import path from "path"; +import { geojsonEquality } from "geojson-equality-ts"; import { fileURLToPath } from "url"; import { loadJsonFileSync } from "load-json-file"; import { truncate } from "@turf/truncate"; -import { booleanEqual } from "@turf/boolean-equal"; import { point, multiPoint, @@ -40,7 +40,7 @@ test("turf-clean-coords", (t) => { if (process.env.REGEN) writeJsonFileSync(directories.out + filename, results); t.true( - booleanEqual(results, loadJsonFileSync(directories.out + filename)), + geojsonEquality(results, loadJsonFileSync(directories.out + filename)), name ); }); @@ -257,7 +257,7 @@ test("turf-clean-coords - overly aggressive removal - issue 2740", (t) => { test("turf-clean-coords - start point protected - issue 2406", (t) => { t.true( - booleanEqual( + geojsonEquality( cleanCoords( polygon([ [ @@ -286,3 +286,35 @@ test("turf-clean-coords - start point protected - issue 2406", (t) => { t.end(); }); + +test("turf-clean-coords - multipolygon - issue #918", (t) => { + // Copied from turf-simplify as (at heart) it's cleanCoords that's being + // tested here. + // simplify hangs on this input #918 + t.throws( + () => + cleanCoords( + multiPolygon([ + [ + [ + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + [0, 90], + ], + ], + ]) + ), + /invalid polygon/, + "invalid polygon" + ); + + t.end(); +}); diff --git a/packages/turf-meta/test.ts b/packages/turf-meta/test.ts index a8589fe0e3..8c59c0e0bd 100644 --- a/packages/turf-meta/test.ts +++ b/packages/turf-meta/test.ts @@ -532,7 +532,7 @@ test("null geometries", (t) => { 0, "coordReduce" ); - /* eslint-enable no-return-assign */ + t.end(); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e92dab0aea..ca8de70d22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2073,9 +2073,6 @@ importers: specifier: ^2.8.1 version: 2.8.1 devDependencies: - '@turf/boolean-equal': - specifier: workspace:^ - version: link:../turf-boolean-equal '@turf/truncate': specifier: workspace:^ version: link:../turf-truncate @@ -2088,6 +2085,9 @@ importers: benchmark: specifier: ^2.1.4 version: 2.1.4 + geojson-equality-ts: + specifier: ^1.0.2 + version: 1.0.2 load-json-file: specifier: ^7.0.1 version: 7.0.1 @@ -2099,7 +2099,7 @@ importers: version: 5.9.0 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.40)(tsx@4.19.2)(typescript@5.5.4) + version: 8.3.5(tsx@4.19.2)(typescript@5.5.4) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -12103,7 +12103,6 @@ packages: resolution: {integrity: sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==} dependencies: '@types/geojson': 7946.0.14 - dev: false /geojson-polygon-self-intersections@1.2.1: resolution: {integrity: sha512-/QM1b5u2d172qQVO//9CGRa49jEmclKEsYOQmWP9ooEjj63tBM51m2805xsbxkzlEELQ2REgTf700gUhhlegxA==} @@ -15416,6 +15415,28 @@ packages: tsx: 4.19.2 dev: true + /postcss-load-config@6.0.1(tsx@4.19.2): + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + dependencies: + lilconfig: 3.1.3 + tsx: 4.19.2 + dev: true + /postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -17030,6 +17051,49 @@ packages: - yaml dev: true + /tsup@8.3.5(tsx@4.19.2)(typescript@5.5.4): + resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + dependencies: + bundle-require: 5.0.0(esbuild@0.24.0) + cac: 6.7.14 + chokidar: 4.0.1 + consola: 3.2.3 + debug: 4.3.7 + esbuild: 0.24.0 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(tsx@4.19.2) + resolve-from: 5.0.0 + rollup: 4.28.0 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tinyexec: 0.3.1 + tinyglobby: 0.2.10 + tree-kill: 1.2.2 + typescript: 5.5.4 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + dev: true + /tsx@4.19.2: resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} engines: {node: '>=18.0.0'}