(** Core GPX types matching the GPX 1.1 XSD schema *) [@@@warning "-30"] (** Geographic coordinates with validation constraints *) type latitude = private float type longitude = private float type degrees = private float (** Smart constructors for validated coordinates *) let latitude f = if f >= -90.0 && f <= 90.0 then Ok (Obj.magic f : latitude) else Error (Printf.sprintf "Invalid latitude: %f (must be between -90.0 and 90.0)" f) let longitude f = if f >= -180.0 && f < 180.0 then Ok (Obj.magic f : longitude) else Error (Printf.sprintf "Invalid longitude: %f (must be between -180.0 and 180.0)" f) let degrees f = if f >= 0.0 && f < 360.0 then Ok (Obj.magic f : degrees) else Error (Printf.sprintf "Invalid degrees: %f (must be between 0.0 and 360.0)" f) (** Convert back to float *) let latitude_to_float (lat : latitude) = (lat :> float) let longitude_to_float (lon : longitude) = (lon :> float) let degrees_to_float (deg : degrees) = (deg :> float) (** GPS fix types as defined in GPX spec *) type fix_type = | None_fix | Fix_2d | Fix_3d | Dgps | Pps (** Person information *) type person = { name : string option; email : string option; link : link option; } (** Link information *) and link = { href : string; text : string option; type_ : string option; } (** Copyright information *) type copyright = { author : string; year : int option; license : string option; } (** Bounding box *) type bounds = { minlat : latitude; minlon : longitude; maxlat : latitude; maxlon : longitude; } (** Metadata container *) type metadata = { name : string option; desc : string option; author : person option; copyright : copyright option; links : link list; time : Ptime.t option; keywords : string option; bounds : bounds option; extensions : extension list; } (** Extension mechanism for custom elements *) and extension = { namespace : string option; name : string; attributes : (string * string) list; content : extension_content; } and extension_content = | Text of string | Elements of extension list | Mixed of string * extension list (** Base waypoint data shared by wpt, rtept, trkpt *) type waypoint_data = { lat : latitude; lon : longitude; ele : float option; time : Ptime.t option; magvar : degrees option; geoidheight : float option; name : string option; cmt : string option; desc : string option; src : string option; links : link list; sym : string option; type_ : string option; fix : fix_type option; sat : int option; hdop : float option; vdop : float option; pdop : float option; ageofdgpsdata : float option; dgpsid : int option; extensions : extension list; } (** Waypoint *) type waypoint = waypoint_data (** Route point *) type route_point = waypoint_data (** Track point *) type track_point = waypoint_data (** Route definition *) type route = { name : string option; cmt : string option; desc : string option; src : string option; links : link list; number : int option; type_ : string option; extensions : extension list; rtepts : route_point list; } (** Track segment *) type track_segment = { trkpts : track_point list; extensions : extension list; } (** Track definition *) type track = { name : string option; cmt : string option; desc : string option; src : string option; links : link list; number : int option; type_ : string option; extensions : extension list; trksegs : track_segment list; } (** Main GPX document *) type gpx = { version : string; (* GPX version: "1.0" or "1.1" *) creator : string; metadata : metadata option; waypoints : waypoint list; routes : route list; tracks : track list; extensions : extension list; } (** Parser/Writer errors *) type error = | Invalid_xml of string | Invalid_coordinate of string | Missing_required_attribute of string * string | Missing_required_element of string | Validation_error of string | Xml_error of string | IO_error of string exception Gpx_error of error (** Result type for operations that can fail *) type 'a result = ('a, error) Result.t (** Utility functions *) (** Convert fix_type to string *) let fix_type_to_string = function | None_fix -> "none" | Fix_2d -> "2d" | Fix_3d -> "3d" | Dgps -> "dgps" | Pps -> "pps" (** Parse fix_type from string *) let fix_type_of_string = function | "none" -> Some None_fix | "2d" -> Some Fix_2d | "3d" -> Some Fix_3d | "dgps" -> Some Dgps | "pps" -> Some Pps | _ -> None (** Create empty waypoint_data with required coordinates *) let make_waypoint_data lat lon = { lat; lon; ele = None; time = None; magvar = None; geoidheight = None; name = None; cmt = None; desc = None; src = None; links = []; sym = None; type_ = None; fix = None; sat = None; hdop = None; vdop = None; pdop = None; ageofdgpsdata = None; dgpsid = None; extensions = []; } (** Create empty metadata *) let empty_metadata = { name = None; desc = None; author = None; copyright = None; links = []; time = None; keywords = None; bounds = None; extensions = []; } (** Create empty GPX document *) let make_gpx ~creator = { version = "1.1"; creator; metadata = None; waypoints = []; routes = []; tracks = []; extensions = []; }