diff --git a/packages/turf-boolean-equal/index.ts b/packages/turf-boolean-equal/index.ts index ffd0729414..7936205d0d 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 9f8cff5d7a..b0ccd910fa 100644 --- a/packages/turf-boolean-equal/package.json +++ b/packages/turf-boolean-equal/package.json @@ -59,7 +59,6 @@ }, "devDependencies": { "@types/benchmark": "^2.1.5", - "@types/geojson-equality": "^0.2.2", "@types/tape": "^4.2.32", "benchmark": "^2.1.4", "boolean-shapely": "*", @@ -75,7 +74,6 @@ "@turf/clean-coords": "workspace:^", "@turf/helpers": "workspace:^", "@turf/invariant": "workspace:^", - "geojson-equality": "0.1.6", "tslib": "^2.6.2" } } diff --git a/packages/turf-boolean-overlap/index.ts b/packages/turf-boolean-overlap/index.ts index 74acd304ba..2527179437 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 9300004a1f..037a627c36 100755 --- a/packages/turf-boolean-overlap/package.json +++ b/packages/turf-boolean-overlap/package.json @@ -58,7 +58,6 @@ }, "devDependencies": { "@types/benchmark": "^2.1.5", - "@types/geojson-equality": "^0.2.2", "@types/tape": "^4.2.32", "benchmark": "^2.1.4", "boolean-shapely": "*", @@ -76,7 +75,6 @@ "@turf/line-intersect": "workspace:^", "@turf/line-overlap": "workspace:^", "@turf/meta": "workspace:^", - "geojson-equality": "0.1.6", "tslib": "^2.6.2" } } 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..cdbedd152e --- /dev/null +++ b/packages/turf-helpers/lib/geojson-equality.ts @@ -0,0 +1,198 @@ +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; + private compareProperties = true; + + constructor(opts?: { + precision?: number; + direction?: boolean; + compareProperties?: boolean; + }) { + this.precision = 10 ** -(opts?.precision ?? 17); + this.direction = opts?.direction ?? false; + this.compareProperties = opts?.compareProperties ?? true; + } + + 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 && + (this.compareProperties ? equal(g1.properties, g2.properties) : true) && + 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/package.json b/packages/turf-helpers/package.json index c2a55db3e0..5f10215f94 100644 --- a/packages/turf-helpers/package.json +++ b/packages/turf-helpers/package.json @@ -59,6 +59,7 @@ }, "devDependencies": { "@types/benchmark": "^2.1.5", + "@types/deep-equal": "^1.0.4", "@types/tape": "^4.2.32", "benchmark": "^2.1.4", "npm-run-all": "^4.1.5", @@ -68,6 +69,7 @@ "typescript": "^5.2.2" }, "dependencies": { + "deep-equal": "^2.2.3", "tslib": "^2.6.2" } } diff --git a/packages/turf-helpers/test.ts b/packages/turf-helpers/test.ts index a2a8ac7604..8c687cf66c 100644 --- a/packages/turf-helpers/test.ts +++ b/packages/turf-helpers/test.ts @@ -21,6 +21,7 @@ import { isObject, isNumber, earthRadius, + GeojsonEquality, } from "./index"; import * as turf from "./index"; @@ -792,3 +793,1114 @@ 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], + } + ) + ); + + const eqNoCompareProperties = new GeojsonEquality({ + compareProperties: false, + }); + t.true( + eqNoCompareProperties.compare( + { + type: "Feature", + id: "id1", + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + properties: { foo: "bar" }, + }, + { + type: "Feature", + id: "id1", + geometry: { + type: "Polygon", + coordinates: [ + [ + [30, 10], + [41, 40], + [20, 40], + [10, 20], + [30, 10], + ], + ], + }, + properties: { foo1: "bar", foo2: "bar" }, + } + ) + ); + + 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/packages/turf-line-overlap/package.json b/packages/turf-line-overlap/package.json index 528a8725fe..d30cef72d6 100644 --- a/packages/turf-line-overlap/package.json +++ b/packages/turf-line-overlap/package.json @@ -76,7 +76,7 @@ "@turf/line-segment": "workspace:^", "@turf/meta": "workspace:^", "@turf/nearest-point-on-line": "workspace:^", - "deep-equal": "^1.1.1", + "deep-equal": "^2.2.3", "tslib": "^2.6.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44ba1cd807..49a9f4a51b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1117,9 +1117,6 @@ importers: '@turf/invariant': specifier: workspace:^ version: link:../turf-invariant - geojson-equality: - specifier: 0.1.6 - version: 0.1.6 tslib: specifier: ^2.6.2 version: 2.6.2 @@ -1127,9 +1124,6 @@ importers: '@types/benchmark': specifier: ^2.1.5 version: 2.1.5 - '@types/geojson-equality': - specifier: ^0.2.2 - version: 0.2.2 '@types/tape': specifier: ^4.2.32 version: 4.13.4 @@ -1224,9 +1218,6 @@ importers: '@turf/meta': specifier: workspace:^ version: link:../turf-meta - geojson-equality: - specifier: 0.1.6 - version: 0.1.6 tslib: specifier: ^2.6.2 version: 2.6.2 @@ -1234,9 +1225,6 @@ importers: '@types/benchmark': specifier: ^2.1.5 version: 2.1.5 - '@types/geojson-equality': - specifier: ^0.2.2 - version: 0.2.2 '@types/tape': specifier: ^4.2.32 version: 4.13.4 @@ -3048,6 +3036,9 @@ importers: packages/turf-helpers: dependencies: + deep-equal: + specifier: ^2.2.3 + version: 2.2.3 tslib: specifier: ^2.6.2 version: 2.6.2 @@ -3055,6 +3046,9 @@ importers: '@types/benchmark': specifier: ^2.1.5 version: 2.1.5 + '@types/deep-equal': + specifier: ^1.0.4 + version: 1.0.4 '@types/tape': specifier: ^4.2.32 version: 4.13.4 @@ -3716,8 +3710,8 @@ importers: specifier: workspace:^ version: link:../turf-nearest-point-on-line deep-equal: - specifier: ^1.1.1 - version: 1.1.2 + specifier: ^2.2.3 + version: 2.2.3 tslib: specifier: ^2.6.2 version: 2.6.2 @@ -8676,12 +8670,6 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true - /@types/geojson-equality@0.2.2: - resolution: {integrity: sha512-KnMp/o7uMyab9jMqWuf+49C9LR22Z85HzE1s6BQOK2nbjWqLThScGjT8HoqDYw1T/cWcQcxax3hLB237kQ92lA==} - dependencies: - '@types/geojson': 7946.0.8 - dev: true - /@types/geojson@7946.0.8: resolution: {integrity: sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==} @@ -9265,7 +9253,6 @@ packages: dependencies: call-bind: 1.0.5 is-array-buffer: 3.0.2 - dev: true /array-differ@3.0.0: resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} @@ -9355,7 +9342,6 @@ packages: /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} - dev: true /axios@1.6.2: resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} @@ -10505,18 +10491,6 @@ packages: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true - /deep-equal@1.1.2: - resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==} - engines: {node: '>= 0.4'} - dependencies: - is-arguments: 1.1.1 - is-date-object: 1.0.5 - is-regex: 1.1.4 - object-is: 1.1.5 - object-keys: 1.1.1 - regexp.prototype.flags: 1.5.1 - dev: false - /deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} @@ -10539,7 +10513,6 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.1 which-typed-array: 1.1.13 - dev: true /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -11073,7 +11046,6 @@ packages: is-string: 1.0.7 isarray: 2.0.5 stop-iteration-iterator: 1.0.0 - dev: true /es-set-tostringtag@2.0.2: resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} @@ -11661,7 +11633,6 @@ packages: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 - dev: true /for-in@1.0.2: resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} @@ -11774,12 +11745,6 @@ packages: engines: {node: '>=6.9.0'} dev: true - /geojson-equality@0.1.6: - resolution: {integrity: sha512-TqG8YbqizP3EfwP5Uw4aLu6pKkg6JQK9uq/XZ1lXQntvTHD1BBKJWhNpJ2M0ax6TuWMP3oyx6Oq7FCIfznrgpQ==} - dependencies: - deep-equal: 1.1.2 - dev: false - /geojson-fixtures@1.0.0: resolution: {integrity: sha512-px8brZEL2HbIUoytDCsmQCEYXU5RHZYrSBMdfOH0MIp7dPqWO54ULi+E/vtwZCF8iVFxidj8GN2ysfOWpo+Gkw==} dependencies: @@ -12174,7 +12139,6 @@ packages: /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true /has-color@0.1.7: resolution: {integrity: sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw==} @@ -12513,7 +12477,6 @@ packages: get-intrinsic: 1.2.2 hasown: 2.0.0 side-channel: 1.0.4 - dev: true /ip@2.0.0: resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} @@ -12558,7 +12521,6 @@ packages: call-bind: 1.0.5 get-intrinsic: 1.2.2 is-typed-array: 1.1.12 - dev: true /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -12572,7 +12534,6 @@ packages: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: has-bigints: 1.0.2 - dev: true /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} @@ -12587,7 +12548,6 @@ packages: dependencies: call-bind: 1.0.5 has-tostringtag: 1.0.0 - dev: true /is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} @@ -12608,7 +12568,6 @@ packages: /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - dev: true /is-ci@3.0.1: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} @@ -12732,7 +12691,6 @@ packages: /is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - dev: true /is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} @@ -12753,7 +12711,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: true /is-number@3.0.0: resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} @@ -12840,13 +12797,11 @@ packages: /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - dev: true /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: call-bind: 1.0.5 - dev: true /is-ssh@1.4.0: resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==} @@ -12874,14 +12829,12 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: true /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 - dev: true /is-text-path@1.0.1: resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} @@ -12895,7 +12848,6 @@ packages: engines: {node: '>= 0.4'} dependencies: which-typed-array: 1.1.13 - dev: true /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -12928,7 +12880,6 @@ packages: /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - dev: true /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} @@ -12941,7 +12892,6 @@ packages: dependencies: call-bind: 1.0.5 get-intrinsic: 1.2.2 - dev: true /is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} @@ -12965,7 +12915,6 @@ packages: /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -14548,7 +14497,6 @@ packages: /object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - dev: true /object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} @@ -14576,7 +14524,6 @@ packages: define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - dev: true /object.pick@1.3.0: resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} @@ -15965,7 +15912,6 @@ packages: call-bind: 1.0.5 get-intrinsic: 1.2.2 object-inspect: 1.13.1 - dev: true /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -16265,7 +16211,6 @@ packages: engines: {node: '>= 0.4'} dependencies: internal-slot: 1.0.6 - dev: true /stream-array@1.1.2: resolution: {integrity: sha512-1yWdVsMEm/btiMa2YyHiC3mDrtAqlmNNaDRylx2F7KHhm3C4tA6kSR2V9mpeMthv+ujvbl8Kamyh5xaHHdFvyQ==} @@ -17441,7 +17386,6 @@ packages: is-number-object: 1.0.7 is-string: 1.0.7 is-symbol: 1.0.4 - dev: true /which-collection@1.0.1: resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} @@ -17450,7 +17394,6 @@ packages: is-set: 2.0.2 is-weakmap: 2.0.1 is-weakset: 2.0.2 - dev: true /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} @@ -17465,7 +17408,6 @@ packages: for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 - dev: true /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}