Skip to content

Instantly share code, notes, and snippets.

@Bodobolero
Last active May 16, 2023 08:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bodobolero/a05319734d683d18f5cf22f0c027a818 to your computer and use it in GitHub Desktop.
Save Bodobolero/a05319734d683d18f5cf22f0c027a818 to your computer and use it in GitHub Desktop.
Pyhonista is an iOS App to develop python scripts. This is a pythonista script that: creates a map from the Exif location metadata in your foto library for a time range. The map is saved as .png file in your scripts folder. In addition a .gpx file is created with the track points from the EXIF locations.
import photos
import dialogs
import location
import canvas
import clipboard
from math import *
### If you like this script you can pay me a coffee here
### https://www.paypal.com/donate/?hosted_button_id=JBFJMV35LX5AY
# ask the user for start date and end date
def pickDateInterval():
startDate = dialogs.date_dialog(title='When did your trip start?',done_button_title='Select trip start date')
endDate = dialogs.date_dialog(title='When did you trip end?', done_button_title='Select trip end date')
print(f'startDate:{startDate} endDate:{endDate}')
return (startDate, endDate)
# go to the foto library and extract all images with location data in a given date range
def getAssetsWithLocationInDateInterval(startDate, endDate):
all_assets = photos.get_assets(media_type='image', include_hidden=False)
print("Number of all assets:")
print(len(all_assets))
location_assets = [asset for asset in all_assets if asset.location != None]
print("Number of assets with location:")
print(len(location_assets))
timed_assets = [asset for asset in location_assets if ( asset.creation_date.date() >= startDate and asset.creation_date.date() <= endDate) ]
print("Number of assets with location in date interval:")
print(len(timed_assets))
return timed_assets
# return an array of (lat,long) points for pictures' metadata (assets is a list of metadata)
def getCoordinates(assets):
return [(asset.location['latitude'],asset.location['longitude']) for asset in assets]
# return quadrupel min lat, max lat, min long, max long
def getBounds(coordinates):
latitudes = [x[0] for x in coordinates]
longitudes= [x[1] for x in coordinates]
return (min(latitudes), max(latitudes), min(longitudes), max(longitudes))
# distance in km
def get_distance(point1, point2):
R = 6370
lat1 = radians(point1[0]) #insert value
lon1 = radians(point1[1])
lat2 = radians(point2[0])
lon2 = radians(point2[1])
dlon = lon2 - lon1
dlat = lat2- lat1
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
distance = R * c
return distance
# width and height in meters
def computeWidthAndHeight(bounds):
height = get_distance([bounds[0],bounds[2]], [bounds[1], bounds[2]]) * 1000.0
width = get_distance([bounds[0], bounds[2]], [bounds[0], bounds[3]]) * 1000.0
return (width,height)
# compute x,y in canvas based on (latitude, longitude) of a point on the map
def translatePoint(img_width, img_height, width, height, lat_bottom_left, long_bottom_left, lat, long):
y_dist_in_m = get_distance([lat_bottom_left, long_bottom_left], [lat, long_bottom_left]) *1000.0
x_dist_in_m = get_distance([lat_bottom_left, long_bottom_left],[lat_bottom_left, long] ) *1000.0
print(f'tP x:{x_dist_in_m} y:{y_dist_in_m}')
maxdist= max(width,height)
if (width > height):
xcorr = 0.0
ycorr = (width-height)/2.0
else:
ycorr = 0.0
xcorr = (height - width)/2.0
return ((x_dist_in_m+xcorr) * img_width / maxdist, (y_dist_in_m+ycorr) * img_height / maxdist)
def createGPXFile(filename, title, assets):
xml = "<?xml version='1.0' encoding='UTF-8'?>"
header = '''\
<gpx version="1.1" creator="https://www.bodobolero.com" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<metadata>
<name>{title}</name>
<author>
<link href="https://www.bodobolero.com">
<text>Bodo Bolero</text>
<type>text/html</type>
</link>
</author>
</metadata>
<trk>
<name>{title}</name>
<trkseg>\
'''.format(title=title, ordinal='second')
pointlist = [xml,header]
for asset in assets:
latitude = asset.location['latitude']
longitude = asset.location['longitude']
altitude = asset.location['altitude']
if altitude == None:
altitude = 0.0
# time format: 2023-05-09T14:10:42.542Z
formattedtime = asset.creation_date.isoformat()
point = '''\
<trkpt lat="{lat}" lon="{lon}"><ele>{ele}</ele>
<time>{time}</time>
</trkpt>\
'''.format(lat=latitude, lon=longitude, time=formattedtime, ele=altitude)
pointlist.append(point)
footer='''\
</trkseg>
</trk>
</gpx>\
'''
pointlist.append(footer)
text = ''.join(pointlist)
with open(filename, "w+") as text_file:
text_file.write(text)
def main():
startDate, endDate = pickDateInterval()
assets = getAssetsWithLocationInDateInterval(startDate, endDate)
coordinates = getCoordinates(assets)
bounds = getBounds(coordinates)
print(bounds)
# compute center of image
latitude = (bounds[0]+bounds[1])/2
longitude = (bounds[2]+bounds[3])/2
# compute width and height of image
width, height = computeWidthAndHeight(bounds)
print(f'width: {width}m, height: {height}m')
maxdim = max(width,height)
img_width = 1024
img_height = 1024
print(f'Location: lat:{latitude} long: {longitude}')
print(f'Image size in pixels x: {img_width} y:{img_height}')
img = location.render_map_snapshot(latitude, longitude, map_type='hybrid', width=width, height=height, img_width=img_width, img_height=img_height, img_scale=1.0)
xw,yw = img.size
scale = img.scale
print(f'Image size x:{xw} y:{yw} scale:{scale}')
clipboard.set_image(img)
canvas.clear()
canvas.set_size(img_width,img_height)
canvas.draw_clipboard(0,0,img_width,img_height)
x,y = translatePoint(img_width, img_height, width, height, bounds[0], bounds[2], coordinates[0][0], coordinates[0][1])
print(f'first Point x:{x} y:{y}')
canvas.set_stroke_color(255,255,0)
canvas.move_to(x,y)
for i in range(len(coordinates)-1):
index = i+1
x,y = translatePoint(img_width, img_height, width, height, bounds[0], bounds[2], coordinates[index][0], coordinates[index][1])
print(f'{index} Point x:{x} y:{y}')
canvas.add_line(x,y)
canvas.close_path()
canvas.set_line_width(3)
canvas.draw_path()
filename = f'trip_from_{startDate}_to_{endDate}'
canvas.save_png(f'{filename}.png')
createGPXFile(f'{filename}.gpx', f'Trip from {startDate} to {endDate}', assets)
if __name__ == '__main__':
main()
@Bodobolero
Copy link
Author

If you like this script you can pay me a coffee here
https://www.paypal.com/donate/?hosted_button_id=JBFJMV35LX5AY

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment