1# mlgpx - an OCaml GPS Exchange Format (GPX) Library
2
3An OCaml library for parsing and generating GPX (GPS Exchange Format) 1.0 and
41.1 files, and a CLI for common manipulation and query options.
5
6## Command Line Usage
7
8The `mlgpx` CLI provides tools for manipulating GPX files from the command line.
9
10### Installation
11
12```bash
13# Install from source
14dune build @install
15dune install
16
17# Or use opam
18opam install mlgpx
19```
20
21### Convert Waypoints to Track
22
23```bash
24# Basic conversion
25mlgpx convert waypoints.gpx track.gpx
26
27# With custom track name
28mlgpx convert --name "My Route" waypoints.gpx route.gpx
29
30# Sort waypoints by timestamp before conversion
31mlgpx convert --sort-time waypoints.gpx sorted_track.gpx
32
33# Sort by name and preserve original waypoints
34mlgpx convert --sort-name --preserve waypoints.gpx mixed.gpx
35
36# Verbose output with description
37mlgpx convert --verbose --desc "Generated route" waypoints.gpx track.gpx
38```
39
40### File Analysis
41
42```bash
43# Basic file information
44mlgpx info file.gpx
45
46# Detailed analysis with waypoint details
47mlgpx info --verbose file.gpx
48```
49
50### Help
51
52```bash
53# General help
54mlgpx --help
55
56# Command-specific help
57mlgpx convert --help
58mlgpx info --help
59```
60
61## Architecture Overview
62
63The library is split into four main components:
64
65### Core Library (`gpx`)
66- **Portable**: No Unix dependencies, works with `js_of_ocaml`
67- **Streaming**: Uses xmlm for memory-efficient XML processing
68- **Type-safe**: Strong typing with validation for coordinates and GPS data
69- **Pure functional**: No side effects in the core parsing/writing logic
70
71### Unix Layer (`gpx_unix`)
72- **File I/O**: Convenient functions for reading/writing GPX files
73- **Result-based**: Explicit error handling with `result` types
74- **Validation**: Built-in validation with detailed error reporting
75- **Utilities**: Helper functions for common GPX operations
76
77### Effects-Style Layer (`gpx_eio`)
78- **Exception-based**: Simplified error handling with exceptions
79- **Effects-style API**: Similar to Eio patterns but using standard Unix I/O
80- **Resource-safe**: Automatic file handle management
81- **High-level**: Convenient functions for common operations
82
83### Command Line Interface (`mlgpx`)
84- **Unix-style CLI**: Built with cmdliner for proper argument parsing
85- **Eio-powered**: Uses Eio backend for efficient I/O operations
86- **Waypoint conversion**: Convert waypoints to tracksets with sorting options
87- **File analysis**: Inspect GPX files with detailed information display
88
89## Key Features
90
91- ✅ **Complete GPX 1.0/1.1 support**: Waypoints, routes, tracks, metadata, extensions
92- ✅ **Streaming parser/writer**: Memory-efficient for large files
93- ✅ **Strong type safety**: Validated coordinates, GPS fix types, etc.
94- ✅ **Comprehensive validation**: Detailed error and warning reporting
95- ✅ **Extension support**: Handle custom XML elements
96- ✅ **Cross-platform**: Core library has no Unix dependencies
97
98## Module Structure
99
100```
101mlgpx/
102├── lib/
103│ ├── gpx/ # Portable core library
104│ │ ├── types.ml # Type definitions with smart constructors
105│ │ ├── parser.ml # Streaming XML parser
106│ │ ├── writer.ml # Streaming XML writer
107│ │ ├── validate.ml # Validation and error checking
108│ │ └── gpx.ml[i] # Main interface with direct access to all types
109│ ├── gpx_unix/ # Unix I/O layer (result-based)
110│ │ ├── gpx_io.ml # File operations with error handling
111│ │ └── gpx_unix.ml # High-level convenience API
112│ └── gpx_eio/ # Effects-style layer (exception-based)
113│ ├── gpx_io.ml # File operations with exceptions
114│ └── gpx_eio.ml # High-level effects-style API
115├── examples/ # Usage examples
116└── test/ # Test suite
117```
118
119## Type System Design
120
121### Validated Coordinates
122```ocaml
123type latitude = private float (* -90.0 to 90.0 *)
124type longitude = private float (* -180.0 to < 180.0 *)
125type degrees = private float (* 0.0 to < 360.0 *)
126
127(* Smart constructors with validation *)
128val latitude : float -> (latitude, string) result
129val longitude : float -> (longitude, string) result
130```
131
132### GPX Elements
133- **Waypoint**: Standalone geographic point with metadata
134- **Route**: Ordered list of waypoints representing a planned path
135- **Track**: Recorded path consisting of track segments with track points
136- **Metadata**: Document-level information (bounds, author, etc.)
137
138### Extension System
139```ocaml
140type extension = {
141 namespace : string option;
142 name : string;
143 attributes : (string * string) list;
144 content : extension_content;
145}
146```
147
148## API Design
149
150### Streaming Operations
151```ocaml
152(* Core streaming API *)
153val Gpx_parser.parse : Xmlm.input -> gpx result
154val Gpx_writer.write : Xmlm.output -> gpx -> unit result
155
156(* String convenience functions *)
157val Gpx_parser.parse_string : string -> gpx result
158val Gpx_writer.write_string : gpx -> string result
159```
160
161### File Operations (Result-based)
162```ocaml
163(* Simple file I/O *)
164val Gpx_unix.read : string -> gpx result
165val Gpx_unix.write : string -> gpx -> unit result
166
167(* With validation *)
168val Gpx_unix.read_validated : string -> gpx result
169val Gpx_unix.write_validated : string -> gpx -> unit result
170
171(* With backup *)
172val Gpx_unix.write_with_backup : string -> gpx -> string result
173```
174
175### Effects-Style Operations (Exception-based)
176```ocaml
177(* Simple file I/O *)
178val Gpx_eio.read : unit -> string -> gpx
179val Gpx_eio.write : unit -> string -> gpx -> unit
180
181(* With validation *)
182val Gpx_eio.read_validated : unit -> string -> gpx
183val Gpx_eio.write_validated : unit -> string -> gpx -> unit
184
185(* With backup *)
186val Gpx_eio.write_with_backup : unit -> string -> gpx -> string
187
188(* Utility functions *)
189val Gpx_eio.make_waypoint : unit -> lat:float -> lon:float -> ?name:string -> unit -> waypoint_data
190val Gpx_eio.make_track_from_coords : unit -> name:string -> (float * float) list -> track
191```
192
193### Validation
194```ocaml
195type validation_result = {
196 issues : validation_issue list;
197 is_valid : bool;
198}
199
200val Gpx_validate.validate_gpx : gpx -> validation_result
201val Gpx_validate.is_valid : gpx -> bool
202```
203
204## Error Handling Strategy
205
206The library uses a comprehensive error type:
207
208```ocaml
209type error =
210 | Invalid_xml of string
211 | Invalid_coordinate of string
212 | Missing_required_attribute of string * string
213 | Missing_required_element of string
214 | Validation_error of string
215 | Xml_error of string
216 | IO_error of string
217```
218
219All operations return `('a, error) result` for explicit error handling.
220
221## Performance Characteristics
222
223- **Memory usage**: O(1) for streaming operations, O(n) for complete document
224- **Time complexity**: O(n) parsing/writing where n = file size
225- **Validation**: Optional, can be disabled for performance-critical applications
226- **Extensions**: Parsed lazily, minimal overhead when unused
227
228## Usage Examples
229
230### Result-based API (Explicit Error Handling)
231
232```ocaml
233open Gpx_unix
234
235let create_simple_gpx () =
236 (* Create waypoints *)
237 let* waypoint = make_waypoint ~lat:37.7749 ~lon:(-122.4194)
238 ~name:"San Francisco" () in
239
240 (* Create track from coordinates *)
241 let coords = [(37.7749, -122.4194); (37.7849, -122.4094)] in
242 let* track = make_track_from_coords ~name:"Sample Track" coords in
243
244 (* Create GPX document *)
245 let gpx = Types.make_gpx ~creator:"mlgpx example" in
246 let gpx = { gpx with waypoints = [waypoint]; tracks = [track] } in
247
248 (* Validate and write *)
249 write_validated "output.gpx" gpx
250
251let () =
252 match create_simple_gpx () with
253 | Ok () -> Printf.printf "GPX created successfully\n"
254 | Error e -> Printf.eprintf "Error: %s\n" (error_to_string e)
255```
256
257### Effects-Style API (Exception-based)
258
259```ocaml
260open Gpx_eio
261
262let create_simple_gpx () =
263 try
264 (* Create waypoints *)
265 let waypoint = make_waypoint () ~lat:37.7749 ~lon:(-122.4194)
266 ~name:"San Francisco" () in
267
268 (* Create track from coordinates *)
269 let coords = [(37.7749, -122.4194); (37.7849, -122.4094)] in
270 let track = make_track_from_coords () ~name:"Sample Track" coords in
271
272 (* Create GPX document *)
273 let gpx = Gpx.make_gpx ~creator:"mlgpx example" in
274 let gpx = { gpx with waypoints = [waypoint]; tracks = [track] } in
275
276 (* Validate and write *)
277 write_validated () "output.gpx" gpx;
278 Printf.printf "GPX created successfully\n"
279
280 with
281 | Gpx.Gpx_error err ->
282 Printf.eprintf "GPX Error: %s\n" (Gpx.error_to_string err)
283
284let () = create_simple_gpx ()
285```