58 lines
2.4 KiB
Python
58 lines
2.4 KiB
Python
|
import argparse
|
||
|
from pathlib import Path
|
||
|
|
||
|
import gpxpy
|
||
|
import piexif
|
||
|
import pytz
|
||
|
from PIL import Image
|
||
|
|
||
|
from lib.geotags import generate_geotags
|
||
|
from lib.gpx import get_closest_point
|
||
|
from lib.image import get_image_timestamp
|
||
|
from lib.time import get_local_timezone
|
||
|
|
||
|
|
||
|
def main(args):
|
||
|
with open(args.gpx, 'r') as gpxfile:
|
||
|
gpx = gpxpy.parse(gpxfile)
|
||
|
|
||
|
for file in args.photos.glob('*'):
|
||
|
if file.name.lower().endswith(('.jpg', '.jpeg')):
|
||
|
img = Image.open(file)
|
||
|
timestamp = get_image_timestamp(img, args.timezone)
|
||
|
del img
|
||
|
if not timestamp:
|
||
|
print(f'FAIL - "{file.name}" does not have a timestamp.')
|
||
|
continue
|
||
|
point = get_closest_point(gpx, timestamp, args.max_diff)
|
||
|
if not point:
|
||
|
point_forced = get_closest_point(gpx, timestamp, 99999999999999999999999999)
|
||
|
print(f'FAIL - Could not match image "{file.name}" @ {timestamp}. Closest point: {point_forced.latitude},{point_forced.longitude} @ {point_forced.time.astimezone(pytz.timezone(args.timezone))}')
|
||
|
continue
|
||
|
else:
|
||
|
original_exif = piexif.load(str(file))
|
||
|
geotags = generate_geotags(point.latitude, point.longitude, point.elevation, point.time, original_exif)
|
||
|
exif_bytes = piexif.dump(geotags)
|
||
|
piexif.insert(exif_bytes, str(file))
|
||
|
print('OKAY - ', file.name)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
parser = argparse.ArgumentParser(description='Add location tags to photos based on a GPX track.')
|
||
|
parser.add_argument('photos', help='Path to the directory containing the photos')
|
||
|
parser.add_argument('gpx', help='Path to the GPX file')
|
||
|
parser.add_argument('--max-diff', type=int, default=300, help='Maximum allowed difference in seconds between image timestamp and track point. Default: 300')
|
||
|
parser.add_argument('--timezone', default=get_local_timezone(), help='Three letter timezone the photos are in (e.g. EST). Default: auto-detect your local timezone')
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
args.photos = Path(args.photos).expanduser().absolute().resolve()
|
||
|
if args.photos.is_file() or not args.photos.exists():
|
||
|
print('Invalid path to photos. Must be a directory.')
|
||
|
quit(1)
|
||
|
|
||
|
args.gpx = Path(args.gpx).expanduser().absolute().resolve()
|
||
|
if args.gpx.is_dir() or not args.gpx.exists():
|
||
|
print('Invalid path to GPX file.')
|
||
|
quit(1)
|
||
|
main(args)
|