Skip to content

Commit

Permalink
Merge pull request #10 from koopjs/f/handle-oob-coords
Browse files Browse the repository at this point in the history
Handle out of bounds WGS84 coordinates
  • Loading branch information
rgwozdz authored Mar 29, 2024
2 parents 7e2f745 + 14667bf commit 264940d
Show file tree
Hide file tree
Showing 14 changed files with 926 additions and 433 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# @koopjs/geoservice-utils

### Unreleased
### Changed
- constrains any WGS84 out-of-bounds coordinates to their known limits (e.g., -95 latitude -> -90 latitude).
- WGS84 latitudes of -90 or 90 are modified to -89.99999 and -89.99999, respectively, allow reprojection to proceed as expected.

## 2.2.3
### Fixed
- add missing WKT-parser dependency to package.json
Expand Down
8 changes: 8 additions & 0 deletions src/standardize-geometry-filter/clip-to-envelope.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { clipToEnvelope } from './clip-to-envelope';

describe('clipToBounds', () => {
test('clipToBoundsOfSpatialReference', () => {
const result = clipToEnvelope([[-190, 95], [-185, 45]], {ymin: -90, ymax: 90, xmin: -180, xmax: 180})
expect(result).toEqual([[-180, 90], [-180, 45]]);
});
});
26 changes: 26 additions & 0 deletions src/standardize-geometry-filter/clip-to-envelope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { transformCoordinates } from './traverse-coordinates';
import { Coordinates } from './common-types';
import { IEnvelope } from '@esri/arcgis-rest-types';

export function clipToEnvelope(coordinates: Coordinates , envelope: IEnvelope): Coordinates {
const { xmin, xmax, ymin, ymax } = envelope;

const repositionInvalidCoordinates = (coords): Coordinates => {
const [lon, lat] = coords;
return [constrainNumber(lon, xmax, xmin), constrainNumber(lat, ymax, ymin)];
};

return transformCoordinates(coordinates, repositionInvalidCoordinates);
}

function constrainNumber (num:number, max:number, min:number): number {
if (num > max) {
return max;
}

if (num < min) {
return min;
}

return num;
}
40 changes: 40 additions & 0 deletions src/standardize-geometry-filter/common-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
Position
} from 'geojson';
import {
IEnvelope,
IPoint,
IPolyline,
IPolygon,
ISpatialReference,
} from '@esri/arcgis-rest-types';

export type Geometry =
| Point
| MultiPoint
| LineString
| MultiLineString
| Polygon
| MultiPolygon;

export type GeometryFilter = IEnvelope | IPoint | IPolyline | IPolygon | number[];

export type Coordinates = Position | Position[] | Position[][] | Position[][][];

export type ArcgisSpatialReference = number | ISpatialReference;

export type SpatialReference = {
wkid?: number;
latestWkid?: number;
wkt?: string;
vcsWkid?: number;
latestVcsWkid?: number;
};

export type SpatialReferenceInput = string | number | SpatialReference;
64 changes: 25 additions & 39 deletions src/standardize-geometry-filter/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import joi from 'joi';
import {
IEnvelope,
IPoint,
IPolyline,
IPolygon,
} from '@esri/arcgis-rest-types';
import * as esriProjCodes from '@esri/proj-codes';
import { GeometryFilter } from './common-types';

export const spatialReferenceSchema = joi.object({
wkid: joi.number().strict().integer().optional(),
latestWkid: joi.number().strict().integer().optional(),
vcsWkid: joi.number().strict().integer().optional(),
latestVcsWkid: joi.number().strict().integer().optional(),
wkt: joi.string().optional(),
}).unknown()
.or('wkid', 'latestWkid', 'vcsWkid', 'latestVcsWkid', 'wkt')

const envelopeSchema = joi
.object({
ymin: joi.number().strict().required(),
ymax: joi.number().strict().required(),
xmin: joi.number().strict().required(),
xmax: joi.number().strict().required(),
spatialReference: joi
.object({
wkid: joi.number().strict().required(),
})
.unknown(true)
.optional(),
spatialReference: spatialReferenceSchema.optional()
})
.unknown(true);

Expand Down Expand Up @@ -112,37 +112,23 @@ export const filterSchema = joi
)
.required();

type GeometryFilter =
| IEnvelope
| IPoint
| IPolyline
| IPolygon
| number[]
| string;

function passesValidation(schema: joi.Schema, input: GeometryFilter): boolean {
const { error } = schema.validate(input);
return !error;
}

export function isArcgisObject(input: GeometryFilter): boolean {
return (
passesValidation(envelopeSchema, input) ||
passesValidation(pointSchema, input) ||
passesValidation(lineStringSchema, input) ||
passesValidation(polygonSchema, input) ||
passesValidation(multiPointSchema, input) ||
passesValidation(multiLineStringSchema, input) ||
passesValidation(multipolygonSchema, input)
);
}

export function isSinglePointArray(input: GeometryFilter): boolean {
export function isSinglePointArray(input: GeometryFilter | string): boolean {
const { error } = pointArraySchema.validate(input);
return !error;
}

export function isEnvelopeArray(input: GeometryFilter): boolean {
export function isEnvelopeArray(input: GeometryFilter | string): boolean {
const { error } = envelopeArraySchema.validate(input);
return !error;
}

const wgs = esriProjCodes.lookup(4326);

export const wgsWkt = wgs.wkt;

export const wgsExtentEnvelope = {
ymin: wgs.extent.slat,
ymax: wgs.extent.nlat,
xmin: wgs.extent.llon,
xmax: wgs.extent.rlon
};
Loading

0 comments on commit 264940d

Please sign in to comment.