diff --git a/contrib/geoJSON_export.lua b/contrib/geoJSON_export.lua
new file mode 100644
index 0000000..30a2adf
--- /dev/null
+++ b/contrib/geoJSON_export.lua
@@ -0,0 +1,320 @@
+--[[
+ This file is part of darktable,
+ Copyright 2016 by Tobias Jakobs.
+
+ 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 .
+]]
+--[[
+darktable geoJSON export script
+
+ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
+* mkdir
+* convert (ImageMagick)
+* xdg-open
+* xdg-user-dir
+
+WARNING
+This script is only tested with Linux
+
+USAGE
+* require this script from your main Lua file
+
+]]
+
+local dt = require "darktable"
+local gettext = dt.gettext
+dt.configuration.check_version(...,{3,0,0})
+
+-- Tell gettext where to find the .mo file translating messages for a particular domain
+gettext.bindtextdomain("geoJSON_export",dt.configuration.config_dir.."/lua/")
+
+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
+
+local function create_geoJSON_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 geoJSON file now")
+
+ exportDirectory = dt.preferences.read("geoJSON_export","ExportDirectory","string")
+
+ -- Creates dir if not exsists
+ imageFoldername = "files/"
+ local mkdirCommand = "mkdir -p "..exportDirectory.."/"..imageFoldername
+ coroutine.yield("RUN_COMMAND", mkdirCommand)
+
+ -- Create the thumbnails
+ for _,image in pairs(image_table) do
+ if ((_.longitude and _.latitude) and
+ (_.longitude ~= 0 and _.latitude ~= 90) -- Sometimes the north-pole but most likely just wrong data
+ ) then
+ local path, filename, filetype = string.match(image, "(.-)([^\\/]-%.?([^%.\\/]*))$")
+ filename = string.upper(string.gsub(filename,"%.", "_"))
+
+ -- 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 concertToThumbCommand = "convert -size 96x96 "..image.." -resize 92x92 -mattecolor \"#FFFFFF\" -frame 2x2 +profile \"*\" "..exportDirectory.."/"..imageFoldername.."thumb_"..filename..".jpg"
+ -- USE coroutine.yield. It does not block the UI
+ coroutine.yield("RUN_COMMAND", concertToThumbCommand)
+ local concertCommand = "convert -size 438x438 "..image.." -resize 438x438 +profile \"*\" "..exportDirectory.."/"..imageFoldername..filename..".jpg"
+ coroutine.yield("RUN_COMMAND", concertCommand)
+ end
+
+ -- delete the original image to not get into the kmz file
+ os.remove(image)
+
+ local pattern = "[/]?([^/]+)$"
+ filmName = string.match(_.film.path, pattern)
+ end
+
+ exportgeoJSONFilename = filmName..".geoJSON"
+ exportMapBoxHTMLFilename = filmName..".html"
+
+ -- Create the geoJSON file
+ geoJSON_file = [[
+{
+ "type": "FeatureCollection",
+ "features": [
+]]
+
+ for image,_ in pairs(image_table) do
+ filename = string.upper(string.gsub(image.filename,"%.", "_"))
+
+ 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
+ geoJSON_file = geoJSON_file..
+[[ {
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+]]
+ geoJSON_file = geoJSON_file.." \"coordinates\": ["..string.gsub(tostring(image.longitude),",", ".")..","..string.gsub(tostring(image.latitude),",", ".").."]\n"
+ geoJSON_file = geoJSON_file..
+[[ },
+ "properties": {
+ "title": "]]..image.title..[[",
+ "description": "]]..image.description..[[",
+ "image": "]]..imageFoldername..filename..[[.jpg",
+ "icon": {
+ "iconUrl": "]]..imageFoldername.."thumb_"..filename..[[.jpg",
+ "iconSize": [96, 65],
+ "iconAnchor": [48, 32],
+ "popupAnchor": [0, -32],
+ "className": "dot"
+ }
+ }
+ }
+ ,]]
+ end
+ end
+
+ geoJSON_file = geoJSON_file:sub(0,geoJSON_file:len()-1)
+ geoJSON_file = geoJSON_file..
+[[
+]
+}
+]]
+
+
+ --HTML
+ if ( dt.preferences.read("geoJSON_export","CreateMapBoxHTMLFile","bool") == true ) then
+ local mapBoxKey = dt.preferences.read("geoJSON_export","mapBoxKey","string")
+ local mapBoxHTML_file = [[
+
+
+
+
+ 2014-05-31 Rieselfelder
+
+
+
+
+
+
+
+
+
+
+]]
+
+ file = io.open(exportDirectory.."/"..exportMapBoxHTMLFilename, "w")
+ file:write(mapBoxHTML_file)
+ file:close()
+ end
+
+ file = io.open(exportDirectory.."/"..exportgeoJSONFilename, "w")
+ file:write(geoJSON_file)
+ file:close()
+
+ dt.print("geoJSON file created in "..exportDirectory)
+
+-- Open the file with the standard programm
+ if ( dt.preferences.read("geoJSON_export","OpengeoJSONFile","bool") == true ) then
+ local geoJSONFileOpenCommand
+ geoJSONFileOpenCommand = "xdg-open "..exportDirectory.."/\""..exportgeoJSONFilename.."\""
+ coroutine.yield("RUN_COMMAND", geoJSONFileOpenCommand)
+ end
+
+
+end
+
+-- Preferences
+dt.preferences.register("geoJSON_export",
+ "CreateMapBoxHTMLFile",
+ "bool",
+ _("geoJSON export: Create an additional HTML file"),
+ _("Creates a HTML file, that loads the geoJASON file. (Needs a MapBox key"),
+ false )
+dt.preferences.register("geoJSON_export",
+ "mapBoxKey",
+ "string",
+ _("geoJSON export: MapBox Key"),
+ _("https://www.mapbox.com/studio/account/tokens"),
+ '' )
+dt.preferences.register("geoJSON_export",
+ "OpengeoJSONFile",
+ "bool",
+ _("geoJSON export: Open geoJSON file after export"),
+ _("Opens the geoJSON file after the export with the standard program for geoJSON files"),
+ false )
+
+local handle = io.popen("xdg-user-dir DESKTOP")
+local result = handle:read()
+handle:close()
+dt.preferences.register("geoJSON_export",
+ "ExportDirectory",
+ "directory",
+ _("geoJSON export: Export directory"),
+ _("A directory that will be used to export the geoJSON files"),
+ result )
+
+-- Register
+dt.register_storage("geoJSON_export", "geoJSON Export", nil, create_geoJSON_file)