(** Main GPX document type *) (** Main GPX document type *) type t = { version : string; (* GPX version: "1.0" or "1.1" *) creator : string; (* Creating application *) metadata : Metadata.t option; (* Document metadata *) waypoints : Waypoint.t list; (* Waypoints *) routes : Route.t list; (* Routes *) tracks : Track.t list; (* Tracks *) extensions : Extension.t list; (* Document-level extensions *) } (** {2 Document Constructors} *) (** Create empty GPX document *) let empty ~creator = { version = "1.1"; creator; metadata = None; waypoints = []; routes = []; tracks = []; extensions = []; } (** Create GPX document with metadata *) let make ~creator ~metadata = { (empty ~creator) with metadata = Some metadata } (** {2 Document Properties} *) (** Get version *) let version t = t.version (** Get creator *) let creator t = t.creator (** Get metadata *) let metadata t = t.metadata (** Get waypoints *) let waypoints t = t.waypoints (** Get routes *) let routes t = t.routes (** Get tracks *) let tracks t = t.tracks (** Get extensions *) let extensions t = t.extensions (** {2 Document Modification} *) (** Update metadata *) let with_metadata t metadata = { t with metadata = Some metadata } (** Add waypoint *) let add_waypoint t waypoint = { t with waypoints = t.waypoints @ [waypoint] } (** Add waypoints *) let add_waypoints t waypoints = { t with waypoints = t.waypoints @ waypoints } (** Add route *) let add_route t route = { t with routes = t.routes @ [route] } (** Add routes *) let add_routes t routes = { t with routes = t.routes @ routes } (** Add track *) let add_track t track = { t with tracks = t.tracks @ [track] } (** Add tracks *) let add_tracks t tracks = { t with tracks = t.tracks @ tracks } (** Add extensions *) let add_extensions t extensions = { t with extensions = t.extensions @ extensions } (** Clear waypoints *) let clear_waypoints t = { t with waypoints = [] } (** Clear routes *) let clear_routes t = { t with routes = [] } (** Clear tracks *) let clear_tracks t = { t with tracks = [] } (** {2 Document Analysis} *) (** Count waypoints *) let waypoint_count t = List.length t.waypoints (** Count routes *) let route_count t = List.length t.routes (** Count tracks *) let track_count t = List.length t.tracks (** Count total points *) let total_points t = let waypoint_points = List.length t.waypoints in let route_points = List.fold_left (fun acc route -> acc + Route.point_count route) 0 t.routes in let track_points = List.fold_left (fun acc track -> acc + Track.point_count track) 0 t.tracks in waypoint_points + route_points + track_points (** Check if document has elevation data *) let has_elevation t = List.exists (fun wpt -> Waypoint.elevation wpt <> None) t.waypoints || List.exists (fun route -> List.exists (fun pt -> Waypoint.elevation pt <> None) (Route.points route) ) t.routes || List.exists (fun track -> List.exists (fun pt -> Waypoint.elevation pt <> None) (Track.all_points track) ) t.tracks (** Check if document has time data *) let has_time t = List.exists (fun wpt -> Waypoint.time wpt <> None) t.waypoints || List.exists (fun route -> List.exists (fun pt -> Waypoint.time pt <> None) (Route.points route) ) t.routes || List.exists (fun track -> List.exists (fun pt -> Waypoint.time pt <> None) (Track.all_points track) ) t.tracks (** Check if document is empty *) let is_empty t = waypoint_count t = 0 && route_count t = 0 && track_count t = 0 (** Get statistics *) type stats = { waypoint_count : int; route_count : int; track_count : int; total_points : int; has_elevation : bool; has_time : bool; } let stats t = { waypoint_count = waypoint_count t; route_count = route_count t; track_count = track_count t; total_points = total_points t; has_elevation = has_elevation t; has_time = has_time t; } (** Pretty print statistics *) let pp_stats ppf t = let s = stats t in Format.fprintf ppf "@[GPX Statistics:@, Waypoints: %d@, Routes: %d@, Tracks: %d@, Total points: %d@, Has elevation data: %s@, Has time data: %s@]" s.waypoint_count s.route_count s.track_count s.total_points (if s.has_elevation then "yes" else "no") (if s.has_time then "yes" else "no") (** {2 Comparison and Utilities} *) (** Compare documents *) let compare t1 t2 = let version_cmp = String.compare t1.version t2.version in if version_cmp <> 0 then version_cmp else let creator_cmp = String.compare t1.creator t2.creator in if creator_cmp <> 0 then creator_cmp else let waypoints_cmp = List.compare Waypoint.compare t1.waypoints t2.waypoints in if waypoints_cmp <> 0 then waypoints_cmp else let routes_cmp = List.compare Route.compare t1.routes t2.routes in if routes_cmp <> 0 then routes_cmp else List.compare Track.compare t1.tracks t2.tracks (** Test document equality *) let equal t1 t2 = compare t1 t2 = 0 (** Pretty print document *) let pp ppf t = let stats = stats t in Format.fprintf ppf "GPX v%s by %s (%d wpt, %d routes, %d tracks, %d total points)" t.version t.creator stats.waypoint_count stats.route_count stats.track_count stats.total_points