GPS Exchange Format library/CLI in OCaml
1(** High-level Unix API for GPX operations *)
2
3(** Result binding operators *)
4let (let*) = Result.bind
5
6(* Re-export core modules *)
7module Types = Gpx.Types
8module Parser = Gpx.Parser
9module Writer = Gpx.Writer
10module Validate = Gpx.Validate
11module IO = Gpx_io
12
13(* Re-export common types *)
14open Gpx.Types
15
16(** Convenience functions for common operations *)
17
18(** Read and parse GPX file *)
19let read = IO.read_file
20
21(** Read and parse GPX file with validation *)
22let read_validated = IO.read_file_validated
23
24(** Write GPX to file *)
25let write = IO.write_file
26
27(** Write GPX to file with validation *)
28let write_validated = IO.write_file_validated
29
30(** Write GPX to file with backup *)
31let write_with_backup = IO.write_file_with_backup
32
33(** Convert GPX to string *)
34let to_string = Writer.write_string
35
36(** Parse GPX from string *)
37let from_string = Parser.parse_string
38
39(** Quick validation check *)
40let is_valid = Validate.is_valid
41
42(** Get validation issues *)
43let validate = Validate.validate_gpx
44
45(** Create simple waypoint *)
46let make_waypoint ~lat ~lon ?name ?desc () =
47 match (latitude lat, longitude lon) with
48 | (Ok lat, Ok lon) ->
49 let wpt = make_waypoint_data lat lon in
50 Ok { wpt with name; desc }
51 | (Error e, _) | (_, Error e) -> Error (Invalid_coordinate e)
52
53(** Create simple track from coordinate list *)
54let make_track_from_coords ~name coords =
55 let make_trkpt (lat, lon) =
56 match (latitude lat, longitude lon) with
57 | (Ok lat, Ok lon) -> Ok (make_waypoint_data lat lon)
58 | (Error e, _) | (_, Error e) -> Error (Invalid_coordinate e)
59 in
60 let rec convert_coords acc = function
61 | [] -> Ok (List.rev acc)
62 | coord :: rest ->
63 match make_trkpt coord with
64 | Ok trkpt -> convert_coords (trkpt :: acc) rest
65 | Error e -> Error e
66 in
67 let* trkpts = convert_coords [] coords in
68 let trkseg = { trkpts; extensions = [] } in
69 Ok {
70 name = Some name;
71 cmt = None; desc = None; src = None; links = [];
72 number = None; type_ = None; extensions = [];
73 trksegs = [trkseg];
74 }
75
76(** Create simple route from coordinate list *)
77let make_route_from_coords ~name coords =
78 let make_rtept (lat, lon) =
79 match (latitude lat, longitude lon) with
80 | (Ok lat, Ok lon) -> Ok (make_waypoint_data lat lon)
81 | (Error e, _) | (_, Error e) -> Error (Invalid_coordinate e)
82 in
83 let rec convert_coords acc = function
84 | [] -> Ok (List.rev acc)
85 | coord :: rest ->
86 match make_rtept coord with
87 | Ok rtept -> convert_coords (rtept :: acc) rest
88 | Error e -> Error e
89 in
90 let* rtepts = convert_coords [] coords in
91 Ok {
92 name = Some name;
93 cmt = None; desc = None; src = None; links = [];
94 number = None; type_ = None; extensions = [];
95 rtepts;
96 }
97
98(** Extract coordinates from waypoints *)
99let waypoint_coords wpt =
100 (latitude_to_float wpt.lat, longitude_to_float wpt.lon)
101
102(** Extract coordinates from track *)
103let track_coords track =
104 List.fold_left (fun acc trkseg ->
105 List.fold_left (fun acc trkpt ->
106 waypoint_coords trkpt :: acc
107 ) acc trkseg.trkpts
108 ) [] track.trksegs
109 |> List.rev
110
111(** Extract coordinates from route *)
112let route_coords route =
113 List.map waypoint_coords route.rtepts
114
115(** Count total points in GPX *)
116let count_points gpx =
117 let waypoint_count = List.length gpx.waypoints in
118 let route_count = List.fold_left (fun acc route ->
119 acc + List.length route.rtepts
120 ) 0 gpx.routes in
121 let track_count = List.fold_left (fun acc track ->
122 List.fold_left (fun acc trkseg ->
123 acc + List.length trkseg.trkpts
124 ) acc track.trksegs
125 ) 0 gpx.tracks in
126 waypoint_count + route_count + track_count
127
128(** Get GPX statistics *)
129type gpx_stats = {
130 waypoint_count : int;
131 route_count : int;
132 track_count : int;
133 total_points : int;
134 has_elevation : bool;
135 has_time : bool;
136}
137
138let get_stats gpx =
139 let waypoint_count = List.length gpx.waypoints in
140 let route_count = List.length gpx.routes in
141 let track_count = List.length gpx.tracks in
142 let total_points = count_points gpx in
143
144 let has_elevation =
145 List.exists (fun wpt -> wpt.ele <> None) gpx.waypoints ||
146 List.exists (fun route ->
147 List.exists (fun rtept -> rtept.ele <> None) route.rtepts
148 ) gpx.routes ||
149 List.exists (fun track ->
150 List.exists (fun trkseg ->
151 List.exists (fun trkpt -> trkpt.ele <> None) trkseg.trkpts
152 ) track.trksegs
153 ) gpx.tracks
154 in
155
156 let has_time =
157 List.exists (fun wpt -> wpt.time <> None) gpx.waypoints ||
158 List.exists (fun route ->
159 List.exists (fun rtept -> rtept.time <> None) route.rtepts
160 ) gpx.routes ||
161 List.exists (fun track ->
162 List.exists (fun trkseg ->
163 List.exists (fun trkpt -> trkpt.time <> None) trkseg.trkpts
164 ) track.trksegs
165 ) gpx.tracks
166 in
167
168 { waypoint_count; route_count; track_count; total_points; has_elevation; has_time }
169
170(** Pretty print GPX statistics *)
171let print_stats gpx =
172 let stats = get_stats gpx in
173 Printf.printf "GPX Statistics:\n";
174 Printf.printf " Waypoints: %d\n" stats.waypoint_count;
175 Printf.printf " Routes: %d\n" stats.route_count;
176 Printf.printf " Tracks: %d\n" stats.track_count;
177 Printf.printf " Total points: %d\n" stats.total_points;
178 Printf.printf " Has elevation data: %s\n" (if stats.has_elevation then "yes" else "no");
179 Printf.printf " Has time data: %s\n" (if stats.has_time then "yes" else "no")