···
(** {1 MLGpx - OCaml GPX Library}
+
A high-quality OCaml library for parsing and generating GPX (GPS Exchange Format) files.
+
GPX is a standardized XML format for exchanging GPS data between applications and devices.
+
The GPX format defines a standard way to describe waypoints, routes, and tracks.
+
This library provides a complete implementation of GPX 1.1 with strong type safety
+
and memory-efficient streaming processing.
+
- ✅ Complete GPX 1.1 support with all standard elements
+
- ✅ Type-safe coordinate validation (WGS84 datum)
+
- ✅ Memory-efficient streaming parser and writer
+
- ✅ Comprehensive validation with detailed error reporting
+
- ✅ Extension support for custom elements
+
- ✅ Cross-platform (core has no Unix dependencies)
+
(* Create coordinates *)
+
let* lat = latitude 37.7749 in
+
let* lon = longitude (-122.4194) in
+
(* Create a waypoint *)
+
let wpt = make_waypoint_data lat lon in
+
let wpt = { wpt with name = Some "San Francisco" } in
+
(* Create GPX document *)
+
let gpx = make_gpx ~creator:"mlgpx" in
+
let gpx = { gpx with waypoints = [wpt] } in
+
(* Convert to XML string *)
+
(** {3 Geographic Coordinates}
+
All coordinates use the WGS84 datum as specified by the GPX standard. *)
+
(** Latitude coordinate (-90.0 to 90.0 degrees).
+
Private type ensures validation through smart constructor. *)
+
type latitude = Types.latitude
+
(** Longitude coordinate (-180.0 to 180.0 degrees).
+
Private type ensures validation through smart constructor. *)
+
type longitude = Types.longitude
+
(** Degrees for magnetic variation (0.0 to 360.0 degrees).
+
Private type ensures validation through smart constructor. *)
+
type degrees = Types.degrees
+
(** Create validated latitude coordinate.
+
@param lat Latitude in degrees (-90.0 to 90.0)
+
@return [Ok lat] if valid, [Error msg] if out of range *)
+
val latitude : float -> (latitude, string) result
+
(** Create validated longitude coordinate.
+
@param lon Longitude in degrees (-180.0 to 180.0)
+
@return [Ok lon] if valid, [Error msg] if out of range *)
+
val longitude : float -> (longitude, string) result
+
(** Create validated degrees value.
+
@param deg Degrees (0.0 to 360.0)
+
@return [Ok deg] if valid, [Error msg] if out of range *)
+
val degrees : float -> (degrees, string) result
+
(** Convert latitude back to float *)
+
val latitude_to_float : latitude -> float
+
(** Convert longitude back to float *)
+
val longitude_to_float : longitude -> float
+
(** Convert degrees back to float *)
+
val degrees_to_float : degrees -> float
+
Standard GPS fix types as defined in the GPX specification. *)
+
(** GPS fix type indicating the quality/type of GPS reading *)
+
type fix_type = Types.fix_type =
+
| None_fix (** No fix available *)
+
| Fix_2d (** 2D fix (latitude/longitude) *)
+
| Fix_3d (** 3D fix (latitude/longitude/altitude) *)
+
| Dgps (** Differential GPS *)
+
| Pps (** Precise Positioning Service *)
+
(** Convert fix type to string representation *)
+
val fix_type_to_string : fix_type -> string
+
(** Parse fix type from string *)
+
val fix_type_of_string : string -> fix_type option
+
(** {3 Metadata Elements} *)
+
(** Person information for author, copyright holder, etc. *)
+
type person = Types.person = {
+
name : string option; (** Person's name *)
+
email : string option; (** Email address *)
+
link : link option; (** Link to person's website *)
+
(** External link with optional description and type *)
+
and link = Types.link = {
+
href : string; (** URL of the link *)
+
text : string option; (** Text description of link *)
+
type_ : string option; (** MIME type of linked content *)
+
(** Copyright information for the GPX file *)
+
type copyright = Types.copyright = {
+
author : string; (** Copyright holder *)
+
year : int option; (** Year of copyright *)
+
license : string option; (** License terms *)
+
(** Geographic bounds - minimum bounding rectangle *)
+
type bounds = Types.bounds = {
+
minlat : latitude; (** Minimum latitude *)
+
minlon : longitude; (** Minimum longitude *)
+
maxlat : latitude; (** Maximum latitude *)
+
maxlon : longitude; (** Maximum longitude *)
+
(** Extension content for custom elements *)
+
type extension_content = Types.extension_content =
+
| Text of string (** Text content *)
+
| Elements of extension list (** Child elements *)
+
| Mixed of string * extension list (** Mixed text and elements *)
+
(** Extension element for custom data *)
+
and extension = Types.extension = {
+
namespace : string option; (** XML namespace *)
+
name : string; (** Element name *)
+
attributes : (string * string) list; (** Element attributes *)
+
content : extension_content; (** Element content *)
+
(** GPX file metadata containing information about the file itself *)
+
type metadata = Types.metadata = {
+
name : string option; (** Name of GPX file *)
+
desc : string option; (** Description of contents *)
+
author : person option; (** Person who created GPX file *)
+
copyright : copyright option; (** Copyright information *)
+
links : link list; (** Related links *)
+
time : Ptime.t option; (** Creation/modification time *)
+
keywords : string option; (** Keywords for searching *)
+
bounds : bounds option; (** Geographic bounds *)
+
extensions : extension list; (** Custom extensions *)
+
(** Create empty metadata record *)
+
val empty_metadata : metadata
+
(** {3 Geographic Points}
+
All geographic points (waypoints, route points, track points) share the same structure. *)
+
(** Base waypoint data structure used for all geographic points.
+
Contains position, time, and various GPS-related fields. *)
+
type waypoint_data = Types.waypoint_data = {
+
lat : latitude; (** Latitude coordinate *)
+
lon : longitude; (** Longitude coordinate *)
+
ele : float option; (** Elevation in meters *)
+
time : Ptime.t option; (** Time of GPS reading *)
+
magvar : degrees option; (** Magnetic variation at point *)
+
geoidheight : float option; (** Height of geoid above WGS84 ellipsoid *)
+
name : string option; (** Point name *)
+
cmt : string option; (** GPS comment *)
+
desc : string option; (** Point description *)
+
src : string option; (** Source of data *)
+
links : link list; (** Related links *)
+
sym : string option; (** GPS symbol name *)
+
type_ : string option; (** Point classification *)
+
fix : fix_type option; (** Type of GPS fix *)
+
sat : int option; (** Number of satellites *)
+
hdop : float option; (** Horizontal dilution of precision *)
+
vdop : float option; (** Vertical dilution of precision *)
+
pdop : float option; (** Position dilution of precision *)
+
ageofdgpsdata : float option; (** Age of DGPS data *)
+
dgpsid : int option; (** DGPS station ID *)
+
extensions : extension list; (** Custom extensions *)
+
(** Create basic waypoint data with required coordinates *)
+
val make_waypoint_data : latitude -> longitude -> waypoint_data
+
(** Individual waypoint - a point of interest *)
+
type waypoint = Types.waypoint
+
(** Route point - point along a planned route *)
+
type route_point = Types.route_point
+
(** Track point - recorded position along an actual path *)
+
type track_point = Types.track_point
+
A route is an ordered list of waypoints representing a planned path. *)
+
(** Route definition - ordered list of waypoints for navigation *)
+
type route = Types.route = {
+
name : string option; (** Route name *)
+
cmt : string option; (** GPS comment *)
+
desc : string option; (** Route description *)
+
src : string option; (** Source of data *)
+
links : link list; (** Related links *)
+
number : int option; (** Route number *)
+
type_ : string option; (** Route classification *)
+
extensions : extension list; (** Custom extensions *)
+
rtepts : route_point list; (** Route points *)
+
A track represents an actual recorded path, consisting of track segments. *)
+
(** Track segment - continuous set of track points *)
+
type track_segment = Types.track_segment = {
+
trkpts : track_point list; (** Track points in segment *)
+
extensions : extension list; (** Custom extensions *)
+
(** Track definition - recorded path made up of segments *)
+
type track = Types.track = {
+
name : string option; (** Track name *)
+
cmt : string option; (** GPS comment *)
+
desc : string option; (** Track description *)
+
src : string option; (** Source of data *)
+
links : link list; (** Related links *)
+
number : int option; (** Track number *)
+
type_ : string option; (** Track classification *)
+
extensions : extension list; (** Custom extensions *)
+
trksegs : track_segment list; (** Track segments *)
+
(** {3 Main GPX Document}
+
The root GPX element contains metadata and collections of waypoints, routes, and tracks. *)
+
(** Main GPX document conforming to GPX 1.1 standard *)
+
type gpx = Types.gpx = {
+
version : string; (** GPX version (always "1.1") *)
+
creator : string; (** Creating application *)
+
metadata : metadata option; (** File metadata *)
+
waypoints : waypoint list; (** Waypoints *)
+
routes : route list; (** Routes *)
+
tracks : track list; (** Tracks *)
+
extensions : extension list; (** Custom extensions *)
+
(** Create GPX document with required creator field *)
+
val make_gpx : creator:string -> gpx
+
(** {3 Error Handling} *)
+
(** Errors that can occur during GPX processing *)
+
type error = Types.error =
+
| Invalid_xml of string (** XML parsing error *)
+
| Invalid_coordinate of string (** Coordinate validation error *)
+
| Missing_required_attribute of string * string (** Missing XML attribute *)
+
| Missing_required_element of string (** Missing XML element *)
+
| Validation_error of string (** GPX validation error *)
+
| Xml_error of string (** XML processing error *)
+
| IO_error of string (** I/O error *)
+
(** Exception type for GPX errors *)
+
exception Gpx_error of error
+
(** Result type for operations that may fail *)
+
type 'a result = ('a, error) Result.t
+
(** {2 Parsing Functions}
+
Parse GPX documents from XML input sources. *)
+
(** Parse GPX document from xmlm input source.
+
@param input The xmlm input source
+
@return [Ok gpx] on success, [Error err] on failure *)
+
val parse : Xmlm.input -> gpx result
+
(** Parse GPX document from string.
+
@param xml_string GPX document as XML string
+
@return [Ok gpx] on success, [Error err] on failure *)
+
val parse_string : string -> gpx result
+
(** {2 Writing Functions}
+
Generate GPX XML from document structures. *)
+
(** Write GPX document to xmlm output destination.
+
@param output The xmlm output destination
+
@param gpx The GPX document to write
+
@return [Ok ()] on success, [Error err] on failure *)
+
val write : Xmlm.output -> gpx -> unit result
+
(** Write GPX document to XML string.
+
@param gpx The GPX document to write
+
@return [Ok xml_string] on success, [Error err] on failure *)
+
val write_string : gpx -> string result
+
(** {2 Validation Functions}
+
Validate GPX documents for correctness and best practices. *)
+
(** Validation issue with severity level *)
+
type validation_issue = Validate.validation_issue = {
+
level : [`Error | `Warning]; (** Severity level *)
+
message : string; (** Issue description *)
+
location : string option; (** Location in document *)
+
(** Result of validation containing all issues found *)
+
type validation_result = Validate.validation_result = {
+
issues : validation_issue list; (** All validation issues *)
+
is_valid : bool; (** True if no errors found *)
+
(** Validate complete GPX document.
+
Checks coordinates, required fields, and best practices.
+
@param gpx GPX document to validate
+
@return Validation result with any issues found *)
+
val validate_gpx : gpx -> validation_result
+
(** Quick validation check.
+
@param gpx GPX document to validate
+
@return [true] if document is valid (no errors) *)
+
val is_valid : gpx -> bool
+
(** Get only error-level validation issues.
+
@param gpx GPX document to validate
+
@return List of validation errors *)
+
val get_errors : gpx -> validation_issue list
+
(** Get only warning-level validation issues.
+
@param gpx GPX document to validate
+
@return List of validation warnings *)
+
val get_warnings : gpx -> validation_issue list
+
(** Format validation issue for display.
+
@param issue Validation issue to format
+
@return Human-readable error message *)
+
val format_issue : validation_issue -> string
+
Direct access to submodules for advanced usage. *)
+
(** Core type definitions and utilities *)
+
(** Streaming XML parser *)
+
(** Streaming XML writer *)
+
(** Validation engine *)
+
module Validate = Validate