Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Display in share sheet the map where a photo has been taken
-
A little script to show how to extract exifs from a photo, get its GPS tags and display an Apple map in the sheet view, with a pin where the photo has been taken.
Select a photo in a mail (long press on the photo) or in the photos app (or anywhere else) and run the script in the Pythonista share sheet.
The script only runs in Python 2 because appex.get_image crashes in Python 3 (bug for @omz).
In appex mode, dezooming too much crashes, perhaps due to a memory problem.
The MKMapView part was initially copied from OMZ MapView demo
then "cleaned" to keep the easiest code as possible#!python2 # coding: utf-8 # # Needs Python 2 because appex.get_image crashes in Python 3 # # MKMapView part initially copied from OMZ MapView demo # https://gist.github.com/omz/451a6685fddcf8ccdfc5 # then "cleaned" to keep the easiest code as possible # # In appex mode, dezooming too much crashes, perhaps memory problem? import console import clipboard from objc_util import * import ctypes import ui from PIL import Image from PIL.ExifTags import TAGS,GPSTAGS import appex #import photos # used to test in non-appex mode class CLLocationCoordinate2D (Structure): _fields_ = [('latitude', c_double), ('longitude', c_double)] class MKCoordinateSpan (Structure): _fields_ = [('d_lat', c_double), ('d_lon', c_double)] class MKCoordinateRegion (Structure): _fields_ = [('center', CLLocationCoordinate2D), ('span', MKCoordinateSpan)] def get_gps(image): gps_latitude = None gps_latitude_ref = None gps_longitude = None gps_longitude_ref = None exif_info = image._getexif() # Extract exifs from photo if exif_info: for tag, value in exif_info.items(): # Loop on exifs of photo decoded = TAGS.get(tag, tag) # Exif code if decoded == "GPSInfo": # Exif is GPSInfo for t in value: # Loop on sub-exifs sub_decoded = GPSTAGS.get(t, t) # Sub-exif code if sub_decoded == 'GPSLatitude': gps_latitude = value[t] elif sub_decoded == 'GPSLatitudeRef': gps_latitude_ref = value[t] elif sub_decoded == 'GPSLongitude': gps_longitude = value[t] elif sub_decoded == 'GPSLongitudeRef': gps_longitude_ref = value[t] if gps_latitude and gps_latitude_ref and gps_longitude and gps_longitude_ref: lat = convert_to_degrees(gps_latitude) lat_lon_txt = str(lat)+gps_latitude_ref if gps_latitude_ref != "N": lat = 0 - lat lon = convert_to_degrees(gps_longitude) lat_lon_txt = lat_lon_txt+' - '+str(lon)+gps_longitude_ref if gps_longitude_ref != "E": lon = 0 - lon return lat, lon, lat_lon_txt return False,False,None def convert_to_degrees(value): # convert the GPS coordinates d m s to degrees in float d0 = value[0][0] d1 = value[0][1] d = float(d0) / float(d1) m0 = value[1][0] m1 = value[1][1] m = float(m0) / float(m1) s0 = value[2][0] s1 = value[2][1] s = float(s0) / float(s1) return d + (m / 60.0) + (s / 3600.0) class MapView(ui.View): @on_main_thread def __init__(self, *args, **kwargs): ui.View.__init__(self, *args, **kwargs) MKMapView = ObjCClass('MKMapView') frame = CGRect(CGPoint(0, 0), CGSize(self.width, self.height)) self.mk_map_view = MKMapView.alloc().initWithFrame_(frame) flex_width, flex_height = (1<<1), (1<<4) self.mk_map_view.setAutoresizingMask_(flex_width|flex_height) self_objc = ObjCInstance(self) self_objc.addSubview_(self.mk_map_view) self.mk_map_view.release() @on_main_thread def add_pin(self, lat, lon, title): '''Add a pin annotation to the map''' MKPointAnnotation = ObjCClass('MKPointAnnotation') coord = CLLocationCoordinate2D(lat, lon) annotation = MKPointAnnotation.alloc().init().autorelease() annotation.setTitle_(title) annotation.setCoordinate_(coord, restype=None, argtypes=[CLLocationCoordinate2D]) self.mk_map_view.addAnnotation_(annotation) @on_main_thread def set_region(self, lat, lon, d_lat, d_lon, animated=False): '''Set latitude/longitude of the view's center and the zoom level (specified implicitly as a latitude/longitude delta)''' region = MKCoordinateRegion(CLLocationCoordinate2D(lat, lon), MKCoordinateSpan(d_lat, d_lon)) self.mk_map_view.setRegion_animated_(region, animated, restype=None, argtypes=[MKCoordinateRegion, c_bool]) def will_close(self): appex.finish() def main(): #----- Main process ----- console.clear() if not appex.is_running_extension(): #img = photos.pick_image() console.hud_alert('Must run in appex mod','error') return else: img = appex.get_image(image_type='pil') if img == None: console.alert('No image passed','','Ok',hide_cancel_button=True) return lat,lon, lat_lon_txt = get_gps(img) if not lat or not lon: console.alert('No GPS tags in image','','Ok',hide_cancel_button=True) return # Hide script back = MapView(frame=(0, 0, 540, 620)) back.background_color='white' back.name = 'GPS = '+lat_lon_txt back.present('sheet', hide_title_bar=False) back.set_region(lat, lon, 0.05, 0.05, animated=True) back.add_pin(lat, lon,str((lat, lon))) # Protect against import if __name__ == '__main__': main()
-
@cvp , thanks. It works really well. I only tried it In Photos. Amazing how many of my pics don't have GPS's info. But I have never really worried about settings for this. I am doing this on my ipad, so the pics could have been stripped of the exilf info in any number of places before arriving on my ipad 😱
But the ones I found from around the world were almost exact with the pin. -
Perhaps, you have remarked that with a few modifications, the script could also work as "normal" mode (not appex):
- uncomment the import photos
- uncomment the img = photos.pick_image()
- comment the hud_alert and the return
And in this mode, you don't have any problem of dezooming