ExifTool Forum

General => Metadata => Topic started by: krzysiu on July 13, 2016, 08:41:25 PM

Title: Geotagging photos in camera - Android tutorial + the script for Windows
Post by: krzysiu on July 13, 2016, 08:41:25 PM
In this tutorial you'll learn:

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 (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=J3XR4JPNEHCHU). 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:

You will need:

Configuration of environment


GPSLogger

  • Download GPSLogger (https://play.google.com/store/apps/details?id=com.mendhak.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) (http://pastebin.com/raw/DX4e3Auv) (click here (http://pastebin.com/DX4e3Auv) 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.


(http://download.krzysiu.net/gpsLogger.png) (http://download.krzysiu.net/gpsLogger.png)
GPSLogger a moment after opening




(http://download.krzysiu.net/gpxtagger-example.png)(http://download.krzysiu.net/gpxtagger-example.png)
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

Why this script, not some nice GUI

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 (http://www.geonames.org/) in manual or PHP use. It's a database and the website which allows search for different geographic data. Use of their API (http://www.geonames.org/export/web-services.html) 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 (http://www.geonames.org/login) (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:
* 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($json, true);		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.223, 18.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.43234, 0.6273, 'someusername'); // $result = [];