Converting GPX data to useful hard copy

I like to use OpenCPN for route planning, but I want to be able to keep a paper printout of critical routes like entering the Golden Gate - for when all the electronics are dead. For key waypoints I put in the description field, the bearings to nearby objects or other useful clues.

Navigation tools like OpenCPN and iNavX can export routes in GPX (GPS Exchange) format, but this provides little more than the Latitude and Longitude of each waypoint, so even if you convert the XML to text, it is of little value.

But with a little math we can fill in the blanks.

To compute the distance and bearing from one Lat/Long to another requires a bit more than basic trigonometry, but the necessary formula are easily found on the web.

GPX format in a nutshell

The GPX data exported by tools like OpenCPN provides a set of routes (rte) containing a list of waypoints (rtept)

Each waypoint has a latitude and longitude in decimal degree format as well as a name, and description.

We want to output each route (they also have names), with each waypoint on a separate line with bearing and distance to next waypoint.

GPX.py

This tool does what we want - it parses the GPX data and allows us to output a configurable set of fields per waypoint.

You can specify the fields via command line option or a configuration file.

You can even specify undefined fields (eg Compass) which will result in a blank field being output - to be filled in by hand. If you provide a deviation table then Compass headings will be computed.

Similarly if you know the error of your log you can provide that, and then the cDistance and cLog fields will be auto-populated, otherwise they will be blank columns.

Waypoint

name of the waypoint

Position

latitude and longitude (in decimal minutes format)

Course

True bearing to next waypoint

Variation

Variation is computed for each waypoint using the latest WMM data and Christopher Weiss's geomag routine.

Magnetic

Magnetic bearing to next waypoint.

Deviation

If a deviation table is provided, we can lookup and report. The data can be supplied as a comma separated list of 12 (30 degree intervals) or 24 (15 degree intervals) values with negative values representing West error.

The data can also be provided in a file with one entry per line.

Compass

If a deviation table is provided we can compute compass course, otherwise this field will be blank.

Distance

Distance in nautical miles to next waypoint

cDistance

Corrected distance - that is what our log will show (see cLog below)

Time

Estimate of time to next waypoint at nominal cruising speed

ETE

Estimated time enroute. This is the cumulative time since the start of this route.

Log

Cumulative distance since the start of this route.

cLog

Corrected log. It is rare for the ships log to be perfectly accurate. We multiply log by the log_error factor.

Speed

Nominal cruising speed (6.0 kts)

Remarks

In OpenCPN at least, there is a description field in which I can record useful things like bearings to nearby objects etc.

This is where the real value in printing this stuff out comes from. Since you can have a printout of critical entry/exit plans for use when all the electronics are dead.

Configuration

In the descriptions below the arg to each option is the variable name you might set in a config file. Eg.:

fields = cLog,Waypoint,Postion,Course,Magnetic,Compass,Distance,cDistance,Remarks
log_error = 0.9
sep = |
fmt = csv

GPX.py will automatically read config from ${progdir}/GPX.rc as well as ${HOME}/.GPXrc and .GPXrc if they exist.

Options

-c config

config can set all the same variables as these options.

-D deviation

deviation is a comma separated list of 12 or 24 values or a file containing 12 or 24 values; one per line. Negative is West error. The first entry represents 000/360 degrees.

-F fields

The list of fields to output for each leg of a route.

-f fmt

Format of output, csv is useful if wanting to populate a spreadsheet.

-l log_error

The error factor to correct the log.

-o ofile

If ofile ends in - or / we will save each route to a separate file with route name appended to ofile.

If ofile contains {} the route name will be inserted at that point.

-p min_precision

How many decimals we show for minutes of lat/long. Default is 1 (approximately 200m resolution)

-s speed

Nominal cruising speed used to compute Time and ETE

-t sep

Separator between fields. For csv the default is , otherwise it is a space.

Example

Below is the command I use to produce a custom CSV format file for importing into Google Sheets (using | as column separator):

GPX.py -D boat.deviation -f csv -t \| \
-F Waypoint,Position,Course,Magnetic,Compass,Distance,Log,Remarks \
-o /tmp/sheets/ routes.gpx

If no deviation data is available, skip -D and the Compass column will be empty.

The above will create a separate .csv file for each route from routes.gpx in /tmp/sheets/.

Which for entry to the Golden Gate staying north of the shipping channel (with a little massasging for readability) looks like:

GG-Entry

Waypoint | Position              | Course | Magnetic | Compass | Distance |      Log | Remarks

GG-G1    | 37 46.3 N, 122 38.2 W |  070 T |    057 M |         | 2.27 NMi | 0.00 NMi | Close abeam G1. Danger bearing for Potato Patch: Pt Bonita NGT 059 T (046 M), steer parallel to shipping lane @ 069 T (056 M) until G7
GG-G7    | 37 47.0 N, 122 35.5 W |  064 T |    051 M |         | 3.53 NMi | 2.27 NMi | Close abeam G7, avoid shipping lane inbound steer for Pt Diablo @ 063 T (050 M) until demarcation line
Demarca. | 37 48.6 N, 122 31.5 W |  068 T |    055 M |         | 1.33 NMi | 5.81 NMi | Transit Pt Bonita and Mile Rocks. Pt Diablo @ 062 T (048 M), Pt Bonita @ 324 T (310 M), RG Bell @ 279 T (266 M)
PtDiablo | 37 49.1 N, 122 29.9 W |  071 T |    058 M |         | 0.97 NMi | 7.13 NMi | GG Nth (Lime Pt) @ 064 T (050 M),  Pt Bonita @ 273 T (260 M), Pt Diablo @ 324 T (311 M)
GG       | 37 49.4 N, 122 28.7 W |        |          |         |          | 8.10 NMi |

latlong.py

Used by GPX.py to compute the course and distance from one position to the next. It also handles converting Latitude and Longitude from one format to another (I like decimal minutes).

Like GPX.py this script will automatically load config from rc files named for it (${progdir}/latlong.rc etc).

For example, take the positions of St Francis Yaught Club and the Farallon Islands as given by maps.google.com - in decimal degrees:

# from St Francis Yaught Club  to Farallon Islands
(echo 37.8073169,-122.4489734; echo 37.7342623,-123.0932778 ) |
latlong.py
37 48.439 N, 122 26.938 W
course 262T distance 30.89 NMi
37 44.056 N, 123  5.597 W

or if you prefer seconds of arc:

(echo 37.8073169,-122.4489734; echo 37.7342623,-123.0932778 ) |
latlong.py -s
37 48 26.3 N, 122 26 56.3 W
course 262T distance 30.89 NMi
37 44 3.3 N, 123 5 35.8 W

Download

You can fetch the latest archive from https://www.crufty.net/ftp/pub/sjg/pyGPX.zip

ReadMe is the latest version of this document.


Author:sjg@crufty.net /* imagine something very witty here */