GPS Exchange Format library/CLI in OCaml
1(** Geographic coordinate types with validation *)
2
3(** Private coordinate types with validation constraints *)
4type latitude = private float
5type longitude = private float
6type degrees = private float
7
8(** Coordinate pair - main type for this module *)
9type t = {
10 lat : latitude;
11 lon : longitude;
12}
13
14(** Smart constructors for validated coordinates *)
15let latitude f =
16 if f >= -90.0 && f <= 90.0 then Ok (Obj.magic f : latitude)
17 else Error (Printf.sprintf "Invalid latitude: %f (must be between -90.0 and 90.0)" f)
18
19let longitude f =
20 if f >= -180.0 && f < 180.0 then Ok (Obj.magic f : longitude)
21 else Error (Printf.sprintf "Invalid longitude: %f (must be between -180.0 and 180.0)" f)
22
23let degrees f =
24 if f >= 0.0 && f < 360.0 then Ok (Obj.magic f : degrees)
25 else Error (Printf.sprintf "Invalid degrees: %f (must be between 0.0 and 360.0)" f)
26
27(** Convert back to float *)
28let latitude_to_float (lat : latitude) = (lat :> float)
29let longitude_to_float (lon : longitude) = (lon :> float)
30let degrees_to_float (deg : degrees) = (deg :> float)
31
32(** Create coordinate pair *)
33let make lat lon = { lat; lon }
34
35(** Create coordinate pair from floats with validation *)
36let make_from_floats lat_f lon_f =
37 match latitude lat_f, longitude lon_f with
38 | Ok lat, Ok lon -> Ok { lat; lon }
39 | Error e, _ | _, Error e -> Error e
40
41(** Extract components *)
42let lat t = t.lat
43let lon t = t.lon
44let to_floats t = (latitude_to_float t.lat, longitude_to_float t.lon)
45
46(** Compare coordinates *)
47let compare t1 t2 =
48 let lat_cmp = Float.compare (latitude_to_float t1.lat) (latitude_to_float t2.lat) in
49 if lat_cmp <> 0 then lat_cmp
50 else Float.compare (longitude_to_float t1.lon) (longitude_to_float t2.lon)
51
52(** Equality *)
53let equal t1 t2 = compare t1 t2 = 0
54
55(** Pretty printer *)
56let pp ppf t =
57 Format.fprintf ppf "(%g, %g)"
58 (latitude_to_float t.lat)
59 (longitude_to_float t.lon)