I have been using GPSPhotoLinker for years. I become bored recently with its dependance on old Perl libraries which were no longer shipped with OS X. Some workaround suggested on manufacturer’s forum helped to deal with problem for the next two OS X releases. Nevertheless, GPSPhotoLinker started to crash with Fujifilm X100T RAF files and with large Canon CR2s as well.
Internally GPSPhotoLinker uses Exiftool to complete geotagging tasks. So I decided to develop my own solution which would essentially run Exiftool console utility with carefully adjusted parameters.
The solution is made as OS X workflow and appears in Finder context menu. So it’s very easy to georeference your fresh or old images. You have not even to remember the app name 🙂
Basically, it is straightforward and self-explanatory, however, I’d comment on the workflow logic.
When started, script asks for a geotagging mode. You have two options, to sync files with a GPS track recorded with your phone of standalone GPS receiver, or enter coordinates manually.
The first option is as easy as selecting one or several track files (think of a few consecutive shooting days).
Then you are suggested an option to specify the time shift between GPS device and your camera. Still, it’s better to set the correct time before shooting.
The second option is to specify coordinates manually for photos taken when your device was off or received no signal (in a cave or canyon). The script will pick up your clipboard text item or use previously stored data after the first run.
Then in both cases you have an option to specify the following XMP geolocation data: country, state/province, city and sublocation. It’s up to you to ignore (leave the field empty) any value, so it will not be embedded in the images. Useful for cases when your photographs were made within the same country and region, but at different locations. Again, these data are stored for next runs.
Previous run data are stored in standard OS X Preference List file here:
~/Library/Preferences/com.pozhvanov.geotagger.plist
When received user data, script runs Exiftool on selected files, and then shows a notification with statistics. In addition, it checks for every file whether that one had geo-coordinates recorded and XMP location data embedded. If a given file contains both, it is marked with green label. If it has either coordinates or text location data only, it is marked with yellow or grey label, respectively. That allows you to easily identify files that require further attention after automated run.
You are free to download the Image Geotagger script in form of ZIP archive:
Geotag_images.workflow.zip
642 KB.
Unarchive it and save to:
~/Library/Services/
You can enable or disable Finder context menu item under > System Preferences > Keyboard > Shortcuts > Services. Happy geotagging!
Source code
For your safety, here is the full AppleScript source code for Image Geotagger workflow:
property preferences_file : "com.pozhvanov.geotagger.plist"
property preferences_path : "~/Library/Preferences/"
property exiftool_path : "/usr/local/bin/exiftool"
to SelectInputGPX(title, folder_location)
set InputGPX to choose file with prompt title default location folder_location with multiple selections allowed
return InputGPX as list
end SelectInputGPX
on replace_chars(this_text, search_string, replacement_string)
set AppleScript's text
item delimiters to the search_string
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the replacement_string
set this_text to the item_list as string
set AppleScript's text item delimiters to ""
return this_text
end replace_chars
to checkPreferences()
tell application "System Events"
if exists file preferences_file of (path to preferences from user domain) then
return 1
else
return 0
end if
end tell
end checkPreferences
on run {input, parameters}
if user locale of (get system info) starts with "ru" then
set lang to "ru"
set msg_coord_src to "Выберите источник координат:"
set msg_coord_src_gpx to "Файл GPX"
set msg_coord_src_man to "Ввести вручную"
set msg_select_gpx to "Выберите файл(ы) трека GPX/KML/XML/TCX:"
set msg_enter_coords to "Введите координаты через пробел (широта долгота высота).
Для ввода координат в западном или южном полушарии используйте знак -.
Значение высоты можно опустить.
Десятичный разделитель – точка.
Чтобы только геокодировать файлы, оставьте поле пустым."
set msg_enter_coords_title to "Ввод координат вручную"
set msg_button_next to "Далее"
set msg_button_cancel to "Отмена"
set msg_gpx_offset to "Укажите сдвиг времени трека относительно съёмки, если необходимо (HH:MM:SS)"
set msg_gpx_offset_title to "Сдвиг по времени"
set msg_enter_country to "Введите название страны:"
set msg_enter_country_title to "Страна"
set msg_enter_state to "Введите название региона или штата:"
set msg_enter_state_title to "Регион/штат/провинция"
set msg_enter_city to "Введите название города:"
set msg_enter_city_title to "Город"
set msg_enter_sublocation to "Введите название локации:"
set msg_enter_sublocation_title to "Локация"
set msg_result_1 to "Запись геоданных завершена. Обработано файлов: "
set msg_result_2 to " Сообщение exiftool: "
else
set lang to "en"
set msg_coord_src to "Select coordinates source:"
set msg_coord_src_gpx to "GPX file"
set msg_coord_src_man to "Enter manually"
set msg_select_gpx to "Select GPX/KML/XML/TCX track file(s):"
set msg_enter_coords to "Enter coordinates delimited by space (latitude longitude
altitude).
Use - sign to enter coordinates for western or southern hemispheres.
Altitude value could be omitted.
Decimal delimiter is dot.
To only geocode files, leave this field empty."
set msg_enter_coords_title to "Manual entry of coordinates"
set msg_button_next to "Next"
set msg_button_cancel to "Cancel"
set msg_gpx_offset to "Specify time difference of track to photos, if necessary
(HH:MM:SS)"
set msg_gpx_offset_title to "Time difference"
set msg_enter_country to "Enter the Country name:"
set msg_enter_country_title to "Country"
set msg_enter_state to "Enter the name of State or Region:"
set msg_enter_state_title to "State/Region/Province"
set msg_enter_city to "Enter the City name:"
set msg_enter_city_title to "City"
set msg_enter_sublocation to "Enter the Sublocation name:"
set msg_enter_sublocation_title to "Sublocation"
set msg_result_1 to "Geodata recording is completed. "
set msg_result_2 to "
files were processed.
Exiftool output: "
end if
set xml_gpx_path to POSIX path of (path to documents folder) as text
set xml_latitude to ""
set xml_longitude to ""
set xml_altitude to ""
set xml_timeDifference to ""
set xml_country to ""
set xml_state to ""
set xml_city to ""
set xml_sublocation to ""
(* Read preferences from Preferences File *)
if checkPreferences() is equal to 1 then
set the geotag_plist to preferences_path & preferences_file
tell application "System Events"
tell property list file geotag_plist
tell contents
if exists property list item "gpx_path" then
set xml_gpx_path to value of property list item "gpx_path"
end if
if exists property list item "latitude" then
set xml_latitude to value of property list item "latitude"
end if
if exists property list item "longitude" then
set xml_longitude to value of property list item "longitude"
end if
if exists property list item "altitude" then
set xml_altitude to value of property list item "altitude"
end if
if exists property list item "timeDifference" then
set xml_timeDifference to value of property list item "timeDifference"
end if
if exists property list item "country" then
set xml_country to value of property list item "country"
end if
if exists property list item "state" then
set xml_state to value of property list item "state"
end if
if exists property list item "city" then
set xml_city to value of property list item "city"
end if
if exists property list item "sublocation" then
set xml_sublocation to value of property list item "sublocation"
end if
end tell
end tell
end tell
end if
set coord_src to display dialog msg_coord_src buttons {msg_coord_src_gpx, msg_coord_src_man} default button msg_coord_src_gpx
if (button returned of coord_src as string) is equal to msg_coord_src_gpx then
set gpx_file to SelectInputGPX(msg_select_gpx, xml_gpx_path)
(*
if ((time to GMT) / hours > 0) then
set gpx_offset to "-" & (round
((time to GMT) / hours)) & ":00:00"
else
set gpx_offset to "+" & (round
(get (characters 2 thru length of (round (time to GMT))) / hours) &
":00:00")
end if
*)
tell application "Finder"
set file1 to (item 1 of (gpx_file as list))
set folder1 to (container of file1) as string
end tell
set xml_gpx_path to (POSIX path of folder1) as text
-- display notification xml_gpx_path
set gpx_offset to xml_timeDifference
set gpx_offset to text returned of (display dialog msg_gpx_offset with title msg_gpx_offset_title default answer gpx_offset buttons {msg_button_next, msg_button_cancel} default button msg_button_next cancel button msg_button_cancel)
set xml_timeDifference to gpx_offset as text
else
if (length of xml_latitude < 1 and length of xml_longitude < 1) then
if (length of (get the clipboard as text)) > 30 then
set msg_clipboard to ((get characters 1 thru 30 of (get the clipboard as string)) as text)
else
set msg_clipboard to get the clipboard as text
end if
else
set msg_clipboard to xml_latitude & " " & xml_longitude & " " & xml_altitude
end if
set coord_manual to text returned of (display dialog msg_enter_coords with title msg_enter_coords_title default answer replace_chars(msg_clipboard, ",", "") buttons {msg_button_next, msg_button_cancel} default button msg_button_next cancel button msg_button_cancel)
end if
set select_Country to text returned of (display dialog msg_enter_country with title msg_enter_country_title default answer xml_country buttons {msg_button_next, msg_button_cancel} default button msg_button_next cancel button msg_button_cancel)
set select_State to text returned of (display dialog msg_enter_state with title msg_enter_state_title default answer xml_state buttons {msg_button_next, msg_button_cancel} default button msg_button_next cancel button msg_button_cancel)
set select_City to text returned of (display dialog msg_enter_city with title msg_enter_city_title default answer xml_city buttons {msg_button_next, msg_button_cancel} default button msg_button_next cancel button msg_button_cancel)
set select_Sublocation to text returned of (display dialog msg_enter_sublocation with title msg_enter_sublocation_title default answer xml_sublocation buttons {msg_button_next, msg_button_cancel} default button msg_button_next cancel button msg_button_cancel)
set xmp_Country to select_Country as text
set xmp_State to select_State as text
set xmp_City to select_City as text
set xmp_Sublocation to select_Sublocation as text
set xml_country to xmp_Country
set xml_state to xmp_State
set xml_city to xmp_City
set xml_sublocation to xmp_Sublocation
set CommandString to exiftool_path & " -overwrite_original "
--set CommandString to "exiftool -overwrite_original "
if (button returned of coord_src as string) is equal to msg_coord_src_gpx then
if length of gpx_offset as text is greater than 0 then
set CommandString to (CommandString & "-geosync=\"" & gpx_offset as text) & "\" "
end if
if (count of gpx_file) as list is greater than 1 then
repeat with i in gpx_file as list
set CommandString to CommandString & "-geotag \"" & POSIX path of (i as string) & "\" "
end repeat
else
set CommandString to CommandString & "-geotag \"" & POSIX path of (gpx_file as string) & "\" "
end if
else
set coord_manual to replace_chars(coord_manual as text, ", ", " ")
if length of coord_manual > 0 then
set AppleScript's text item delimiters to " "
set the coord_list to every text item of coord_manual
if ((count of coord_list) as list) is greater than 2 then
set CommandString to (((CommandString & "-exif:GPSLatitude=\"" & (item 1 of coord_list) as text) & "\" -exif:GPSLongitude=\"" & (item 2 of coord_list) as text) & "\" -exif:GPSAltitude=\"" & (item 3 of coord_list) as text) & "\" "
set xml_latitude to (item 1 of coord_list) as text
set xml_longitude to (item 2 of coord_list) as text
set xml_altitude to (item 3 of coord_list) as text
else
set CommandString to ((CommandString & "-exif:GPSLatitude=\"" & (item 1 of coord_list) as text) & "\" -exif:GPSLongitude=\"" & (item 2 of coord_list) as text) & "\" "
set xml_latitude to (item 1 of coord_list) as text
set xml_longitude to (item 2 of coord_list) as text
end if
end if
end if
if length of xmp_Country is greater than 0 then
set CommandString to CommandString & "-xmp:Country=\"" & xmp_Country & "\" "
end if
if length of xmp_State is greater than 0 then
set CommandString to CommandString & "-xmp:State=\"" & xmp_State & "\" "
end if
if length of xmp_City is greater than 0 then
set CommandString to CommandString & "-xmp:City=\"" & xmp_City & "\" "
end if
if length of xmp_Sublocation is greater than 0 then
set CommandString to CommandString & "-xmp:Sublocation=\"" & xmp_Sublocation & "\" "
end if
repeat with i in input as list
set CommandString to CommandString & "\"" & POSIX path of (i as text) & "\" "
end repeat
set ScriptResult to do shell script CommandString
(* Color label files depending on their geotagging completeness *)
repeat with i in input as list
set counter to 0
set exifstatus to (do shell script exiftool_path & " -exif:GPSLatitude -exif:GPSLongitude -xmp:Country -xmp:State -xmp:City -xmp:Sublocation \"" & POSIX path of (i as text) & "\"") as text
if (exifstatus contains "GPS Latitude") and (exifstatus contains "GPS Longitude") then
set counter to counter + 2
end if
if (exifstatus contains "Country") or (exifstatus contains "State") or (exifstatus contains "City") then
set counter to counter + 1
end if
if counter is equal to 3 then
--green label
tell application "Finder"
set thefile to (i as alias)
set label index of thefile to 6
end tell
else if counter is equal to 2 then
--yellow label
tell application "Finder"
set thefile to (i as alias)
set label index of thefile to 3
end tell
else if counter is equal to 1 then
--grey label
tell application "Finder"
set thefile to (i as alias)
set label index of thefile to 7
end tell
end if
end repeat
(* Save preferences in Preferences File *)
tell application "System Events"
set geotag_dictionary to make new property list item with properties {kind:record}
set geotag_plist to preferences_path & preferences_file
set this_plist to make new property list file with properties {contents:geotag_dictionary, name:geotag_plist}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"gpx_path", value:xml_gpx_path}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"latitude", value:xml_latitude}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"longitude", value:xml_longitude}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"altitude", value:xml_altitude}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"timeDifference", value:xml_timeDifference}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"country", value:xml_country}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"state", value:xml_state}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"city", value:xml_city}
make new property list item at end of property list items of contents of this_plist with properties {kind:string, name:"sublocation", value:xml_sublocation}
end tell
display notification ((msg_result_1 & ((count of input) as list) as text) & msg_result_2 & ScriptResult as text) with title "Geotagger" sound name "Submarine"
return ScriptResult
end run
Please be aware that exhibiting photos online with full metadata left may potentially cause problems with your privacy. To learn more: www.comparitech.com/blog/vpn-privacy/exif-metadata-privacy/