From e3860f429a9bc8250f260548c209c5cb4cd98e95 Mon Sep 17 00:00:00 2001 From: Samir Shah Date: Thu, 9 Nov 2023 10:23:11 +0300 Subject: [PATCH] Replace geojson-equality with a fresh implementation that fixes precision handling. --- packages/turf-boolean-equal/index.ts | 2 +- packages/turf-boolean-equal/package.json | 2 - packages/turf-boolean-overlap/index.ts | 2 +- packages/turf-boolean-overlap/package.json | 2 - packages/turf-helpers/index.ts | 1 + packages/turf-helpers/lib/geojson-equality.ts | 192 +++ packages/turf-helpers/test.js | 1070 +++++++++++++++++ yarn.lock | 13 - 8 files changed, 1265 insertions(+), 19 deletions(-) create mode 100644 packages/turf-helpers/lib/geojson-equality.ts diff --git a/packages/turf-boolean-equal/index.ts b/packages/turf-boolean-equal/index.ts index a9d123106a..dd939c94f7 100644 --- a/packages/turf-boolean-equal/index.ts +++ b/packages/turf-boolean-equal/index.ts @@ -1,5 +1,5 @@ import { Feature, Geometry } from "geojson"; -import GeojsonEquality from "geojson-equality"; +import { GeojsonEquality } from "@turf/helpers"; import cleanCoords from "@turf/clean-coords"; import { getGeom } from "@turf/invariant"; diff --git a/packages/turf-boolean-equal/package.json b/packages/turf-boolean-equal/package.json index c82aa4a1c2..6078d69a65 100644 --- a/packages/turf-boolean-equal/package.json +++ b/packages/turf-boolean-equal/package.json @@ -54,7 +54,6 @@ "test:tape": "tsx test.js" }, "devDependencies": { - "@types/geojson-equality": "^0.2.0", "@types/tape": "*", "benchmark": "*", "boolean-shapely": "*", @@ -69,7 +68,6 @@ "@turf/clean-coords": "^7.0.0-alpha.2", "@turf/helpers": "^7.0.0-alpha.2", "@turf/invariant": "^7.0.0-alpha.2", - "geojson-equality": "0.1.6", "tslib": "^2.3.0" } } diff --git a/packages/turf-boolean-overlap/index.ts b/packages/turf-boolean-overlap/index.ts index 5526799e84..5f1001dc42 100644 --- a/packages/turf-boolean-overlap/index.ts +++ b/packages/turf-boolean-overlap/index.ts @@ -3,7 +3,7 @@ import { segmentEach } from "@turf/meta"; import { getGeom } from "@turf/invariant"; import lineOverlap from "@turf/line-overlap"; import lineIntersect from "@turf/line-intersect"; -import GeojsonEquality from "geojson-equality"; +import { GeojsonEquality } from "@turf/helpers"; /** * Compares two geometries of the same dimension and returns true if their intersection set results in a geometry diff --git a/packages/turf-boolean-overlap/package.json b/packages/turf-boolean-overlap/package.json index 27cffedab2..c98f9c1770 100755 --- a/packages/turf-boolean-overlap/package.json +++ b/packages/turf-boolean-overlap/package.json @@ -53,7 +53,6 @@ "test:tape": "tsx test.js" }, "devDependencies": { - "@types/geojson-equality": "^0.2.0", "@types/tape": "*", "benchmark": "*", "boolean-shapely": "*", @@ -70,7 +69,6 @@ "@turf/line-intersect": "^7.0.0-alpha.2", "@turf/line-overlap": "^7.0.0-alpha.2", "@turf/meta": "^7.0.0-alpha.2", - "geojson-equality": "0.1.6", "tslib": "^2.3.0" } } diff --git a/packages/turf-helpers/index.ts b/packages/turf-helpers/index.ts index 5c12679377..ba0693baa6 100644 --- a/packages/turf-helpers/index.ts +++ b/packages/turf-helpers/index.ts @@ -17,6 +17,7 @@ import { import { Id } from "./lib/geojson"; export * from "./lib/geojson"; +export * from "./lib/geojson-equality"; // TurfJS Combined Types export type Coord = Feature | Point | Position; diff --git a/packages/turf-helpers/lib/geojson-equality.ts b/packages/turf-helpers/lib/geojson-equality.ts new file mode 100644 index 0000000000..8005f2e771 --- /dev/null +++ b/packages/turf-helpers/lib/geojson-equality.ts @@ -0,0 +1,192 @@ +import { + Feature, + LineString, + Position, + GeoJSON, + Point, + Polygon, + GeometryCollection, + FeatureCollection, + MultiLineString, + MultiPoint, + MultiPolygon, +} from "geojson"; +import equal from "deep-equal"; + +/** + + * GeoJSON equality checking utility. + * Adapted from https://github.com/geosquare/geojson-equality + * + * @memberof helpers + * @type {Class} + */ +export class GeojsonEquality { + private precision: number; + private direction = false; + + constructor(opts?: { precision?: number; direction?: boolean }) { + this.precision = 10 ** -(opts?.precision ?? 17); + this.direction = opts?.direction ?? false; + } + + compare(g1: GeoJSON, g2: GeoJSON): boolean { + if (g1.type !== g2.type) { + return false; + } + + if (!sameLength(g1, g2)) { + return false; + } + + switch (g1.type) { + case "Point": + return this.compareCoord(g1.coordinates, (g2 as Point).coordinates); + case "LineString": + return this.compareLine(g1.coordinates, (g2 as LineString).coordinates); + case "Polygon": + return this.comparePolygon(g1, g2 as Polygon); + case "GeometryCollection": + return this.compareGeometryCollection(g1, g2 as GeometryCollection); + case "Feature": + return this.compareFeature(g1, g2 as Feature); + case "FeatureCollection": + return this.compareFeatureCollection(g1, g2 as FeatureCollection); + default: + if (g1.type.startsWith("Multi")) { + const g1s = explode(g1); + const g2s = explode( + g2 as MultiLineString | MultiPoint | MultiPolygon + ); + return g1s.every((g1part) => + g2s.some((g2part) => this.compare(g1part as any, g2part as any)) + ); + } + } + return false; + } + + private compareCoord(c1: Position, c2: Position) { + return ( + c1.length === c2.length && + c1.every((c, i) => Math.abs(c - c2[i]) < this.precision) + ); + } + + private compareLine( + path1: Position[], + path2: Position[], + ind = 0, + isPoly = false + ): boolean { + if (!sameLength(path1, path2)) { + return false; + } + const p1 = path1; + let p2 = path2; + if (isPoly && !this.compareCoord(p1[0], p2[0])) { + // fix start index of both to same point + const startIndex = this.fixStartIndex(p2, p1); + if (!startIndex) { + return false; + } else { + p2 = startIndex; + } + } + // for linestring ind =0 and for polygon ind =1 + const sameDirection = this.compareCoord(p1[ind], p2[ind]); + if (this.direction || sameDirection) { + return this.comparePath(p1, p2); + } else { + if (this.compareCoord(p1[ind], p2[p2.length - (1 + ind)])) { + return this.comparePath(p1.slice().reverse(), p2); + } + return false; + } + } + + private fixStartIndex(sourcePath: Position[], targetPath: Position[]) { + //make sourcePath first point same as of targetPath + let correctPath, + ind = -1; + for (let i = 0; i < sourcePath.length; i++) { + if (this.compareCoord(sourcePath[i], targetPath[0])) { + ind = i; + break; + } + } + if (ind >= 0) { + correctPath = ([] as Position[]).concat( + sourcePath.slice(ind, sourcePath.length), + sourcePath.slice(1, ind + 1) + ); + } + return correctPath; + } + + private comparePath(p1: Position[], p2: Position[]) { + return p1.every((c, i) => this.compareCoord(c, p2[i])); + } + + private comparePolygon(g1: Polygon, g2: Polygon) { + if (this.compareLine(g1.coordinates[0], g2.coordinates[0], 1, true)) { + const holes1 = g1.coordinates.slice(1, g1.coordinates.length); + const holes2 = g2.coordinates.slice(1, g2.coordinates.length); + return holes1.every((h1) => + holes2.some((h2) => this.compareLine(h1, h2, 1, true)) + ); + } + return false; + } + + private compareGeometryCollection( + g1: GeometryCollection, + g2: GeometryCollection + ) { + return ( + sameLength(g1.geometries, g2.geometries) && + this.compareBBox(g1, g2) && + g1.geometries.every((g, i) => this.compare(g, g2.geometries[i])) + ); + } + + private compareFeature(g1: Feature, g2: Feature) { + return ( + g1.id === g2.id && + equal(g1.properties, g2.properties) && + this.compareBBox(g1, g2) && + this.compare(g1.geometry, g2.geometry) + ); + } + + private compareFeatureCollection( + g1: FeatureCollection, + g2: FeatureCollection + ) { + return ( + sameLength(g1.features, g2.features) && + this.compareBBox(g1, g2) && + g1.features.every((f, i) => this.compare(f, g2.features[i])) + ); + } + + private compareBBox(g1: GeoJSON, g2: GeoJSON): boolean { + return ( + Boolean(!g1.bbox && !g2.bbox) || + (g1.bbox && g2.bbox ? this.compareCoord(g1.bbox, g2.bbox) : false) + ); + } +} + +function sameLength(g1: any, g2: any) { + return g1.coordinates + ? g1.coordinates.length === g2.coordinates.length + : g1.length === g2.length; +} + +function explode(g: MultiLineString | MultiPoint | MultiPolygon) { + return g.coordinates.map((part) => ({ + type: g.type.replace("Multi", ""), + coordinates: part, + })); +} diff --git a/packages/turf-helpers/test.js b/packages/turf-helpers/test.js index a29258bf88..a02db6770e 100644 --- a/packages/turf-helpers/test.js +++ b/packages/turf-helpers/test.js @@ -21,6 +21,7 @@ const { isObject, isNumber, earthRadius, + GeojsonEquality, } = require("./index"); const turf = require("./index"); @@ -792,3 +793,1072 @@ test("turf-helpers -- Issue #1284 - Prevent mutating Properties", (t) => { t.deepEqual(feature(coord).properties, {}); t.end(); }); + +test("turf-helpers -- GeojsonEquality for Point", (t) => { + const eq = new GeojsonEquality(); + const g1 = { type: "Point", coordinates: [30, 10] }, + g2 = { type: "Point", coordinates: [30, 10] }, + g3 = { type: "Point", coordinates: [30, 11] }, + g4 = { type: "Point", coordinates: [30, 10, 5] }, + g5 = { type: "Point", coordinates: [30, 10, 5] }; + + t.true(eq.compare(g1, g2)); + t.false(eq.compare(g1, g3)); + t.false(eq.compare(g1, g4)); + t.true(eq.compare(g4, g5)); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for LineString", (t) => { + const eq = new GeojsonEquality(); + const g1 = { + type: "LineString", + coordinates: [ + [30, 10], + [10, 30], + [40, 40], + ], + }, + g2 = { + type: "LineString", + coordinates: [ + [30, 10], + [10, 30], + [40, 40], + ], + }, + g3 = { + type: "LineString", + coordinates: [ + [31, 10], + [10, 30], + [40, 40], + ], + }, + g4 = { + type: "LineString", + coordinates: [ + [40, 40], + [10, 30], + [30, 10], + ], + }; + + t.true(eq.compare(g1, g2)); + t.false(eq.compare(g1, g3)); + t.true(eq.compare(g1, g4)); // reverse direction, direction is not matched + + const eqWithDirection = new GeojsonEquality({ direction: true }); + t.false(eqWithDirection.compare(g1, g4)); // reverse direction, direction is matched + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for Polygon", (t) => { + const eq = new GeojsonEquality(), + eqWithDirection = new GeojsonEquality({ direction: true }); + const g1 = { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [40, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + g2 = { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [40, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + g3 = { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + g4 = { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [10, 20], + [20, 40], + [40, 40], + [30, 10], + ], + ], + }, + g5 = { + type: "Polygon", + coordinates: [ + [ + [10, 20], + [20, 40], + [40, 40], + [30, 10], + [10, 20], + ], + ], + }, + gh1 = { + type: "Polygon", + coordinates: [ + [ + [45, 45], + [15, 40], + [10, 20], + [35, 10], + [45, 45], + ], + [ + [20, 30], + [35, 35], + [30, 20], + [20, 30], + ], + ], + }, + gh2 = { + type: "Polygon", + coordinates: [ + [ + [35, 10], + [45, 45], + [15, 40], + [10, 20], + [35, 10], + ], + [ + [20, 30], + [35, 35], + [30, 20], + [20, 30], + ], + ], + }, + gprecision1 = { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [40.12345, 40.12345], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + gprecision2 = { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [40.123389, 40.123378], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }; + + t.true(eq.compare(g1, g2)); + t.false(eq.compare(g1, g3)); + + t.true(eq.compare(g1, g4)); // reverse direction, direction is not matched + t.false(eqWithDirection.compare(g1, g4)); // reverse direction, direction is matched + + t.true(eq.compare(g1, g5)); // reverse direction, diff start index, direction is not matched + t.false(eqWithDirection.compare(g1, g5)); // reverse direction, diff start index, direction is matched + + t.true(eq.compare(gh1, gh2)); // with holes, diff start ind, direction is not matched + + const lowPrecisioneq = new GeojsonEquality({ precision: 3 }); + + t.true(lowPrecisioneq.compare(gprecision1, gprecision2)); + + const highPrecisioneq = new GeojsonEquality({ precision: 10 }); + + t.false(highPrecisioneq.compare(gprecision1, gprecision2)); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for Feature", (t) => { + const eq = new GeojsonEquality(); + + t.false( + eq.compare({ type: "Feature", id: "id1" }, { type: "Feature", id: "id2" }) + ); + + t.false( + eq.compare( + { type: "Feature", id: "id1", properties: { foo: "bar" } }, + { type: "Feature", id: "id1", properties: { foo1: "bar", foo2: "bar" } } + ) + ); + + t.false( + eq.compare( + { type: "Feature", id: "id1", properties: { foo1: "bar" } }, + { type: "Feature", id: "id1", properties: { foo2: "bar" } } + ) + ); + + t.false( + eq.compare( + { type: "Feature", id: "id1", properties: { foo: "bar1" } }, + { type: "Feature", id: "id1", properties: { foo: "bar2" } } + ) + ); + + t.false( + eq.compare( + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + }, + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [40, 20], + [31, 10], + [30, 20], + [30, 10], + [10, 40], + ], + ], + }, + } + ) + ); + + t.true( + eq.compare( + { + type: "Feature", + id: "id1", + properties: { foo: { bar: "baz" } }, + geometry: { type: "Point", coordinates: [0, 1] }, + }, + { + type: "Feature", + id: "id1", + properties: { foo: { bar: "baz" } }, + geometry: { type: "Point", coordinates: [0, 1] }, + } + ) + ); + + t.false( + eq.compare( + { + type: "Feature", + id: "id1", + properties: { foo: { bar: "baz" } }, + geometry: { type: "Point", coordinates: [0, 1] }, + }, + { + type: "Feature", + id: "id1", + properties: { foo: { bar: "baz2" } }, + geometry: { type: "Point", coordinates: [0, 1] }, + } + ) + ); + + t.false( + eq.compare( + { type: "Feature", id: "id1", bbox: [1, 2, 3, 4] }, + { type: "Feature", id: "id1" } + ) + ); + + t.false( + eq.compare( + { type: "Feature", id: "id1", bbox: [1, 2, 3, 4] }, + { type: "Feature", id: "id1", bbox: [1, 2, 3, 5] } + ) + ); + + t.true( + eq.compare( + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + bbox: [10, 10, 41, 40], + }, + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + bbox: [10, 10, 41, 40], + } + ) + ); + + t.false( + eq.compare( + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + bbox: [10, 10, 41, 40], + }, + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 1], + ], + ], + }, + bbox: [10, 10, 41, 40], + } + ) + ); + + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for FeatureCollection", (t) => { + const eq = new GeojsonEquality(); + + t.false( + eq.compare( + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] }, + }, + ], + }, + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] }, + }, + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] }, + }, + ], + } + ) + ); + + t.false( + eq.compare( + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] }, + }, + ], + }, + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [1, 1] }, + }, + ], + } + ) + ); + + t.false( + eq.compare( + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] }, + }, + { + type: "Feature", + geometry: { type: "Point", coordinates: [1, 1] }, + }, + ], + }, + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [1, 1] }, + }, + { + type: "Feature", + geometry: { type: "Point", coordinates: [0, 0] }, + }, + ], + } + ) + ); + + t.true( + eq.compare( + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [1, 1] }, + }, + ], + }, + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { type: "Point", coordinates: [1, 1] }, + }, + ], + } + ) + ); + + t.true( + eq.compare( + { + type: "FeatureCollection", + features: [], + }, + { + type: "FeatureCollection", + features: [], + } + ) + ); + + t.false( + eq.compare( + { type: "FeatureCollection", features: [], bbox: [1, 2, 3, 4] }, + { type: "FeatureCollection", features: "[]" } + ) + ); + + t.false( + eq.compare( + { type: "FeatureCollection", features: [], bbox: [1, 2, 3, 4] }, + { type: "FeatureCollection", features: [], bbox: [1, 2, 3, 5] } + ) + ); + + t.true( + eq.compare( + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + }, + ], + bbox: [10, 10, 41, 40], + }, + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + }, + ], + bbox: [10, 10, 41, 40], + } + ) + ); + + t.false( + eq.compare( + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + }, + ], + bbox: [10, 10, 41, 40], + }, + { + type: "FeatureCollection", + features: [ + { + type: "Feature", + id: "id1", + properties: { foo: "bar1" }, + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 1], + ], + ], + }, + }, + ], + bbox: [10, 10, 41, 40], + } + ) + ); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for MultiPoint", (t) => { + const eq = new GeojsonEquality(); + + const g1 = { + type: "MultiPoint", + coordinates: [ + [0, 40], + [40, 30], + [20, 20], + [30, 10], + ], + }, + g2 = { + type: "MultiPoint", + coordinates: [ + [0, 40], + [20, 20], + [40, 30], + [30, 10], + ], + }, + g3 = { + type: "MultiPoint", + coordinates: [ + [10, 40], + [20, 20], + [40, 30], + [30, 10], + ], + }; + + t.true(eq.compare(g1, g2)); + t.false(eq.compare(g1, g3)); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for MultiLineString", (t) => { + const eq = new GeojsonEquality(); + const eqWithDirection = new GeojsonEquality({ direction: true }); + const g1 = { + type: "MultiLineString", + coordinates: [ + [ + [30, 10], + [10, 30], + [40, 40], + ], + [ + [0, 10], + [10, 0], + [40, 40], + ], + ], + }, + g2 = { + type: "MultiLineString", + coordinates: [ + [ + [40, 40], + [10, 30], + [30, 10], + ], + [ + [0, 10], + [10, 0], + [40, 40], + ], + ], + }, + g3 = { + type: "MultiLineString", + coordinates: [ + [ + [10, 10], + [20, 20], + [10, 40], + ], + [ + [40, 40], + [30, 30], + [40, 20], + [30, 10], + ], + ], + }; + + t.true(eq.compare(g1, g2)); + t.false(eqWithDirection.compare(g1, g2)); + t.false(eq.compare(g1, g3)); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for MultiPolygon", (t) => { + const eq = new GeojsonEquality(); + const g1 = { + type: "MultiPolygon", + coordinates: [ + [ + [ + [30, 20], + [45, 40], + [10, 40], + [30, 20], + ], + ], + [ + [ + [15, 5], + [40, 10], + [10, 20], + [5, 10], + [15, 5], + ], + ], + ], + }, + g2 = { + type: "MultiPolygon", + coordinates: [ + [ + [ + [30, 20], + [45, 40], + [10, 40], + [30, 20], + ], + ], + [ + [ + [15, 5], + [40, 10], + [10, 20], + [5, 10], + [15, 5], + ], + ], + ], + }, + g3 = { + type: "MultiPolygon", + coordinates: [ + [ + [ + [30, 20], + [45, 40], + [10, 40], + [30, 20], + ], + ], + [ + [ + [15, 5], + [400, 10], + [10, 20], + [5, 10], + [15, 5], + ], + ], + ], + }, + gh1 = { + type: "MultiPolygon", + coordinates: [ + [ + [ + [40, 40], + [20, 45], + [45, 30], + [40, 40], + ], + ], + [ + [ + [20, 35], + [10, 30], + [10, 10], + [30, 5], + [45, 20], + [20, 35], + ], + [ + [30, 20], + [20, 15], + [20, 25], + [30, 20], + ], + [ + [20, 10], + [30, 10], + [30, 15], + [20, 10], + ], + ], + ], + }, + gh2 = { + type: "MultiPolygon", + coordinates: [ + [ + [ + [20, 35], + [10, 30], + [10, 10], + [30, 5], + [45, 20], + [20, 35], + ], + [ + [20, 10], + [30, 10], + [30, 15], + [20, 10], + ], + [ + [30, 20], + [20, 15], + [20, 25], + [30, 20], + ], + ], + [ + [ + [40, 40], + [20, 45], + [45, 30], + [40, 40], + ], + ], + ], + }; + + t.true(eq.compare(g1, g2)); + t.false(eq.compare(g1, g3)); + + t.true(eq.compare(gh1, gh2)); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality for GeometryCollection", (t) => { + const eq = new GeojsonEquality(); + + t.false( + eq.compare( + { + type: "GeometryCollection", + geometries: [{ type: "Point", coordinates: [0, 0] }], + }, + { + type: "GeometryCollection", + geometries: [ + { type: "Point", coordinates: [0, 0] }, + { type: "Point", coordinates: [0, 0] }, + ], + } + ) + ); + + t.false( + eq.compare( + { + type: "GeometryCollection", + geometries: [{ type: "Point", coordinates: [0, 0] }], + }, + { + type: "GeometryCollection", + geometries: [{ type: "Point", coordinates: [1, 1] }], + } + ) + ); + + t.false( + eq.compare( + { + type: "GeometryCollection", + geometries: [ + { type: "Point", coordinates: [0, 0] }, + { type: "Point", coordinates: [1, 1] }, + ], + }, + { + type: "GeometryCollection", + geometries: [ + { type: "Point", coordinates: [1, 1] }, + { type: "Point", coordinates: [0, 0] }, + ], + } + ) + ); + + t.true( + eq.compare( + { + type: "GeometryCollection", + geometries: [{ type: "Point", coordinates: [0, 0] }], + }, + { + type: "GeometryCollection", + geometries: [{ type: "Point", coordinates: [0, 0] }], + } + ) + ); + + t.true( + eq.compare( + { + type: "GeometryCollection", + geometries: [], + }, + { + type: "GeometryCollection", + geometries: [], + } + ) + ); + + t.false( + eq.compare( + { type: "GeometryCollection", geometries: [], bbox: [1, 2, 3, 4] }, + { type: "GeometryCollection", geometries: [] } + ) + ); + + t.false( + eq.compare( + { type: "GeometryCollection", geometries: [], bbox: [1, 2, 3, 4] }, + { type: "GeometryCollection", geometries: [], bbox: [1, 2, 3, 5] } + ) + ); + + t.true( + eq.compare( + { + type: "GeometryCollection", + geometries: [ + { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + ], + bbox: [10, 10, 41, 40], + }, + { + type: "GeometryCollection", + geometries: [ + { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + ], + bbox: [10, 10, 41, 40], + } + ) + ); + + t.false( + eq.compare( + { + type: "GeometryCollection", + geometries: [ + { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + ], + bbox: [10, 10, 41, 40], + }, + { + type: "GeometryCollection", + geometries: [ + { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 1], + ], + ], + }, + ], + bbox: [10, 10, 41, 40], + } + ) + ); + t.end(); +}); + +test("turf-helpers -- GeojsonEquality precision", (t) => { + const eq6 = new GeojsonEquality({ precision: 6 }); + const eq7 = new GeojsonEquality({ precision: 7 }); + const eq10 = new GeojsonEquality({ precision: 10 }); + + const g1 = { type: "Point", coordinates: [100.55719328, 23.0365925] }; + const g2 = { type: "Point", coordinates: [100.5571932792, 23.0365924999] }; + + t.true(eq6.compare(g1, g2)); + t.true(eq7.compare(g1, g2)); + t.false(eq10.compare(g1, g2)); + t.end(); +}); diff --git a/yarn.lock b/yarn.lock index d899887f8c..7a73a5ae64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2173,13 +2173,6 @@ resolved "https://registry.yarnpkg.com/@types/extend/-/extend-3.0.2.tgz#cabe436ca01e981f4ec6a49b55effe5e4eb1b8c2" integrity sha512-CqDQhn7jxaN9zw7zAu926zIx51ZzMaX8U8Wa4jGpKI6jeBr9ejFE68AQ+h+ztfrNJD+leo7K1cLbvMjpHfZSRg== -"@types/geojson-equality@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@types/geojson-equality/-/geojson-equality-0.2.0.tgz#2c7d411f534bee6bf2e7689c14c4c6f3dced2a8c" - integrity sha512-JH6J3S3MW79WKbEuSrwosuu3oUnC6mujFL7UosxlcIWIHwC77Zg1+edkC4oZTRHXAp8SfqTisIEPBBOWLAwlzw== - dependencies: - "@types/geojson" "*" - "@types/geojson@*", "@types/geojson@7946.0.8": version "7946.0.8" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" @@ -5635,12 +5628,6 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -geojson-equality@0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/geojson-equality/-/geojson-equality-0.1.6.tgz#a171374ef043e5d4797995840bae4648e0752d72" - dependencies: - deep-equal "^1.0.0" - geojson-fixtures@*: version "1.0.0" resolved "https://registry.yarnpkg.com/geojson-fixtures/-/geojson-fixtures-1.0.0.tgz#7d2cf22b176cecf4b98efc1d65f3c5d3ee1b0ae8"