Getting ready
You need to make sure you have QGIS installed as well as the Python Imaging Library (PIL) for the Python distribution that QGIS uses. QGIS should already have PIL included. You can download some sample geotagged images here or you can use any photo taken outdoors with your smartphone. Unzip these photos and place them in a directory accessible to QGIS.How to do it...
PIL can parse EXIF tags. We will gather the file names of the photos, parse the location information, convert it to decimal degrees, create the point vector layer, add the photo locations, and add a QGIS action link to the attributes. To do this, we need to perform the following steps:In the QGIS Python Console, import the libraries that we'll need, including the Image module (PIL) and its ExifTags module for parsing image data and the glob module for doing wildcard file searches:
import glob import Image from ExifTags import TAGS
Next, we'll create a function that can parse the header data:
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
Now, we'll create a function that can convert degrees-minute-seconds to decimal degrees, which is how coordinates are stored in JPEG images:
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)
Next, we'll define a function to parse the location data from the header data:
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
Next, we'll loop through the photos directory, get the file names, 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]
Now, we'll set up the vector layer for editing:
lyr_info = "Point?crs=epsg:4326&field=photo:string(75)" vectorLyr = QgsVectorLayer(lyr_info, \"Geotagged Photos" , "memory") vpr = vectorLyr.dataProvider()
We'll add the photo details to the vector layer:
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()
Now, we can add the layer to the map and make the active layer:
QgsMapLayerRegistry.instance().addMapLayer(vectorLyr) iface.setActiveLayer(vectorLyr) activeLyr = iface.activeLayer()
Finally, we'll add an action that allows you to click on it and open the photo. Actions are a simple yet powerful feature of QGIS that let you define the result of certain user actions like clicking on features:
actions = activeLyr.actions() actions.addAction(QgsAction.OpenUrl, "Photos", \'[% "photo" %]')
How it works...
Using the included PIL EXIF parser, getting location information and adding it to a vector layer is relatively straight forward. 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:
There's more...
You can download the complete code sample here. Another plugin called Photo2Shape is available that performs a similar function, but it requires you to install an external EXIF tag parser while this approach uses the parser in PIL included with QGIS.
No comments:
Post a Comment