Geotagging photos in camera - Android tutorial + the script for Windows

Started by krzysiu, July 13, 2016, 08:41:25 PM

Previous topic - Next topic

krzysiu

In this tutorial you'll learn:

  • the amazingly simple way of logging GPS data on Android
  • making geotagging more effective
  • geotagging using Exiftool or using ready to use batch script which is the main dish of this tutorial
  • basics on how to get place name from public domain database (and PHP proof of concept)
  • the structure of GeoNames API - hacks and traps that waits for you
  • ideas for geotag uses
  • ...and more

Disclaimer
I wrote this tutorial to help you all. The tutorial, if you want to reuse it, is on MIT, LGPL and CC BY 4.0 license, but honestly it's just a formality. I won't sue anybody for reusing it without obeying license, but I'd love if you'd keep this note. The script, to make it easier to share, is CC-0. Overall it's to help and share that knowledge. Taking photos is my passion, but tagging it, categorizing is another one. As you can see by my website - sharing (and opening that content) is my way of live. If you think this tutorial is worth it, you can donate me using PayPal. I'm currently jobless and I'd really appreciate any donation, if you think this tutorial is worth it. I also want to make a Windows GUI version with GPS time syncing and other options. I'll do it anyways and I'll share it with joy, same with expanding this tutorial, but for sure it would encourage me to make it quicker.

Uses of geotagging
Let me give a few real life examples of geotagging:

  • Searching photos in collection that are x miles/kilometers from some place/given photo
  • Displaying photos on the map. Nice and trendy effect that can show e.g. your mountain trip in an enjoyable way, which changes boring "photo from trip presentation and talking about changing places meanwhile" into a dynamic and multimedial presentation which needs from us only choice of photos - the rest is done automatically
  • Automatic categorizing photo by country, city, or even district and then searching it by e.g. city name - either by you or guests of some website
  • Sometimes, e.g. for architecture photos, it's possible to automatically describe photographed objects
  • Time capsule - "create and display photo sets which contains at least 5 photos, the first and last must have at least 5 years difference and they need to be within 50 meters"
  • Negative results - find places you didn't visit
  • OCD ;)

You will need:

  • Exiftool
  • A camera which you love, you named it, you skip on the meadow with it, and then you lie with it on the grass, looking in the sky [optionally regular camera]
  • Smartphone with Android system (probably 2.x is enough, 4.x for sure) and GPS
  • To run the script, you need Windows (I think it won't work in Windows XP and earlier, I'm not sure), but the tutorial isn't only about it, so if you are *nix user, just skip on the grass... err, just skip the script section

Configuration of environment


GPSLogger

  • Download GPSLogger. Why this one? Because it's open source, it's without adverts and it's made only for logging, so you can customize it a lot and because no map, internetz etc. it's extremly lightweight (the battery will thank you!)
  • Click on the menu button, choose Logging details. For lightweightness, check only Log to GPX. You can choose creating new files every session (default: one per day) - the script supports multiple files
  • Go back, click the menu button and set General options and Performance (!!!) to your prefered options. Performace is the heard of setting like how often, what to log, when to stop etc. It has a nice precision filter, which discards points outside given precision, minimizing GPS error count
  • You'll probably want to configure "Auto send, email and upload" - if you don't have an idea for the best way of sharing files between PC and smartphone, you can use Filezilla Server on PC and create account, which will be used for autoupload of GPS files. Since now you can start logging.

