Geolocating photos on the map

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.

Getting ready

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.

How to do it...

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:

  1. In the QGIS Python Console, import the libraries that we'll need, including k for parsing image data and the glob module for doing wildcard file searches:
    import glob
    import Image
    from ExifTags import TAGS
    
  2. 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
    
  3. 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)
    
  4. 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
    
  5. Next, we'll loop through the 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]
    
  6. 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()
    
  7. 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()
    
  8. Now, we can add the layer to the map and make the active layer:
    QgsMapLayerRegistry.instance().addMapLayer(vectorLyr)
    iface.setActiveLayer(vectorLyr)
    activeLyr = iface.activeLayer()
    
  9. Finally, we'll add an action that allows you to click on it and open the photo:
    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 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:

How it works...

There's more...

Another plugin called Photo2Shape is available, but it requires you to install an external EXIF tag parser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset