Geotagging Photos using Google Location History and Exiftool

5 minute read

A few years back, I went on a hiking trip, where I used a Campark ACT74 Action Camera, an extremely cheap 4K camera.

One of the features was that it could take a 4K picture 5 times a second, or a 4K 60fps video in 5 minute chunks. Although the low battery life made the camera difficult to use (and the fact that when the battery died), I made almost 100 GB of photos/videos with this camera.

The camera didn’t have a GPS unit installed. Additionally, the camera had an internal clock that constantly reset itself to 2017-01-01 whenever the battery went out, so I also had loads of photographs incorrectly dated.

However, I realised that I could geotag these photos by using my Google Location History and exiftool, so it was off to do that, before Google Photos ended their unlimited storage on June 1st 2021.

Mounting NAS folder locally

Firstly, since my photos were on my NAS, and I wanted to view the photos and videos locally, I used SSHFS to mount all the photos locally.

SSHFS is slower than other methods of mounting a folder, but it’s definitely the easiest, so it’s what I used.

I disabled compression and used the fastest cipher still supported by my OpenSSH server/client, since as my NAS was on my local LAN, I was not worried about security or network speed, I was more worried about my slow NAS CPU!

mkdir --parents /tmp/geotag-photos
sshfs server:/zfspool/geotag-photos /tmp/geotag-photos -o Compression=no -o Ciphers=aes128-ctr

Getting Geographical Information from Google

To geotag my images, I used data from Google Takeout, both my Google Fit workout history in Garmin TCX format, and my Google Location history, in JSON format.

I exported both of these as a gzipped tar (.tgz file).

Then, to extract the files I wanted, I ran the following:

mkdir --parents ../extracted/google-fit
tar -xvf takeout-20201212T143859Z-001.tgz --directory=../extracted/google-fit --strip-components=3 'Takeout/Fit/Activities/'
tar -xvf takeout-20201212T143859Z-001.tgz --directory=../extracted --strip-components=2 'Takeout/Location History/Location History.json'

Converting Google Location Histroy JSON to KML

Next, I wanted to use exiftool to geotag my images.

However, unfortunately, although exiftool currently supports the Garmin TCX format produced by Google Fit, it doesn’t support the Google Histroy GeoJSON file.

Because of that, I used Scarygami/location-history-json-converter to convert the Google History GeoJSON file to a KML file that exiftool supports.

cd ../extracted
git clone https://github.com/Scarygami/location-history-json-converter.git
python3 location-history-json-converter/location_history_json_converter.py 'Location History.json' location-history.kml

Then, finally I copied over all the location history tracks to my NAS:

mkdir --parents /tmp/geotag-photos/geoinfo
cp -r location-history.kml google-fit/ /tmp/geotag-photos/geoinfo

Geotagging images

Firstly, I found that some of my images were correctly dated, but some of my images were set to 2017-01-01, probably due to the internal clock of the camera getting accidentally reset.

Correctly dated images

Firstly, I copied over all the correctly dated images and videos into a new folder:

mkdir --parents accurate-time/ungeotagged
cp ../camera/{photo,video}/201806* accurate-time/ungeotagged

First, I checked what the timezone for DateTimeOriginal was on my images:

me@server:/zfspool/geotag-photos $ exiftool -DateTimeOriginal accurate-time/ungeotagged/20180613_191228A.jpg
Date/Time Original              : 2018:06:13 19:12:28

However, DateTimeOriginal did not exist on my videos, and the one in my photos was missing the correct timezone, so I had to add that in using their filename:

exiftool '-datetimeoriginal<${filename}+01:00' -overwrite_original accurate-time/ungeotagged/

Looks like it’s in UTC+00:00 (the photos were taken in +01:00, aka British Summer Time).

mkdir --parents 'accurate-time/geotagged' # output directory
exiftool -geotag 'geoinfo/google-fit/2018-06-1*.tcx' -geotag 'geoinfo/location-history.kml' '-geotime<${DateTimeOriginal}+01:00' -o 'accurate-time/geotagged/' 'accurate-time/ungeotagged'

For Google Photos to understand the geotagged location of the videos, we additionally need to add the Keys:GPSCoordinates tag.

As Google Photos doesn’t seem to like floating-point numbers with more than 8 decimal points, we can limit this by using -coordFormat '%.8f'.

We can do this via:

exiftool -coordFormat '%.8f' '-keys:GPSCoordinates<$GPSLatitude, $GPSLongitude' -overwrite_original accurate-time/geotagged/*.mp4

Incorrectly dated images

2018-06-12 Afternoon

Firstly, I copied over the first batch of photos that were incorrectly dated starting from: 2017-01-01.

I manually looked through the images and found that the one called 20170101_014459A.jpg was roughly at 20180612-203800.

This meant that the time offset was (running this in python):

from datetime import datetime

labelled_date = datetime.fromisoformat('2017-01-01T01:44:59')
actual_date = datetime.fromisoformat('2018-06-12T20:38:00')

print(actual_date - labelled_date)
# 527 days, 18:53:01

To fix these, we can then run:

mkdir --parents 2018-06-12-afternoon/incorrect-time
exiftool '-AllDates<${filename}+01:00' ./2018-06-12-time-fucked/afternoon/ -o ./2018-06-12-afternoon/incorrect-time/
exiftool '-AllDates+=1:05:11 18:53:01' ./2018-06-12-afternoon/incorrect-time/ -o ./2018-06-12-afternoon/ungeotagged/
exiftool -geotag 'geoinfo/google-fit/2018-06-1*.tcx' -geotag 'geoinfo/location-history.kml' '-geotime<${DateTimeOriginal}+01:00' -o '2018-06-12-afternoon/geotagged/' './2018-06-12-afternoon/ungeotagged/'
exiftool -coordFormat '%.8f' '-keys:GPSCoordinates<$GPSLatitude, $GPSLongitude' -overwrite_original ./2018-06-12-afternoon/geotagged/*.mp4

2018-06-12 Morning

I did the same thing with the next batch, taken on the same day, but in the morning.

Luckily, I had a frame in the video where I took a picture of my phone, and the time on it, so that I knew that 2017-01-01 03:00:00 was equal to roughly 20180612-161100.

from datetime import datetime

labelled_date = datetime.fromisoformat('2017-01-01T03:00:00')
actual_date = datetime.fromisoformat('2018-06-12T16:11:00')

print(actual_date - labelled_date)
# 527 days, 13:11:00

To fix these, we can then run:

mkdir --parents 2018-06-12-morning/incorrect-time
exiftool '-AllDates<${filename}+01:00' ./2018-06-12-time-fucked/morning/ -o ./2018-06-12-morning/incorrect-time/
exiftool '-AllDates+=1:05:11 13:11:00' ./2018-06-12-morning/incorrect-time/ -o ./2018-06-12-morning/ungeotagged/
exiftool -geotag 'geoinfo/google-fit/2018-06-1*.tcx' -geotag 'geoinfo/location-history.kml' '-geotime<${DateTimeOriginal}+01:00' -o '2018-06-12-morning/geotagged/' './2018-06-12-morning/ungeotagged/'
exiftool -coordFormat '%.8f' '-keys:GPSCoordinates<$GPSLatitude, $GPSLongitude' -overwrite_original ./2018-06-12-morning/geotagged/*.mp4

2018-06-13

Luckily, one of my pictures was on my phone, so I had an exact time match: 2017-01-01T15:41:32 was roughly equal to 2018-06-13T16:14:30

from datetime import datetime

labelled_date = datetime.fromisoformat('2017-01-01T15:41:32')
actual_date = datetime.fromisoformat('2018-06-13T16:14:30')

print(actual_date - labelled_date)
# 528 days, 0:32:58
mkdir --parents 2018-06-13-afternoon/incorrect-time
exiftool '-AllDates<${filename}+01:00' ./unknown/20170101_1* -o ./2018-06-13-afternoon/incorrect-time/
exiftool '-AllDates+=1:05:12 0:32:58' ./2018-06-13-afternoon/incorrect-time/ -o ./2018-06-13-afternoon/ungeotagged/
exiftool -geotag 'geoinfo/google-fit/2018-06-1*.tcx' -geotag 'geoinfo/location-history.kml' '-geotime<${DateTimeOriginal}+01:00' -o '2018-06-13-afternoon/geotagged/' './2018-06-13-afternoon/ungeotagged/'
exiftool -coordFormat '%.8f' '-keys:GPSCoordinates<$GPSLatitude, $GPSLongitude' -overwrite_original ./2018-06-13-afternoon/geotagged/*.mp4

Uploading Images to Google Photos

Finally, in order to upload the photos/videos, I just opened up https://photos.google.com in my local computer’s web-browser, and dragged the sshfs-mounted NAS folder to the web-page.

Although my intranet uses very slow Ethernet-over-Powerline (<100 Mbps), which makes sshfs very inefficient, my internet upload speed is very limited, and is less than 20Mbps, which remains the bottle-neck.

Manually correcting video times on Google Photos

Unfortunately, once everything was uploaded, my videos still had the incorrect time. Their UTC time was shown as the local time, which in Britian during the summer, is +01:00 hour behind.

I didn’t bother to use exiftool to fix this, as I was too close to the deadline to reupload many gigabytes of videos. So instead, I just manually selected all the videos in the Google Photos browser, pressed Edit date & time, then Shift dates & times, and manually gave all the videos a 01:00 hour offset.

It’s not the best way of doing this, I’d have to find the correct flag to label videos for Google Photos using exiftool in the future, but with the lack of unlimited storage for Google Photos nowadays, it’s unlikely I will upload many more vidoe files.

Updated: