Skip to content

Instantly share code, notes, and snippets.

@fay59
Last active April 9, 2017 07:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fay59/ca0f77895338245f982b939660218bdc to your computer and use it in GitHub Desktop.
Save fay59/ca0f77895338245f982b939660218bdc to your computer and use it in GitHub Desktop.
Set GPS coordinates of media items in Photos using GPX file
(*
To use:
1- Launch Photos
2- Select photos for which you want to set the GPS location based on a .gpx file
3- Adjust date/time of photos (if necessary)
4- Launch script, select GPX file, wait
*)
set tzString to do shell script "date '+%z'"
set hoursOffset to (text 1 thru 3 of tzString) as number
set minutesOffset to (text 1 thru 1 of tzString) & (text 4 thru 5 of tzString) as number
set timeOffset to hoursOffset * 60 + minutesOffset
set decimalSeparator to text 2 thru 2 of (0.0 as text)
on iso8601(dateString)
set resultDate to the current date
set the year of resultDate to (text 1 thru 4 of dateString)
set the month of resultDate to (text 6 thru 7 of dateString)
set the day of resultDate to (text 9 thru 10 of dateString)
set the hours of resultDate to (text 12 thru 13 of dateString)
set the minutes of resultDate to (text 15 thru 16 of dateString)
set the seconds of resultDate to (text 18 thru 19 of dateString)
return resultDate + (my timeOffset) * minutes
end iso8601
on parseDecimal(decimalString)
set oldDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "."
set decimalResult to (text item 1 of decimalString & my decimalSeparator & text item 2 of decimalString) as real
set AppleScript's text item delimiters to oldDelimiters
return decimalResult
end parseDecimal
on binarySearch(xmlFilePath, photoTime)
tell application "System Events" to tell XML element "trk" of XML element "gpx" of XML file xmlFilePath
repeat with seg in (every XML element whose name = "trkseg")
set lastRecordedTime to my iso8601((value of XML element "time" of last XML element of seg) as string)
if photoTime < lastRecordedTime then
set trks to every XML element of seg
set startIndex to 1
set endIndex to count of trks
repeat until startIndex = endIndex
set midpoint to startIndex + (endIndex - startIndex) div 2
set midpointTime to my iso8601(value of XML element "time" of item midpoint of trks)
if photoTime ≤ midpointTime then
set endIndex to midpoint
else
set startIndex to midpoint + 1
end if
end repeat
exit repeat
end if
end repeat
tell XML element startIndex of seg
set latitude to my parseDecimal(value of XML attribute "lat" as text)
set longitude to my parseDecimal(value of XML attribute "lon" as text)
set altitude to my parseDecimal(value of XML element "ele" as text)
return {latitude, longitude, altitude}
end tell
end tell
end binarySearch
tell application "Photos"
set photos to (get the selection)
set gpxFile to (choose file with prompt "Select a GPX file" of type {"com.apple.dt.document.gpx"}) as string
set AppleScript's progress description to "Geotagging photos"
set AppleScript's progress total steps to count of photos
set AppleScript's progress completed steps to 0
repeat with photo in photos
set thisItem to my binarySearch(gpxFile, date of photo)
-- No interpolation; assumes that you haven't moved in a while if the time isn't exact.
set location of photo to items 1 thru 2 of thisItem
-- Apparently, the Photos script defs lie and media items don't have an 'altitude' property.
--set altitude of photo to item 3 of thisItem
set AppleScript's progress completed steps to (AppleScript's progress completed steps) + 1
end repeat
end tell
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment