package mlgpx

  1. Overview
  2. Docs

Module GpxSource

OCaml library for reading and writing GPX (GPS Exchange Format) files.

Overview

GPX (GPS Exchange Format) is an XML-based format for GPS data interchange, standardized by Topografix. This library provides a complete implementation of the GPX 1.1 specification with strong type safety and validation.

GPX files can contain three main types of GPS data:

  • Waypoints: Individual points of interest with coordinates
  • Routes: Ordered sequences of waypoints representing a planned path
  • Tracks: Recorded GPS traces, typically from actual journeys

All coordinates in GPX use the WGS84 datum (World Geodetic System 1984), the same coordinate system used by GPS satellites. Coordinates are expressed as decimal degrees, with elevations in meters above mean sea level.

Quick Start Example

  open Gpx
  
  (* Create coordinates *)
  let lat = Coordinate.latitude 37.7749 |> Result.get_ok in
  let lon = Coordinate.longitude (-122.4194) |> Result.get_ok in
  
  (* Create a waypoint *)
  let waypoint = Waypoint.make lat lon 
    |> Waypoint.with_name "San Francisco"
    |> Waypoint.with_description "Golden Gate Bridge area" in
  
  (* Create GPX document *)
  let gpx = make_gpx ~creator:"my-app" 
    |> Doc.add_waypoint waypoint in
  
  (* Write to file or string *)
  match write_string gpx with
  | Ok xml -> print_endline xml
  | Error e -> Printf.eprintf "Error: %s\n" (Error.to_string e)

This library provides a clean, modular interface for working with GPX files, with separate modules for each major component of the GPX specification.

Core Modules

The library is organized into focused modules, each handling a specific aspect of GPX data. Each module provides complete functionality for its domain with strong type safety and validation.

Sourcemodule Coordinate : sig ... end
Sourcemodule Extension : sig ... end
Sourcemodule Waypoint : sig ... end
Sourcemodule Metadata : sig ... end
Sourcemodule Route : sig ... end
Sourcemodule Track : sig ... end
Sourcemodule Error : sig ... end
Sourcemodule Doc : sig ... end

Main Document Type

Sourcetype t = Doc.t

A complete GPX document containing waypoints, routes, tracks, and metadata.

This is the main type representing a complete GPX file. GPX documents must have a creator string (identifying the creating application) and follow the GPX 1.1 specification format.

Error Handling

Sourcetype error = Error.t

Comprehensive error type covering all possible GPX operation failures.

Errors can occur during:

  • XML parsing (malformed XML, invalid structure)
  • Coordinate validation (out of range values)
  • Missing required GPX elements or attributes
  • File I/O operations
Sourceexception Gpx_error of error

GPX exception raised for unrecoverable errors.

Most functions return Result.t for error handling, but this exception may be raised in exceptional circumstances.

Parsing Functions

Parse GPX data from various sources. All parsing functions support optional validation to check compliance with GPX specification constraints.

Sourceval parse : ?validate:bool -> Xmlm.input -> (t, error) result

Parse GPX from XML input source.

Reads GPX data from an Xmlm.input source, which can be created from files, strings, or other input sources using the Xmlm library.

  • parameter validate

    If true (default false), validates the parsed document against GPX specification rules. Validation checks coordinate ranges, required elements, and data consistency.

  • parameter input

    XMLm input source created with Xmlm.make_input

  • returns

    Ok gpx with parsed document, or Error e if parsing fails

Example:

  let input = Xmlm.make_input (`String (0, gpx_xml_string)) in
  match parse ~validate:true input with
  | Ok gpx -> Printf.printf "Parsed %d waypoints\n" (List.length (Doc.waypoints gpx))
  | Error e -> Printf.eprintf "Parse error: %s\n" (Error.to_string e)
Sourceval parse_string : ?validate:bool -> string -> (t, error) result

Parse GPX from XML string.

Convenience function for parsing GPX data from a string. Equivalent to creating an Xmlm.input from the string and calling parse.

  • parameter validate

    If true (default false), validates the parsed document

  • parameter s

    Complete GPX XML document as a string

  • returns

    Ok gpx with parsed document, or Error e if parsing fails

Example:

  let gpx_xml = {|<?xml version="1.0"?>
  <gpx version="1.1" creator="my-app">
    <wpt lat="37.7749" lon="-122.4194">
      <name>San Francisco</name>
    </wpt>
  </gpx>|} in
  match parse_string ~validate:true gpx_xml with
  | Ok gpx -> print_endline "Successfully parsed GPX"
  | Error e -> Printf.eprintf "Error: %s\n" (Error.to_string e)

Writing Functions

Generate GPX XML from document structures. All writing functions support optional validation before output generation.

Sourceval write : ?validate:bool -> Xmlm.dest -> t -> (unit, error) result

Write GPX to XML output destination.

Generates standard GPX 1.1 XML and writes it to an Xmlm.dest destination. The output destination can target files, buffers, or other sinks.

  • parameter validate

    If true (default false), validates the document before writing to ensure GPX specification compliance

  • parameter dest

    XMLm output destination created with Xmlm.make_output

  • parameter gpx

    GPX document to write

  • returns

    Ok () on success, or Error e if writing fails

Example:

  let output = Buffer.create 1024 in
  let dest = Xmlm.make_output (`Buffer output) in
  match write ~validate:true dest gpx with
  | Ok () -> Buffer.contents output
  | Error e -> failwith (Error.to_string e)
