[an error occurred while processing this directive]

gps.py

    1: #!/usr/bin/env python
    2: 
    3: r"""gps - a program to update the exif data in images using GPX files
    4: 
    5: This requires:
    6:     surd.py - surd support (rational numbers)
    7:     pyexiv2.py - libexiv2 support (for reading/writing EXIF data)
    8:     
    9: To use this:
   10:     run gps.py in a directory of .jpg and .gpx files
   11:     
   12:     the program reads all the gpx files to construct a time dictionary
   13:     then it reads all the photos and updates the GPS/EXIF data to give
   14:     the photo the closest location in the time dictionary
   15:     
   16:     The program has to guess the difference between GPS time and camera time.
   17:     GPS time is UTC (GMT) time.
   18:     Camera time is whateve you set in the camera.
   19:     This program will write/read a file tzinfo with the delta info.
   20:     The program guesses that this will be the offset from UTC time
   21:     for your desktop computer!  If the tzinfo exists, it will be read
   22:     and respected.   You can use the tzinfo file to correct for
   23:     camera inaccuracy, incorrect timezone setting and so.
   24:     
   25:     For example for PSD (Pacific Daylight) -07:00, this will be 3600*7 = 25200 (seconds)
   26:     The Camera clock is 28800 seconds behind ZULU
   27:     If you are to the EAST of ZULU, tzinfo will be negative.
   28:     
   29: --------------------
   30: Revision information: 
   31:     $Id: //depot/bin/gps.py#11 $ 
   32:     $Header: //depot/bin/gps.py#11 $ 
   33:     $Date: 2008/04/16 $ 
   34:     $DateTime: 2008/04/16 00:53:00 $ 
   35:     $Change: 107 $ 
   36:     $File: //depot/bin/gps.py $ 
   37:     $Revision: #11 $ 
   38:     $Author: rmills $
   39: --------------------
   40: """
   41: 
   42: __author__  = "Robin Mills <robin@clanmills.com>"
   43: __date__    = "$Date: 2008/04/16 $"
   44: __version__ = "$Id: //depot/bin/gps.py#11 $"
   45: __credits__ = """Everybody who contributed to Python.
   46: 
   47: Especially: Guido van Rossum for creating the language.
   48: And: Mark Lutz for the O'Reilly Books to explain it.
   49: And: Ka-Ping Yee for the wonderful module 'pydoc'.
   50: 
   51: Olivier Tilloy for the pyexiv2 python wrapper code
   52: Andreas Huggel for the libexiv2 library
   53: """
   54: 
   55: import sys
   56: import os
   57: import glob
   58: import surd
   59: import pyexiv2
   60: from   time import altzone,daylight,timezone
   61: import time
   62: import datetime
   63: import xml.dom.minidom
   64: 
   65: ##
   66: # Ration number support
   67: def R(f):
   68:     """R(float) - get a Rational number for a float"""
   69:     s = surd.surd(float(f))
   70:     return pyexiv2.Rational(s.num,s.denom) 
   71:     
   72: def d(angle):
   73:     """d(any) - get degrees from a number :eg d(33.41) -> 33"""
   74:     return int(angle)
   75:     
   76: def m(angle):
   77:     """m(any) - get minutes from a number :eg d(33.41) -> 24"""
   78:     return int( angle*60 - d(angle)* 60)
   79: 
   80: def s(angle):
   81:     """s(any) - get seconds from a number :eg s(33.41) -> 36"""
   82:     return int( angle*3600 - d(angle)*3600 - m(angle)*60 )
   83: 
   84: #
   85: ##
   86: 
   87: 
   88: ##
   89: # dictionary search (closest match)
   90: def search(dict,target):
   91:     """search(dict,taget) - search for closest match"""
   92:     s    = sorted(dict.keys())
   93:     N    = len(s)
   94:     low  = 0
   95:     high = N-1
   96:     
   97:     while low < high:
   98:         mid = (low + high)/2
   99:         if s[mid] < target:
  100:             low = mid + 1
  101:         else:
  102:             high = mid
  103:     return s[low]       
  104: #
  105: ##
  106: 
  107: ## 
  108: # XML functions
  109: def getXMLtimez(phototime,delta):
  110:     """getXMLtimez - convert a datetime to an XML formatted date"""
  111:     #
  112:     # phototime = timedate.timedate("2008-03-16 08:52:15")
  113:     # delta     = seconds
  114:     # -----------------------
  115:     
  116:     timedelta = datetime.timedelta(0,delta,0)
  117:     newtime = phototime + timedelta 
  118:     return newtime.strftime('%Y-%m-%dT%H:%M:%SZ') ;
  119: 
  120: def getText(nodelist):
  121:     """getText(nodeList) - return the text in nodelist"""
  122:     rc = ""
  123:     for node in nodelist:
  124:         if node.nodeType == node.TEXT_NODE:
  125:             rc = rc + node.data
  126:     return rc
  127: 
  128: def getNodeValue(node):
  129:     """getNodeValue(node) - return the value of a node"""
  130:     return getText(node.childNodes)
  131: 
  132: def handleTRKPT(trkpt,timedict):
  133:     """handleTRKPT"""
  134:     ele  = getNodeValue(trkpt.getElementsByTagName("ele")[0])
  135:     time = getNodeValue(trkpt.getElementsByTagName("time")[0])
  136:     lat  = trkpt.getAttribute("lat")
  137:     lon  = trkpt.getAttribute("lon")
  138:     # print "lat, lon = %s %s ele,time = %s %s" % ( lat,lon  , ele,time )
  139:     timedict[time] = [ ele,lat,lon ]
  140: 
  141: def handleTRKSEG(trkseg,timedict):
  142:     """handleTRKSEG"""
  143:     trkpts =     trkseg.getElementsByTagName("trkpt")
  144:     for trkpt in trkpts:
  145:         handleTRKPT(trkpt,timedict)
  146: 
  147: def handleTRK(trk,timedict):
  148:     """handleTRK"""
  149:     trksegs = trk.getElementsByTagName("trkseg")
  150:     for trkseg in trksegs:
  151:         handleTRKSEG(trkseg,timedict)
  152: 
  153: def handleGPX(gpx,timedict):
  154:     """handleGPSX"""
  155:     trks =    gpx.getElementsByTagName("trk")
  156:     for trk in trks:
  157:         handleTRK(trk,timedict)
  158: # 
  159: ##
  160: 
  161: ##
  162: # read the tzinfo file to get the time offset (or use the clock)
  163: def getDelta(filedict):
  164:     tzinfo = 'tzinfo'
  165:     delta = altzone if daylight == 1 else timezone
  166: 
  167:     read = False
  168:     try:
  169:         f = open(tzinfo,'r')
  170:         delta = int(f.readline())
  171:         f.close()
  172:         read = True
  173:     except:
  174:         read= False
  175:     if not read:
  176:         try:
  177:             f = open(tzinfo,'w')
  178:             f.writelines(str(delta))
  179:             f.close()
  180:             read = True
  181:         except:
  182:             read = False
  183:             
  184:     if read:
  185:         tzinfo = os.path.abspath(tzinfo)
  186:         filedict[tzinfo] = tzinfo
  187:             
  188:     
  189:     hours = float(delta) / 3600.0 
  190:     print "delta from tz to UTC = %d:%d:%d hrs" % (d(hours),m(hours),s(hours))
  191:     return delta
  192: #
  193: ##
  194: 
  195: ## 
  196: # the program
  197: def gps():
  198:     """ gps - main entry point for program """
  199:     
  200:     filedict = {} # contains file which we find useful (*.gpx and tzinfo)
  201:     delta = getDelta(filedict)
  202:     ##
  203:     # build the timedict from the gpx files in this directory
  204:     # 
  205:     timedict = {}
  206:     for path in glob.glob("*.gpx"):
  207:         file     = open(path,"r")
  208:         data     = file.read(os.path.getsize(path))
  209:         print "reading ",path
  210:         file.close()
  211:         dom = xml.dom.minidom.parseString(data)
  212: 
  213:         handleGPX(dom,timedict)
  214:         
  215:         path = os.path.abspath(path)
  216:         filedict[path] = path ;
  217: 
  218:     print "number of timepoints = ",len(timedict)
  219: 
  220:     ##
  221:     # fild all the images in this this directory
  222:     if len(timedict):
  223:         image       = 0
  224:         firstTime   = True
  225:         imagedict = {}
  226:         for path in glob.glob("*"):
  227:             path = os.path.abspath(path)
  228:             if os.path.isfile(path) and not filedict.has_key(path):
  229:                 try:
  230:                     image = pyexiv2.Image(path)
  231:                     image.readMetadata()
  232:                     timestamp = image['Exif.Image.DateTime']
  233:                     xmlDate = getXMLtimez(timestamp,delta)
  234:                     imagedict[xmlDate] = path
  235:                 except:
  236:                     print "*** unable to work with ",path , " ***"
  237:         
  238:         for xmlDate in sorted(imagedict.keys()):
  239:             path = imagedict[xmlDate] ;
  240:             try:
  241:                 image       = pyexiv2.Image(path)
  242:                 image.readMetadata()
  243:                 stamp       = str(getXMLtimez(image['Exif.Image.DateTime'],delta))
  244: 
  245:                 timestamp   = search(timedict,stamp) 
  246:                 data        = timedict[timestamp]
  247:                 ele         = float(data[0])
  248:                 lat         = float(data[1])
  249:                 lon         = float(data[2])
  250:         
  251:                 latR    = 'N'
  252:                 lonR    = 'E'
  253:                 eleR    =  0
  254:                 if lat  < 0:
  255:                     lat = -lat
  256:                     latR= 'S'
  257:                 if lon  < 0:
  258:                     lon = -lon
  259:                     lonR= 'W'
  260:                 if ele  < 0:
  261:                     ele = -ele
  262:                     eleR= 1
  263:                 if firstTime:
  264:                     print "camera time          nearest gps          latitude   longitude     elev photofile"
  265:                     firstTime = False
  266:                 slat = "%02d.%02d'" '%02d"%s' % (d(lat),m(lat),s(lat),latR )
  267:                 slon = "%02d.%02d'" '%02d"%s' % (d(lon),m(lon),s(lon),lonR )
  268:                 sele = "%6.1f" % (ele)
  269:                 print  "%s %s %s %s %s %s" % ( stamp,timestamp,slat,slon,sele,path )
  270:                 
  271:                 # get Rational number for ele
  272:                 # don't why R(ele) is causing trouble!
  273:                 # it might be that the denominator is overflowing 32 bits!
  274:                 # and this would also import lat and lon
  275:                 rele = pyexiv2.Rational(int(ele*10.0),10)
  276:     
  277:                 image['Exif.GPSInfo.GPSAltitude'            ] = rele
  278:                 image['Exif.GPSInfo.GPSAltitudeRef'         ] = eleR
  279:                 image['Exif.GPSInfo.GPSDateStamp'           ] = stamp
  280:                 image['Exif.GPSInfo.GPSLatitude'            ] = [R(d(lat)),R(m(lat)),R(s(lat))]
  281:                 image['Exif.GPSInfo.GPSLatitudeRef'         ] = latR
  282:                 image['Exif.GPSInfo.GPSLongitude'           ] = [R(d(lon)),R(m(lon)),R(s(lon))]
  283:                 image['Exif.GPSInfo.GPSLongitudeRef'        ] = lonR
  284:                 image['Exif.GPSInfo.GPSMapDatum'            ] = 'WGS-84'
  285:                 image['Exif.GPSInfo.GPSProcessingMethod'    ] = '65 83 67 73 73 0 0 0 72 89 66 82 73 68 45 70 73 88 ' 
  286:                 image['Exif.GPSInfo.GPSTimeStamp'           ] = [R(10),R(20),R(30)]
  287:                 image['Exif.GPSInfo.GPSVersionID'           ] = '2 2 0 0'
  288:                 image.writeMetadata()
  289:             except:
  290:                 print "*** problem with ",path , " ***"
  291:         
  292:         image = 0
  293: 
  294:     # That's all folks!
  295:     ##
  296: #
  297: ##
  298: 
  299: if __name__ == '__main__':
  300:     gps()