#!/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()