Skip to content

Instantly share code, notes, and snippets.

@jl2
Created May 7, 2010 05:14
Show Gist options
  • Save jl2/393083 to your computer and use it in GitHub Desktop.
Save jl2/393083 to your computer and use it in GitHub Desktop.
Parse GPX file using libxml2
/* gpxparse.c
Copyright (c) 2010, Jeremiah LaRocco [email protected]
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Use libxml2 to parse GPX files and display some simple statistics.
Created for comparison with http://github.com/jl2/RubyGPS/blob/master/parsegpx.rb
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define __USE_XOPEN // For strptime
#include <time.h>
#include <math.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include "utmconvert/utmconvert.h"
struct gpx_point {
double lat, lon;
double x, y;
double ele;
bool valid;
struct tm time;
};
// Compute duration, distance and speed between two points
void timeDistSpeed(struct gpx_point *pt1, struct gpx_point *pt2, double *tdiff, double *dist, double *speed) {
if (!pt1->valid || !pt2->valid) return;
if (tdiff==0 && dist == 0 && speed == 0) return;
double dx = (pt2->x-pt1->x);
double dy = (pt2->y-pt1->y);
double dz = (pt2->ele-pt1->ele);
double dc = sqrt(dx*dx + dy*dy + dz*dz);
double dt = difftime(mktime(&pt1->time), mktime(&pt2->time));
if (dist) *dist = dc;
if (tdiff) *tdiff = dt;
if (speed) *speed = dc/dt;
}
// Convenience function to create an XPath context at a particular node
xmlXPathContextPtr xmlXPathNewContextNode(xmlDocPtr doc, xmlNodePtr node) {
xmlXPathContextPtr retval = xmlXPathNewContext(doc);
xmlXPathRegisterNs(retval, (xmlChar*)"gpx", (xmlChar*)"http://www.topografix.com/GPX/1/0");
retval->node = node;
return retval;
}
int main(int argc, char *argv[]){
if (argc<2) {
printf("No file name given!\n");
return 1;
}
// Initialize libxml
xmlInitParser();
// Load file
xmlDocPtr doc = xmlParseFile(argv[1]);
// Create initial xpath context
xmlXPathContextPtr context = xmlXPathNewContext(doc);
xmlXPathRegisterNs(context, (xmlChar*)"gpx", (xmlChar*)"http://www.topografix.com/GPX/1/0");
// Find track segments
xmlXPathObjectPtr tracks = xmlXPathEvalExpression((xmlChar*)"//gpx:trk/gpx:trkseg", context);
if(tracks == NULL || xmlXPathNodeSetIsEmpty(tracks->nodesetval)){
printf("No track segments found in \"%s\"\n", argv[1]);
return 1;
}
xmlNodeSetPtr trackNodes = tracks->nodesetval;
// Book keeping variables
struct gpx_point curPt = {0};
struct gpx_point prevPt = {0};
double dist=0;
double speed=0;
double tdiff=0;
double curMax = 0;
double totDist = 0;
double totTime = 0;
xmlChar *end = 0;
// Loop through tracks
for(int i = 0; i<trackNodes->nodeNr; ++i) {
// Skip non-elements
if(trackNodes->nodeTab[i]->type != XML_ELEMENT_NODE) continue;
// Find all points in the current track
xmlXPathContextPtr ptContext = xmlXPathNewContextNode(doc, trackNodes->nodeTab[i]);
xmlXPathObjectPtr points = xmlXPathEvalExpression((xmlChar*)"gpx:trkpt", ptContext);
if (points != 0 && !xmlXPathNodeSetIsEmpty(points->nodesetval)) {
// Reset variables for each track
curMax = totDist = totTime = 0;
prevPt.valid = false;
// Loop through track points
for (int j=0; j<points->nodesetval->nodeNr; ++j) {
curPt.valid = true;
// Current point
xmlNodePtr ptNode = points->nodesetval->nodeTab[j];
// Check for lat and lon attributes
if (xmlHasProp(ptNode, (xmlChar*)"lat") && xmlHasProp(ptNode, (xmlChar*)"lon")) {
// Parse latitude
xmlChar *lat = xmlGetProp(ptNode, (xmlChar*)"lat");
curPt.lat = strtod((char*)lat, (char**)&end);
if (end == lat) {
curPt.valid = false;
}
if (lat != 0) xmlFree(lat);
// Parse longitude
xmlChar *lon = xmlGetProp(ptNode, (xmlChar*)"lon");
curPt.lon = strtod((char*)lon, (char**)&end);
if (end == lon) {
curPt.valid = false;
}
if (lon != 0) xmlFree(lon);
// If everything went okay, calculate the utm coordinates
if (curPt.valid)
to_utm(curPt.lat, curPt.lon, &curPt.x, &curPt.y);
} else {
curPt.valid = false;
continue;
}
// Get the elevation
xmlXPathContextPtr eleContext = xmlXPathNewContextNode(doc, ptNode);
xmlXPathObjectPtr elevation = xmlXPathEvalExpression((xmlChar*)"gpx:ele/text()", eleContext);
if (elevation != 0 && xmlXPathNodeSetIsEmpty(elevation->nodesetval)) {
curPt.valid = false;
} else if (elevation != 0) {
xmlChar *eVal = elevation->nodesetval->nodeTab[0]->content;
curPt.ele = strtod((char*)eVal, (char**)&end);
if (end == eVal) {
curPt.valid = false;
}
}
if (elevation) xmlXPathFreeObject(elevation);
xmlXPathFreeContext(eleContext);
// Get the time
xmlXPathContextPtr timeContext = xmlXPathNewContextNode(doc, ptNode);
xmlXPathObjectPtr time = xmlXPathEvalExpression((xmlChar*)"gpx:time/text()", timeContext);
if ( time != 0 && xmlXPathNodeSetIsEmpty(time->nodesetval)) {
curPt.valid = false;
} else if (time != 0) {
xmlChar *tVal = time->nodesetval->nodeTab[0]->content;
if (strptime((char*)tVal, "%Y-%m-%dT%H:%M:%SZ", &(curPt.time)) ==NULL)
curPt.valid = false;
}
if (time) xmlXPathFreeObject(time);
xmlXPathFreeContext(timeContext);
// If the point is good then update the stats
if (curPt.valid && prevPt.valid) {
timeDistSpeed(&curPt, &prevPt, &tdiff, &dist, &speed);
totTime += tdiff;
totDist += dist;
if (speed > curMax)
curMax = speed;
}
prevPt = curPt;
}
// Print track stats
if (totDist > 0 && totTime > 0)
printf("Went %.3f meters in %.3f seconds, average speed was %.3f, maximum speed was %.3f\n", totDist, totTime, totDist/totTime, curMax);
}
if (points) xmlXPathFreeObject(points);
xmlXPathFreeContext(ptContext);
}
if (tracks) xmlXPathFreeObject(tracks);
xmlXPathFreeContext(context);
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment