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```