GPX tagging script (Windows only)

  • Open this link (pastebin) (click here to see highlighted version) and save (ctrl+s) this script to some location (I'll use c:\windows\gpx.bat in this tutorial)
  • Open it in your favorite editor (not run) and edit the line, that starts with set exiftool=. Set it to the full path (directory + file) of exiftool. Remember to keep it in quotes if the path contains spaces - that's the global Windows rule. If you have exiftool in your PATH environment variable, like all the heroes, change it to set exiftool=exiftool.


GPSLogger a moment after opening




Script in action

Use
Geotagging your photos for the first time

  • Download GPX file(s) to PC. Put it (them) in the directory, where your photos are. They could be raw as well, at least CR2 from Canon 50D works fine for me.
  • Right click on the file, choose Open with..., if it exists, then Choose default application. If you don't see "Open with" then GPX files aren't registerd - just double click on the GPX file in this case.
  • Click on the Browse... button, choose the batch file we've saved and customized earlier, e.g.  c:\windows\gpx.bat

Geotagging your photos second time and later

  • Double click on GPX file
  • Eventually set which files will be used by answering the questions
That's it! ;D Remember - it/they have/has to be in the same path as photos to geo tag.

What the script does

  • It checks for other GPX files in the same directory
  • If they exist, it will ask if you want to use all GPX files or only the one that you've ran
  • It applies data from GPX file(s) to photos from the same directory
  • Now, if you want, you have time to check if your geotagged files are OK! Switch the window, check it. Recommended - every time. Minimal - try to do it at least once.
  • If everything is OK, you can choose to delete originals
  • If there's some problem, create file named "restore.gpx" and run it. It's a kind of shortcut to "exiftool -restore_original".

Why this script, not some nice GUI

  • These "nice GUI" have mostly other target - to manually set GPS data. In our case we already have it.
  • Because all tools I tried to use had problems with raw files. They all used Exiftool and it was fine, but the preview function often froze them.
  • Because making Windows Batch scripts is like programming in BrainFuck - it's difficult, but in this case it's not about knowledge, but rather knowing tricks and ability to merge them in one, working script. That's the adventure for me.

The dessert - getting geonames (but not writting them YET)
Introduction to Geonames
I think I should write something in Python (and compile it for Windows), so you'd have a nice, system independent reader of such data, which could be used in scripts. For now, let me present Geonames in manual or PHP use. It's a database and the website which allows search for different geographic data. Use of their API is kind of weird, yet it's easy for use - you can use GET method, so in simpler words - you ask by sending URL (Internet address), even from browser and you'll get answer in JSON format, supported by most computer languages. You need the free account (with big limit, 10k requests per day). There's no issues with commercial use, it can be easily used from own applications and that's why I choose it instead services that steals your privacy, use is non-commercial only (so even posting geotagged photo on most social services would be illegal) and they need bloated libraries.

Queries, answers and issues
In this tutorial we are interested in converting raw, geographic data to country name, region (state, voivodeship, land etc.), city and often even district!

We will use findNearbyPlaceNameJSON - it's not a database of borders, so nearby almost always means the thing that we want. For the data I provided in GeoLogger screenshot, we need to create such link:
http://api.geonames.org/findNearbyPlaceNameJSON?lat=50.223&lng=18.995&username=YOURUSERNAME&style=full
Parameters lat=50.223 and lng=18.995 are the coordinates - the only parameters we need to give.

The answer is JSON encoded data:
{"geonames":[{"distance":"1.24197","timezone": "gmtOffset":1,"timeZoneId":"Europe/Warsaw","dstOffset":2},"asciiName":"Brynow","countryId":"798544","fcl":"P","adminId2":"7530792","adminId3":"7532033","countryCode":"PL","adminId1":"3337497","lat":"50.23188","fcode":"PPL","continentCode":"EU","elevation":0,"adminCode2":"2469","adminCode3":"246901","adminCode1":"83","lng":"19.00555","geonameId":3102470,"toponymName":"Brynów","population":0,"adminName5":"","adminName4":"","adminName3":"Katowice","alternateNames":[{"name":"http://en.wikipedia.org/wiki/Bryn%C3%B3w","lang":"link"}],"adminName2":"Katowice","name":"Brynów","fclName":"city, village,...","countryName":"Poland","fcodeName":"populated place","adminName1":"Silesian Voivodeship"}]}

You can see that it even has link to Wikipedia for my district! With other API queries you could get altitude. It can show a few places, if there's a few nearby, but we will choice the first - the nearest ("distance":"1.24197"). Here are fields that are interesting for us:

  • countryName - country (in English)
  • adminName1 - region (in English*)
  • adminName2 - city, village etc.
  • name - "subregion" - the part of the previous item, mostly district**
* there's possibility of getting local name of region; if you want it, please tell me
** sometimes it's the same as city - it means there's no subregion


If there will be error, mostly due to not existing account or not verified, the GeoNames will create status field. Important notice: it's not really a status, but error code. The lack of status field means that everything should be fine.

PHP implementation
<?php

function getGeoNames($lat$lon$user) {
$geonamesURL sprintf('http://api.geonames.org/findNearbyPlaceNameJSON?lat=%.8f&lng=%.8f&username=%s&style=full'$lat$lon$user);
if (!$json file_get_contents($geonamesURL)) return false// can't get API answer
$json json_decode($jsontrue);
if (json_last_error() !== JSON_ERROR_NONE) return false// API answer incorrect
if (isset($json['status']['message'])) {
trigger_error(sprintf('GeoNames error: "%s"'$json['status']['message']), E_USER_NOTICE); // other problems, mostly account related
return false;
}

if (!isset($json['geonames'][0])) return []; // no place found, empty array
$json $json['geonames'][0]; // get first place, i.e. the nearest
$ret = ['country' => $json['countryName'], 'region' => $json['adminName1'], 'city' => $json['adminName2'], 'subregion' => $json['name']]; // convert weird field names to something more userfriendly
if ($ret['city'] === $ret['subregion']) unset($ret['subregion']);
return $ret;
}


function getGeoNames
* (float) $lat - decimal latitude (decimal i.e. this one we will get from GPSLogger or that one that's provided in Exif)
* (float) $lon - decimal longitude
* (string) $username - GeoNames account name (see introduction for register link)

returns:

  • On success: (array) either 3- or 4-element array with following keys: country, city, region, subregion, with the last being optional
  • When place's not found: (array) empty array; except mistakes it happens extremely rarely
  • On technical problems: (bool) false

In the following example, I assume that someusername is valid and verified username, whilst wrongusername isn't.
<?php
$result 
getGeoNames(50.22318.995'someusername')); // $result = ['country' => 'Poland', 'region' => 'Silesian Voivodeship', 'city' => 'Katowice', 'subregion' => 'Brynów'];
$result getGeoNames(15.342426, -25'wrongusername')); // Displays "Notice: GeoNames error: "user does not exist." in C:\tmp.geonames.php on line 9"
$result getGeoNames(2.432340.6273'someusername'); // $result = [];

"We would use teleporters and live on another planets, if only ExifTool would be present when I was researching cosmos and physics"
Albert Einstein