The current (updated) version 1.5 of the executables and the source code can be downloaded from http://berniestrackviewer.codeplex.com/ [^].
Introduction
There are many tools available for viewing tracks recorded with GPS devices. They can often do lots of things, but editing is quite uncommon. There are many good reasons for editing, e.g., you might want to publish just a part of your track, need to correct some misplaced points, or want to remove the short deviation into the bushes when you provided a tree with liquid fertilizer... If you want to add data to OpenStreetMap [^], it could be advisable to record one point per second in order to get bends nicely, and then remove unnecessary points from straight sections of the road.
Hence, I decided to write my own software for analyzing and editing GPX files. Here, I wish to present some steps of the development and a small sample program.
The GPX Format
This format was developed by TopoGrafix, and they publish the XML schema for it (see http://www.topografix.com/GPX/1/1/gpx.xsd [^]). It shows a hierarchical structure:
The WayPoint
s in the GPX proper are some kind of "points of interest" which you explicitly save on your device. The RoutePoint
s and TrackPoint
s are of the same type, but they belong to Route
s and Track
s, resp. The tracks use an extra layer, the "TrackSegment
". The documentation says, that where GPS reception was lost, or the GPS receiver was turned off, a new TrackSegment
is to be started for each continuous span of track data. There are some discussions whether this is still useful.
Now we need a means to get C# classes.xsd.exe included with current Windows SDKs (it can be found at C:\Program Files (x86)\Microsoft SDKs\Windows\v<Version>\Bin\ or a subfolder therein. (Previously, it was shipped with Visual Studio which installed it in C:\Program Files (x86)\Microsoft Visual Studio <Version>\SDK\v<Version>\Bin). Let us save the schema from the browser into a local file and then call xsd:
xsd.exe D:\Temp\gpx.xsd /classes /out:D:\Temp /language:CS
And voilà, there is one big C# code file with all the classes required for a GPX file.
Several property names look quite ugly - e.g., public decimal ele
. I would prefer to use a well readable name here: Elevation
. When we rename it, serialization / deserialization will become incompatible. But there is a trick - XmlElementAttribute
. We can thus tell the serializer how the properties are mapped to the XML elements:
[System.Xml.Serialization.XmlElementAttribute("ele")]
public decimal Elevation
And let's add the documentation found at TopoGrafix's website as code comments. Further editing pertains to cryptic names of classes (e.g., wptType
-> WayPoint
), and generic lists are better for the purpose of editing than arrays.
Now, we have nice classes which are just data containers - they have no inherent functionality. Let's add some, e.g., for reading and writing GPX files:
public static GPXType FromFile(string fileName)
public void ToFile(string fileName)
When we want to add further properties to a waypoint, e.g., the speed, we must consider that the XML serializer will write such a value to the file - which might render it unreadable for other devices. We can use two tricks:
- Use a function instead of a property, e.g.,
double GetSpeed()
instead of double Speed
(the private member variable _Speed
is ignored by the serializer).
- Use the
XmlIgnoreAttribute
attribute.
I hope the automatically generated classes could thus become more intelligible and more useful.
Elevation Quirks
The determination of the elevation from the satellite is not good. Modern GPS receivers also have a barometric altimeter. They typically use the values from the barometric altimeter, but from time to time calibrate it with the satellite values. Since no information on that calibration is recorded, you cannot know when it happened and by how much the elevation values were changed from then on. The elevation plot can be totally useless.
I decided to switch the automatic calibration off, and then found systematic deviations from the expected values. E.g., my brother's home's location was 10m higher than expected. After a lot of search through the web, I found the major reason: air temperature (see also "Grundlagen - Luft + Luftdruck"; link removed, original page is no more available and redirection points to spam). The altimeter uses a conversion factor for air pressure to elevation, about 12 hectopascal per 100 m - that's valid for 15 C or 288 K (degrees Kelvin). With the cold temperature of some -10C at that time, the elevation difference from my home to my brother's home stretched from 100m to 110 m = 100m * 288K / 263K.
Another issue is that there are minor oscillations by typically less than one meter. When calculating total ascent/descent, these oscillations could sum up to several meters and severely distort the results for tracks in the plains. Also in mountain regions, erratic values can be reported. I added a setting for the minimum distance to a previous point for calculating the ascent / descent.
Timestamp Issues
The Global Positioning System actually relies on extremely exact time values. Hence, we should think that the timestamps of our way points are perfect. But they are not. I discovered two cases where a later waypoint has an earlier timestamp than its predecessor - I published one of these cases in the Lounge (Proof of Time Travel[^]).
This could be caused by a clock issue of my Garmin Oregon, but I did not find any related information on the web. Another cause could be a willful distortion by the US armed forces who are the owner of the GPS, but "Selective Availability" is said to have been switched off.
Sample Application
The sample application requires .NET Framework 2. For maps and aerial photographs, a high speed internet connection is required.
Just download the files and unpack the archive into a folder. Then edit the configuration file (BerniesTrackViewer.exe.config) - you should enter valid data for the folder for caching aerial photographs and maps and the file name of the image shown.
Now double click the executable and drag a GPX file into it, select the track and track segment to be edited, and go to Edit-Track to display the track in various forms. Edit a point by double clicking it, dragging it (right mouse click) to a different location, correct the elevation of all points, etc., and finally save your work from the main page.
Further Development
The base classes work quite nice, hence the development will be focused on the "sample application".
- The algorithm for total ascent/descent needs improvement
- Display/edit more than one track segment at a time
- Loading the map tiles sometimes seems to hang on single-core computers (i.e., one or two tiles are loaded, and for the next minute nothing happens, then suddenly further tiles show up)
- Many settings are actually user settings, but nonetheless stored in the application config file - some differentiation will be required here
- Undo/redo functionality
- Add an installation package (because of configuration)
- Make the application fool proof...
I do not know when I will find the time to do that...
Acknowledgements / License
This sample application uses code from several authors published on CodeProject:
For those parts, their licensing schemes may apply. For that part which is my work, the CodeProject Open License(^) applies.
History
Version 1.1: 15 Sep 2010
- Added mapping of photographs
Version 1.2: 9 Jan 2011
- New design of the user interface of main page
- Also displays waypoints which are not part of a track
- Can convert a route into a track
- Maximum age of cached maps can be set
Version 1.3: 13 Feb 2011
- Compressing a track segment by removal of unnecessary points
- Further enhancement of user interface
Version 1.4: 8 Sep 2013
- Added Garmin extensions for heartrate, etc.
- Improved usability: automatically open first track segment with a map / aerial images as background (configurable)
Version 1.5: 12 Jun 2016
- Since the altimeter is not such exact, the ascent / descent calculation can be rather erratic when calculating with the directly previous waypoint. Now, a minimum distance between waypoints can be given, and previous waypoints will be searched till that distance is met.