package mlgpx

  1. Overview
  2. Docs

Source file doc.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
(** 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 "@[<v>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

OCaml

Innovation. Community. Security.