···
1
-
(** {1 MLGpx - OCaml GPX Library}
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.
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.
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)
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 *)
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 *)
1
+
(** OCaml library for reading and writing GPX (GPS Exchange Format) files
3
+
This library provides a clean, modular interface for working with GPX files,
4
+
the standard format for GPS data exchange. *)
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 *)
8
+
The library is organized into focused modules, each handling a specific aspect
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 *)
11
+
(** Geographic coordinate handling with validation *)
12
+
module Coordinate = Coordinate
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 *)
14
+
(** Links, persons, and copyright information *)
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 *)
17
+
(** Extension mechanism for custom GPX elements *)
18
+
module Extension = Extension
159
-
(** Create empty metadata record *)
160
-
val empty_metadata : metadata
20
+
(** GPS waypoint data and fix types *)
21
+
module Waypoint = Waypoint
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. *)
23
+
(** GPX metadata including bounds *)
24
+
module Metadata = Metadata
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 *)
26
+
(** Route data and calculations *)
27
+
module Route = Route
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 *)
29
+
(** Track data with segments *)
30
+
module Track = Track
244
-
(** {3 Main GPX Document}
32
+
(** Error handling *)
33
+
module Error = Error
246
-
The root GPX element contains metadata and collections of waypoints, routes, and tracks. *)
35
+
(** Main GPX document type *)
36
+
module Gpx_doc = Gpx_doc
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 *)
38
+
(** {1 Main Document Type} *)
259
-
(** Create GPX document with required creator field *)
260
-
val make_gpx : creator:string -> gpx
40
+
(** Main GPX document type *)
262
-
(** {3 Error Handling} *)
43
+
(** {1 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 *)
46
+
type error = Error.t
274
-
(** Exception type for GPX errors *)
48
+
(** GPX exception raised for errors *)
exception Gpx_error of error
277
-
(** Result type for operations that may fail *)
278
-
type 'a result = ('a, error) Result.t
51
+
(** {1 Parsing Functions} *)
280
-
(** {2 Parsing Functions}
53
+
(** Parse GPX from XML input.
55
+
@param validate Whether to validate the document after parsing
56
+
@param input XMLm input source
57
+
@return Parsed GPX document or error *)
58
+
val parse : ?validate:bool -> Xmlm.input -> (t, error) result
282
-
Parse GPX documents from XML input sources. *)
284
-
(** Parse GPX document from xmlm input source.
285
-
@param input The xmlm input source
286
-
@param ?validate Optional validation flag (default: false)
287
-
@return [Ok gpx] on success, [Error err] on failure *)
288
-
val parse : ?validate:bool -> Xmlm.input -> gpx result
290
-
(** Parse GPX document from string.
291
-
@param xml_string GPX document as XML string
292
-
@param ?validate Optional validation flag (default: false)
293
-
@return [Ok gpx] on success, [Error err] on failure *)
294
-
val parse_string : ?validate:bool -> string -> gpx result
296
-
(** {2 Writing Functions}
298
-
Generate GPX XML from document structures. *)
60
+
(** Parse GPX from string.
62
+
@param validate Whether to validate the document after parsing
63
+
@param s XML string to parse
64
+
@return Parsed GPX document or error *)
65
+
val parse_string : ?validate:bool -> string -> (t, error) result
300
-
(** Write GPX document to xmlm output destination.
301
-
@param output The xmlm output destination
302
-
@param gpx The GPX document to write
303
-
@param ?validate Optional validation flag (default: false)
304
-
@return [Ok ()] on success, [Error err] on failure *)
305
-
val write : ?validate:bool -> Xmlm.output -> gpx -> unit result
67
+
(** {1 Writing Functions} *)
307
-
(** Write GPX document to XML string.
308
-
@param gpx The GPX document to write
309
-
@param ?validate Optional validation flag (default: false)
310
-
@return [Ok xml_string] on success, [Error err] on failure *)
311
-
val write_string : ?validate:bool -> gpx -> string result
69
+
(** Write GPX to XML output.
71
+
@param validate Whether to validate before writing
72
+
@param output XMLm output destination
73
+
@param gpx GPX document to write
74
+
@return Success or error *)
75
+
val write : ?validate:bool -> Xmlm.dest -> t -> (unit, error) result
313
-
(** {2 Validation Functions}
77
+
(** Write GPX to string.
79
+
@param validate Whether to validate before writing
80
+
@param gpx GPX document to write
81
+
@return XML string or error *)
82
+
val write_string : ?validate:bool -> t -> (string, error) result
84
+
(** {1 Validation Functions}
Validate GPX documents for correctness and best practices. *)
(** Validation issue with severity level *)
···
(** Result of validation containing all issues found *)
type validation_result = Validate.validation_result = {
issues : validation_issue list; (** All validation issues *)
327
-
is_valid : bool; (** True if no errors found *)
98
+
is_valid : bool; (** Whether document is valid *)
330
-
(** Validate complete GPX document.
331
-
Checks coordinates, required fields, and best practices.
332
-
@param gpx GPX document to validate
333
-
@return Validation result with any issues found *)
334
-
val validate_gpx : gpx -> validation_result
101
+
(** Validate complete GPX document *)
102
+
val validate_gpx : t -> validation_result
336
-
(** Quick validation check.
337
-
@param gpx GPX document to validate
338
-
@return [true] if document is valid (no errors) *)
339
-
val is_valid : gpx -> bool
104
+
(** Quick validation - returns true if document is valid *)
105
+
val is_valid : t -> bool
341
-
(** Get only error-level validation issues.
342
-
@param gpx GPX document to validate
343
-
@return List of validation errors *)
344
-
val get_errors : gpx -> validation_issue list
107
+
(** Get only error messages *)
108
+
val get_errors : t -> validation_issue list
346
-
(** Get only warning-level validation issues.
347
-
@param gpx GPX document to validate
348
-
@return List of validation warnings *)
349
-
val get_warnings : gpx -> validation_issue list
110
+
(** Get only warning messages *)
111
+
val get_warnings : t -> validation_issue list
351
-
(** Format validation issue for display.
352
-
@param issue Validation issue to format
353
-
@return Human-readable error message *)
113
+
(** Format validation issue for display *)
val format_issue : validation_issue -> string
356
-
(** {2 Utility Functions}
116
+
(** {1 Constructors and Utilities} *)
358
-
Convenient functions for creating and analyzing GPX data. *)
118
+
(** Create new GPX document with required fields *)
119
+
val make_gpx : creator:string -> t
360
-
(** Create waypoint from float coordinates.
361
-
@param lat Latitude in degrees (-90.0 to 90.0)
362
-
@param lon Longitude in degrees (-180.0 to 180.0)
363
-
@param ?name Optional waypoint name
364
-
@param ?desc Optional waypoint description
365
-
@return Waypoint data
366
-
@raises Gpx_error on invalid coordinates *)
367
-
val make_waypoint_from_floats : lat:float -> lon:float -> ?name:string -> ?desc:string -> unit -> waypoint_data
369
-
(** Create track from coordinate list.
370
-
@param name Track name
371
-
@param coords List of (latitude, longitude) pairs
372
-
@return Track with single segment
373
-
@raises Gpx_error on invalid coordinates *)
374
-
val make_track_from_coord_list : name:string -> (float * float) list -> track
376
-
(** Create route from coordinate list.
377
-
@param name Route name
378
-
@param coords List of (latitude, longitude) pairs
380
-
@raises Gpx_error on invalid coordinates *)
381
-
val make_route_from_coord_list : name:string -> (float * float) list -> route
383
-
(** Extract coordinates from waypoint.
384
-
@param wpt Waypoint data
385
-
@return (latitude, longitude) as floats *)
386
-
val waypoint_coords : waypoint_data -> float * float
388
-
(** Extract coordinates from track.
390
-
@return List of (latitude, longitude) pairs *)
391
-
val track_coords : track -> (float * float) list
393
-
(** Extract coordinates from route.
395
-
@return List of (latitude, longitude) pairs *)
396
-
val route_coords : route -> (float * float) list
398
-
(** Count total points in GPX document.
399
-
@param gpx GPX document
400
-
@return Total number of waypoints, route points, and track points *)
401
-
val count_points : gpx -> int
403
-
(** GPX statistics record *)
405
-
waypoint_count : int; (** Number of waypoints *)
406
-
route_count : int; (** Number of routes *)
407
-
track_count : int; (** Number of tracks *)
408
-
total_points : int; (** Total geographic points *)
409
-
has_elevation : bool; (** Document contains elevation data *)
410
-
has_time : bool; (** Document contains time data *)
413
-
(** Get GPX document statistics.
414
-
@param gpx GPX document
415
-
@return Statistics summary *)
416
-
val get_stats : gpx -> gpx_stats
418
-
(** Print GPX statistics to stdout.
419
-
@param gpx GPX document *)
420
-
val print_stats : gpx -> unit
422
-
(** {2 Module Access}
424
-
Direct access to submodules for advanced usage. *)
426
-
(** Core type definitions and utilities *)
427
-
module Types = Types
429
-
(** Streaming XML parser *)
430
-
module Parser = Parser
432
-
(** Streaming XML writer *)
433
-
module Writer = Writer
435
-
(** Validation engine *)
436
-
module Validate = Validate
121
+
(** Create empty GPX document *)
122
+
val empty : creator:string -> t