···
(** {1 MLGpx - OCaml GPX Library}
3
-
A library for parsing and generating GPX (GPS Exchange Format) files.
3
+
A high-quality OCaml library for parsing and generating GPX (GPS Exchange Format) files.
4
+
GPX is a standardized XML format for exchanging GPS data between applications and devices.
5
-
The library is split into two main components:
6
-
- {b Core Library (gpx)}: Portable core library with no Unix dependencies
7
-
- {b Unix Layer (gpx_unix)}: Convenient functions for file I/O and validation
8
+
The GPX format defines a standard way to describe waypoints, routes, and tracks.
9
+
This library provides a complete implementation of GPX 1.1 with strong type safety
10
+
and memory-efficient streaming processing.
11
-
- ✅ Complete GPX 1.1 support: Waypoints, routes, tracks, metadata, extensions
12
-
- ✅ Streaming parser/writer: Memory-efficient for large files
13
-
- ✅ Strong type safety: Validated coordinates, GPS fix types, etc.
14
-
- ✅ Comprehensive validation: Detailed error and warning reporting
15
-
- ✅ Extension support: Handle custom XML elements
16
-
- ✅ Cross-platform: Core library has no Unix dependencies
13
+
- ✅ Complete GPX 1.1 support with all standard elements
14
+
- ✅ Type-safe coordinate validation (WGS84 datum)
15
+
- ✅ Memory-efficient streaming parser and writer
16
+
- ✅ Comprehensive validation with detailed error reporting
17
+
- ✅ Extension support for custom elements
18
+
- ✅ Cross-platform (core has no Unix dependencies)
23
-
let create_simple_gpx () =
24
-
(* Create waypoints *)
25
-
let* waypoint = Types.make_waypoint ~lat:37.7749 ~lon:(-122.4194)
26
-
~name:"San Francisco" () in
28
-
(* Create track from coordinates *)
29
-
let coords = [(37.7749, -122.4194); (37.7849, -122.4094)] in
30
-
let* track = make_track_from_coords ~name:"Sample Track" coords in
32
-
(* Create GPX document *)
33
-
let gpx = Types.make_gpx ~creator:"mlgpx example" in
34
-
let gpx = { gpx with waypoints = [waypoint]; tracks = [track] } in
36
-
(* Write to string *)
37
-
Writer.write_string gpx
25
+
(* Create coordinates *)
26
+
let* lat = latitude 37.7749 in
27
+
let* lon = longitude (-122.4194) in
29
+
(* Create a waypoint *)
30
+
let wpt = make_waypoint_data lat lon in
31
+
let wpt = { wpt with name = Some "San Francisco" } in
33
+
(* Create GPX document *)
34
+
let gpx = make_gpx ~creator:"mlgpx" in
35
+
let gpx = { gpx with waypoints = [wpt] } in
37
+
(* Convert to XML string *)
40
-
{2 Module Organization} *)
43
+
(** {3 Geographic Coordinates}
45
+
All coordinates use the WGS84 datum as specified by the GPX standard. *)
47
+
(** Latitude coordinate (-90.0 to 90.0 degrees).
48
+
Private type ensures validation through smart constructor. *)
49
+
type latitude = Types.latitude
51
+
(** Longitude coordinate (-180.0 to 180.0 degrees).
52
+
Private type ensures validation through smart constructor. *)
53
+
type longitude = Types.longitude
55
+
(** Degrees for magnetic variation (0.0 to 360.0 degrees).
56
+
Private type ensures validation through smart constructor. *)
57
+
type degrees = Types.degrees
59
+
(** Create validated latitude coordinate.
60
+
@param lat Latitude in degrees (-90.0 to 90.0)
61
+
@return [Ok lat] if valid, [Error msg] if out of range *)
62
+
val latitude : float -> (latitude, string) result
64
+
(** Create validated longitude coordinate.
65
+
@param lon Longitude in degrees (-180.0 to 180.0)
66
+
@return [Ok lon] if valid, [Error msg] if out of range *)
67
+
val longitude : float -> (longitude, string) result
69
+
(** Create validated degrees value.
70
+
@param deg Degrees (0.0 to 360.0)
71
+
@return [Ok deg] if valid, [Error msg] if out of range *)
72
+
val degrees : float -> (degrees, string) result
74
+
(** Convert latitude back to float *)
75
+
val latitude_to_float : latitude -> float
77
+
(** Convert longitude back to float *)
78
+
val longitude_to_float : longitude -> float
80
+
(** Convert degrees back to float *)
81
+
val degrees_to_float : degrees -> float
83
+
(** {3 GPS Fix Types}
85
+
Standard GPS fix types as defined in the GPX specification. *)
87
+
(** GPS fix type indicating the quality/type of GPS reading *)
88
+
type fix_type = Types.fix_type =
89
+
| None_fix (** No fix available *)
90
+
| Fix_2d (** 2D fix (latitude/longitude) *)
91
+
| Fix_3d (** 3D fix (latitude/longitude/altitude) *)
92
+
| Dgps (** Differential GPS *)
93
+
| Pps (** Precise Positioning Service *)
95
+
(** Convert fix type to string representation *)
96
+
val fix_type_to_string : fix_type -> string
98
+
(** Parse fix type from string *)
99
+
val fix_type_of_string : string -> fix_type option
101
+
(** {3 Metadata Elements} *)
103
+
(** Person information for author, copyright holder, etc. *)
104
+
type person = Types.person = {
105
+
name : string option; (** Person's name *)
106
+
email : string option; (** Email address *)
107
+
link : link option; (** Link to person's website *)
110
+
(** External link with optional description and type *)
111
+
and link = Types.link = {
112
+
href : string; (** URL of the link *)
113
+
text : string option; (** Text description of link *)
114
+
type_ : string option; (** MIME type of linked content *)
117
+
(** Copyright information for the GPX file *)
118
+
type copyright = Types.copyright = {
119
+
author : string; (** Copyright holder *)
120
+
year : int option; (** Year of copyright *)
121
+
license : string option; (** License terms *)
124
+
(** Geographic bounds - minimum bounding rectangle *)
125
+
type bounds = Types.bounds = {
126
+
minlat : latitude; (** Minimum latitude *)
127
+
minlon : longitude; (** Minimum longitude *)
128
+
maxlat : latitude; (** Maximum latitude *)
129
+
maxlon : longitude; (** Maximum longitude *)
132
+
(** Extension content for custom elements *)
133
+
type extension_content = Types.extension_content =
134
+
| Text of string (** Text content *)
135
+
| Elements of extension list (** Child elements *)
136
+
| Mixed of string * extension list (** Mixed text and elements *)
138
+
(** Extension element for custom data *)
139
+
and extension = Types.extension = {
140
+
namespace : string option; (** XML namespace *)
141
+
name : string; (** Element name *)
142
+
attributes : (string * string) list; (** Element attributes *)
143
+
content : extension_content; (** Element content *)
146
+
(** GPX file metadata containing information about the file itself *)
147
+
type metadata = Types.metadata = {
148
+
name : string option; (** Name of GPX file *)
149
+
desc : string option; (** Description of contents *)
150
+
author : person option; (** Person who created GPX file *)
151
+
copyright : copyright option; (** Copyright information *)
152
+
links : link list; (** Related links *)
153
+
time : Ptime.t option; (** Creation/modification time *)
154
+
keywords : string option; (** Keywords for searching *)
155
+
bounds : bounds option; (** Geographic bounds *)
156
+
extensions : extension list; (** Custom extensions *)
42
-
(** {2 Core Types and Data Structures}
44
-
All GPX data types, coordinate validation, and smart constructors. *)
159
+
(** Create empty metadata record *)
160
+
val empty_metadata : metadata
162
+
(** {3 Geographic Points}
164
+
All geographic points (waypoints, route points, track points) share the same structure. *)
166
+
(** Base waypoint data structure used for all geographic points.
167
+
Contains position, time, and various GPS-related fields. *)
168
+
type waypoint_data = Types.waypoint_data = {
169
+
lat : latitude; (** Latitude coordinate *)
170
+
lon : longitude; (** Longitude coordinate *)
171
+
ele : float option; (** Elevation in meters *)
172
+
time : Ptime.t option; (** Time of GPS reading *)
173
+
magvar : degrees option; (** Magnetic variation at point *)
174
+
geoidheight : float option; (** Height of geoid above WGS84 ellipsoid *)
175
+
name : string option; (** Point name *)
176
+
cmt : string option; (** GPS comment *)
177
+
desc : string option; (** Point description *)
178
+
src : string option; (** Source of data *)
179
+
links : link list; (** Related links *)
180
+
sym : string option; (** GPS symbol name *)
181
+
type_ : string option; (** Point classification *)
182
+
fix : fix_type option; (** Type of GPS fix *)
183
+
sat : int option; (** Number of satellites *)
184
+
hdop : float option; (** Horizontal dilution of precision *)
185
+
vdop : float option; (** Vertical dilution of precision *)
186
+
pdop : float option; (** Position dilution of precision *)
187
+
ageofdgpsdata : float option; (** Age of DGPS data *)
188
+
dgpsid : int option; (** DGPS station ID *)
189
+
extensions : extension list; (** Custom extensions *)
192
+
(** Create basic waypoint data with required coordinates *)
193
+
val make_waypoint_data : latitude -> longitude -> waypoint_data
195
+
(** Individual waypoint - a point of interest *)
196
+
type waypoint = Types.waypoint
198
+
(** Route point - point along a planned route *)
199
+
type route_point = Types.route_point
201
+
(** Track point - recorded position along an actual path *)
202
+
type track_point = Types.track_point
206
+
A route is an ordered list of waypoints representing a planned path. *)
208
+
(** Route definition - ordered list of waypoints for navigation *)
209
+
type route = Types.route = {
210
+
name : string option; (** Route name *)
211
+
cmt : string option; (** GPS comment *)
212
+
desc : string option; (** Route description *)
213
+
src : string option; (** Source of data *)
214
+
links : link list; (** Related links *)
215
+
number : int option; (** Route number *)
216
+
type_ : string option; (** Route classification *)
217
+
extensions : extension list; (** Custom extensions *)
218
+
rtepts : route_point list; (** Route points *)
223
+
A track represents an actual recorded path, consisting of track segments. *)
225
+
(** Track segment - continuous set of track points *)
226
+
type track_segment = Types.track_segment = {
227
+
trkpts : track_point list; (** Track points in segment *)
228
+
extensions : extension list; (** Custom extensions *)
231
+
(** Track definition - recorded path made up of segments *)
232
+
type track = Types.track = {
233
+
name : string option; (** Track name *)
234
+
cmt : string option; (** GPS comment *)
235
+
desc : string option; (** Track description *)
236
+
src : string option; (** Source of data *)
237
+
links : link list; (** Related links *)
238
+
number : int option; (** Track number *)
239
+
type_ : string option; (** Track classification *)
240
+
extensions : extension list; (** Custom extensions *)
241
+
trksegs : track_segment list; (** Track segments *)
244
+
(** {3 Main GPX Document}
246
+
The root GPX element contains metadata and collections of waypoints, routes, and tracks. *)
248
+
(** Main GPX document conforming to GPX 1.1 standard *)
249
+
type gpx = Types.gpx = {
250
+
version : string; (** GPX version (always "1.1") *)
251
+
creator : string; (** Creating application *)
252
+
metadata : metadata option; (** File metadata *)
253
+
waypoints : waypoint list; (** Waypoints *)
254
+
routes : route list; (** Routes *)
255
+
tracks : track list; (** Tracks *)
256
+
extensions : extension list; (** Custom extensions *)
259
+
(** Create GPX document with required creator field *)
260
+
val make_gpx : creator:string -> gpx
262
+
(** {3 Error Handling} *)
264
+
(** Errors that can occur during GPX processing *)
265
+
type error = Types.error =
266
+
| Invalid_xml of string (** XML parsing error *)
267
+
| Invalid_coordinate of string (** Coordinate validation error *)
268
+
| Missing_required_attribute of string * string (** Missing XML attribute *)
269
+
| Missing_required_element of string (** Missing XML element *)
270
+
| Validation_error of string (** GPX validation error *)
271
+
| Xml_error of string (** XML processing error *)
272
+
| IO_error of string (** I/O error *)
274
+
(** Exception type for GPX errors *)
275
+
exception Gpx_error of error
277
+
(** Result type for operations that may fail *)
278
+
type 'a result = ('a, error) Result.t
280
+
(** {2 Parsing Functions}
282
+
Parse GPX documents from XML input sources. *)
284
+
(** Parse GPX document from xmlm input source.
285
+
@param input The xmlm input source
286
+
@return [Ok gpx] on success, [Error err] on failure *)
287
+
val parse : Xmlm.input -> gpx result
289
+
(** Parse GPX document from string.
290
+
@param xml_string GPX document as XML string
291
+
@return [Ok gpx] on success, [Error err] on failure *)
292
+
val parse_string : string -> gpx result
294
+
(** {2 Writing Functions}
296
+
Generate GPX XML from document structures. *)
298
+
(** Write GPX document to xmlm output destination.
299
+
@param output The xmlm output destination
300
+
@param gpx The GPX document to write
301
+
@return [Ok ()] on success, [Error err] on failure *)
302
+
val write : Xmlm.output -> gpx -> unit result
304
+
(** Write GPX document to XML string.
305
+
@param gpx The GPX document to write
306
+
@return [Ok xml_string] on success, [Error err] on failure *)
307
+
val write_string : gpx -> string result
309
+
(** {2 Validation Functions}
311
+
Validate GPX documents for correctness and best practices. *)
313
+
(** Validation issue with severity level *)
314
+
type validation_issue = Validate.validation_issue = {
315
+
level : [`Error | `Warning]; (** Severity level *)
316
+
message : string; (** Issue description *)
317
+
location : string option; (** Location in document *)
320
+
(** Result of validation containing all issues found *)
321
+
type validation_result = Validate.validation_result = {
322
+
issues : validation_issue list; (** All validation issues *)
323
+
is_valid : bool; (** True if no errors found *)
326
+
(** Validate complete GPX document.
327
+
Checks coordinates, required fields, and best practices.
328
+
@param gpx GPX document to validate
329
+
@return Validation result with any issues found *)
330
+
val validate_gpx : gpx -> validation_result
332
+
(** Quick validation check.
333
+
@param gpx GPX document to validate
334
+
@return [true] if document is valid (no errors) *)
335
+
val is_valid : gpx -> bool
337
+
(** Get only error-level validation issues.
338
+
@param gpx GPX document to validate
339
+
@return List of validation errors *)
340
+
val get_errors : gpx -> validation_issue list
342
+
(** Get only warning-level validation issues.
343
+
@param gpx GPX document to validate
344
+
@return List of validation warnings *)
345
+
val get_warnings : gpx -> validation_issue list
347
+
(** Format validation issue for display.
348
+
@param issue Validation issue to format
349
+
@return Human-readable error message *)
350
+
val format_issue : validation_issue -> string
352
+
(** {2 Module Access}
354
+
Direct access to submodules for advanced usage. *)
356
+
(** Core type definitions and utilities *)
47
-
(** {2 Streaming Parser}
49
-
Memory-efficient streaming XML parser for GPX documents.
52
-
- Validates coordinates and GPS fix types during parsing
53
-
- Handles extensions and custom elements
54
-
- Reports detailed parsing errors with location information
55
-
- Works with any [Xmlm.input] source *)
359
+
(** Streaming XML parser *)
58
-
(** {2 Streaming Writer}
60
-
Memory-efficient streaming XML writer for GPX documents.
63
-
- Generates compliant GPX 1.1 XML
64
-
- Handles proper namespace declarations
65
-
- Supports extensions and custom elements
66
-
- Works with any [Xmlm.output] destination *)
362
+
(** Streaming XML writer *)
69
-
(** {2 Validation Engine}
71
-
Comprehensive validation for GPX documents with detailed error reporting.
74
-
- Validates coordinates are within proper ranges
75
-
- Checks required fields and proper structure
76
-
- Provides warnings for best practices
77
-
- Supports custom validation rules *)
78
-
module Validate = Validate
365
+
(** Validation engine *)
366
+
module Validate = Validate