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, arcpyfrom 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] = valuereturn ret#----------PARSE EXIF------------def _parseXYsFromExif(aDict):'''parse out the GPS info from the exif datahttp://www.exiv2.org/tags.html'''latDegree = aDict.get(2)[0][0]latMin = aDict.get(2)[1][0]latMin = latMin * 0.01longDegree = aDict.get(4)[0][0]longMin = aDict.get(4)[1][0]longMin = longMin * 0.01return 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 fgdbtry:hour = aDict.get(7)[0][0]minute = aDict.get(7)[1][0]except:passtry:DOPnum = aDict.get(11)[0][0]DOPdec = aDict.get(11)[0][1]except:pass##rotation = the Numbers don't make sense##return time, DOTpass#----------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 = coordTuplelatDD = 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"passelse:arcpy.CreateFileGDB_management(path, fgdbName)print "...created the file gdb"if arcpy.Exists(path + "\\" + fgdbName + "\\" + fcName):print "...the fc already exists"passelse: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.ShapeFieldNamepics = 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 picpprint.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 recognizesrows = arcpy.InsertCursor(path + "\\" + fgdb + "\\" + fc + "WGS84")row = rows.newRow()row.setValue(shpFld, pnt)##row.time##row.DOProw.Path = path + "\\" + fgdb + "\\geoPics" + "\\"row.PicName = picrows.insertRow(row)# move from pic folder to fgdb\geoPicsshutil.copy2(inPicFolder + "\\" + pic, path + "\\" + fgdb + "\\geoPics" + "\\" + pic)print "...added a point, " + str(i - 1) + " to go."i -= 1except: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?
Thanks for posting this!