#!/usr/bin/env python3
# ---------------------------------------------------------
# Nautilus extension to add Geolocalisation properties tab
# Dependency :
# - gir1.2-gexiv2-0.10
# - python3-geopy
# Procedure :
# http://bernaerts.dyndns.org/linux/...
#
# Revision history :
# 30/08/2016, V1.0 - Creation by N. Bernaerts
# 05/11/2018, V1.1 - Add useragent to Nominatim
# 25/04/2020, v2.0 - rewrite for python3 compatibility
# ---------------------------------------------------
apikey = "your-google-key"
# -------------------
# Import libraries
# -------------------
import os
import codecs
import re
import urllib.request
import gi
gi.require_version('Nautilus', '3.0')
gi.require_version("GExiv2", "0.10")
from urllib.parse import unquote
from gi.repository import Nautilus, GObject, Gtk, Gio
from gi.repository.GExiv2 import Metadata
from geopy.geocoders import Nominatim
# -------------------
# Property page
# -------------------
class GeotagPropertyPage(GObject.GObject, Nautilus.PropertyPageProvider):
def __init__(self): pass
# -------------------
# Display text cell
# -------------------
def DisplayText(self, value, x, y, width, height, align):
gtk_label = Gtk.Label()
gtk_label.set_markup(value)
if align == "left": gtk_label.set_alignment(0.0, 0)
elif align == "right": gtk_label.set_alignment(1.0, 0)
else: gtk_label.set_alignment(0.5, 0)
gtk_label.set_padding(5, 2)
gtk_label.show()
self.grid.attach(gtk_label, x, y, width, height)
# --------------------
# Display image cell
# --------------------
def DisplayImage(self, filename, x, y, width, height):
# create image from jpeg
gtk_image = Gtk.Image()
gtk_image.set_from_file(filename)
gtk_image.set_padding(5, 5)
gtk_image.show()
self.grid.attach(gtk_image, x, y, width, height)
# -------------------------
# Display property tab
# -------------------------
def get_property_pages(self, files):
# default map size and zoom factor
sizeMap = "512x512"
zoomMap = "7"
# test file type
file = files[0]
if len(files) != 1: return
if file.get_uri_scheme() != 'file': return
# if mimetype corresponds to JPG image, read data and populate tab
mimetype = file.get_mime_type().split('/')
if mimetype[0] in ('image'):
# create tab
self.tab = Gtk.Label('GPS')
self.tab.show()
# create grid
self.grid = Gtk.Grid()
self.grid.set_margin_start(10)
self.grid.set_margin_end(10)
self.grid.set_margin_top(5)
self.grid.set_margin_bottom(5)
self.grid.show()
# create main scrolled window
self.window = Gtk.ScrolledWindow()
self.window.add_with_viewport(self.grid)
self.window.show_all()
# read metadata from file
filename = unquote(file.get_uri()[7:])
self.tags = Metadata()
self.tags.open_path(filename)
# get signed GPS position
latitude = self.tags.get_gps_latitude()
longitude = self.tags.get_gps_longitude()
altitude = self.tags.get_gps_altitude()
# if no GPS data, return
if latitude == 0 and longitude == 0 and altitude == 0: return
# trunk GPS data to 6 digits
parts = str(latitude).split(".")
strLatitude = parts[0] + "." + parts[1][:6]
parts = str(longitude).split(".")
strLongitude = parts[0] + "." + parts[1][:6]
strAltitude = str(altitude)
# generate GPS position
strPosition = strLatitude + ',' + strLongitude
# generate Google Maps links
urlMaps = 'https://www.google.com/maps/place/' + strPosition + '/@' + strPosition + ',' + zoomMap + 'z/'
urlJpeg = 'https://maps.googleapis.com/maps/api/staticmap?maptype=hybrid&zoom=' + zoomMap + '&size=' + sizeMap
urlJpeg += '¢er=' + strPosition + '&markers=' + strPosition + '&key=' + apikey
# generate cache folder, and create if needed
dirHomeCache = os.environ['HOME'] + '/.cache'
dirGeotagCache = os.getenv('XDG_CACHE_HOME', dirHomeCache) + '/geotag'
if not os.path.exists(dirGeotagCache): os.makedirs(dirGeotagCache)
# generate cache file names
fileMap = dirGeotagCache + '/map_' + strLatitude + '_' + strLongitude + '_' + sizeMap + '_' + zoomMap + '.png'
fileDesc = dirGeotagCache + '/map_' + strLatitude + '_' + strLongitude + '.txt'
# if description is not in the cache, retrieve it from Nominatim
if not os.path.exists(fileDesc):
# retrieve place description
geolocator = Nominatim(user_agent="nautilus-exif-geotag")
location = geolocator.reverse(strPosition)
strDescription = location.address
# write description to cache
file = codecs.open(fileDesc, "w", "utf-8")
file.write(strDescription)
file.close()
# if map is not in the cache, retrieve it from Google Maps
if not os.path.exists(fileMap): urllib.request.urlretrieve(urlJpeg, fileMap)
# retrieve description from cache
file = codecs.open(fileDesc, "r", "utf-8")
strDescription = file.read()
file.close()
# dislay GPS data
self.DisplayText("latitude", 0, 0, 1, 1, "right")
self.DisplayText(strLatitude, 1, 0, 1, 1, "left")
self.DisplayText("longitude", 0, 1, 1, 1, "right")
self.DisplayText(strLongitude, 1, 1, 1, 1, "left")
self.DisplayText("altitude", 0, 2, 1, 1, "right")
self.DisplayText(strAltitude, 1, 2, 1, 1, "left")
# dislay address
value = re.compile(',').sub('\n', strDescription)
self.DisplayText("address", 2, 0, 1, 3, "right")
self.DisplayText("" + value + "", 3, 0, 1, 5, "left")
# dislay gmaps image
self.DisplayImage(fileMap, 0, 5, 4, 1)
# return label and tab content
return Nautilus.PropertyPage( name="NautilusPython::geotag_info", label=self.tab, page=self.window ),