Two years ago I bought a Sony FDR-X3000 actioncam to record video on my bike rides. And I’m really happy about it. It’s just great reliving my rides in 4K, going downhill for kilometers from some col I climbed. I also make compilation videos for fellow cyclists. Like these:
Making these compilation videos is a great way to again relive the holiday. But editing these videos (one for every day) takes weeks. And last year I suddenly had other priorities halfway and I was only able to finish them months later.
In my videos I put some annotation on what village I rode by, what col I climbed, etc. But after 7-8 months it was hard to remember what video happened where. Fortunately every video on the Sony FDR-X3000 comes with an XML file with GPS data. So that data can answer that question.
About Sony’s XML
The XML file generated by Sony looks like this (or check the example on Github):
So we have GPS coordinates in degrees, minutes and seconds. And there’s altitude. Even speed. At first I tried using the GPS data manually, but to look them up in Google Maps I had to convert them to decimal GPS coordinates. A typical thing you’d automate.
There are also some XMLs that don’t have GPS data. I’m not exactly sure why. Maybe it didn’t pick up the GPS signal when I was close to a mountain wall or something.
Reading the XML in Python
In the past year I had a lot of experience dealing with XML, but not in Python. In my mind I would traverse the XML tree, finding the items I want. But in the examples I found Googling for this, like this one, it was more like a search on names. Well, since this is a spare time side project, we don’t have to get fancy here.
But fancy I didn’t get. I’m not sure yet what the correct way is to pick out just CreationDate, but I seem to end up with for loops to pick up one value. Oh well.
Here is a simple Python snippet that picks up just CreationDate. You can find the complete code on Github in the repository for this. (If you want to copy the code, don’t forget to get your own map and change the file paths.)
from xml.dom import minidom import os path_of_the_directory= 'D:\Video\Vercors en Drome 2021' ext = ".XML" for filename in os.listdir(path_of_the_directory): sonyfile = os.path.join(path_of_the_directory, filename) sonyxml = minidom.parse(sonyfile) CreationDate = sonyxml.getElementsByTagName('CreationDate') for createdate in CreationDate: print(createdate.attributes['value'].value) creationdate = createdate.attributes['value'].value
GPS: degrees to decimal
Most data solutions work with decimal GPS coordinates. Take Google Maps for example. For plotting purposes we need to convert this. Converting degrees to decimal is not that hard. You need to split the latitude and longitude strings on the : sign and multiply minutes by 60 and seconds by 60*60.
latdeg, latmin, latsec = re.split(':', latitude) latdecimal = float(latdeg) + float(latmin)/60 + float(latsec)/(60*60) longdeg, longmin, longsec = re.split(':', longitude) longdecimal = float(longdeg) + float(longmin)/60 + float(longsec)/(60*60)
Put it in a dataframe
To make my life easier, I put all the coordinates in a Pandas dataframe:
geodf = pd.DataFrame(columns=['creationdate', 'latitude', 'longitude', 'altitude'])
The results of every XML file are put in a row of the dataframe. I used the filename as index.
geodf.loc[filename, :] = [pd.to_datetime(creationdate), latdecimal, longdecimal, altitude]
Putting it on a map
This was actually a bit of a stretch goal for me, but this blogpost was actually really helpful and quickly led to results. It was easy to follow it, so if you want the same, read that blogpost. The only thing was that my Openstreetmap file was apparently too big, because I got Gateway timeouts all the time on the mapbox I wanted to generate.
But my earlier version of the map is workable. And in the future I will probably draw these plots per ride, which results in much smaller maps.
My Python code is actually not that different from said blogpost:
BBox = ((geodf.longitude.min(), geodf.longitude.max(), geodf.latitude.min(), geodf.latitude.max())) print(BBox) vdm2021_m = plt.imread('D:\Video\Vercors en Drome 2021\VDMmap.png') fig, ax = plt.subplots(figsize=(8, 7)) ax.scatter(geodf.longitude, geodf.latitude, zorder=1, c='b', s=10) ax.set_title('Vercors en Drôme 2021') ax.set_xlim(BBox, BBox) ax.set_ylim(BBox, BBox) ax.imshow(vdm2021_m, zorder=0, extent=BBox, aspect='equal') plt.show()
And here is the endresult. These are all the locations of videos from 2 weeks of cycling.
And here is a map of just one ride (from last week). It got a bit warped, because the map was landscape. Otherwise it’s fine.
Again, you can find the Python code here: https://github.com/Marcel-Jan/media_gpsplot. I hope it helps someone else.
So this is something I can really use after my next cycling vacation.
What would it make even better, is if I could integrate it with Strava data. So that I could plot video locations on the track of my ride. A totally strech goal would be to have thumbnails from the videos on the map. We’ll see.