GPS Exchange Format library/CLI in OCaml
at main 5.3 kB view raw
1(** Main GPX document type *) 2 3(** Main GPX document type *) 4type t = { 5 version : string; (* GPX version: "1.0" or "1.1" *) 6 creator : string; (* Creating application *) 7 metadata : Metadata.t option; (* Document metadata *) 8 waypoints : Waypoint.t list; (* Waypoints *) 9 routes : Route.t list; (* Routes *) 10 tracks : Track.t list; (* Tracks *) 11 extensions : Extension.t list; (* Document-level extensions *) 12} 13 14(** {2 Document Constructors} *) 15 16(** Create empty GPX document *) 17let empty ~creator = { 18 version = "1.1"; 19 creator; 20 metadata = None; 21 waypoints = []; 22 routes = []; 23 tracks = []; 24 extensions = []; 25} 26 27(** Create GPX document with metadata *) 28let make ~creator ~metadata = 29 { (empty ~creator) with metadata = Some metadata } 30 31(** {2 Document Properties} *) 32 33(** Get version *) 34let version t = t.version 35 36(** Get creator *) 37let creator t = t.creator 38 39(** Get metadata *) 40let metadata t = t.metadata 41 42(** Get waypoints *) 43let waypoints t = t.waypoints 44 45(** Get routes *) 46let routes t = t.routes 47 48(** Get tracks *) 49let tracks t = t.tracks 50 51(** Get extensions *) 52let extensions t = t.extensions 53 54(** {2 Document Modification} *) 55 56(** Update metadata *) 57let with_metadata t metadata = { t with metadata = Some metadata } 58 59(** Add waypoint *) 60let add_waypoint t waypoint = { t with waypoints = t.waypoints @ [waypoint] } 61 62(** Add waypoints *) 63let add_waypoints t waypoints = { t with waypoints = t.waypoints @ waypoints } 64 65(** Add route *) 66let add_route t route = { t with routes = t.routes @ [route] } 67 68(** Add routes *) 69let add_routes t routes = { t with routes = t.routes @ routes } 70 71(** Add track *) 72let add_track t track = { t with tracks = t.tracks @ [track] } 73 74(** Add tracks *) 75let add_tracks t tracks = { t with tracks = t.tracks @ tracks } 76 77(** Add extensions *) 78let add_extensions t extensions = { t with extensions = t.extensions @ extensions } 79 80(** Clear waypoints *) 81let clear_waypoints t = { t with waypoints = [] } 82 83(** Clear routes *) 84let clear_routes t = { t with routes = [] } 85 86(** Clear tracks *) 87let clear_tracks t = { t with tracks = [] } 88 89(** {2 Document Analysis} *) 90 91(** Count waypoints *) 92let waypoint_count t = List.length t.waypoints 93 94(** Count routes *) 95let route_count t = List.length t.routes 96 97(** Count tracks *) 98let track_count t = List.length t.tracks 99 100(** Count total points *) 101let total_points t = 102 let waypoint_points = List.length t.waypoints in 103 let route_points = List.fold_left (fun acc route -> 104 acc + Route.point_count route) 0 t.routes in 105 let track_points = List.fold_left (fun acc track -> 106 acc + Track.point_count track) 0 t.tracks in 107 waypoint_points + route_points + track_points 108 109(** Check if document has elevation data *) 110let has_elevation t = 111 List.exists (fun wpt -> Waypoint.elevation wpt <> None) t.waypoints || 112 List.exists (fun route -> 113 List.exists (fun pt -> Waypoint.elevation pt <> None) (Route.points route) 114 ) t.routes || 115 List.exists (fun track -> 116 List.exists (fun pt -> Waypoint.elevation pt <> None) (Track.all_points track) 117 ) t.tracks 118 119(** Check if document has time data *) 120let has_time t = 121 List.exists (fun wpt -> Waypoint.time wpt <> None) t.waypoints || 122 List.exists (fun route -> 123 List.exists (fun pt -> Waypoint.time pt <> None) (Route.points route) 124 ) t.routes || 125 List.exists (fun track -> 126 List.exists (fun pt -> Waypoint.time pt <> None) (Track.all_points track) 127 ) t.tracks 128 129(** Check if document is empty *) 130let is_empty t = 131 waypoint_count t = 0 && route_count t = 0 && track_count t = 0 132 133(** Get statistics *) 134type stats = { 135 waypoint_count : int; 136 route_count : int; 137 track_count : int; 138 total_points : int; 139 has_elevation : bool; 140 has_time : bool; 141} 142 143let stats t = { 144 waypoint_count = waypoint_count t; 145 route_count = route_count t; 146 track_count = track_count t; 147 total_points = total_points t; 148 has_elevation = has_elevation t; 149 has_time = has_time t; 150} 151 152(** Pretty print statistics *) 153let pp_stats ppf t = 154 let s = stats t in 155 Format.fprintf ppf "@[<v>GPX Statistics:@, Waypoints: %d@, Routes: %d@, Tracks: %d@, Total points: %d@, Has elevation data: %s@, Has time data: %s@]" 156 s.waypoint_count s.route_count s.track_count s.total_points 157 (if s.has_elevation then "yes" else "no") 158 (if s.has_time then "yes" else "no") 159 160(** {2 Comparison and Utilities} *) 161 162(** Compare documents *) 163let compare t1 t2 = 164 let version_cmp = String.compare t1.version t2.version in 165 if version_cmp <> 0 then version_cmp 166 else 167 let creator_cmp = String.compare t1.creator t2.creator in 168 if creator_cmp <> 0 then creator_cmp 169 else 170 let waypoints_cmp = List.compare Waypoint.compare t1.waypoints t2.waypoints in 171 if waypoints_cmp <> 0 then waypoints_cmp 172 else 173 let routes_cmp = List.compare Route.compare t1.routes t2.routes in 174 if routes_cmp <> 0 then routes_cmp 175 else List.compare Track.compare t1.tracks t2.tracks 176 177(** Test document equality *) 178let equal t1 t2 = compare t1 t2 = 0 179 180(** Pretty print document *) 181let pp ppf t = 182 let stats = stats t in 183 Format.fprintf ppf "GPX v%s by %s (%d wpt, %d routes, %d tracks, %d total points)" 184 t.version t.creator 185 stats.waypoint_count stats.route_count stats.track_count stats.total_points 186