Photos taken with GPS-enabled cameras, including smartphones, store location information in the header of the file, in a format called EXIF tags. These tags are largely based on the same header tags used by the TIFF image standard. In this recipe, we'll use these tags to create locations on a map for some photos and provide links to open them.
You will need to download some sample geotagged photos from https://github.com/GeospatialPython/qgis/blob/gh-pages/photos.zip?raw=true and place them in a directory named photos
in your qgis_data
directory.
QGIS requires the Python Imaging Library (PIL), which should already be included with your installation. PIL can parse EXIF tags. We will gather the filenames of the photos, parse the location information, convert it to decimal degrees, create the point vector layer, add the photo locations, and add an action link to the attributes. To do this, we need to perform the following steps:
glob
module for doing wildcard file searches:import glob import Image from ExifTags import TAGS
def exif(img): exif_data = {} try: i = Image.open(img) tags = i._getexif() for tag, value in tags.items(): decoded = TAGS.get(tag, tag) exif_data[decoded] = value except: pass return exif_data
def dms2dd(d, m, s, i): sec = float((m * 60) + s) dec = float(sec / 3600) deg = float(d + dec) if i.upper() == 'W': deg = deg * -1 elif i.upper() == 'S': deg = deg * -1 return float(deg)
def gps(exif): lat = None lon = None if exif['GPSInfo']: # Lat coords = exif['GPSInfo'] i = coords[1] d = coords[2][0][0] m = coords[2][1][0] s = coords[2][2][0] lat = dms2dd(d, m ,s, i) # Lon i = coords[3] d = coords[4][0][0] m = coords[4][1][0] s = coords[4][2][0] lon = dms2dd(d, m ,s, i) return lat, lon
photos
directory, get the filenames, parse the location information, and build a simple dictionary to store the information, as follows:photos = {} photo_dir = "/Users/joellawhead/qgis_data/photos/" files = glob.glob(photo_dir + "*.jpg") for f in files: e = exif(f) lat, lon = gps(e) photos[f] = [lon, lat]
lyr_info = "Point?crs=epsg:4326&field=photo:string(75)" vectorLyr = QgsVectorLayer(lyr_info, "Geotagged Photos" , "memory") vpr = vectorLyr.dataProvider()
features = [] for pth, p in photos.items(): lon, lat = p pnt = QgsGeometry.fromPoint(QgsPoint(lon,lat)) f = QgsFeature() f.setGeometry(pnt) f.setAttributes([pth]) features.append(f) vpr.addFeatures(features) vectorLyr.updateExtents()
QgsMapLayerRegistry.instance().addMapLayer(vectorLyr) iface.setActiveLayer(vectorLyr) activeLyr = iface.activeLayer()
actions = activeLyr.actions() actions.addAction(QgsAction.OpenUrl, "Photos", '[% "photo" %]')
Using the included PIL EXIF parser, getting location information and adding it to a vector layer is relatively straightforward. The interesting part of this recipe is the QGIS action to open the photo. This action is a default option for opening a URL. However, you can also use Python expressions as actions to perform a variety of tasks. The following screenshot shows an example of the data visualization and photo popup: