-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgeotag_io.lua
194 lines (157 loc) · 7.06 KB
/
geotag_io.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
dt = require "darktable"
table = require "table"
local _debug = false
local _geotag_io_dry_run = false
local exiftool_path = "/usr/local/bin/exiftool"
local function debug_print(message)
if _debug then
print(message)
end
end
local function comma_to_dot(number)
return tostring(number):gsub(",", ".")
end
local nil_geo_tag = dt.tags.create("darktable|geo|nil")
local function getImagePath(i) return "'"..i.path.."/"..i.filename.."'" end
local function read_geotags(image)
local tags = {}
local exifReadProcess = io.popen(exiftool_path.." -n "..getImagePath(image))
local exifLine = exifReadProcess:read()
while exifLine do
if (exifLine ~= '') then
local gpsTag, gpsValue = string.match(exifLine, "(GPS [%a ]-)%s+: (.-)$")
if (gpsTag ~= nil) then
tags[gpsTag] = gpsValue
end
end
exifLine = exifReadProcess:read()
end
exifReadProcess:close()
return tags
end
local function _write_geotag()
local images_to_write = {}
local image_table = dt.gui.selection()
local precheck_fraction = 0.2
local image_table_count = 0
local tagged_files_skipped = 0
save_job = dt.gui.create_job ("Saving exif geotags", true)
for _,image in pairs(image_table) do
--Will silently skip if coordinates are nil or 0.0
if (image.longitude and image.latitude) then
dt.tags.detach(nil_geo_tag,image)
local includeImage = true
if (not dt.preferences.read("geotag_io","OverwriteGeotag","bool")) then
local tags = read_geotags(image)
--Skip image if it has ANY GPS tag, not just location
if next(tags) ~= nil then
includeImage = false
end
if (not includeImage) then
tagged_files_skipped = tagged_files_skipped + 1
end
end
if includeImage then
table.insert(images_to_write,image)
image_table_count = image_table_count + 1
end
end
end
save_job.percent = precheck_fraction
local image_done_count = 0
for _,image in pairs(images_to_write) do
local writeExifCommand = exiftool_path
if (dt.preferences.read("geotag_io","DeleteOriginal","bool")) then
writeExifCommand = writeExifCommand.." -overwrite_original"
end
if (dt.preferences.read("geotag_io","KeepFileDate","bool")) then
writeExifCommand = writeExifCommand.." -preserve"
end
local imagePath = getImagePath(image)
local testIsFileSuccess = os.execute("test -f "..imagePath)
assert(testIsFileSuccess == true)
writeExifCommand = writeExifCommand.." -exif:GPSLatitude="..comma_to_dot(image.latitude).." -exif:GPSLatitudeRef="..comma_to_dot(image.latitude).." -exif:GPSLongitude="..comma_to_dot(image.longitude).." -exif:GPSLongitudeRef="..comma_to_dot(image.longitude).." -exif:GPSAltitude= -exif:GPSAltitudeRef= -exif:GPSHPositioningError= "..imagePath
if _geotag_io_dry_run == true then
debug_print (writeExifCommand)
else
local writeExifSuccess = os.execute(writeExifCommand)
assert(writeExifSuccess == true)
end
image_done_count = image_done_count + 1
save_job.percent = (image_done_count/image_table_count)*(1-precheck_fraction) + precheck_fraction
end
save_job.valid = false
if (tagged_files_skipped > 0) then
dt.print(tagged_files_skipped.." image(s) were skipped as they already had a EXIF geotag")
end
end
local function _reset_geotag()
local image_table = dt.gui.selection()
local processed_count = 0
local skipped_count = 0
for _,image in pairs(image_table) do
--read_geotags will fail silently (return empty table) if file was not found
local tags = read_geotags(image)
local lat = nil
local lon = nil
if next(tags) ~= nil then
if (tags['GPS Latitude'] ~= nil and tags['GPS Longitude'] ~= nil) then
lat = tonumber(tags['GPS Latitude'])
lon = tonumber(tags['GPS Longitude'])
elseif (tags['GPS Position'] ~= nil) then
lat, lon = string.match(tags['GPS Position'], "([%d%.]+) ([%d%.]+)")
end
end
if (lat ~= nil and lon ~= nil) then
image.latitude = lat
image.longitude = lon
processed_count = processed_count + 1
else
if (dt.preferences.read("geotag_io","ClearIfEmpty","bool")) then
image.latitude = nil
image.longitude = nil
--Mitigation for bug http://darktable.org/redmine/issues/10450
dt.tags.attach(nil_geo_tag,image)
end
skipped_count = skipped_count + 1
end
end
local skipped_verb = "skipped"
if (dt.preferences.read("geotag_io","ClearIfEmpty","bool")) then
skipped_verb = "cleared"
end
dt.print(processed_count.." image geotags reset ("..skipped_count.." "..skipped_verb..")")
end
-------- Error handling wrappers --------
function write_geotag_handler()
if (_debug) then
--Do a regular call, which will output complete error traceback to console
_write_geotag()
else
local main_success, main_error = pcall(_write_geotag)
if (not main_success) then
--Do two print calls, in case tostring conversion fails, user will still see a message
dt.print("An error prevented write geotag script from completing")
dt.print("An error prevented write geotag script from completing: "..tostring(main_error))
end
end
end
function reset_geotag_handler()
if (_debug) then
--Do a regular call, which will output complete error traceback to console
_reset_geotag()
else
local main_success, main_error = pcall(_reset_geotag)
if (not main_success) then
--Do two print calls, in case tostring conversion fails, user will still see a message
dt.print("An error prevented reset geotag script from completing")
dt.print("An error prevented reset geotag script from completing: "..tostring(main_error))
end
end
end
dt.preferences.register("geotag_io", "OverwriteGeotag", "bool", "Write geotag: allow overwriting existing file geotag", "Replace existing geotag in file. If unchecked, files with lat & lon data will be silently skipped.", false )
dt.preferences.register("geotag_io", "DeleteOriginal", "bool", "Write geotag: delete original image file", "Delete original image file after updating EXIF. When off, keep it in the same folder, appending _original to its name", false )
dt.preferences.register("geotag_io", "KeepFileDate", "bool", "Write geotag: carry over original image file's creation & modification date", "Sets same creation & modification date as original file when writing EXIF. When off, time and date will be that at time of writing new file, to reflect that it was altered. Camera EXIF date and time code are never altered, regardless of this setting.", true )
dt.preferences.register("geotag_io", "ClearIfEmpty", "bool", "Reset geotag: if file has no geotag, clear Darktable geotag when resetting.", "Clear Darktable geotag if file about to be reset has no geotag. When off, Darktable geotag will only be altered if geotag exists in file.", true )
dt.register_event("shortcut",write_geotag_handler, "Write geotag to image file")
dt.register_event("shortcut",reset_geotag_handler, "Reset geotag to value in file")