Files
lua-scripts-w-api/contrib/kml_export.lua
Pascal Obry 76daa583b3 Move all translation into the main locale directory.
This is for consistency reason as the lib/dtutils/file.lua use this
directory. Also all translation into contrib where not usable as
not checked into this directory which was expected to be just lua.

All in all using locale directory will avoid clutter the main
directory with all possible languages and fixes the current scripts.
2017-04-10 16:56:59 +02:00

413 lines
15 KiB
Lua

--[[
This file is part of darktable,
Copyright 2016 by Tobias Jakobs.
Copyright 2016 by Erik Augustin.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
darktable KML export script
ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
* mkdir
* zip (only if you create KMZ files)
* convert (ImageMagick)
* xdg-open
* xdg-user-dir
WARNING
This script is only tested with Linux
USAGE
* require script "official/yield" from your main Lua file in the first line
* require this script from your main Lua file
* when choosing file format, pick JPEG or PNG as Google Earth doesn't support other formats
]]
local dt = require "darktable"
require "official/yield"
local gettext = dt.gettext
dt.configuration.check_version(...,{3,0,0},{4,0,0})
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("kml_export",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("kml_export", msgid)
end
-- Sort a table
local function spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
-- collect the keys
local keys = {}
for _key in pairs(_table) do keys[#keys + 1] = _key end
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
table.sort(keys, function(a,b) return order(_table, a, b) end)
else
table.sort(keys)
end
-- return the iterator function
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], _table[keys[i]]
end
end
end
local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
dt.print(string.format(_("Export Image %i/%i"), number, total))
end
local function checkIfBinExists(bin)
local handle = io.popen("which "..bin)
local result = handle:read()
local ret
handle:close()
if (not result) then
dt.print_error(bin.." not found")
ret = false
end
ret = true
return ret
end
-- Strip accents from a string
-- Copied from https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/
function string.stripAccents( str )
local tableAccents = {}
tableAccents["à"] = "a"
tableAccents["á"] = "a"
tableAccents["â"] = "a"
tableAccents["ã"] = "a"
tableAccents["ä"] = "a"
tableAccents["ç"] = "c"
tableAccents["è"] = "e"
tableAccents["é"] = "e"
tableAccents["ê"] = "e"
tableAccents["ë"] = "e"
tableAccents["ì"] = "i"
tableAccents["í"] = "i"
tableAccents["î"] = "i"
tableAccents["ï"] = "i"
tableAccents["ñ"] = "n"
tableAccents["ò"] = "o"
tableAccents["ó"] = "o"
tableAccents["ô"] = "o"
tableAccents["õ"] = "o"
tableAccents["ö"] = "o"
tableAccents["ù"] = "u"
tableAccents["ú"] = "u"
tableAccents["û"] = "u"
tableAccents["ü"] = "u"
tableAccents["ý"] = "y"
tableAccents["ÿ"] = "y"
tableAccents["À"] = "A"
tableAccents["Á"] = "A"
tableAccents["Â"] = "A"
tableAccents["Ã"] = "A"
tableAccents["Ä"] = "A"
tableAccents["Ç"] = "C"
tableAccents["È"] = "E"
tableAccents["É"] = "E"
tableAccents["Ê"] = "E"
tableAccents["Ë"] = "E"
tableAccents["Ì"] = "I"
tableAccents["Í"] = "I"
tableAccents["Î"] = "I"
tableAccents["Ï"] = "I"
tableAccents["Ñ"] = "N"
tableAccents["Ò"] = "O"
tableAccents["Ó"] = "O"
tableAccents["Ô"] = "O"
tableAccents["Õ"] = "O"
tableAccents["Ö"] = "O"
tableAccents["Ù"] = "U"
tableAccents["Ú"] = "U"
tableAccents["Û"] = "U"
tableAccents["Ü"] = "U"
tableAccents["Ý"] = "Y"
local normalizedString = ""
for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
if tableAccents[strChar] ~= nil then
normalizedString = normalizedString..tableAccents[strChar]
else
normalizedString = normalizedString..strChar
end
end
return normalizedString
end
-- Escape XML characters
-- Keep &amp; first, otherwise it will double escape other characters
-- https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents
function string.escapeXmlCharacters( str )
str = string.gsub(str,"&", "&amp;")
str = string.gsub(str,"\"", "&quot;")
str = string.gsub(str,"'", "&apos;")
str = string.gsub(str,"<", "&lt;")
str = string.gsub(str,">", "&gt;")
return str
end
local function create_kml_file(storage, image_table, extra_data)
if not checkIfBinExists("mkdir") then
return
end
if not checkIfBinExists("convert") then
return
end
if not checkIfBinExists("xdg-open") then
return
end
if not checkIfBinExists("xdg-user-dir") then
return
end
dt.print_error("Will try to export KML file now")
local imageFoldername
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true ) then
if not checkIfBinExists("zip") then
return
end
exportDirectory = dt.configuration.tmp_dir
imageFoldername = ""
else
exportDirectory = dt.preferences.read("kml_export","ExportDirectory","string")
-- Creates dir if not exsists
imageFoldername = "files/"
local mkdirCommand = "mkdir -p "..exportDirectory.."/"..imageFoldername
dt.control.execute(mkdirCommand)
end
-- Create the thumbnails
for image,exported_image in pairs(image_table) do
if ((image.longitude and image.latitude) and
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
) then
local path, filename, filetype = string.match(exported_image, "(.-)([^\\/]-%.?([^%.\\/]*))$")
filename = string.upper(string.gsub(filename,"%.%w*", ""))
-- convert -size 92x92 filename.jpg -resize 92x92 +profile "*" thumbnail.jpg
-- In this example, '-size 120x120' gives a hint to the JPEG decoder that the image is going to be downscaled to
-- 120x120, allowing it to run faster by avoiding returning full-resolution images to GraphicsMagick for the
-- subsequent resizing operation. The '-resize 120x120' specifies the desired dimensions of the output image. It
-- will be scaled so its largest dimension is 120 pixels. The '+profile "*"' removes any ICM, EXIF, IPTC, or other
-- profiles that might be present in the input and aren't needed in the thumbnail.
local convertToThumbCommand = "convert -size 96x96 "..exported_image.." -resize 92x92 -mattecolor \"#FFFFFF\" -frame 2x2 +profile \"*\" "..exportDirectory.."/"..imageFoldername.."thumb_"..filename..".jpg"
dt.control.execute(convertToThumbCommand)
else
-- Remove exported image if it has no GPS data
os.remove(exported_image)
end
local pattern = "[/]?([^/]+)$"
filmName = string.match(image.film.path, pattern)
-- Strip accents from the filename, because GoogleEarth can't open them
-- https://github.com/darktable-org/lua-scripts/issues/54
filmName = string.stripAccents(filmName)
end
exportKMLFilename = filmName..".kml"
exportKMZFilename = filmName..".kmz"
-- Create the KML file
local kml_file = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
kml_file = kml_file.."<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
kml_file = kml_file.."<Document>\n"
--image_table = dt.gui.selection();
kml_file = kml_file.."<name>"..filmName.."</name>\n"
kml_file = kml_file.." <description>Exported from darktable</description>\n"
for image,exported_image in pairs(image_table) do
-- Extract filename, e.g DSC9784.ARW -> DSC9784
filename = string.upper(string.gsub(image.filename,"%.%w*", ""))
-- Extract extension from exported image (user can choose JPG or PNG), e.g DSC9784.JPG -> .JPG
extension = string.match(exported_image,"%.%w*$")
if ((image.longitude and image.latitude) and
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
) then
kml_file = kml_file.." <Placemark>\n"
local image_title, image_description
if (image.title and image.title ~= "") then
image_title = string.escapeXmlCharacters(image.title)
else
image_title = image.filename
end
-- Characters should not be escaped in CDATA, but we are using HTML fragment, so we must escape them
image_description = string.escapeXmlCharacters(image.description)
kml_file = kml_file.." <name>"..image_title.."</name>\n"
kml_file = kml_file.." <description>"..image_description.."</description>\n"
kml_file = kml_file.." <Style>\n"
kml_file = kml_file.." <IconStyle>\n"
kml_file = kml_file.." <Icon>\n"
kml_file = kml_file.." <href>"..imageFoldername.."thumb_"..filename..".jpg</href>\n"
kml_file = kml_file.." </Icon>\n"
kml_file = kml_file.." </IconStyle>\n"
kml_file = kml_file.." <BalloonStyle>\n"
kml_file = kml_file.." <text><![CDATA[<p><b>"..image_title.."</b></p><img src=\""..imageFoldername..filename..extension.."\"><p>"..image_description.."</p>]]></text>\n"
kml_file = kml_file.." <textColor>ff000000</textColor>\n"
kml_file = kml_file.." <displayMode>default</displayMode>\n"
kml_file = kml_file.." </BalloonStyle>\n"
kml_file = kml_file.." </Style>\n"
kml_file = kml_file.." <Point>\n"
kml_file = kml_file.." <extrude>1</extrude>\n"
kml_file = kml_file.." <coordinates>"..string.gsub(tostring(image.longitude),",", ".")..","..string.gsub(tostring(image.latitude),",", ".")..",0</coordinates>\n"
kml_file = kml_file.." <TimeStamp>\n"
kml_file = kml_file.." <when>"..string.gsub(image.exif_datetime_taken," ", "T").."Z".."</when>\n"
kml_file = kml_file.." </TimeStamp>\n"
kml_file = kml_file.." </Point>\n"
kml_file = kml_file.." </Placemark>\n"
end
end
-- Connects all images with an path
if ( dt.preferences.read("kml_export","CreatePath","bool") == true ) then
kml_file = kml_file.." <Placemark>\n"
kml_file = kml_file.." <name>Path</name>\n" -- ToDo: I think a better name would be nice
--kml_file = kml_file.." <description></description>\n"
kml_file = kml_file.." <Style>\n"
kml_file = kml_file.." <LineStyle>\n"
kml_file = kml_file.." <color>ff0000ff</color>\n"
kml_file = kml_file.." <width>5</width>\n"
kml_file = kml_file.." </LineStyle>\n"
kml_file = kml_file.." </Style>\n"
kml_file = kml_file.." <LineString>\n"
kml_file = kml_file.." <coordinates>\n"
for image,exported_image in spairs(image_table, function(t,a,b) return t[b] < t[a] end) do
if ((image.longitude and image.latitude) and
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
) then
local altitude = 0;
if (image.elevation) then
altitude = image.elevation;
end
kml_file = kml_file.." "..string.gsub(tostring(image.longitude),",", ".")..","..string.gsub(tostring(image.latitude),",", ".")..",altitude\n"
end
end
kml_file = kml_file.." </coordinates>\n"
kml_file = kml_file.." </LineString>\n"
kml_file = kml_file.." </Placemark>\n"
end
kml_file = kml_file.."</Document>\n"
kml_file = kml_file.."</kml>"
local file = io.open(exportDirectory.."/"..exportKMLFilename, "w")
file:write(kml_file)
file:close()
dt.print("KML file created in "..exportDirectory)
-- Compress the files to create a KMZ file
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true ) then
exportDirectory = dt.preferences.read("kml_export","ExportDirectory","string")
local createKMZCommand = "zip --test --move --junk-paths "
createKMZCommand = createKMZCommand .."\""..exportDirectory.."/"..exportKMZFilename.."\" " -- KMZ filename
createKMZCommand = createKMZCommand .."\""..dt.configuration.tmp_dir.."/"..exportKMLFilename.."\" " -- KML file
for image,exported_image in pairs(image_table) do
if ((image.longitude and image.latitude) and
(image.longitude ~= 0 and image.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
) then
local filename = string.upper(string.gsub(image.filename,"%.%w*", ""))
createKMZCommand = createKMZCommand .."\""..dt.configuration.tmp_dir.."/"..imageFoldername.."thumb_"..filename..".jpg\" " -- thumbnails
createKMZCommand = createKMZCommand .."\""..exported_image.."\" " -- images
end
end
dt.control.execute(createKMZCommand)
end
-- Open the file with the standard programm
if ( dt.preferences.read("kml_export","OpenKmlFile","bool") == true ) then
local kmlFileOpenCommand
if ( dt.preferences.read("kml_export","CreateKMZ","bool") == true ) then
kmlFileOpenCommand = "xdg-open "..exportDirectory.."/\""..exportKMZFilename.."\""
else
kmlFileOpenCommand = "xdg-open "..exportDirectory.."/\""..exportKMLFilename.."\""
end
dt.control.execute(kmlFileOpenCommand)
end
end
-- Preferences
dt.preferences.register("kml_export",
"OpenKmlFile",
"bool",
_("KML export: Open KML/KMZ file after export"),
_("Opens the KML file after the export with the standard program for KML files"),
false )
local handle = io.popen("xdg-user-dir DESKTOP")
local result = handle:read()
handle:close()
dt.preferences.register("kml_export",
"ExportDirectory",
"directory",
_("KML export: Export directory"),
_("A directory that will be used to export the KML/KMZ files"),
result )
dt.preferences.register("kml_export",
"CreatePath",
"bool",
_("KML export: Connect images with path"),
_("connect all images with a path"),
false )
dt.preferences.register("kml_export",
"CreateKMZ",
"bool",
_("KML export: Create KMZ file"),
_("Compress all imeges to one KMZ file"),
true )
-- Register
dt.register_storage("kml_export", _("KML/KMZ Export"), nil, create_kml_file)