Sourceval write_string : ?validate:bool -> t -> (string, error) result

Write GPX to XML string.

Convenience function that generates a complete GPX XML document as a string. The output includes XML declaration and proper namespace declarations.

  • parameter validate

    If true (default false), validates before writing

  • parameter gpx

    GPX document to serialize

  • returns

    Ok xml_string with complete GPX XML, or Error e if generation fails

Example:

  match write_string ~validate:true gpx with
  | Ok xml -> 
      print_endline "Generated GPX:";
      print_endline xml
  | Error e -> 
      Printf.eprintf "Failed to generate GPX: %s\n" (Error.to_string e)

Validation Functions

Comprehensive validation against GPX specification rules and best practices. Validation checks coordinate ranges, required elements, data consistency, and common issues that may cause problems for GPS applications.

Sourcetype validation_issue = {
  1. level : [ `Error | `Warning ];
    (*

    `Error for specification violations, `Warning for best practice issues

    *)
  2. message : string;
    (*

    Human-readable description of the issue

    *)
  3. location : string option;
    (*

    Optional location context (e.g., "waypoint 1", "track segment 2")

    *)
}

A validation issue found during GPX document checking.

Issues are classified as either errors (specification violations that make the GPX invalid) or warnings (best practice violations or suspicious data).

Sourcetype validation_result = {
  1. issues : validation_issue list;
    (*

    All validation issues found, both errors and warnings

    *)
  2. is_valid : bool;
    (*

    true if no errors found (warnings are allowed)

    *)
}

Complete validation result with all issues and validity status.

The is_valid field indicates whether the document contains any errors. Documents with only warnings are considered valid.

Sourceval validate_gpx : t -> validation_result

Perform comprehensive validation of a GPX document.

Checks all aspects of the GPX document against the specification:

  • Coordinate ranges (latitude -90 to +90, longitude -180 to +180)
  • Required elements and attributes
  • Data consistency (e.g., time ordering in tracks)
  • Reasonable value ranges for GPS quality metrics
  • Proper structure and nesting
  • parameter gpx

    The GPX document to validate

  • returns

    Complete validation result with all issues found

