123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412(* Copyright (c) 2021-2022 Patrick Ferris <patrick@sirref.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*)(** The GeoJSON library does not force you to use a particular JSON parsing
library. You must provide one. See the tests and benchmarks for an [Ezjsone]
parser and one for JS using [Brr]'s [Jv] library. *)moduletypeJson=sigtypet(** The type your parser uses to represent a parsed JSON object. *)valfind:t->stringlist->toption(** Recursively find keys in nested objects. *)valto_string:t->(string,[`Msgofstring])result(** Convert the JSON to a string. *)valstring:string->t(** Create a JSON string. *)valto_float:t->(float,[`Msgofstring])result(** Convert the JSON to a float. *)valfloat:float->t(** Converts a float to JSON *)valto_int:t->(int,[`Msgofstring])result(** Convert the JSON to an integer. *)valint:int->t(** Converts an integer to JSON *)valto_list:(t->'a)->t->('alist,[`Msgofstring])result(** [to_list f] converts the JSON array to a list and applies [f] to each
element to convert them too. *)vallist:('a->t)->'alist->t(** Make a JSON array from a list *)valto_array:(t->'a)->t->('aarray,[`Msgofstring])result(** Like {!to_list} except to an array. *)valarray:('a->t)->'aarray->t(** Like {!list} except for OCaml arrays *)valto_obj:t->((string*t)list,[`Msgofstring])result(** Convert the JSON object to an association list *)valobj:(string*t)list->t(** A JSON object from an association list *)valnull:t(** Null value *)valis_null:t->bool(** Test for null *)end(** {2 GeoJSON Geometry Objects}
The basic primitives for building geometrical shapes in GeoJSON. *)moduletypeGeometry=sigtypejson(** A type to represt JSON values. *)modulePosition:sigtypet=floatarray(** A position - a longitude and latitude with an optional altitude *)vallng:t->float(** The longitude value of the position *)vallat:t->float(** The latitude value of the position *)valaltitude:t->floatoption(** Optional altitude/elevation value of the position *)valequal:t->t->bool(** Whether two positions are equal by comparing each value *)valv:?altitude:float->lng:float->lat:float->unit->t(** A position constructor *)endmodulePoint:sigtypet(** A point is a single {!Position.t} *)valposition:t->Position.t(** Convert a point to a position *)valv:Position.t->t(** Create a poitn from a position. *)endmoduleMultiPoint:sigtypet(** A multipoint is an array of positions. *)valcoordinates:t->Position.tarray(** Get the positions that make up this multipoint object. *)valv:Position.tarray->t(** Create a multipoint object from an array of positions. *)endmoduleLineString:sigtypet(** A line string is two or more points *)valcoordinates:t->Position.tarray(** Convert the line into a position array *)valv:Position.tarray->t(** Create a line string from positions, will raise [Invalid_argument] if
the array doesn't have at least two positions. *)endmoduleMultiLineString:sigtypet(** A collection of line strings *)vallines:t->LineString.tarray(** Access the lines *)valv:LineString.tarray->t(** Create a multiline string *)valto_positions:t->Position.tarrayarray(** Convert directly to the positions that make up the lines. *)valof_positions:Position.tarrayarray->t(** Convert directly from positions to lines *)endmodulePolygon:sigtypet(** A close loop with optional rings *)valrings:t->LineString.tarray(** [rings t] returns the linear rings contained in [t] (a Polygon object) *)valexterior_ring:t->LineString.t(** [exterior_ring t] returns the first linear ring contained in [t] (a
Polygon object). This ring bounds the surface *)valinterior_rings:t->LineString.tarray(** If [t] (a Polygon object) contains more than 1 linear ring,
[interior_rings t] returns the rest of the linear rings apart from the
first. These rings (if present), bound the holes. *)valv:LineString.tarray->t(** Create a polygon object from an array of close line strings (note no
checking is down here to ensure the loops are indeed closed.) *)valto_positions:t->Position.tarrayarray(** Convert directly to the positions that make up the lines. *)valof_positions:Position.tarrayarray->t(** Convert directly from positions to lines *)endmoduleMultiPolygon:sigtypet(** A multi-polygon object *)valpolygons:t->Polygon.tarray(** Access the polygons *)valv:Polygon.tarray->t(** Create a multi-polygon object from an array of {!Polygon.t}s *)valto_positions:t->Position.tarrayarrayarray(** Convert directly to the positions that make up the polygons *)valof_positions:Position.tarrayarrayarray->t(** Convert directly from positions to polygons *)endtypegeometry=|PointofPoint.t|MultiPointofMultiPoint.t|LineStringofLineString.t|MultiLineStringofMultiLineString.t|PolygonofPolygon.t|MultiPolygonofMultiPolygon.t|Collectionoftlistandtvalforeign_members:t->(string*json)list(** [foreign_members t] will extract name/value pair of a foreign member from
t (a GeoJSON object) *)valgeometry:t->geometry(** [geometry t] will extract the underlying geometry. *)valv:?foreign_members:(string*json)list->geometry->tendmoduletypeS=sigtypejson(** The internal representation of a JSON value. *)moduleGeometry:Geometrywithtypejson=json(** Geometries *)(** Features which contain a geometry *)moduleFeature:sigtypet(** A feature object is a geojson object with optional geometry and
properties members. *)valgeometry:t->Geometry.toptionvalproperties:t->jsonoptionvalforeign_members:t->(string*json)list(** [foreign_members t] will extract name/value pair of a foreign member
from t (a GeoJSON object) *)valid:t->[`Stringofstring|`Floatoffloat]option(** [id f] extracts the identifier for the feature if it exists. *)valv:?id:[`Stringofstring|`Floatoffloat]->?properties:json->?foreign_members:(string*json)list->Geometry.t->t(** [v geo] creates a new feature object, you may wish to provide a
[properties] JSON object for the feature too. *)moduleCollection:sigtypefeature=ttypetvalfeatures:t->featurelistvalv:?foreign_members:(string*json)list->featurelist->t(** [v features] creates a feature collection from a list of features *)valforeign_members:t->(string*json)list(** [foreign_members t] will extract name/value pair of a foreign member
from t (a GeoJSON object) *)endendtypegeojson=|FeatureofFeature.t|FeatureCollectionofFeature.Collection.t|GeometryofGeometry.t(** A {!geojson} object which could be a geometry, a feature or a collection
of features. *)typet(** The type for GeoJSON objects. *)valgeojson:t->geojson(** [geojson t] will extract geojson value from t (a GeoJSON object) *)valbbox:t->floatarrayoption(** [bbox t] will extract bbox value from t (a GeoJSON object) *)valv:?bbox:floatarray->geojson->t(** [v geojson bbox] combines geojson and bbox to return a GeoJSON object (a
type {!t}) *)valof_json:json->(t,[`Msgofstring])result(** [of_json json] converts the JSON to a GeoJSON object (a type {!t}) or an
error. *)valto_json:t->json(** [to_json g] converts the GeoJSON object [g] to JSON *)moduleAccessor:sigmoduleOptics=Optics(** The accessor module uses optics to allow users to build reusable values
that can be used to get values deeply nested in GeoJSON values. Bare in
mind if you care more about performance and/or memory footprint, you are
probably better off writing pattern-matching statements by hand than
using accessors.*)valget:('a,'b)Optics.Lens.t->'a->'b(** [get lens v] focuses onto the field in [lens] for the value [v]. *)valgeojson:(t,geojson)Optics.Lens.t(** A lens for focusing on the [geojson] value. *)valbbox:(t,floatarrayoption)Optics.Lens.t(** A lens for focusing on the bounding box if any. *)valfeature:(geojson,Feature.t)Optics.Prism.t(** A prism for matching on a feature. *)valgeometry:(geojson,Geometry.t)Optics.Prism.t(** A prism for matching on a geometry. *)valfeature_collection:(geojson,Feature.Collection.t)Optics.Prism.t(** A prism for matching on a feature collection. *)moduleFeature:sigvalproperties:(Feature.t,jsonoption)Optics.Lens.t(** A lens for focusing on the properties if any. *)valforeign_members:(Feature.t,(string*json)list)Optics.Lens.t(** A lens for focusing on the foreign members if any. *)valgeometry:(Feature.t,Geometry.toption)Optics.Lens.t(** A lens for focusing on the feature's geometry if any. *)valgeometry_exn:(Feature.t,Geometry.t)Optics.Lens.t(** Like {!geometry} except using [Option.get] internally. *)endmoduleGeometry:sigvalgeometry:(Geometry.t,Geometry.geometry)Optics.Lens.t(** A lens for focusing on the geometry value. *)valforeign_members:(Geometry.t,(string*json)list)Optics.Lens.t(** A lens for focusing on the possibly empty foreign members. *)(** {3 Prisms for Geometries} *)valpoint:(Geometry.geometry,Geometry.Point.t)Optics.Prism.tvalmultipoint:(Geometry.geometry,Geometry.MultiPoint.t)Optics.Prism.tvallinestring:(Geometry.geometry,Geometry.LineString.t)Optics.Prism.tvalmultilinestring:(Geometry.geometry,Geometry.MultiLineString.t)Optics.Prism.tvalpolygon:(Geometry.geometry,Geometry.Polygon.t)Optics.Prism.tvalmultipolygon:(Geometry.geometry,Geometry.MultiPolygon.t)Optics.Prism.tend(** {3 Infix Operators}
These operators allow you to combine lenses and prisms into more
complicated lenses and prisms.*)openOpticsval(>>):('a,'b)Optional.t->('b,'c)Optional.t->('a,'c)Optional.tval(&>):('a,'b)Optional.t->('b,'c)Lens.t->('a,'c)Optional.tval($>):('a,'b)Optional.t->('b,'c)Prism.t->('a,'c)Optional.tval(>&):('a,'b)Lens.t->('b,'c)Prism.t->('a,'c)Optional.tval(>$):('a,'b)Prism.t->('b,'c)Lens.t->('a,'c)Optional.tval(&):('a,'b)Lens.t->('b,'c)Lens.t->('a,'c)Lens.tval($):('a,'b)Prism.t->('b,'c)Prism.t->('a,'c)Prism.tendmoduleRandom:sigtypegeometry=|Point|MultiPointofint|LineStringofint|MultiLineStringofint*int|Polygonofint|MultiPolygonofint*int|Collectionofgeometrylisttypefeature={properties:jsonoption;geometry:geometry}typer=FCoffeaturelist|Foffeature|Gofgeometry(** {3 Generate random geojson}
The random module provides a way of quickly constructing random, correct
GeoJSON objects. You provide the skeleton of the document using type
{!t} and tweaking some of the parameters. For example:
[{
let random_structure =
FC (List.init 100 (fun _ -> { properties = None; geometry = Point }))
}]*)valrandom:f:(unit->float)->r->t(** [random ~f r] produces random GeoJSON based on the structure provided by
[r] and using the random float generator [f]. Note the random geometry
maker will follow the rules of GeoJSON (for making Polygons for
example). *)endendmoduletypeGeojson=sigmoduletypeS=S(** Types for GeoJSON texts and objects *)moduletypeJson=Json(** Types for the JSON parser *)(** A functor that takes a JSON parsing implementation and returns a GeoJSON
parser and constructor. *)moduleMake(J:Json):Swithtypejson=J.tend