#!/usr/bin/python # -*- encoding: utf-8 -*- # Grab GPS information from each JPG image in the current directory, # and generate a GPX file containing a waypoint for each image. import glob, os, time, urllib import xmlgen ALBUM_ROOT = '/home/bb/public_html/album/' ALBUM_URL = 'http://drbeat.li/album/-image/' CHARSET_IN = 'utf-8' CHARSET_OUT = 'utf-8' def parseCoords(lat, lon=None): # Split at a semicolon if ';' in lat and lon is None: lat, lon = lat.split(';', 1) return parseCoord(lat), parseCoord(lon) def parseCoord(c): """This function handles the following formats: 7.1345 -122.65 7.1345E 122.65 W 7 12 44.3 7° 12'44.3" 7° 12' 44.3" -122 40 12 7/1 12/1 4430/100 """ # Discard unit symbols if '\xB0' in c: c = c.replace('\xB0', ' ').replace("'", ' ').replace('"', ' ') # Try to find the hemisphere c = c.lower() if c[-1] in 'ne': sign = 1 c = c[:-1] elif c[-1] in 'sw': sign = -1 c = c[:-1] elif c[0] == '+': sign = 1 c = c[1:] elif c[0] == '-': sign = -1 c = c[1:] else: sign = 1 # Split into words dms = (c.strip().split() + ['0', '0'])[:3] # right-pad with zeros try: return sign * abs(eval(dms[0])) + abs(eval(dms[1])) / 60. + abs(eval(dms[2]) / 3600.) except: return None def isInteresting(key): return ( key.startswith('Exif.GPSInfo.') or key.startswith('Exif.Photo.DateTimeDigitized ') or key.startswith('Exif.Photo.UserComment ') ) def f2str(f): return '%.6f' % f class GpxGen(object): def __init__(self, dir=None): self.dir = None self.files = [] self.gpx = None if dir is not None: self.addDir(dir) def addDir(self, dir): self.dir = os.path.abspath(dir) self.addFiles(glob.glob(os.path.join(self.dir, '*.jpg'))) def addFiles(self, files): self.files.extend(files) def generateGpx(self, bounds=None): self.gpx = xmlgen.Factory().gpx( xmlns='http://www.topografix.com/GPX/1/1', version='1.1', creator='gpxgen (+http://www.drbeat.li/py/)' ) self.meta = self.gpx.metadata if self.dir: self.meta.desc('Waypoints of all images in %s' % self.dir) if bounds: self.meta.bounds( minlat=f2str(bounds[0]), minlon=f2str(bounds[1]), maxlat=f2str(bounds[2]), maxlon=f2str(bounds[3]) ) def generateWpt(self, file, lat, lon, tm, alt, cmt): fn = os.path.split(file) wpt = self.gpx.wpt(lat=f2str(lat), lon=f2str(lon)) wpt.name(fn[1]) if tm: wpt.time(time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(tm))) if alt: wpt.ele(str(alt)) if cmt: wpt.cmt(cmt) if fn[0].startswith(ALBUM_ROOT): wpt.link(href=ALBUM_URL + urllib.quote((fn[0][len(ALBUM_ROOT):] + '/' + fn[1]).encode(CHARSET_OUT))) def getGpsData(self, file): fp = os.popen('exiv2 -b -Pkv "%s"' % file, 'r') gpsdata = [ l.split(' ', 1) for l in fp.readlines() if isInteresting(l) ] gpsdict = dict( [ (f[0].strip().split('.')[-1], f[1].strip()) for f in gpsdata ] ) fp.close() try: lat = parseCoord(gpsdict['GPSLatitude'] + gpsdict['GPSLatitudeRef']) lon = parseCoord(gpsdict['GPSLongitude'] + gpsdict['GPSLongitudeRef']) except: return None try: alt = eval(gpsdict['GPSAltitude']) * (-1, 1)[gpsdict['GPSAltitudeRef'] == '0'] except: alt = None try: tm = time.strptime(gpsdict['DateTimeDigitized'], '%Y:%m:%d %H:%M:%S') tm = time.mktime(tm) except: tm = None try: cmt = gpsdict['UserComment'].replace('\0', '') except: cmt = None return (file.decode(CHARSET_IN), lat, lon, tm, alt, cmt) def genGpx(self): self.files.sort() gpsdata = [g for g in [self.getGpsData(f) for f in self.files] if g] if not gpsdata: return u'' bounds = [ min([g[1] for g in gpsdata]), min([g[2] for g in gpsdata]), max([g[1] for g in gpsdata]), max([g[2] for g in gpsdata]) ] self.generateGpx(bounds) for g in gpsdata: self.generateWpt(*g) return unicode(self.gpx).strip() def main(): import sys args = sys.argv[1:] gen = GpxGen() if not args: args = ['.'] for d in args: gen.addDir(d) print (u'''<?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="/lib/gpx2xhtml.xsl"?> ''' + gen.genGpx()).encode(CHARSET_OUT) if __name__ == '__main__': main()