iPhone Photos to Esri File Geodatabase via arcPy Python Script

Here’s a rough script (but it does work) that reads EXIF information from already downloaded photos (tested with an iPhone)  and creates a file geodatabase using the latitude and longitude stored with the photo.

You have to have had your GPS active while taking the photos.  The pictures are moved into the file geodatabase and a hardcoded path is stored.  Eventually I’d like to calc that as a relative path and leave the photos where they are.  The benefit to moving them into the geodatabase means they won’t get lost, but you duplicate the photos and increase storage needs.

The settings for running the script to accept arguments, or to run with a hard coded path, are near the bottom. Be sure to modify the paths to be what you want.

Click on through to get to the script:

I came by this script at the Utah GIS Portal and the original author was Kevin Bell.  I updated it to version 10.  Kevin’s to do notes and wish list are in the script too if anyone want to help tackle that and make it better.

# buildPointFCfromGeotaggedPhoto.py
# Author: Kevin Bell Utah GIS portal http://gis.utah.gov/code-python/python-iphone-pics-to-filegeodb
# Date 4/2/2010
# Updated by: Ann Stark http://gisstudio.wordpress.com http://www.scsciences.com
# 4/3/2012
# Purpose:
#            Creates a file geodatabase from a directory of iPhone pictures
#            for best results, the phone should be on long enough to acquire
#            a gps fix.
#            The script will create a folder in your output fgdb named "geoPics" and
#            move you pics into it. You'll get a point feature class with a "hyperlink" field
#            that points to the picture in the new folder.
#            This script has a hardcoded output projection that you may want to change.
#            You need PIL, the Python Image Library. It has functions to scrape the XY's from
#            the exif header in your pic. The XY's come out a little differently for other cameras,
#            so if you're not using an iPhone you may need to modify the _parseXYsFromExif function.
#           PIL: http://www.pythonware.com/products/pil/
## STILL NEED TO DO!!! :
##          -get the rotation data 17: (3873, 727)
##          -get the DOP data 11: (3, 1),
##          -get the timestamp data 7, but is there a date? 7: ((18, 1), (24, 1), (2646, 100))
import shutil, os, sys, pprint, arcpy
from PIL import Image   #PIL: http://www.pythonware.com/products/pil/
from PIL.ExifTags import TAGS
#----------GET EXIF------------
def _get_exif(fn):
    '''Extract all exif metadata'''
    ret = {}
    i = Image.open(fn)
    info = i._getexif()
    for tag, value in info.items():
        decoded = TAGS.get(tag, tag)
        ret[decoded] = value
    return ret
#----------PARSE EXIF------------
def _parseXYsFromExif(aDict):
    '''parse out the GPS info from the exif data
    http://www.exiv2.org/tags.html'''
    latDegree = aDict.get(2)[0][0]
    latMin = aDict.get(2)[1][0]
    latMin = latMin * 0.01
    longDegree = aDict.get(4)[0][0]
    longMin = aDict.get(4)[1][0]
    longMin = longMin * 0.01
    return latDegree, latMin, longDegree, longMin
#----------NOT WORKING YET - NON-EXIF------------
def _parseNonXYexifData(aDict):  ## Not implimented
    '''get the rotation, DOP, and time here'''
    ## this still isn't populating the fgdb
    try:
        hour = aDict.get(7)[0][0]
        minute = aDict.get(7)[1][0]
    except:
        pass
    try:
        DOPnum = aDict.get(11)[0][0]
        DOPdec = aDict.get(11)[0][1]
    except:
        pass
    ##rotation = the Numbers don't make sense
    ##return time, DOT
    pass
#----------CONVER DECDEGMIN 2 DD------------
def _convertDegreeDecMin2DD(latD, latMin, longD, longMin):
    '''convert From Degrees decimal-minutes (D m) to decimal-degrees (d) '''
    #latD, latMin, longD, longMin = coordTuple
    latDD = latD + (latMin / 60)
    longDD = longD + (longMin / 60)
    return latDD, longDD
#----------CREATE FGDB ------------
def _createFGDB(path, fgdbName, fcName):
    '''create a fgdb and a fc with a field to contain the path to a picture'''
    if arcpy.Exists(path + "\\" + fgdbName):
        print "...the file gdb already exists"
        pass
    else:
        arcpy.CreateFileGDB_management(path, fgdbName)
        print "...created the file gdb"
    if arcpy.Exists(path + "\\" + fgdbName + "\\" + fcName):
        print "...the fc already exists"
        pass
    else:
        spRef = r"Coordinate Systems\Geographic Coordinate Systems\World\WGS 1984.prj"
        arcpy.CreateFeatureclass_management(path + "\\" + fgdbName, fcName, "POINT", "#", "#", "#", spRef)
        print "...made fc"
        arcpy.AddField_management(path + "\\" + fgdbName + "\\" + fcName, "Path", "TEXT", "#", "#", "155","#", "#", "#", "#")
        arcpy.AddField_management(path + "\\" + fgdbName + "\\" + fcName, "PicName", "TEXT", "#", "#", "100","#", "#", "#", "#")
        arcpy.AddField_management(path + "\\" + fgdbName + "\\" + fcName, "FullPicPath", "TEXT", "#", "#", "255","#", "#", "#", "#")
        print "...added field"
        os.mkdir(path + "\\" + fgdbName + "\\" + "geoPics")
        print "...made geoPics dir in fgdb"
#----------MAKE THE POINTS FEATURECLASS ------------
def makeGISpointsFromPics(inPicFolder, path, fgdb, fc):
    '''Top level function... '''
    print "----Beginning to build a GIS Point data for geotagged photos----"
    #build the geodatabase
    _createFGDB(path,fgdb, fc + "WGS84")
    dsc = arcpy.Describe(path + "\\" + fgdb + "\\" + fc + "WGS84")
    shpFld = dsc.ShapeFieldName
    pics = os.listdir(inPicFolder)
    pics = [p for p in pics if p.endswith(".JPG") or p.endswith(".jpg")]
    i = len(pics)
    for pic in pics:
        try:
            d = _get_exif(inPicFolder + "\\" + pic)
            print "\n"
            print pic
            pprint.pprint(d.get("GPSInfo"))
            latDegree, latMin, longDegree, longMin = _parseXYsFromExif(d.get("GPSInfo"))
            Lat, Lon = _convertDegreeDecMin2DD(latDegree, latMin, longDegree, longMin)
##        theTime, theDOP = _parseNonXYexifData(d.get("GPSInfo"))
            pnt = arcpy.Point(-Lon,Lat) #pnt is now an "object" that arcmap recognizes
            rows = arcpy.InsertCursor(path + "\\" + fgdb + "\\" + fc + "WGS84")
            row = rows.newRow()
            row.setValue(shpFld, pnt)
            ##row.time
            ##row.DOP
            row.Path = path + "\\" + fgdb + "\\geoPics" + "\\"
            row.PicName = pic
            rows.insertRow(row)
            # move from pic folder to fgdb\geoPics
            shutil.copy2(inPicFolder + "\\" + pic, path + "\\" + fgdb + "\\geoPics" + "\\" + pic)
            print "...added a point, " + str(i - 1) + " to go."
            i -= 1
        except:
            print "there was a problem here"
    print "----------------------done------------------------------------"
#------------------------------------------------------------------------------------
# FOR USING IN A MODEL OR TOOLBOX
#inPicFolder = gp.getParameterAsText(0)  # The path to your pics
#path = gp.getParameterAsText(1)          # The path to the output fgdb
#fgdb = gp.getParameterAsText(2)          # The name of your new fgdb
#fc = gp.getParameterAsText(3)             # The name of the point feature class of your pics
#USAGE
#makeGISpointsFromPics(inPicFolder, path, fgdb, fc)
#For using if you want to hard code the paths...
makeGISpointsFromPics(r"C:\Temp\MyPics", r"C:\Temp\MyPics", "MyPics.gdb", "MyPicturePoints")
#make feature layer
#calc values - concatanate path and image name
#can it use a relative path?
About these ads

One thought on “iPhone Photos to Esri File Geodatabase via arcPy Python Script

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s