Task 266: .GPX File Format
Task 266: .GPX File Format
1. List of All Properties Intrinsic to the .GPX File Format
The .GPX (GPS Exchange Format) is an XML-based file format defined by the GPX 1.1 schema. It uses the namespace http://www.topografix.com/GPX/1/1
and follows WGS84 datum for coordinates. The format is lightweight, extensible via <extensions>
, and supports lax validation for non-GPX namespace elements. All coordinates are in decimal degrees. Below is a comprehensive hierarchical list of all intrinsic properties (elements, attributes, data types, cardinalities, and constraints) derived from the official GPX 1.1 schema.
Root Element: gpx
- Attributes:
version
: string (fixed value: "1.1", required)creator
: string (required, identifies the software/device that created the file)- Child Elements (sequence, in any order but typically metadata first):
metadata
(0..1, optional): Contains file-level metadata.name
(0..1): string (file name or title)desc
(0..1): string (description)author
(0..1): person or organization.name
(0..1): stringemail
(0..1): emailType (format:id@domain
)link
(0..1): link to author's site.href
: anyURI (required)text
(0..1): stringtype
(0..1): string (MIME type)copyright
(0..1): copyright info.year
(0..1): gYear (e.g., 2025)author
: string (required)license
(0..1): anyURIlink
(0..1): Same structure as above (general file link)time
(0..1): dateTime (ISO 8601, e.g., 2025-09-25T12:00:00Z)keywords
(0..1): string (comma-separated)bounds
(0..1): bounding box.minlat
: decimal (-90.0 to 90.0, required)minlon
: decimal (-180.0 to 180.0, required)maxlat
: decimal (-90.0 to 90.0, required)maxlon
: decimal (-180.0 to 180.0, required)extensions
(0..1): extensionsType (any XML from other namespaces, lax validation)wpt
(0..unbounded): Waypoint (single GPS point).- Attributes:
lat
: decimal (-90.0 to 90.0, required)lon
: decimal (-180.0 to 180.0, required)- Child Elements (all 0..1 unless noted):
ele
: decimal (elevation in meters)time
: dateTimemagvar
: degreesType (-180.0 to 180.0, magnetic variation)geoidheight
: decimal (height of geoid above WGS84 ellipsoid)name
: stringcmt
: string (comment)desc
: string (description)src
: string (source of data)link
: linkType (as above)sym
: string (symbol name)type
: string (waypoint category)fix
: fixType (enum: none | 2d | 3d | dgps | pps)sat
: nonNegativeInteger (number of satellites)hdop
: decimal (horizontal dilution of precision)vdop
: decimal (vertical DOP)pdop
: decimal (position DOP)ageofdgpsdata
: decimal (seconds since last DGPS update)dgpsid
: integer (0 to 1023, DGPS station ID)extensions
(0..1)rte
(0..unbounded): Route (ordered list of waypoints).- Child Elements:
name
(0..1): stringcmt
(0..1): stringdesc
(0..1): stringsrc
(0..1): stringlink
(0..1): linkTypenumber
(0..1): nonNegativeInteger (route number)type
(0..1): stringextensions
(0..1)rtept
(1..unbounded): wptType (same aswpt
, lat/lon required)trk
(0..unbounded): Track (GPS track log, time-ordered).- Child Elements:
name
(0..1): stringcmt
(0..1): stringdesc
(0..1): stringsrc
(0..1): stringlink
(0..1): linkTypenumber
(0..1): nonNegativeIntegertype
(0..1): stringextensions
(0..1)trkseg
(0..unbounded): Track segment.trkpt
(0..unbounded): wptType (same aswpt
)extensions
(0..1): File-level extensions (any XML, lax validation)
General Constraints
- Data Types:
- Coordinates (lat/lon): decimal, precise to 5+ decimal places.
- Degrees (magvar): decimal, -180 to 180 or 0 to 360.
- Time: UTC dateTime, optional fractional seconds.
- Elevation/Heights: decimal meters.
- Enumerations: fixType as listed; others are free-form strings unless specified.
- File Structure: XML declaration optional; must be well-formed XML. No strict order within sequences except for logical grouping (e.g., metadata first).
- Extensibility:
<extensions>
allows vendor-specific elements (e.g., Garmin extensions). - Validation: Conforms to GPX 1.1 XSD; coordinates relative to WGS84.
This list covers all defined properties; extensions may add more but are not intrinsic.
2. Two Direct Download Links for .GPX Files
- Sample 1: Middlesex Fells mountain bike loop (official TopoGrafix sample): https://www.topografix.com/fells_loop.gpx
- Sample 2: Mapbox running track sample: https://docs.mapbox.com/help/data/run.gpx
3. Ghost Blog Embedded HTML JavaScript
Embed this full HTML snippet in a Ghost blog post (via HTML card). It creates a drag-and-drop zone for .GPX files, parses the XML, and dumps all intrinsic properties (elements/attributes/values) to a <pre>
block as structured JSON for readability.
4. Python Class
This class uses xml.etree.ElementTree
(standard library) to load a .GPX file, recursively extract and print all properties to console (as indented key-value pairs), and write the unmodified content back to a new file (output.gpx
).
import xml.etree.ElementTree as ET
class GPXParser:
def __init__(self, file_path):
self.tree = ET.parse(file_path)
self.root = self.tree.getroot()
def print_properties(self):
self._print_recursive(self.root, '')
def _print_recursive(self, node, indent):
tag = node.tag.split('}')[-1] # Strip namespace
attrs = dict(node.attrib)
text = node.text.strip() if node.text else None
print(f"{indent}{tag}:")
if attrs:
print(f"{indent} Attributes: {attrs}")
if text:
print(f"{indent} Text: {text}")
for child in node:
self._print_recursive(child, indent + ' ')
def write(self, output_path='output.gpx'):
self.tree.write(output_path, encoding='utf-8', xml_declaration=True)
# Usage example:
# parser = GPXParser('sample.gpx')
# parser.print_properties()
# parser.write()
5. Java Class
This class uses javax.xml.parsers
and org.w3c.dom
(standard JDK) to load a .GPX file, recursively extract and print all properties to console (as indented structure), and write the unmodified content back to output.gpx
.
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import java.io.File;
public class GPXParser {
private Document doc;
public GPXParser(String filePath) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
this.doc = builder.parse(new File(filePath));
}
public void printProperties() {
printRecursive(doc.getDocumentElement(), "");
}
private void printRecursive(Node node, String indent) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
String tagName = node.getNodeName().split(":")[1]; // Strip namespace if present
NamedNodeMap attrs = node.getAttributes();
System.out.println(indent + tagName + ":");
if (attrs != null && attrs.getLength() > 0) {
System.out.print(indent + " Attributes: ");
for (int i = 0; i < attrs.getLength(); i++) {
Node attr = attrs.item(i);
System.out.print(attr.getNodeName() + "=" + attr.getNodeValue() + " ");
}
System.out.println();
}
String text = node.getTextContent().trim();
if (!text.isEmpty()) {
System.out.println(indent + " Text: " + text);
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
printRecursive(children.item(i), indent + " ");
}
}
}
public void write(String outputPath) throws Exception {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(outputPath));
transformer.transform(source, result);
}
// Usage: GPXParser parser = new GPXParser("sample.gpx"); parser.printProperties(); parser.write("output.gpx");
}
6. JavaScript Class
This class (for browser/Node.js with fs
for Node) loads a .GPX file (via File or path), parses with DOMParser/XMLHttpRequest, recursively extracts and prints all properties to console (as indented object), and writes the unmodified content back to output.gpx
(using Blob for browser or fs
for Node).
class GPXParser {
constructor(filePathOrFile) {
this.doc = null;
if (typeof filePathOrFile === 'string') {
// Node.js: assume fs is available
const fs = require('fs');
const xmlString = fs.readFileSync(filePathOrFile, 'utf8');
const parser = new DOMParser();
this.doc = parser.parseFromString(xmlString, 'text/xml');
} else {
// Browser: assume File object
const reader = new FileReader();
reader.onload = (e) => {
const parser = new DOMParser();
this.doc = parser.parseFromString(e.target.result, 'text/xml');
this.printProperties();
};
reader.readAsText(filePathOrFile);
return; // Async, print called in onload
}
}
printProperties() {
if (!this.doc) return;
this._printRecursive(this.doc.documentElement, '');
}
_printRecursive(node, indent) {
if (node.nodeType === Node.ELEMENT_NODE) {
const tagName = node.tagName.split(':').pop(); // Strip namespace
console.log(`${indent}${tagName}:`);
const attrs = {};
for (let attr of node.attributes) {
attrs[attr.name] = attr.value;
}
if (Object.keys(attrs).length > 0) {
console.log(`${indent} Attributes:`, attrs);
}
const text = node.textContent.trim();
if (text) {
console.log(`${indent} Text: ${text}`);
}
for (let child of node.children) {
this._printRecursive(child, indent + ' ');
}
}
}
write(outputPath = 'output.gpx') {
if (!this.doc) return;
const serializer = new XMLSerializer();
const xmlString = '<?xml version="1.0" encoding="UTF-8"?>' + serializer.serializeToString(this.doc);
// Browser: download
const blob = new Blob([xmlString], { type: 'application/gpx+xml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputPath;
a.click();
// Node.js: fs.writeFileSync(outputPath, xmlString);
}
}
// Usage (browser): new GPXParser(fileInput.files[0]); // prints in onload
// Usage (Node): new GPXParser('sample.gpx'); parser.write();
7. C "Class" (Struct with Functions)
C lacks classes, so this uses a struct with functions (assuming libxml2 is linked: gcc -o gpx_parser gpx_parser.c -lxml2
). It loads a .GPX file, recursively prints all properties to stdout (indented), and writes unmodified content to output.gpx
.
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
typedef struct {
xmlDocPtr doc;
} GPXParser;
void gpx_parser_init(GPXParser *parser, const char *file_path) {
parser->doc = xmlReadFile(file_path, NULL, 0);
if (parser->doc == NULL) {
fprintf(stderr, "Error parsing file\n");
}
}
void print_properties(GPXParser *parser) {
if (!parser->doc) return;
xmlNodePtr root = xmlDocGetRootElement(parser->doc);
print_recursive(root, 0);
}
void print_recursive(xmlNodePtr node, int indent) {
if (node == NULL || node->type != XML_ELEMENT_NODE) return;
// Print indent
for (int i = 0; i < indent; i++) printf(" ");
printf("%s:\n", node->name);
// Attributes
xmlAttrPtr attr = node->properties;
if (attr != NULL) {
for (int i = 0; i < indent + 1; i++) printf(" ");
printf("Attributes: ");
while (attr != NULL) {
printf("%s=%s ", attr->name, xmlNodeListGetString(parser->doc, attr->children, 1));
attr = attr->next;
}
printf("\n");
}
// Text
xmlChar *text = xmlNodeListGetString(parser->doc, node->children, 1);
if (text && xmlStrlen(text) > 0) {
for (int i = 0; i < indent + 1; i++) printf(" ");
printf("Text: %s\n", text);
xmlFree(text);
}
// Children
for (xmlNodePtr child = node->children; child; child = child->next) {
if (child->type == XML_ELEMENT_NODE) {
print_recursive(child, indent + 1);
}
}
}
void gpx_parser_write(GPXParser *parser, const char *output_path) {
if (!parser->doc) return;
xmlSaveFormatFileEnc(output_path, parser->doc, "UTF-8", 1);
}
void gpx_parser_free(GPXParser *parser) {
xmlFreeDoc(parser->doc);
xmlCleanupParser();
}
// Usage:
// GPXParser parser = {0};
// gpx_parser_init(&parser, "sample.gpx");
// print_properties(&parser);
// gpx_parser_write(&parser, "output.gpx");
// gpx_parser_free(&parser);