2020-04-28

GPX to KML converter (Strava to Google Earth)

TL;DR: I wrote a little utility that'll take a directory full of GPX files and convert them to KML. Available on github as GpxToKml.

Strava allows downloading all of your data as a zip archive (see bulk export). This includes all GPS recordings of your activities in GPX format. I maintain a large file of tracks for Google Earth. I find it satisfying to have a growing web of tracks from my human powered adventures covering the Alps. Google Earth's native format is KML. Google Earth is smart enough to import GPX files, but it pops up a dialog box for each one of them. This quickly becomes annoying if you import hundreds at once. It also doesn't quite import them the way I want (wrong track labels; color and width). Finally it preserves the timestamps for each GPS location. This sounds like a good feature, but since I find Earth's time controls cumbersome and annoying, I actually prefer simple polylines to timestamped tracks. There are a few online services that convert one format to another, but I found them all lacking in one way or another. So I decided to write my own conversion utility.

I have spent the last 10 years writing C++ mostly within Google's internal ecosystem. So I took this as an opportunity to see how the development experience has changed in the outside world in the meantime. A few random observations:

  • Working in a mono-repo of ~2Billion lines of code is really nice. A single unified build system; homogenous API design (e.g. widespread use of std::string_view) and reduced redundancy.
  • For this project I chose to use boost and TinyXML2. Boost is a mixed bag with some libraries in it going well overboard with their template trickery. But overall I think it's still a solid collection. I remember build times of an hour or more. My new rig rips through a full rebuild in a few minutes. TinyXML is exactly what it says on the label: small and easy to use.
  • I squeezed the entire project into a few hours of a single day between baby sitting quarantined kids. So don't expect production quality.
  • Before getting a fully working run, I solved three noteworthy bugs: my first attempt swapped latitude and longitude, so all my tracks showed up somewhere in Africa. The second attempt ditched some floating point precision, so tracks showed up jaggy. Turns out you need 7 digits of precision after the decimal point. Being able to debug these issues visually on Google Earth was super valuable. A single glance immediately gave away what the problem was. I think I'll try and remember this for other debugging sessions - visualization tools are super valuable! The third and final bug was Windows' crazy UTF handling. I use some sanitized UTF-8 data from the input GPX file as the output filename. Windows file APIs are either ASCII or some weird UTF16 dialect. Thus non-ASCII characters would lead to garbled filenames. Luckily boost released the NoWide library just in the nick of time (today in fact). This works around the problem nicely.
  • I have come to appreciate Google's return value (util::Status) based error handling strategy over exception handling.
  • I wonder what an XSLT based solution might look like? From the label it seems like this would be an appropriate fit. But since it is enterprise-y technology, I kind of shudder to imagine what the experience will be. Maybe I should give it a go?
  • Using boost::asio::io_service as a thread queue was surprisingly easy to set up and seems to work extremely well.