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")