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