Source file topojson_intf.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
module type Json = sig
  type t
  (** The type your parser uses to represent a parsed JSON object. *)

  val find : t -> string list -> t option

  val to_string : t -> (string, [ `Msg of string ]) result
  (** Convert the JSON to a string. *)

  val string : string -> t
  (** Create a JSON string. *)

  val to_float : t -> (float, [ `Msg of string ]) result
  (** Convert the JSON to a float. *)

  val float : float -> t
  (** Converts a float to JSON *)

  val to_int : t -> (int, [ `Msg of string ]) result
  (** Convert the JSON to an integer. *)

  val int : int -> t
  (** Converts an integer to JSON *)

  val to_list : (t -> 'a) -> t -> ('a list, [ `Msg of string ]) result
  (** [to_list f] converts the JSON array to a list and applies [f] to each
      element to convert them too. *)

  val list : ('a -> t) -> 'a list -> t
  (** Make a JSON array from a list *)

  val to_array : (t -> 'a) -> t -> ('a array, [ `Msg of string ]) result
  (** Like {!to_list} except to an array. *)

  val array : ('a -> t) -> 'a array -> t
  (** Like {!list} except for OCaml arrays *)

  val to_obj : t -> ((string * t) list, [ `Msg of string ]) result
  (** Convert the JSON object to an association list *)

  val obj : (string * t) list -> t
  (** A JSON object from an association list *)

  val null : t
  (** Null value *)

  val is_null : t -> bool
  (** Test for null *)
end

(* {2 Json Conversion} *)

module type Json_conv = sig
  type t
  type json

  val to_json : t -> json
  (** Convert a [t] to a JSON representation. *)

  val of_json : json -> (t, [ `Msg of string ]) result
  (** Attempt to deserialise a [t] from JSON.*)
end

(** {2 topojson Geometry Objects}

    The basic primitives for building geometrical shapes in topojson. *)

module type Geometry = sig
  type json

  module Position : sig
    type t = float array
    (** A position - a longitude and latitude with an optional altitude *)

    val lng : t -> float
    (** The longitude value of the position *)

    val lat : t -> float
    (** The latitude value of the position *)

    val altitude : t -> float option
    (** Optional altitude/elevation value of the position *)

    val equal : t -> t -> bool
    (** Whether two positions are equal by comparing each value *)

    val v : ?altitude:float -> lng:float -> lat:float -> unit -> t
    (** A position constructor *)
  end

  module Point : sig
    type t
    (** A point is a single {!Position.t} *)

    val position : t -> Position.t
    (** Convert a point to a position *)

    val v : Position.t -> t
    (** Create a point from a position. *)
  end

  module Arc_index : sig
    type t
    (** An array of indexes into the arcs field. *)

    val v : int list -> t
    (** A constructors for building an arc-index from an integer array. *)
  end

  module MultiPoint : sig
    type t
    (** A multipoint is an array of positions. *)

    val coordinates : t -> Position.t array
    (** Get the positions that make up this multipoint object. *)

    val v : Position.t array -> t
    (** Create a multipoint object from an array of positions. *)
  end

  module LineString : sig
    type t
    (** A line string is two or more points *)

    val v : Arc_index.t -> t
    (** Creates a line string from an arc index. *)
  end

  module MultiLineString : sig
    type t
    (** A collection of line strings *)

    val v : LineString.t array -> t
    (** Create a multiline string *)
  end

  module Polygon : sig
    type t
    (** A close loop with optional rings *)

    val rings : t -> LineString.t array
    (** [rings t] returns the linear rings contained in [t] (a Polygon object) *)

    val exterior_ring : t -> LineString.t
    (** [exterior_ring t] returns the first linear ring contained in [t] (a
        Polygon object). This ring bounds the surface *)

    val interior_rings : t -> LineString.t array
    (** 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. *)

    val v : LineString.t array -> t
    (** Create a polygon object from an array of close line strings *)
  end

  module MultiPolygon : sig
    type t
    (** A multi-polygon object *)

    val polygons : t -> Polygon.t array
    (** Access the polygons *)

    val v : Polygon.t array -> t
    (** Create a multi-polygon object from an array of {!Polygon.t}s *)
  end

  type geometry =
    | Point of Point.t
    | MultiPoint of MultiPoint.t
    | LineString of LineString.t
    | MultiLineString of MultiLineString.t
    | Polygon of Polygon.t
    | MultiPolygon of MultiPolygon.t
    | Collection of t list

  and properties = [ `None | `Null | `Obj of (string * json) list ]
  (** The properties associated with a Geometry object can be left out
      ([`None]), could be specifically a null value ([`Null]) or an object. *)

  and t

  (** {2 Constructors} *)

  val point : Position.t -> geometry
  val multipoint : Point.t array -> geometry
  val linestring : Arc_index.t -> geometry
  val multilinestring : LineString.t array -> geometry
  val polygon : LineString.t array -> geometry
  val multipolygon : Polygon.t array -> geometry
  val collection : t list -> geometry

  (** {2 Conversion functions}*)

  val get_point : geometry -> (Point.t, [> `Msg of string ]) result
  val get_point_exn : geometry -> Point.t
  val get_multipoint : geometry -> (MultiPoint.t, [> `Msg of string ]) result
  val get_multipoint_exn : geometry -> MultiPoint.t
  val get_linestring : geometry -> (LineString.t, [> `Msg of string ]) result
  val get_linestring_exn : geometry -> LineString.t

  val get_multilinestring :
    geometry -> (MultiLineString.t, [> `Msg of string ]) result

  val get_multilinestring_exn : geometry -> MultiLineString.t
  val get_polygon : geometry -> (Polygon.t, [> `Msg of string ]) result
  val get_polygon_exn : geometry -> Polygon.t

  val get_multipolygon :
    geometry -> (MultiPolygon.t, [> `Msg of string ]) result

  val get_multipolygon_exn : geometry -> MultiPolygon.t

  val properties : t -> properties
  (** [properties t] returns the properties associated with a given object. If
      there aren't any this returns [`None]. The empty list is the empty object
      [{}]. *)

  val geometry : t -> geometry
  (** [geometry t] returns the geometry associated with the object. *)

  val foreign_members : t -> (string * json) list
  (** [foreign_members t] will extract the name-value pairs from an object that
      are not a part of the TopoJSON specification. *)

  val id : t -> json option
  (** [id t] returns the id associated with a given object. If there aren't any
      this returns [None]s. *)

  val v :
    ?id:json ->
    ?properties:properties ->
    ?foreign_members:(string * json) list ->
    ?bbox:float array ->
    geometry ->
    t

  (** Creates a new Geometry object. *)

  val to_json : t -> json
  val of_json : json -> (t, [ `Msg of string ]) result
end

module type S = sig
  type json
  (** The internal representation of JSON *)

  (** {2 Geometries} *)

  (** TopoJSON objects are primarily made up of geometry primitives that are the
      same as those used in {!Geojson}. For example you can have a single
      [Point] or [Linestring].

      These are grouped under the {!Geometry} module and in particular the
      {!Geometry.geometry} type. These can be accessed and pattern-matched
      against using the {!Geometry.geometry function}. For example:

      {[
        let is_linestring t =
          let open Topojson in
          match Geometry.geometry t with
          | Geometry.LineString _ -> true
          | _ -> false
      ]}*)

  module Geometry : Geometry with type json = json

  (** {2 Topologies} *)

  (** The {!Topology.t} is the most common TopoJSON object for the main
      document. This contains a map of geometry objects along with other
      important components of the topology including the arc index and transform
      information. *)

  module Topology : sig
    type t
    (** A topology object *)

    type transform = { scale : float * float; translate : float * float }
    (** A transform object *)

    val objects : t -> (string * Geometry.t) list
    (** The underlying objects of the topology object. *)

    val arcs : t -> Geometry.Position.t array array
    (** The database of linestrings used by other geometries. *)

    val foreign_members : t -> (string * json) list
    (** The extra fields that were in the topology object. *)

    val transform : t -> transform option
    (** Get the transform object of a Topology object. *)

    val v :
      ?foreign_members:(string * json) list ->
      ?transform:transform ->
      arcs:Geometry.Position.t array array ->
      (string * Geometry.t) list ->
      t
    (** Construct a new topology object getting the arcs and the geometry
        objects. *)

    val to_json : ?bbox:float array -> t -> json
    val of_json : json -> (t, [ `Msg of string ]) result
  end

  (** {2 TopoJSON Objects} *)

  (** Finally we have the main TopoJSON object. Most frequently this will be a
      {!Topology.t} but could technically be a standalone {!Geometry.t}
      according to the specification.*)

  type topojson =
    | Topology of Topology.t
    | Geometry of Geometry.t  (** The underlying TopoJSON datastructure. *)

  type t
  (** The TopoJSON object. *)

  val topology_exn : t -> Topology.t
  (** Accessor for the TopoJSON data. This will try to access a
      {!Topology.t}.contents

      @raise Invalid_argument if the data is a geometry. *)

  val topojson : t -> topojson
  (** Accessor for the TopoJSON data. *)

  val bbox : t -> float array option
  (** Accessor for the optional bounding-box for the entire TopoJSON object. *)

  val v : ?bbox:float array -> topojson -> t
  (** Construct a new TopoJSON object, optionally with a bounding-box. *)

  val of_json : json -> (t, [ `Msg of string ]) result
  (** [of_json json] converts the JSON to a topojson object (a type {!t}) or an
      error. *)

  val to_json : t -> json
  (** [to_json t] converts the TopoJSON object [t] to JSON. *)
end

module type Topojson = sig
  module type S = S
  (** Types for TopoJSON objects *)

  module type Json = Json
  (** Types for the JSON parser. A user must provide a JSON parser to the
      {!Make} functor in order to have a working TopoJSON library. *)

  (** A functor that takes a Json parsing implementation and returns a TopoJSON
      parser and constructor. *)
  module Make (J : Json) : S with type json = J.t
end