1(** Core GPX types matching the GPX 1.1 XSD schema *) 2 3[@@@warning "-30"] 4 5(** Geographic coordinates with validation constraints *) 6type latitude = private float 7type longitude = private float 8type degrees = private float 9 10(** Smart constructors for validated coordinates *) 11let latitude f = 12 if f >= -90.0 && f <= 90.0 then Ok (Obj.magic f : latitude) 13 else Error (Printf.sprintf "Invalid latitude: %f (must be between -90.0 and 90.0)" f) 14 15let longitude f = 16 if f >= -180.0 && f < 180.0 then Ok (Obj.magic f : longitude) 17 else Error (Printf.sprintf "Invalid longitude: %f (must be between -180.0 and 180.0)" f) 18 19let degrees f = 20 if f >= 0.0 && f < 360.0 then Ok (Obj.magic f : degrees) 21 else Error (Printf.sprintf "Invalid degrees: %f (must be between 0.0 and 360.0)" f) 22 23(** Convert back to float *) 24let latitude_to_float (lat : latitude) = (lat :> float) 25let longitude_to_float (lon : longitude) = (lon :> float) 26let degrees_to_float (deg : degrees) = (deg :> float) 27 28(** GPS fix types as defined in GPX spec *) 29type fix_type = 30 | None_fix 31 | Fix_2d 32 | Fix_3d 33 | Dgps 34 | Pps 35 36(** Person information *) 37type person = { 38 name : string option; 39 email : string option; 40 link : link option; 41} 42 43(** Link information *) 44and link = { 45 href : string; 46 text : string option; 47 type_ : string option; 48} 49 50(** Copyright information *) 51type copyright = { 52 author : string; 53 year : int option; 54 license : string option; 55} 56 57(** Bounding box *) 58type bounds = { 59 minlat : latitude; 60 minlon : longitude; 61 maxlat : latitude; 62 maxlon : longitude; 63} 64 65(** Metadata container *) 66type metadata = { 67 name : string option; 68 desc : string option; 69 author : person option; 70 copyright : copyright option; 71 links : link list; 72 time : Ptime.t option; 73 keywords : string option; 74 bounds : bounds option; 75 extensions : extension list; 76} 77 78(** Extension mechanism for custom elements *) 79and extension = { 80 namespace : string option; 81 name : string; 82 attributes : (string * string) list; 83 content : extension_content; 84} 85 86and extension_content = 87 | Text of string 88 | Elements of extension list 89 | Mixed of string * extension list 90 91(** Base waypoint data shared by wpt, rtept, trkpt *) 92type waypoint_data = { 93 lat : latitude; 94 lon : longitude; 95 ele : float option; 96 time : Ptime.t option; 97 magvar : degrees option; 98 geoidheight : float option; 99 name : string option; 100 cmt : string option; 101 desc : string option; 102 src : string option; 103 links : link list; 104 sym : string option; 105 type_ : string option; 106 fix : fix_type option; 107 sat : int option; 108 hdop : float option; 109 vdop : float option; 110 pdop : float option; 111 ageofdgpsdata : float option; 112 dgpsid : int option; 113 extensions : extension list; 114} 115 116(** Waypoint *) 117type waypoint = waypoint_data 118 119(** Route point *) 120type route_point = waypoint_data 121 122(** Track point *) 123type track_point = waypoint_data 124 125(** Route definition *) 126type route = { 127 name : string option; 128 cmt : string option; 129 desc : string option; 130 src : string option; 131 links : link list; 132 number : int option; 133 type_ : string option; 134 extensions : extension list; 135 rtepts : route_point list; 136} 137 138(** Track segment *) 139type track_segment = { 140 trkpts : track_point list; 141 extensions : extension list; 142} 143 144(** Track definition *) 145type track = { 146 name : string option; 147 cmt : string option; 148 desc : string option; 149 src : string option; 150 links : link list; 151 number : int option; 152 type_ : string option; 153 extensions : extension list; 154 trksegs : track_segment list; 155} 156 157(** Main GPX document *) 158type gpx = { 159 version : string; (* GPX version: "1.0" or "1.1" *) 160 creator : string; 161 metadata : metadata option; 162 waypoints : waypoint list; 163 routes : route list; 164 tracks : track list; 165 extensions : extension list; 166} 167 168(** Parser/Writer errors *) 169type error = 170 | Invalid_xml of string 171 | Invalid_coordinate of string 172 | Missing_required_attribute of string * string 173 | Missing_required_element of string 174 | Validation_error of string 175 | Xml_error of string 176 | IO_error of string 177 178exception Gpx_error of error 179 180(** Result type for operations that can fail *) 181type 'a result = ('a, error) Result.t 182 183(** Utility functions *) 184 185(** Convert fix_type to string *) 186let fix_type_to_string = function 187 | None_fix -> "none" 188 | Fix_2d -> "2d" 189 | Fix_3d -> "3d" 190 | Dgps -> "dgps" 191 | Pps -> "pps" 192 193(** Parse fix_type from string *) 194let fix_type_of_string = function 195 | "none" -> Some None_fix 196 | "2d" -> Some Fix_2d 197 | "3d" -> Some Fix_3d 198 | "dgps" -> Some Dgps 199 | "pps" -> Some Pps 200 | _ -> None 201 202(** Create empty waypoint_data with required coordinates *) 203let make_waypoint_data lat lon = { 204 lat; lon; 205 ele = None; time = None; magvar = None; geoidheight = None; 206 name = None; cmt = None; desc = None; src = None; links = []; 207 sym = None; type_ = None; fix = None; sat = None; 208 hdop = None; vdop = None; pdop = None; ageofdgpsdata = None; 209 dgpsid = None; extensions = []; 210} 211 212(** Create empty metadata *) 213let empty_metadata = { 214 name = None; desc = None; author = None; copyright = None; 215 links = []; time = None; keywords = None; bounds = None; 216 extensions = []; 217} 218 219(** Create empty GPX document *) 220let make_gpx ~creator = { 221 version = "1.1"; 222 creator; 223 metadata = None; 224 waypoints = []; 225 routes = []; 226 tracks = []; 227 extensions = []; 228}