#!/usr/bin/python

# waypoint_file_from_pictures.py
#       --copyright--                   Copyright 2007 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       July 21, 2007           bar
#       July 22, 2007           bar     kmz files
#       July 27, 2007           bar     better file name globbing
#                                       --tzone
#       July 28, 2007           bar     --points etc
#       August 18, 2007         bar     protect from spurious command line args
#       August 21, 2007         bar     point_...._description
#       October 17, 2007        bar     better help
#       November 18, 2007       bar     turn on doxygen
#       November 27, 2007       bar     insert boilerplate copyright
#       February 9, 2008        bar     hey, make --points gpx input file(s) optional
#       May 17, 2008            bar     email adr
#       November 15, 2008       bar     egad! I've been making default params as [] and {}
#       May 27, 2012            bar     doxygen namespace
#       May 9, 2013             bar     able to run with 64-bit Windows' pillow subbing for PIL
#       --eodstamps--
##      \file
#       \namespace              tzpython.waypoint_file_from_pictures
#
#       Create a .kml or .kmz or .gpx file from picture(s)' exif geocode information.
#       Optionally can copy a track over from a gpx file to include in the output.
#
#       Todo:
#
#           Put the exif information in the created files. Either copy it all or just do the lat/lon/alt stuff.
#             (Image should do this.)
#
#


import  os
import  re
import  time
import  zipfile

have_image_module       = False
try :
    from    PIL     import  Image
    have_image_module   = True
except ImportError :
    pass

import  tz_gps
import  tz_jpg
import  replace_file



IMAGE_WIDTH     = 400           # maximum image width
ICON_WIDTH      = 32            # maximum icon width



def _get_picture_when(jpg, fn, tzone) :
    mtime       = 0.0

    try :
        mtime   = jpg.picture_taken_time(dflt = mtime, time_func = time.gmtime)
        mtime  -= tzone
    except :
        mtime   = os.path.getmtime(fn)
    pass

    return(mtime)


def _file_name_to_html_text(fn) :
    """
        Convert a file name to descriptive text that'll go in the HTML.

        That is, strip trailing numerics and such-like from the display name.
    """

    fnns = os.path.basename(fn)
    fnns = re.sub(r"_", " ", fnns)
    fnns = fnns.strip()
    # print "_file", fn, fnns

    return(fnns)



def _get_a_point_from_file(fn, jpg = None, tzone = 0) :
    try :
        jpg     = jpg or tz_jpg.a_jpg(fn)

        exif    = jpg.get_exif()

        try :
            lat = exif.get_latitude()
        except :
            lat = None

        try :
            lon = exif.get_longitude()
        except :
            lon = None

        try :
            alt = exif.get_altitude()
        except :
            alt = None

        if  (lat != None) and (lon != None) :
            t   = _get_picture_when(jpg, fn, tzone)

            return(tz_gps.a_point(name = _file_name_to_html_text(fn), when = float(t), lat = lat, lon = lon, altitude = alt))

        pass
    except :
        # print "except point", fn
        # e       = sys.exc_info()
        # print   e[0], e[1], e[2]
        pass


    return(None)



kml_description = """<![CDATA[%s]]>"""
kmz_description = """<![CDATA[%s<P><IMG SRC="images/%s" WIDTH="%u">]]>"""




def make_waypoint_file_string(picture_file_names, ext = ".kml", not_done_files = None, file_name = "", tzone = 0, track = None) :
    """
        Make a .kml or .gpx waypoint file string from the given .jpg files.
    """

    track   = track or []

    if  not_done_files == None :
        not_done_files  = []

    if  (ext != ".kml") and (ext != ".kmz") and (ext != ".gpx") :
        raise "Can only output .kml or .kmz or .gpx files!"

    points  = []
    for fn in picture_file_names :
        p   = _get_a_point_from_file(fn, tzone = tzone)
        if  not p :
            not_done_files.append(fn)
        else :
            p.image_file_name   = fn
            points.append(p)
        pass


    def _cmp_when(pa, pb) :
        return(cmp(pa.when, pb.when))

    points.sort(_cmp_when)


    if  ext == ".gpx" :
        s   = tz_gps.gpx_header("waypoint_file_from_pictures.py", file_name)

        sa      = []
        for p in points :
            ps  = p.to_gpx(is_waypoint = True)
            sa.append(ps)

        if  track :
            sa.append(tz_gps.gpx_route_header())
            for p in track :
                ps  = p.to_gpx()
                sa.append(ps)
            sa.append(tz_gps.gpx_route_trailer())

        sa.append(tz_gps.gpx_trailer())
        s  += "".join(sa)

    else :
        s   = tz_gps.kml_header("waypoint_file_from_pictures.py", file_name)

        sa  = []
        for p in points :

            ts  = "<BR>"
            ts += p.time_description()
            if  ts :
                ts     += "<BR>"
            ts += p.where_description()

            icon_image_url  = None
            if  ext == ".kmz" :
                width   = IMAGE_WIDTH
                if  have_image_module :
                    ii      = Image.open(p.image_file_name)
                    (w, h)  = ii.size
                    width   = min(width, w)
                    del(ii)

                    ifn = os.path.splitext(p.image_file_name)[0] + "_icon.jpg"
                    icon_image_url  = "images/" + _file_name_to_html_text(ifn)

                ds  = kmz_description % ( p.name + ts, p.name, width )
                pass
            else :
                ds  = kml_description % ( p.name + ts )

            ps  = p.to_kml(is_waypoint = True, icon_image_url = icon_image_url, description = ds)
            sa.append(ps)

        if  track :
            sa.append(tz_gps.kml_timed_route(track))

        sa.append(tz_gps.kml_trailer())
        s  += "".join(sa)

    return(s)



