pure-geojson-validation
v0.3.0
Published
A geojson validator and coordinate checker with purify-ts support.
Downloads
607
Readme
pure-geojson-validation
pure-geojson-validation is a typescript library to check the internal consistency of geojson objects. In addition to checking the JSON structure of a possible geojson blob, the library can also check the internal consistency of the given coordinates.
Dependencies
The library will use the geojson type definitions as found in the package: @types/geojson
. It uses algorithmic data structures from purify-ts and is intended to work with it. Especially the Maybe monad is used extensively when parsing and validating a possible geojson strings or objects. It is worth considering to look at purify, even if you don't intend to work with monads.
If you never heard of a monad or a maybe, it is a form of control structure found in pureley functional languages like haskell. What a monad is is hard to explain, an I can only point you to people that are far better at explaining it than me: Like the haskell wikibook, Computerphile or Bartosz Milewski.
Limitations
The library currently cannot parse GeometryCollection geometries correctly. For now validating a GeometryCollection geometry will yield
Nothing
.The library does not and will not support deprecated geojson features like the
crs
property. Although any additional properties are valid (RFC7946, #6), only geojson object members will be returned by parser- or validation functions. That means, if you parse a GeoJSON object with an additional property like e.g.crs
it will not be represented in the resulting object.The Polygon and MultiPolygon geometries are checked for right handedness. If a left handed polygon-ring is found a warning is issued. The validator will not reverse the order of left handed rings.
Usage
Git
To check out the project, run:
git clone https://github.com/maze-le/pure-geojson-validation.git
Test
To test the repository, run:
npm run test
Install as lib
In your project:
npm i -D @types/geojson
npm i purify-ts pure-geojson-validation
Code Example
With Maybe
The library exposes several functions to parse strings as GeoJSON objects. Methods that start with maybe always return a Maybe (a value wrapped in a Just
or Nothing
). It is recommended to use the Maybe implementations of the library.
In the Maybe context
import { maybeFeatureCollection } from "pure-geojson-validation";
const storeFeatureCollection = (path: string) =>
maybeFeatureCollection(content)
.ifNothing(() => console.error("error parsing feature collection"))
.caseOf({
Just: (FeatureCollection) =>
writeToFile(path, JSON.stringify(FeatureCollection)),
Nothing: () => console.warn(`skipped writing file "${path}"`),
});
From the Maybe context to a regular context
import { maybeFeatureCollection } from "pure-geojson-validation";
function getFeatureCollectionOrNull() {
const content = readFileSync("path/to/file.geojson");
const maybeFC = maybeFeatureCollection(content)
.ifJust(() => console.info("successfully parsed feature collection"))
.ifNothing(() => console.error("could not parse feature collection"));
return maybeFC.isNothing() ? null : maybeFC.unsafeCoerce();
}
With try/catch
Alternatively you can also use the "unsafe" try methods that will raise an error if a string cannot be parsed or validated.
import { tryFeatureCollection } from "pure-geojson-validation";
function getFeatureCollectionOrNull() {
try {
const content = readFileSync("path/to/file.geojson");
const unsafeFC = tryFeatureCollection(content);
console.info("successfully parsed feature collection");
return unsafeFC;
} catch (err) {
console.error("could not parse feature collection");
return null;
}
}
Methods
String validation methods
The following methods turns a string representation of a possible FeatureCollection eventually into a FeatureCollection as defined in @types/geojson
.
Sanity checks are performed on coordinate values, if they fail the function returns with the associated geometry set to null and a warning is issued. Additional checks are performed on bounding boxes and features. If they fail the function returns Nothing. If the collection is valid, Just(GeoJSONobject)
is returned.
The associated try methods will raise an error when the validations fail.
const maybeGeoJSON: (content: string) => Maybe<GeoJSONobject>;
const tryGeoJSON: (content: string) => GeoJSONobject;
Attempts to parse and validate the content string as Maybe of a GeoJSON object (Geometry, Feature or FeatureCollection).
const maybeFeatureCollection: (content: string) => Maybe<FeatureCollection>;
const tryFeatureCollection: (content: string) => FeatureCollection;
Attempts to parse and validate the content string as Maybe of a GeoJSON FeatureCollection.
const maybeFeature: (content: string) => Maybe<Feature>;
const tryFeature: (content: string) => Feature;
Attempts to parse and validate the content string as Maybe of a GeoJSON Geometry.
const maybeGeometry: (content: string) => Maybe<Geometry>;
const tryGeometry: (content: string) => Geometry;
Attempts to parse and validate the content string as Maybe
of a GeoJSON FeatureCollection.
Object validation methods
validateBBox
const validateBBox: (bbox: unknown) => Maybe<BBox>;
Validates a possible bounding box and eventually returns with a GeoJSON BBox object. bbox is expected to be an array of length 4 or 6.
validateFeature
const validateFeature: (feat: unknown) => Maybe<Feature>;
Validates a possible GeoJSON feature and eventually returns with a Feature object as found in @types/geojson
. feat is expected to be a GeoJSON feature object (RFC7946,3.1.2).
validateFeatureCollection
const validateFeatureCollection: (fc: unknown) => Maybe<FeatureCollection>;
Validates a possible GeoJSON feature collections and eventually returns with a FeatureCollection object as found in @types/geojson
.
validateGeometry
const validateGeometry: (geometry: record | null) => Maybe<Geometry>;
Coordinate Predicates
The following methods check if coordinate values are valid and within the bounds of a WSG84 coordinate projection. The arguments (except for isLat and isLon) are of type unknown, but are assumed to be nested number arrays, depending on the chosen geometry type. If you want to work with these predicates and with Maybe
create a Maybe
from a predicate like this:
import { isPoint } from "pure-geojson-validation";
const maybePoint = (point: unknown) => Maybe.fromPredicate(isPoint, point);
isLat
const isLat: (lat: number) => boolean;
Returns true if lat is a number representing a latitude angle in WSG84.
isLon
const isLon: (lat: number) => boolean;
Returns true if lat is a number representing a longitude angle in WSG84.
isPoint
const isPoint: (p: unknown) => boolean;
Returns true if p is a point geometry as defined in RFC7946,3.1.2.
The time complexity of this function is O(1).
isMultiPoint
const isMultiPoint: (mp: unknown) => boolean;
Returns true if mp is a multipoint geometry as defined in RFC7946,3.1.3.
The time complexity of this function is O(1).
isLineString
const isLine: (pa: unknown) => boolean;
Returns true if pa is a line- or multipoint geometry as defined in RFC7946,3.1.4.
The time complexity of this function is O(n), with n=length(pa).
isMultiLineString
const isMultiLineString: (pa: unknown) => boolean;
Returns true if pa is a line- or multipoint geometry as defined in RFC7946,3.1.5.
The time complexity of this function is O(n*m), with n=length(pa); m=length(pa).
isPolygon
const isPolygon: (la: unknown) => boolean;
Returns true if la is a polygon- or multiline geometry as defined in RFC7946,3.1.6.
The time complexity of this function is O(n^2).
isMultiPolygon
const isMultiPolygon: (multipolygon: unknown) => boolean;
Returns true if multipolygon is a polygon array as defined in RFC7946,3.1.7.
The time complexity of this function is O(n^3).
isRightHand
const isRightHand = (xs: Position[]): boolean;
Checks whether a ring (closed LineString) has right hand winding number. see RFC7946,3.1.6 The time complexity of this function is O(n) with n: length of xs.
isLinearRing
const isLinearRing = (xs: unknown[][]): boolean;
Returns true if xs is an array of closed line segments
isLinearRingArray
const isLinearRingArray = (xs: unknown[][][]): boolean;
Returns true if xs is an array of linear rings
Types and Objects
Constants
The Array of possible geometry types is exposed to give non-typescript users access to valid geometry types. The geometryTypes
array contains all currently supported geometry types as string.
const geometryTypes: GeoJsonGeometryTypes[];
Types
The following types are exposed to ensure type consistency when using it as a library.
record
type record = Record<string, unknown>;
A shorthand type for objects with entries of unknown value.
BBoxTuple
type BBoxTuple<T> = [T, T, T, T] | [T, T, T, T, T, T];
An internal type that describes bounding boxes.
Position
export type Position = number[];
The internal representation of point geometries. Point geometries with more than 3 entries are valid, but any entry past index 3 in an array will be ignored.
Coordinates
export type Coordinates = Position | Position[] | Position[][] | Position[][][];
The internal representation of coordinates that describe more complex geometries than the point.
Geometry
type Geom =
| LineString
| MultiLineString
| MultiPoint
| MultiPolygon
| Point
| Polygon;
A geojson geomtry as defined in @types/geojson
except the GeometryCollection
type as it is not implemented yet.
Future Developments
The library should be able to read and validate any geojson object (not just FeatureCollections) in a furure iteration.
I plan to implement further sanity checks on geometries (avoiding self intersections, zero length line segments, etc.) in the future, as well as having an options-object that can be passed to the library methods to enforce strict or loose validation rules.