From dd5eaff86a43d5e2025a16497214f0401e3c1985 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Fri, 29 Mar 2024 11:24:41 -0700 Subject: [PATCH] feat: handle out of bounds wgs84 coords --- .../clip-to-bounds.ts | 4 +- src/standardize-geometry-filter/index.spec.ts | 216 ++++++++++++++---- src/standardize-geometry-filter/index.ts | 190 +++++++-------- .../project-coordinates.spec.ts | 26 ++- .../project-coordinates.ts | 21 +- .../traverse-coordinates.spec.ts | 20 +- .../traverse-coordinates.ts | 10 +- tsconfig.json | 2 +- 8 files changed, 328 insertions(+), 161 deletions(-) diff --git a/src/standardize-geometry-filter/clip-to-bounds.ts b/src/standardize-geometry-filter/clip-to-bounds.ts index 804d5f1..40391c3 100644 --- a/src/standardize-geometry-filter/clip-to-bounds.ts +++ b/src/standardize-geometry-filter/clip-to-bounds.ts @@ -1,4 +1,4 @@ -import { traverseCoordinates } from './traverse-coordinates'; +import { transformCoordinates } from './traverse-coordinates'; import { Coordinates } from './common-types'; import { IEnvelope } from '@esri/arcgis-rest-types'; @@ -10,7 +10,7 @@ export function clipToEnvelope(coordinates: Coordinates , envelope: IEnvelope): return [constrainNumber(lon, xmax, xmin), constrainNumber(lat, ymax, ymin)]; }; - return traverseCoordinates(coordinates, repositionInvalidCoordinates); + return transformCoordinates(coordinates, repositionInvalidCoordinates); } function constrainNumber (num:number, max:number, min:number): number { diff --git a/src/standardize-geometry-filter/index.spec.ts b/src/standardize-geometry-filter/index.spec.ts index 8274d98..3a44ba4 100644 --- a/src/standardize-geometry-filter/index.spec.ts +++ b/src/standardize-geometry-filter/index.spec.ts @@ -1,6 +1,19 @@ -const { standardizeGeometryFilter } = require('./'); +import { ISpatialReference } from '@esri/arcgis-rest-types'; +import { standardizeGeometryFilter } from './'; +import * as projCodes from '@esri/proj-codes'; +import { GeometryFilter } from './common-types'; +jest.mock('@esri/proj-codes', () => ({ + __esModule: true, + // @ts-ignore + ...jest.requireActual('@esri/proj-codes'), +})); + +const mockLogger = describe('standardizeGeometryFilter', () => { + afterAll(() => { + jest.resetAllMocks(); + }); test('delimited point', () => { const result = standardizeGeometryFilter({ geometry: '-123, 48' }); expect(result).toEqual({ @@ -132,7 +145,7 @@ describe('standardizeGeometryFilter', () => { xmax: -122, ymin: 48, ymax: 49, - spatialReference: { wkid: 4326, latestWkid: 9999 }, + spatialReference: { wkid: 4326 }, }, reprojectionSR: 3857, }); @@ -157,6 +170,44 @@ describe('standardizeGeometryFilter', () => { }); }); + test('envelope object with unsupported spatial reference', () => { + try { + const result = standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 48, + ymax: 49, + spatialReference: 'foo-bar' as unknown as ISpatialReference, + }, + }); + throw new Error('should have thrown'); + } catch (error) { + expect(error.message).toBe( + 'Unsupported inSR format; must be a spatial reference ID or object', + ); + } + }); + + test('envelope object with unsupported spatial reference wkt', () => { + try { + const result = standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 48, + ymax: 49, + spatialReference: { wkt: 'foo-bar' }, + }, + }); + throw new Error('should have thrown'); + } catch (error) { + expect(error.message).toBe( + 'Spatial reference WKT is unparseable: "foo-bar"', + ); + } + }); + test('envelope object with unknown spatial reference', () => { const result = standardizeGeometryFilter({ geometry: { @@ -164,8 +215,8 @@ describe('standardizeGeometryFilter', () => { xmax: -122, ymin: 48, ymax: 49, - spatialReference: { wkid: 99999 }, }, + inSR: 9999 }); expect(result).toEqual({ @@ -186,7 +237,7 @@ describe('standardizeGeometryFilter', () => { }); }); - test('envelope object with spatial reference and clip option', () => { + test('envelope object with WGS84 spatial reference and clip option', () => { const result = standardizeGeometryFilter({ geometry: { xmin: -123, @@ -195,7 +246,6 @@ describe('standardizeGeometryFilter', () => { ymax: 95, spatialReference: { wkid: 4326 }, }, - clipToValidBounds: true, }); expect(result).toEqual({ geometry: { @@ -226,7 +276,6 @@ describe('standardizeGeometryFilter', () => { ymin: 48, ymax: 49, }, - clipToValidBounds: true, }); expect(result).toEqual({ geometry: { @@ -278,7 +327,46 @@ describe('standardizeGeometryFilter', () => { }); }); - test('envelope object with WKT spatial reference and clip option', () => { + test('envelope object with reprojection spatial reference, without source spatial reference', () => { + try { + const result = standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 45, + ymax: 90, + }, + reprojectionSR: 3857, + }); + throw new Error('should have thrown'); + } catch (error) { + expect(error.message).toBe( + 'Unknown geometry filter spatial reference; unable to reproject', + ); + } + }); + + test('envelope object with unknown reprojection spatial reference', () => { + try { + const result = standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 45, + ymax: 90, + }, + inSR: 4326, + reprojectionSR: 99999, + }); + throw new Error('should have thrown'); + } catch (error) { + expect(error.message).toBe( + 'Unknown reprojection spatial reference; unable to reproject', + ); + } + }); + + test('envelope object with WKT spatial reference', () => { const result = standardizeGeometryFilter({ geometry: { xmin: -123, @@ -289,7 +377,6 @@ describe('standardizeGeometryFilter', () => { wkt: `GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]`, }, }, - clipToValidBounds: true, }); expect(result).toEqual({ geometry: { @@ -311,36 +398,6 @@ describe('standardizeGeometryFilter', () => { }); }); - test('envelope object with unknown spatial reference and clip option', () => { - const result = standardizeGeometryFilter({ - geometry: { - xmin: -123, - xmax: -122, - ymin: 48, - ymax: 49, - spatialReference: { wkid: 99999 }, - }, - clipToValidBounds: true, - }); - - expect(result).toEqual({ - geometry: { - coordinates: [ - [ - [-122, 49], - [-123, 49], - [-123, 48], - [-122, 48], - [-122, 49], - ], - ], - type: 'Polygon', - }, - relation: 'esriSpatialRelIntersects', - spatialReference: undefined, - }); - }); - test('geometry submitted as stringified JSON', () => { const filter = { geometry: JSON.stringify({ @@ -473,9 +530,90 @@ describe('standardizeGeometryFilter', () => { }); }); + test('envelope object with spatial reference that has no wkt will not reproject', () => { + jest.spyOn(projCodes, 'lookup').mockReturnValue({}); + + try { + standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 48, + ymax: 95, + }, + inSR: 99999, + reprojectionSR: 3857, + }); + throw new Error('should have thrown'); + } catch (error) { + expect(error.message).toBe( + 'Unknown geometry filter spatial reference WKT; unable to reproject', + ); + } + }); + + test('reprojection spatial reference that has no wkt will not reproject', () => { + jest + .spyOn(projCodes, 'lookup') + .mockReturnValueOnce({ + wkt: `GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]`, + }) + .mockReturnValueOnce({}); + + try { + standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 48, + ymax: 95, + spatialReference: { wkid: 4326 }, + }, + reprojectionSR: 3857, + }); + throw new Error('should have thrown'); + } catch (error) { + expect(error.message).toBe( + 'Unknown reprojection spatial reference WKT; unable to reproject', + ); + } + }); + + test('envelope object with spatial reference that has no extent will not clip', () => { + jest.spyOn(projCodes, 'lookup').mockReturnValue({}); + const result = standardizeGeometryFilter({ + geometry: { + xmin: -123, + xmax: -122, + ymin: 48, + ymax: 95, + }, + inSR: 99999 + }); + + expect(result).toEqual({ + geometry: { + coordinates: [ + [ + [-122, 95], + [-123, 95], + [-123, 48], + [-122, 48], + [-122, 95], + ], + ], + type: 'Polygon', + }, + relation: 'esriSpatialRelIntersects', + spatialReference: { wkt: undefined, wkid: undefined }, + }); + }); + test('unsupported filter format', () => { try { - standardizeGeometryFilter({ geometry: { hello: 'world' } }); + standardizeGeometryFilter({ + geometry: { hello: 'world' } as unknown as GeometryFilter, + }); throw new Error('should have thrown'); } catch (error) { expect(error.message).toEqual( diff --git a/src/standardize-geometry-filter/index.ts b/src/standardize-geometry-filter/index.ts index 6684a34..b1aec18 100644 --- a/src/standardize-geometry-filter/index.ts +++ b/src/standardize-geometry-filter/index.ts @@ -1,10 +1,8 @@ import * as _ from 'lodash'; -import proj4 from 'proj4'; import Joi from 'joi'; import wktParser from 'wkt-parser'; import * as esriProjCodes from '@esri/proj-codes'; import { - IEnvelope, ISpatialReference, SpatialRelationship, } from '@esri/arcgis-rest-types'; @@ -53,8 +51,6 @@ interface IStandardizedGeometryFilterParams { inSR?: ArcgisSpatialReference; reprojectionSR?: ArcgisSpatialReference; spatialRel?: SpatialRelationship; - clipEnvelope?: IEnvelope; - logger?: { [key: string]: any }; } class StandardizedGeometryFilter { @@ -62,11 +58,8 @@ class StandardizedGeometryFilter { public spatialReference; public relation; private filter; - private logger; private filterSpatialReference; private reprojectionSpatialReference; - private targetSpatialReference; - private clipEnvelope; static build( params: IStandardizedGeometryFilterParams, @@ -82,37 +75,43 @@ class StandardizedGeometryFilter { inSR, reprojectionSR, spatialRel, - clipEnvelope, - logger = console, } = params; - this.logger = logger; this.filter = _.isString(geometry) ? parseString(geometry) : geometry; this.relation = spatialRel || 'esriSpatialRelIntersects'; - this.filterSpatialReference = this.extractSR({ - inSR: this.filter?.spatialReference || inSR, - }); - this.reprojectionSpatialReference = this.extractSR({ reprojectionSR }); + this.filterSpatialReference = this.extractSR( + 'inSR', + this.filter?.spatialReference || inSR, + ); + this.reprojectionSpatialReference = this.extractSR( + 'reprojectionSR', + reprojectionSR, + ); this.validateFilterShape(); - this.validateReproject(); this.geometry = this.transformGeometry(); - this.targetSpatialReference = - this.reprojectionSpatialReference || this.filterSpatialReference; - - this.spatialReference = this.packageSpatialReference(); + this.spatialReference = packageSpatialReference( + this.filterSpatialReference, + ); if (this.shouldClipOutOfBoundsFilter()) { - this.geometry.coordinates = this.clipToEnvelope(clipEnvelope); + this.geometry.coordinates = clipToEnvelope( + this.geometry.coordinates, + wgsExtentEnvelope, + ); } - if (this.shouldReproject()) { + if (reprojectionSR && this.validateReproject()) { this.geometry.coordinates = projectCoordinates( this.geometry.coordinates, this.filterSpatialReference.wkt, - this.targetSpatialReference.wkt, + this.reprojectionSpatialReference.wkt, + ); + + this.spatialReference = packageSpatialReference( + this.reprojectionSpatialReference, ); } } @@ -130,68 +129,86 @@ class StandardizedGeometryFilter { return this; } - extractSR(srInput: { [key: string]: any } | number): ISpatialReference { - const srOption = Object.keys(srInput)[0]; - const srOptionValue = srInput[srOption]; - - if (!srOptionValue) { + extractSR( + srSource: string, + spatialReference: ArcgisSpatialReference, + ): ISpatialReference { + if (!spatialReference) { return; } - const { error } = inputSpatialReferenceSchema.validate(srOptionValue); + const { error } = inputSpatialReferenceSchema.validate(spatialReference); if (error) { - throw new Error(`Unsupported ${srOption} format; ${error.message}`); + throw new Error( + `Unsupported ${srSource} format; must be a spatial reference ID or object`, + ); } try { - if (Number.isInteger(srOptionValue) || getSrid(srOptionValue)) { - return getSpatialReferenceFromCode(srOptionValue); - } - - if (srOptionValue.wkt) { - weakValidateWkt(srOptionValue.wkt); - return srOptionValue; + if ( + Number.isInteger(spatialReference) || + getSrid(spatialReference as ISpatialReference) + ) { + return getSpatialReferenceFromCode(spatialReference); } } catch (error) { - this.logger.debug(error.message, error.stacktrace); + console.warn(error.message); } - } - validateReproject(): void { - if (this.reprojectionSpatialReference && !this.filterSpatialReference) { - this.logger.debug(`Unknown inSR; unable to reproject`); + if ((spatialReference as ISpatialReference).wkt) { + weakValidateWkt((spatialReference as ISpatialReference).wkt); + return spatialReference as ISpatialReference; } } - shouldClipOutOfBoundsFilter(): boolean { - return this.hasRequiredSpatialReferences() && this.hasOOBCoords(); + validateReproject(): boolean { + if (!this.filterSpatialReference) { + throw new Error( + 'Unknown geometry filter spatial reference; unable to reproject', + ); + } + + if (!this.filterSpatialReference.wkt) { + throw new Error( + `Unknown geometry filter spatial reference WKT; unable to reproject`, + ); + } + + if (!this.reprojectionSpatialReference) { + throw new Error( + `Unknown reprojection spatial reference; unable to reproject`, + ); + } + + if (!this.reprojectionSpatialReference.wkt) { + throw new Error( + `Unknown reprojection spatial reference WKT; unable to reproject`, + ); + } + + return true; } - hasRequiredSpatialReferences(): boolean { - return ( - this.filterSpatialReference?.wkt && - (this.reprojectionSpatialReference?.extent || - this.filterSpatialReference.extent) - ); + shouldClipOutOfBoundsFilter(): boolean { + return this.filterSpatialReference?.wkt === wgsWkt && this.hasOOBCoords(); } hasOOBCoords(): boolean { - const extentEnvelope = this.clipEnvelope || wgsExtentEnvelope; - const conditions = (coords: Coordinates): boolean => { - const coordinatesWkt = this.filterSpatialReference.wkt; - const [lon, lat] = - coordinatesWkt === wgsWkt || this.clipEnvelope - ? coords - : proj4(coordinatesWkt, wgsWkt, coords); + const extent = wgsExtentEnvelope; + + const predicate = (coords: Coordinates): boolean => { + const [lon, lat] = coords; + return ( - lon > extentEnvelope.xmax || - lon < extentEnvelope.xmin || - lat > extentEnvelope.ymax || - lat < extentEnvelope.ymin + lon > extent.xmax || + lon < extent.xmin || + lat > extent.ymax || + lat < extent.ymin ); }; - return someCoordinates(this.geometry.coordinates, conditions); + + return someCoordinates(this.geometry.coordinates, predicate); } transformGeometry(): Geometry { @@ -208,51 +225,16 @@ class StandardizedGeometryFilter { return arcgisToGeoJSON(this.filter); } +} - clipToEnvelope(clipEnvelope?: IEnvelope): Coordinates { - // no need to reproject coordinates for clipping to spatial reference extent - const filterCoordinatesWgs84 = - this.filterSpatialReference.wkt === wgsWkt - ? this.geometry.coordinates - : projectCoordinates( - this.geometry.coordinates, - this.filterSpatialReference.wkt, - wgsWkt, - ); - - const clippedCoordinates = clipToEnvelope( - filterCoordinatesWgs84, - clipEnvelope || wgsExtentEnvelope, - ); - - return this.filterSpatialReference.wkt === wgsWkt - ? clippedCoordinates - : projectCoordinates( - clippedCoordinates, - wgsWkt, - this.filterSpatialReference.wkt, - ); - } - - shouldReproject(): boolean { - return ( - this.targetSpatialReference?.wkt && - this.filterSpatialReference?.wkt && - this.targetSpatialReference?.wkt !== this.filterSpatialReference?.wkt - ); - } - - packageSpatialReference(): ISpatialReference | void { - if (this.shouldReproject()) { - const { wkid, wkt } = this.reprojectionSpatialReference; - return { wkid, wkt }; - } - - if (this.filterSpatialReference) { - const { wkid, wkt } = this.filterSpatialReference; - return { wkid, wkt }; - } +function packageSpatialReference( + spatialReference?: any, +): ISpatialReference | void { + if (!spatialReference) { + return; } + const { wkid, wkt } = spatialReference; + return { wkid, wkt }; } function getSpatialReferenceFromCode(sr: ArcgisSpatialReference): any { diff --git a/src/standardize-geometry-filter/project-coordinates.spec.ts b/src/standardize-geometry-filter/project-coordinates.spec.ts index 873aec4..8bfe440 100644 --- a/src/standardize-geometry-filter/project-coordinates.spec.ts +++ b/src/standardize-geometry-filter/project-coordinates.spec.ts @@ -5,7 +5,7 @@ jest.mock('proj4', () => { return jest.fn(() => ['lon', 'lat']); }); -describe('project-coordinates', () => { +describe.only('project-coordinates', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -44,6 +44,30 @@ describe('project-coordinates', () => { ]); }); + test('should constrain 90 degree latitude to avoid proj4 errors', () => { + const transformed = projectCoordinates([75, 90], 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]', '2991'); + expect(transformed[0]).toEqual('lon'); + expect(transformed[1]).toEqual('lat'); + expect((proj4 as any as jest.Mock).mock.calls.length).toEqual(1); + expect((proj4 as any as jest.Mock).mock.calls[0]).toEqual([ + 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]', + '2991', + [75, 89.99999999], + ]); + }); + + test('should constrain -90 degree latitude to avoid proj4 errors', () => { + const transformed = projectCoordinates([75, -90], 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]', '2991'); + expect(transformed[0]).toEqual('lon'); + expect(transformed[1]).toEqual('lat'); + expect((proj4 as any as jest.Mock).mock.calls.length).toEqual(1); + expect((proj4 as any as jest.Mock).mock.calls[0]).toEqual([ + 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]', + '2991', + [75, -89.99999999], + ]); + }); + test('should reproject nested array', () => { const transformed = projectCoordinates( [ diff --git a/src/standardize-geometry-filter/project-coordinates.ts b/src/standardize-geometry-filter/project-coordinates.ts index 31b5bc7..fe448b0 100644 --- a/src/standardize-geometry-filter/project-coordinates.ts +++ b/src/standardize-geometry-filter/project-coordinates.ts @@ -1,24 +1,35 @@ import proj4 from 'proj4'; import * as _ from 'lodash'; -import { traverseCoordinates } from './traverse-coordinates'; +import { transformCoordinates } from './traverse-coordinates'; import { Coordinates } from './common-types'; -import { wgsWkt } from './helpers'; +import { wgsExtentEnvelope, wgsWkt } from './helpers'; +const WGS_MIN_LAT = wgsExtentEnvelope.ymin; +const WGS_MAX_LAT = wgsExtentEnvelope.ymax; export function projectCoordinates(coordinates: Coordinates , fromSR: string, toSR: string): Coordinates { const reproject = (coords: Coordinates): Coordinates => { if (shouldReproject(coords)) { - if(fromSR === wgsWkt && (coords[1] === 90 || coords[1] === -90)) { - coords[1] = coords[1] === 90 ? 90 - 1e-8 : -90 + 90 - 1e-8; + const [, lat] = coords; + if(shouldConstrainSourceX(fromSR, lat as number)) { + coords[1] = constrainX(lat as number); } return proj4(fromSR, toSR, coords); } return coords; } - return traverseCoordinates(coordinates, reproject) + return transformCoordinates(coordinates, reproject) } // Prevent error in event of null or undefined coordinates function shouldReproject(coordinates: Coordinates): boolean { return coordinates && _.isNumber(coordinates[0]) && _.isNumber(coordinates[1]); } + +function shouldConstrainSourceX(fromSR: string, x: number): boolean { + return fromSR === wgsWkt && (x === WGS_MIN_LAT || x === WGS_MAX_LAT); +} + +function constrainX(x: number): number { + return x === WGS_MAX_LAT ? WGS_MAX_LAT - 1e-8 : WGS_MIN_LAT + 1e-8; +} \ No newline at end of file diff --git a/src/standardize-geometry-filter/traverse-coordinates.spec.ts b/src/standardize-geometry-filter/traverse-coordinates.spec.ts index 0880ddb..7b6d521 100644 --- a/src/standardize-geometry-filter/traverse-coordinates.spec.ts +++ b/src/standardize-geometry-filter/traverse-coordinates.spec.ts @@ -1,13 +1,13 @@ import { Coordinates } from './common-types'; -import { traverseCoordinates } from './traverse-coordinates'; +import { someCoordinates, transformCoordinates } from './traverse-coordinates'; -describe('traverseCoordinates', () => { +describe('transformCoordinates', () => { test('traverse and transform array', () => { const transform = (coordinates: Coordinates): Coordinates => { const [x, y] = coordinates; return [x as number + 1, y as number + 1]; }; - const result = traverseCoordinates([[45, 60]], transform); + const result = transformCoordinates([[45, 60]], transform); expect(result).toEqual([[46, 61]]); }); @@ -16,7 +16,19 @@ describe('traverseCoordinates', () => { const [x, y] = coordinates; return [x as number + 1, y as number + 1]; }; - const result = traverseCoordinates([[45, 60], [35, 40]], transform); + const result = transformCoordinates([[45, 60], [35, 40]], transform); expect(result).toEqual([[46, 61], [36, 41]]); }); }); + + +describe('someCoordinates', () => { + test('traverse and until predicate is fufilled', () => { + const predicate = (coordinates: Coordinates): boolean => { + const [x, y] = coordinates; + return y as number > 90; + }; + const result = someCoordinates([[45, 60], [35, 95]], predicate); + expect(result).toEqual(true); + }); +}); \ No newline at end of file diff --git a/src/standardize-geometry-filter/traverse-coordinates.ts b/src/standardize-geometry-filter/traverse-coordinates.ts index ba2fee9..3084f86 100644 --- a/src/standardize-geometry-filter/traverse-coordinates.ts +++ b/src/standardize-geometry-filter/traverse-coordinates.ts @@ -2,22 +2,22 @@ import { Coordinates } from "./common-types"; type TransformFunction = (coordinates: Coordinates) => Coordinates; -export function traverseCoordinates(coordinates: Coordinates, transform: TransformFunction): Coordinates { +export function transformCoordinates(coordinates: Coordinates, transform: TransformFunction): Coordinates { if (Array.isArray(coordinates[0])) { return coordinates.map((coords) => { - return traverseCoordinates(coords, transform) + return transformCoordinates(coords, transform) }) as Coordinates; } return transform(coordinates); } -type ConditionFunction = (coordinates: Coordinates) => boolean; +type Predicate = (coordinates: Coordinates) => boolean; -export function someCoordinates(coordinates: Coordinates, condition: ConditionFunction): boolean { +export function someCoordinates(coordinates: Coordinates, condition: Predicate): boolean { if (Array.isArray(coordinates[0])) { return coordinates.some((coords) => { return someCoordinates(coords, condition) }); } return condition(coordinates); -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 761feca..29647c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "module": "commonjs", "esModuleInterop": true, - "target": "es6", + "target": "es2022", "moduleResolution": "node", "sourceMap": true, "outDir": "dist",