Example:

  let result = validate_gpx gpx in
  if result.is_valid then
    Printf.printf "Document is valid with %d warnings\n" 
      (List.length (List.filter (fun i -> i.level = `Warning) result.issues))
  else begin
    print_endline "Document has errors:";
    List.iter (fun issue ->
      if issue.level = `Error then
        Printf.printf "  ERROR: %s\n" (format_issue issue)
    ) result.issues
  end
Sourceval is_valid : t -> bool

Quick validation check - returns true if document has no errors.

Equivalent to (validate_gpx gpx).is_valid but potentially more efficient as it can stop at the first error found.

  • parameter gpx

    The GPX document to validate

  • returns

    true if valid (no errors), false if errors found

Sourceval errors : t -> validation_issue list

Get only validation errors (specification violations).

Returns only the issues marked as errors, filtering out warnings. If this list is empty, the document is valid according to the GPX specification.

  • parameter gpx

    The GPX document to validate

  • returns

    List of error-level validation issues

Sourceval warnings : t -> validation_issue list

Get only validation warnings (best practice violations).

Returns only the issues marked as warnings. These don't make the document invalid but may indicate potential problems or areas for improvement.

  • parameter gpx

    The GPX document to validate

  • returns

    List of warning-level validation issues

Sourceval format_issue : validation_issue -> string

Format a validation issue for human-readable display.

Combines the issue message with location context if available.

  • parameter issue

    The validation issue to format

  • returns

    Formatted string suitable for display to users

Example output: "Error in waypoint 1: Latitude out of range (-95.0)"

Document Constructors and Utilities

Functions for creating GPX documents and basic document operations.

Sourceval make_gpx : creator:string -> t

Create a new GPX document with the required creator field.

Every GPX document must identify its creating application through the creator attribute. This is required by the GPX specification and helps identify the source of GPS data.

The created document:

  • Uses GPX version 1.1 (the current standard)
  • Contains no waypoints, routes, or tracks initially
  • Has no metadata initially
  • Can be extended using Doc module functions
  • parameter creator

    Name of the creating application (e.g., "MyGPS App v1.0")

  • returns

    Empty GPX document ready for data addition

Example:

  let gpx = make_gpx ~creator:"MyTracker v2.1" in
  let gpx = Doc.add_waypoint gpx some_waypoint in
  let gpx = Doc.add_track gpx some_track in
  (* gpx now contains waypoints and tracks *)
Sourceval empty : creator:string -> t

Create an empty GPX document with the required creator field.

Alias for make_gpx provided for consistency with module naming patterns. Creates a document with no GPS data that can be populated using the Doc module functions.

  • parameter creator

    Name of the creating application

  • returns

    Empty GPX document

Example:

  let gpx = empty ~creator:"GPS Logger" in
  assert (List.length (Doc.waypoints gpx) = 0);
  assert (List.length (Doc.tracks gpx) = 0);
  assert (Doc.creator gpx = "GPS Logger");

Common Patterns and Best Practices

Reading GPX Files

The most common use case is reading existing GPX files:

  (* From a file using platform-specific modules *)
  match Gpx_unix.read "track.gpx" with
  | Ok gpx -> process_gpx gpx
  | Error e -> handle_error e
  
  (* From a string *)
  match parse_string ~validate:true gpx_content with
  | Ok gpx -> process_gpx gpx
  | Error e -> handle_error e

Creating GPX Files

To create new GPX files with waypoints:

  (* Create coordinates *)
  let lat = Coordinate.latitude 37.7749 |> Result.get_ok in
  let lon = Coordinate.longitude (-122.4194) |> Result.get_ok in
  
  (* Create waypoint *)
  let waypoint = Waypoint.make lat lon
    |> Waypoint.with_name "Golden Gate"
    |> Waypoint.with_description "Famous San Francisco bridge" in
  
  (* Create document *)
  let gpx = make_gpx ~creator:"My App v1.0"
    |> Doc.add_waypoint waypoint in
  
  (* Write to file *)
  match Gpx_unix.write "output.gpx" gpx with
  | Ok () -> print_endline "File written successfully"
  | Error e -> Printf.eprintf "Write error: %s\n" (Error.to_string e)

Working with Tracks

Tracks represent recorded GPS traces with timestamped points:

  (* Create track points with timestamps *)
  let points = List.map (fun (lat_f, lon_f, time) ->
    let lat = Coordinate.latitude lat_f |> Result.get_ok in
    let lon = Coordinate.longitude lon_f |> Result.get_ok in
    Waypoint.make lat lon |> Waypoint.with_time (Some time)
  ) gps_data in
  
  (* Create track segment *)
  let segment = Track.Segment.make points in
  
  (* Create track *)
  let track = Track.make ~name:"Morning Run"
    |> Track.add_segment segment in
  
  (* Add to document *)
  let gpx = make_gpx ~creator:"Fitness App"
    |> Doc.add_track track

Coordinate Systems and Units

  • All coordinates use WGS84 datum (World Geodetic System 1984)
  • Latitude ranges from -90.0 (South Pole) to +90.0 (North Pole)
  • Longitude ranges from -180.0 to +180.0 degrees
  • Elevations are in meters above mean sea level
  • Times use RFC 3339 format (ISO 8601 subset)

Validation Recommendations

  • Always validate when parsing untrusted GPX data
  • Validate before writing to catch data consistency issues
  • Handle both errors and warnings appropriately
  • Use is_valid for quick checks, validate_gpx for detailed analysis

Performance Considerations

  • The library uses streaming XML parsing for memory efficiency
  • Large GPX files with many track points are handled efficiently
  • Coordinate validation occurs at construction time
  • Consider using platform-specific modules (Gpx_unix, Gpx_eio) for file I/O

Extension Support

The library supports GPX extensions for custom data:

  (* Create extension *)
  let ext = Extension.make_text
    ~name:"temperature" 
    ~namespace:"http://example.com/weather"
    "25.5" in
  
  (* Add to waypoint *)
  let waypoint = Waypoint.make lat lon
    |> Waypoint.add_extensions [ext]

This core module provides the foundation. For complete applications, consider:

  • Gpx_unix: File I/O operations using standard Unix libraries
  • Gpx_eio: Concurrent file I/O using the Eio effects library
  • Xmlm: Underlying XML processing library
  • Ptime: Time representation used for timestamps
OCaml

Innovation. Community. Security.