"""Add geotagged images to a kml for viewing in Google Earth. This script takes a folder of images, and creates a placemark with a thumbnail of each image, based on the GPS coordinates in the EXIF tags. Usage: photo_to_kml [-h] IMAGE_FOLDER Positional arguments: IMAGE_FOLDER name of input folder Optional arguments: -h, --help show this help message and exit Examples: Create a kml for a folder of photos, named 'image_folder'. > photo_to_kml image_folder """ import os import sys import argparse from PIL import Image from PIL.ExifTags import TAGS, GPSTAGS import simplekml def get_exif_data(image): """ Return a dictionary from the exif data of an PIL Image object, and convert the GPS uags. https://gist.github.com/valgur/2fbed04680864fab1bfc """ info = image._getexif() if not info: return {} exif_data = {TAGS.get(tag, tag): value for tag, value in info.items()} def is_fraction(val): return isinstance(val, tuple) and len(val) == 2 and isinstance( val[0], int) and isinstance(val[1], int) def frac_to_dec(frac): return float(frac[0]) / float(frac[1]) if 'GPSInfo' in exif_data: gpsinfo = { GPSTAGS.get(t, t): v for t, v in exif_data['GPSInfo'].items() } for tag, value in gpsinfo.items(): if is_fraction(value): gpsinfo[tag] = frac_to_dec(value) elif all(is_fraction(x) for x in value): gpsinfo[tag] = tuple(map(frac_to_dec, value)) exif_data['GPSInfo'] = gpsinfo return exif_data def get_lat_lon(exif_data): """ Return the latitude and longitude (if available) from exif_data. https://gist.github.com/valgur/2fbed04680864fab1bfc """ lat = None lon = None gps_info = exif_data.get('GPSInfo') def convert_to_degrees(value): d, m, s = value return d + (m / 60.0) + (s / 3600.0) if gps_info: gps_latitude = gps_info.get('GPSLatitude') gps_latitude_ref = gps_info.get('GPSLatitudeRef') gps_longitude = gps_info.get('GPSLongitude') gps_longitude_ref = gps_info.get('GPSLongitudeRef') if (gps_latitude and gps_latitude_ref and gps_longitude and gps_longitude_ref): lat = convert_to_degrees(gps_latitude) if gps_latitude_ref == 'S': lat = -lat lon = convert_to_degrees(gps_longitude) if gps_longitude_ref == 'W': lon = -lon return lat, lon def export_kml_file(dirname, fnames, dirname_thumbs, kml_name): """ Create the kml document """ kml = simplekml.Kml() for fname in fnames: print('Reading {}...'.format(fname)) with Image.open(os.path.join(dirname, fname)) as image: # Get EXIF tags exif_data = get_exif_data(image) # Save thumbnail image.thumbnail((128, 128)) image.save(os.path.join(dirname_thumbs, fname), 'jpeg') lat, lon = get_lat_lon(exif_data) pnt = kml.newpoint(name=fname) pnt.coords = [(lon, lat)] # Add popup window with full-size image pnt.description = ( ']]>'.format( os.path.join(dirname, fname))) # Show groups of photos as camera icon pnt.stylemap.normalstyle.iconstyle.scale = 1 pnt.stylemap.normalstyle.iconstyle.icon.href = ( 'http://maps.google.com/' 'mapfiles/kml/shapes/camera.png') # Show placemark as image thumbnail pnt.stylemap.highlightstyle.iconstyle.scale = 3 pnt.stylemap.highlightstyle.iconstyle.icon.href = os.path.join( dirname_thumbs, fname) kml.save(kml_name) def main(): parser = argparse.ArgumentParser() parser.add_argument( 'folder', metavar='IMAGE_FOLDER', help='name of input folder', default=None) # Print usage if no arguments are provided if len(sys.argv) == 1: parser.print_help(sys.stderr) sys.exit(1) args = parser.parse_args() # Get files in image directory ext = ['.jpg', '.jpeg', '.tif', '.tiff'] dirname = args.folder fnames = [ f for f in os.listdir(dirname) if os.path.splitext(f)[-1].lower() in ext ] # Create thumbnails folder beside originals dirname_thumbs = os.path.join(os.path.dirname(dirname), 'thumbs') try: os.mkdir(dirname_thumbs) except FileExistsError: pass # Create kml file kml_name = dirname + '.kml' export_kml_file(dirname, fnames, dirname_thumbs, kml_name) if __name__ == '__main__': main()