#!/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
# February 23, 2023 bar get rid of has_keys
# March 5, 2023 bar future print
# April 13, 2023 bar listize keys values items
# --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.)
#
#
from __future__ import print_function
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 = """"""
kmz_description = """]]>"""
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 = "
"
ts += p.time_description()
if ts :
ts += "
"
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 = []
not_done_files = list(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 (fn in ndf) :
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