def make_waypoint_file(ofile_name, picture_file_names, ext = None, not_done_files = None, tzone = 0, track = None) :
    """
        Make a .kml or .kmz or .gpx waypoint file from the given .jpg files.
    """

    track   = track or []

    if  not_done_files == None :
        not_done_files  = []

    if  not ext :
        ext = os.path.splitext(ofile_name)[1].lower()

    s       = make_waypoint_file_string(picture_file_names, ext, not_done_files, file_name = ofile_name, tzone = tzone, track = track)

    tfname  = ofile_name + ".tmp"

    if  ext == ".kmz" :
        zf  = zipfile.ZipFile(tfname, "w", zipfile.ZIP_DEFLATED)
        zf.writestr("doc.kml", s)
        ndf = tzlib.make_dictionary(not_done_files)
        for fn in picture_file_names :
            if  not ndf.has_key(fn) :
                iext    = os.path.splitext(fn)[1]
                if  iext == ".bmp" :
                    cm  = zipfile.ZIP_DEFLATED
                else :
                    cm  = zipfile.ZIP_STORED

                need_w  = 3
                if  have_image_module :
                    ii  = Image.open(fn)
                    ii  = ii.convert("RGBA")
                    iw  = ICON_WIDTH
                    (w, h)  = ii.size
                    if  w > iw :
                        ii  = ii.resize( ( iw, int(h * (float(iw) / w)) ), Image.ANTIALIAS)
                        ifn = os.path.splitext(fn)[0] + "_icon.jpg"
                        ii.save(ifn)
                        zf.write(ifn, "images/%s" % ( _file_name_to_html_text(ifn) ), zipfile.ZIP_STORED )
                        os.unlink(ifn)
                        need_w &= ~2

                        ii  = Image.open(fn)
                        iw  = IMAGE_WIDTH
                        (w, h)  = ii.size
                        if  w > iw :
                            ii  = ii.convert("RGBA")
                            ii  = ii.resize( ( iw, int(h * (float(iw) / w)) ), Image.ANTIALIAS)

                            ifn = os.path.splitext(fn)[0] + "_wpfp.jpg"
                            ii.save(ifn)

                            zf.write(ifn, "images/%s" % ( _file_name_to_html_text(fn) ), zipfile.ZIP_STORED)
                            os.unlink(ifn)
                            need_w &= ~1
                        pass

                    del(ii)

                if  need_w & 1:
                    zf.write(fn, "images/%s" % ( _file_name_to_html_text(fn) ), cm)
                if  need_w & 2 :
                    (n, e)  = os.path.splitext(_file_name_to_html_text(fn))
                    zf.write(fn, "images/%s" % ( n + "_icon" + e ), zipfile.ZIP_STORED)
                pass
            pass
        zf.close()
    else :
        fo  = open(tfname, "w")
        fo.write(s)
        fo.close()

    replace_file.replace_file(ofile_name, tfname, ofile_name + ".bak")


helpstr = """
Tell me an output .kml, .kmz, or .gpx file name and any number of ambiguous input picture file names.

Options:

    --time_offset   (-)hh:mm:ss     Set the offset from GMT the camera's clock is set to.
    --points ampbiguous_file_name   Read GPS points from given file(s).

"""

if  __name__ == '__main__' :

    import  glob
    import  sys


    import  TZCommandLineAtFile
    import  tzlib
    import  tz_parse_time


    del(sys.argv[0])
    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)


    tzone   = 0
    ppoints = []


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--help", "-h"] )
        if  oi < 0 :    break
        print helpstr
        sys.exit(254)


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--tzone", "--time_offset", "-t"] )
        if  oi < 0 :    break
        del sys.argv[oi]
        tzone   = tz_parse_time.parse_time_zone(sys.argv.pop(oi))
        if  tzone == None :
            print "I cannot understand the timezone/offset!"
            sys.exit(102)
        pass


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--path", "--points", "-p", "--locations", "-l"] )
        if  oi < 0 :    break
        del sys.argv[oi]
        afn     = sys.argv.pop(oi)
        pfn     = glob.glob(afn)

        if  not pfn :
            print "No files found from", afn
            sys.exit(107)

        for fn in pfn :
            pts         = tz_gps.points_from_all_tracks(tz_gps.tracks_from_gpx_file(fn))
            if  not pts :
                print "Cannot find points in", fn
                sys.exit(104)
            ppoints    += pts
        pass

    if  len(sys.argv) < 2 :
        print helpstr
        sys.exit(105)

    if  False :
        if  not ppoints :
            print "I found no GPS points given in any GPS location file!"
            sys.exit(108)
        pass


    ofile_name  = sys.argv.pop(0)

    if  ofile_name.startswith("-") :
        print "Please tell me an output file name not starting with a dash!"
        sys.exit(106)

    ext         = os.path.splitext(ofile_name)[1].lower()
    if  (ext != ".kml") and (ext != ".kmz") and (ext != ".gpx") :
        raise "Can only output .kml .kmz .gpx files, not %s files (%s)!" % ( ext, ofile_name )

    fnames  = {}
    while len(sys.argv) :
        fn  = sys.argv.pop(0)

        fns = tzlib.make_dictionary(glob.glob(fn))
        if  not fns :
            print "Cannot find file(s):", fn
            sys.exit(103)
        fnames.update(fns)

    not_done_files  = []
    make_waypoint_file(ofile_name, fnames.keys(), ext, not_done_files, tzone = tzone, track = ppoints)

    if  not_done_files :
        print "Not done:"
        for fn in not_done_files :
            print fn
        pass

    pass

#
#
#
# eof
