Index: /trunk/build.xml
===================================================================
--- /trunk/build.xml	(revision 9879)
+++ /trunk/build.xml	(revision 9880)
@@ -619,5 +619,5 @@
     <target name="taginfo" depends="dist">
         <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpath="${groovy.jar};tools/commons-cli-1.3.1.jar"/>
-        <property name="taginfoextract" value="scripts/taginfoextract.groovy"/>
+        <property name="taginfoextract" value="scripts/TagInfoExtract.groovy"/>
         <property name="imgurlprefix" value="http://josm.openstreetmap.de/download/taginfo/taginfo-img"/>
         <_taginfo type="mappaint" output="taginfo_style.json"/>
@@ -629,5 +629,5 @@
         <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpath="${groovy.jar};tools/commons-cli-1.3.1.jar"/>
         <echo message="Checking editor imagery difference"/>
-        <groovy src="scripts/sync_editor_imagery_index.groovy" classpath="${dist.dir}/josm-custom.jar">
+        <groovy src="scripts/SyncEditorImageryIndex.groovy" classpath="${dist.dir}/josm-custom.jar">
             <arg value="-nomissingeii"/>
         </groovy>
Index: /trunk/scripts/SyncEditorImageryIndex.groovy
===================================================================
--- /trunk/scripts/SyncEditorImageryIndex.groovy	(revision 9880)
+++ /trunk/scripts/SyncEditorImageryIndex.groovy	(revision 9880)
@@ -0,0 +1,430 @@
+// License: GPL. For details, see LICENSE file.
+/**
+ * Compare and analyse the differences of the editor imagery index and the JOSM imagery list.
+ * The goal is to keep both lists in sync.
+ *
+ * The editor imagery index project (https://github.com/osmlab/editor-imagery-index)
+ * provides also a version in the JOSM format, but the JSON is the original source
+ * format, so we read that.
+ *
+ * How to run:
+ * -----------
+ *
+ * Main JOSM binary needs to be in classpath, e.g.
+ *
+ * $ groovy -cp ../dist/josm-custom.jar sync_editor-imagery-index.groovy
+ *
+ * Add option "-h" to show the available command line flags.
+ */
+import java.io.FileReader
+import java.util.List
+
+import javax.json.Json
+import javax.json.JsonArray
+import javax.json.JsonObject
+import javax.json.JsonReader
+
+import org.openstreetmap.josm.io.imagery.ImageryReader
+import org.openstreetmap.josm.data.imagery.ImageryInfo
+import org.openstreetmap.josm.tools.Utils
+
+class SyncEditorImageryIndex {
+
+    List<ImageryInfo> josmEntries;
+    JsonArray eiiEntries;
+
+    def eiiUrls = new HashMap<String, JsonObject>()
+    def josmUrls = new HashMap<String, ImageryInfo>()
+
+    static String eiiInputFile = 'imagery.json'
+    static String josmInputFile = 'maps.xml'
+    static FileWriter outputFile = null
+    static BufferedWriter outputStream = null
+    static int skipCount = 0;
+    static def skipEntries = [:]
+
+    static def options
+
+    /**
+     * Main method.
+     */
+    static main(def args) {
+        parse_command_line_arguments(args)
+        def script = new SyncEditorImageryIndex()
+        script.loadSkip()
+        script.start()
+        script.loadJosmEntries()
+        script.loadEIIEntries()
+        script.checkInOneButNotTheOther()
+        script.checkCommonEntries()
+        script.end()
+        if(outputStream != null) {
+            outputStream.close();
+        }
+        if(outputFile != null) {
+            outputFile.close();
+        }
+    }
+
+    /**
+     * Parse command line arguments.
+     */
+    static void parse_command_line_arguments(args) {
+        def cli = new CliBuilder(width: 160)
+        cli.o(longOpt:'output', args:1, argName: "output", "Output file, - prints to stdout (default: -)")
+        cli.e(longOpt:'eii_input', args:1, argName:"eii_input", "Input file for the editor imagery index (json). Default is $eiiInputFile (current directory).")
+        cli.j(longOpt:'josm_input', args:1, argName:"josm_input", "Input file for the JOSM imagery list (xml). Default is $josmInputFile (current directory).")
+        cli.s(longOpt:'shorten', "shorten the output, so it is easier to read in a console window")
+        cli.n(longOpt:'noskip', argName:"noskip", "don't skip known entries")
+        cli.x(longOpt:'xhtmlbody', argName:"xhtmlbody", "create XHTML body for display in a web page")
+        cli.X(longOpt:'xhtml', argName:"xhtml", "create XHTML for display in a web page")
+        cli.m(longOpt:'nomissingeii', argName:"nomissingeii", "don't show missing editor imagery index entries")
+        cli.h(longOpt:'help', "show this help")
+        options = cli.parse(args)
+
+        if (options.h) {
+            cli.usage()
+            System.exit(0)
+        }
+        if (options.eii_input) {
+            eiiInputFile = options.eii_input
+        }
+        if (options.josm_input) {
+            josmInputFile = options.josm_input
+        }
+        if (options.output && options.output != "-") {
+            outputFile = new FileWriter(options.output)
+            outputStream = new BufferedWriter(outputFile)
+        }
+    }
+
+    void loadSkip() {
+        /* TMS proxies for our wms */
+        skipEntries["-  Czech CUZK:KM tiles proxy - http://osm-{switch:a,b,c}.zby.cz/tiles_cuzk.php/{zoom}/{x}/{y}.png"] = 1
+        skipEntries["-  [CH] Stadt Zürich Luftbild 2011 - http://mapproxy.sosm.ch:8080/tiles/zh_luftbild2011/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
+        skipEntries["-  [CH] Übersichtsplan Zürich - http://mapproxy.sosm.ch:8080/tiles/zh_uebersichtsplan/EPSG900913/{zoom}/{x}/{y}.png?origin=nw"] = 1
+        skipEntries["-  [CH] Kanton Solothurn 25cm (SOGIS 2011-2014) - http://mapproxy.osm.ch:8080/tiles/sogis2014/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
+        /* URL style mismatch */
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://cyberjapandata.gsi.go.jp/xyz/ort/{z}/{x}/{y}.jpg"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://tms.cadastre.openstreetmap.fr/*/tout/{z}/{x}/{y}.png"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://sdi.provincia.bz.it/geoserver/gwc/service/tms/1.0.0/WMTS_TOPOMAP_APB-PAB@GoogleMapsCompatible@png8/{z}/{x}/{-y}.png"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.osm.ch:8080/tiles/AGIS2014/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.osm.ch:8080/tiles/sogis2014/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openmap.lt/ort10lt/g/{z}/{x}/{y}.jpeg"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openstreetmap.lu/tiles/ortho2010/EPSG900913/{z}/{x}/{y}.jpeg"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openstreetmap.lu/tiles/ortho2013/EPSG900913/{z}/{x}/{y}.jpeg"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.sosm.ch:8080/tiles/zh_luftbild2011/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
+        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openmap.lt/ort10lt/g/{z}/{x}/{y}.jpeg"] = 1
+
+        skipEntries["+++ EII-URL is not unique: http://geolittoral.application.equipement.gouv.fr/wms/metropole?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=ortholittorale&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  Streets NRW Geofabrik.de - http://tools.geofabrik.de/osmi/view/strassennrw/wxs?REQUEST=GetMap&SERVICE=wms&VERSION=1.1.1&FORMAT=image/png&SRS={proj}&STYLES=&LAYERS=unzugeordnete_strassen,kreisstrassen_ast,kreisstrassen,landesstrassen_ast,landesstrassen,bundesstrassen_ast,bundesstrassen,autobahnen_ast,autobahnen,endpunkte&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  Czech UHUL:ORTOFOTO - http://geoportal2.uhul.cz/cgi-bin/oprl.asp?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS={proj}&LAYERS=Ortofoto_cb&STYLES=default&FORMAT=image/jpeg&TRANSPARENT=TRUE&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  Czech ÚHUL:ORTOFOTO tiles proxy - http://osm-{switch:a,b,c}.zby.cz/tiles_uhul.php/{zoom}/{x}/{y}.jpg"] = 1
+        skipEntries["-  [CH] Kanton Solothurn 25cm (SOGIS 2011-2014) - http://www.sogis1.so.ch/cgi-bin/sogis/sogis_orthofoto.wms?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Orthofoto_SO&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  [CH] Kanton Solothurn Infrarot 12.5cm (SOGIS 2011) - http://www.sogis1.so.ch/cgi-bin/sogis/sogis_ortho.wms?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=Orthofoto11_CIR&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  [CH] Stadt Bern 10cm/25cm (2008) - http://map.bern.ch/arcgis/services/Orthofoto_2008/MapServer/WMSServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=0,1&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  [EE] Estonia Basemap (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=pohi_vr2&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  [EE] Estonia Forestry (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=cir_ngr&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  [EE] Estonia Hillshading (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=reljeef&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  [EE] Estonia Ortho (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?VERSION=1.1.1&REQUEST=GetMap&LAYERS=of10000&SRS={proj}&FORMAT=image/jpeg&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  Hamburg (DK5) - http://gateway.hamburg.de/OGCFassade/HH_WMS_Geobasisdaten.aspx?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=1&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["-  Hamburg (40 cm) - http://gateway.hamburg.de/OGCFassade/HH_WMS_DOP40.aspx?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
+        skipEntries["  name differs: http://wms.openstreetmap.fr/tms/1.0.0/tours_2013/{zoom}/{x}/{y}"] = 3
+        skipEntries["  name differs: http://wms.openstreetmap.fr/tms/1.0.0/tours/{zoom}/{x}/{y}"] = 3
+        skipEntries["  name differs: https://secure.erlangen.de/arcgiser/services/Luftbilder2011/MapServer/WmsServer?FORMAT=image/bmp&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Erlangen_ratio10_5cm_gk4.jp2&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 3
+        skipEntries["  name differs: http://wms.openstreetmap.fr/tms/1.0.0/iomhaiti/{zoom}/{x}/{y}"] = 3
+        skipEntries["  name differs: http://{switch:a,b,c}.layers.openstreetmap.fr/bano/{zoom}/{x}/{y}.png"] = 3
+        skipEntries["  name differs: http://ooc.openstreetmap.org/os1/{zoom}/{x}/{y}.jpg"] = 3
+        skipEntries["  name differs: http://www.gisnet.lv/cgi-bin/osm_latvia?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=piekraste&SRS={proj}&WIDTH={width}&height={height}&BBOX={bbox}"] = 3
+        skipEntries["  name differs: http://tms.cadastre.openstreetmap.fr/*/tout/{zoom}/{x}/{y}.png"] = 3
+        skipEntries["  name differs: http://{switch:a,b,c}.tiles.mapbox.com/v4/enf.e0b8291e/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJhNVlHd29ZIn0.ti6wATGDWOmCnCYen-Ip7Q"] = 3
+        skipEntries["  name differs: http://geo.nls.uk/mapdata2/os/25_inch/scotland_1/{zoom}/{x}/{y}.png"] = 3
+        skipEntries["  name differs: http://geo.nls.uk/mapdata3/os/6_inch_gb_1900/{zoom}/{x}/{y}.png"] = 3
+        skipEntries["  name differs: http://geoserver.infobex.hu/Budapest2014/IST/{zoom}/{x}/{y}.jpg"] = 3
+        skipEntries["  name differs: http://mapproxy.openmap.lt/ort10lt/g/{zoom}/{x}/{y}.jpeg"] = 3
+        skipEntries["  name differs: http://e.tile.openstreetmap.hu/ortofoto2000/{zoom}/{x}/{y}.jpg"] = 3
+        skipEntries["  maxzoom differs: [DE] Bavaria (2 m) - http://geodaten.bayern.de/ogc/ogc_dop200_oa.cgi?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=adv_dop200c&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries County - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=County&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries NPWS Reserve - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=NPWSReserve&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries Parish - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=Parish&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries Suburb - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=Suburb&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Imagery - http://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Imagery/MapServer/tile/{zoom}/{y}/{x}"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Topographic Map - http://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Topo_Map/MapServer/tile/{zoom}/{y}/{x}"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries State Forest - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=StateForest&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries LGA - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=LocalGovernmentArea&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
+        skipEntries["  minzoom differs: [AU] LPI NSW Base Map - http://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Base_Map/MapServer/tile/{zoom}/{y}/{x}"] = 3
+        skipEntries["  country code differs: [LT] ORT10LT (Lithuania) - http://mapproxy.openmap.lt/ort10lt/g/{zoom}/{x}/{y}.jpeg"] = 3
+    }
+
+    void myprintlnfinal(String s) {
+        if(outputStream != null) {
+            outputStream.write(s);
+            outputStream.newLine();
+        } else {
+            println s;
+        }
+    }
+
+    void myprintln(String s) {
+        if(skipEntries.containsKey(s)) {
+            skipCount = skipEntries.get(s)
+            skipEntries.remove(s)
+        }
+        if(skipCount) {
+            skipCount -= 1;
+            if(options.xhtmlbody || options.xhtml) {
+                s = "<pre style=\"margin:3px;color:green\">"+s.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")+"</pre>"
+            }
+            if (!options.noskip) {
+                return;
+            }
+        } else if(options.xhtmlbody || options.xhtml) {
+            String color = s.startsWith("***") ? "black" : (s.startsWith("+ ") ? "blue" : "red")
+            s = "<pre style=\"margin:3px;color:"+color+"\">"+s.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")+"</pre>"
+        }
+        myprintlnfinal(s)
+    }
+
+    void start() {
+        if (options.xhtml) {
+            myprintlnfinal "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+            myprintlnfinal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/><title>JOSM - EII differences</title></head><body>\n"
+        }
+    }
+
+    void end() {
+        for (def s: skipEntries.keySet()) {
+            myprintln "+++ Obsolete skip entry: " + s
+        }
+        if (options.xhtml) {
+            myprintlnfinal "</body></html>\n"
+        }
+    }
+
+    void loadEIIEntries() {
+        FileReader fr = new FileReader(eiiInputFile)
+        JsonReader jr = Json.createReader(fr)
+        eiiEntries = jr.readArray()
+        jr.close()
+
+        for (def e : eiiEntries) {
+            def url = getUrl(e)
+            if (url.contains("{z}")) {
+                myprintln "+++ EII-URL uses {z} instead of {zoom}: "+url
+                url = url.replace("{z}","{zoom}")
+            }
+            if (eiiUrls.containsKey(url)) {
+                myprintln "+++ EII-URL is not unique: "+url
+            } else {
+                eiiUrls.put(url, e)
+            }
+        }
+        myprintln "*** Loaded ${eiiEntries.size()} entries (EII). ***"
+    }
+
+    void loadJosmEntries() {
+        def reader = new ImageryReader(josmInputFile)
+        josmEntries = reader.parse()
+
+        for (def e : josmEntries) {
+            def url = getUrl(e)
+            if (url.contains("{z}")) {
+                myprintln "+++ JOSM-URL uses {z} instead of {zoom}: "+url
+                url = url.replace("{z}","{zoom}")
+            }
+            if (josmUrls.containsKey(url)) {
+                myprintln "+++ JOSM-URL is not unique: "+url
+            } else {
+              josmUrls.put(url, e)
+            }
+            for (def m : e.getMirrors()) {
+                url = getUrl(m)
+                if (josmUrls.containsKey(url)) {
+                    myprintln "+++ JOSM-Mirror-URL is not unique: "+url
+                } else {
+                  josmUrls.put(url, m)
+                }
+            }
+        }
+        myprintln "*** Loaded ${josmEntries.size()} entries (JOSM). ***"
+    }
+
+    List inOneButNotTheOther(Map m1, Map m2) {
+        def l = []
+        for (def url : m1.keySet()) {
+            if (!m2.containsKey(url)) {
+                def name = getName(m1.get(url))
+                l += "  "+getDescription(m1.get(url))
+            }
+        }
+        l.sort()
+    }
+
+    void checkInOneButNotTheOther() {
+        def l1 = inOneButNotTheOther(eiiUrls, josmUrls)
+        myprintln "*** URLs found in EII but not in JOSM (${l1.size()}): ***"
+        if (!l1.isEmpty()) {
+            for (def l : l1)
+                myprintln "-"+l
+        }
+
+        if (options.nomissingeii)
+            return
+        def l2 = inOneButNotTheOther(josmUrls, eiiUrls)
+        myprintln "*** URLs found in JOSM but not in EII (${l2.size()}): ***"
+        if (!l2.isEmpty()) {
+            for (def l : l2)
+                myprintln "+" + l
+        }
+    }
+
+    void checkCommonEntries() {
+        myprintln "*** Same URL, but different name: ***"
+        for (def url : eiiUrls.keySet()) {
+            def e = eiiUrls.get(url)
+            if (!josmUrls.containsKey(url)) continue
+            def j = josmUrls.get(url)
+            if (!getName(e).equals(getName(j))) {
+                myprintln "  name differs: $url"
+                myprintln "     (IEE):     ${getName(e)}"
+                myprintln "     (JOSM):    ${getName(j)}"
+            }
+        }
+
+        myprintln "*** Same URL, but different type: ***"
+        for (def url : eiiUrls.keySet()) {
+            def e = eiiUrls.get(url)
+            if (!josmUrls.containsKey(url)) continue
+            def j = josmUrls.get(url)
+            if (!getType(e).equals(getType(j))) {
+                myprintln "  type differs: ${getName(j)} - $url"
+                myprintln "     (IEE):     ${getType(e)}"
+                myprintln "     (JOSM):    ${getType(j)}"
+            }
+        }
+
+        myprintln "*** Same URL, but different zoom bounds: ***"
+        for (def url : eiiUrls.keySet()) {
+            def e = eiiUrls.get(url)
+            if (!josmUrls.containsKey(url)) continue
+            def j = josmUrls.get(url)
+
+            Integer eMinZoom = getMinZoom(e)
+            Integer jMinZoom = getMinZoom(j)
+            if (eMinZoom != jMinZoom  && !(eMinZoom == 0 && jMinZoom == null)) {
+                myprintln "  minzoom differs: ${getDescription(j)}"
+                myprintln "     (IEE):     ${eMinZoom}"
+                myprintln "     (JOSM):    ${jMinZoom}"
+            }
+            Integer eMaxZoom = getMaxZoom(e)
+            Integer jMaxZoom = getMaxZoom(j)
+            if (eMaxZoom != jMaxZoom) {
+                myprintln "  maxzoom differs: ${getDescription(j)}"
+                myprintln "     (IEE):     ${eMaxZoom}"
+                myprintln "     (JOSM):    ${jMaxZoom}"
+            }
+        }
+
+        myprintln "*** Same URL, but different country code: ***"
+        for (def url : eiiUrls.keySet()) {
+            def e = eiiUrls.get(url)
+            if (!josmUrls.containsKey(url)) continue
+            def j = josmUrls.get(url)
+            if (!getCountryCode(e).equals(getCountryCode(j))) {
+                myprintln "  country code differs: ${getDescription(j)}"
+                myprintln "     (IEE):     ${getCountryCode(e)}"
+                myprintln "     (JOSM):    ${getCountryCode(j)}"
+            }
+        }
+        myprintln "*** Same URL, but different quality: ***"
+        for (def url : eiiUrls.keySet()) {
+            def e = eiiUrls.get(url)
+            if (!josmUrls.containsKey(url)) {
+              def q = getQuality(e)
+              if("best".equals(q)) {
+                myprintln "  quality best entry not in JOSM for ${getDescription(e)}"
+              }
+              continue
+            }
+            def j = josmUrls.get(url)
+            if (!getQuality(e).equals(getQuality(j))) {
+                myprintln "  quality differs: ${getDescription(j)}"
+                myprintln "     (IEE):     ${getQuality(e)}"
+                myprintln "     (JOSM):    ${getQuality(j)}"
+            }
+        }
+    }
+
+    /**
+     * Utility functions that allow uniform access for both ImageryInfo and JsonObject.
+     */
+    static String getUrl(Object e) {
+        if (e instanceof ImageryInfo) return e.url
+        return e.getString("url")
+    }
+    static String getName(Object e) {
+        if (e instanceof ImageryInfo) return e.name
+        return e.getString("name")
+    }
+    static String getType(Object e) {
+        if (e instanceof ImageryInfo) return e.getImageryType().getTypeString()
+        return e.getString("type")
+    }
+    static Integer getMinZoom(Object e) {
+        if (e instanceof ImageryInfo) {
+            int mz = e.getMinZoom()
+            return mz == 0 ? null : mz
+        } else {
+            def ext = e.getJsonObject("extent")
+            if (ext == null) return null
+            def num = ext.getJsonNumber("min_zoom")
+            if (num == null) return null
+            return num.intValue()
+        }
+    }
+    static Integer getMaxZoom(Object e) {
+        if (e instanceof ImageryInfo) {
+            int mz = e.getMaxZoom()
+            return mz == 0 ? null : mz
+        } else {
+            def ext = e.getJsonObject("extent")
+            if (ext == null) return null
+            def num = ext.getJsonNumber("max_zoom")
+            if (num == null) return null
+            return num.intValue()
+        }
+    }
+    static String getCountryCode(Object e) {
+        if (e instanceof ImageryInfo) return "".equals(e.getCountryCode()) ? null : e.getCountryCode()
+        return e.getString("country_code", null)
+    }
+    static String getQuality(Object e) {
+        //if (e instanceof ImageryInfo) return "".equals(e.getQuality()) ? null : e.getQuality()
+        if (e instanceof ImageryInfo) return null
+        return e.get("best") ? "best" : null
+    }
+    String getDescription(Object o) {
+        def url = getUrl(o)
+        def cc = getCountryCode(o)
+        if (cc == null) {
+            def j = josmUrls.get(url)
+            if (j != null) cc = getCountryCode(j)
+            if (cc == null) {
+                def e = eiiUrls.get(url)
+                if (e != null) cc = getCountryCode(e)
+            }
+        }
+        if (cc == null) {
+            cc = ''
+        } else {
+            cc = "[$cc] "
+        }
+        def d = cc + getName(o) + " - " + getUrl(o)
+        if (options.shorten) {
+            def MAXLEN = 140
+            if (d.length() > MAXLEN) d = d.substring(0, MAXLEN-1) + "..."
+        }
+        return d
+    }
+
+}
Index: /trunk/scripts/TagInfoExtract.groovy
===================================================================
--- /trunk/scripts/TagInfoExtract.groovy	(revision 9880)
+++ /trunk/scripts/TagInfoExtract.groovy	(revision 9880)
@@ -0,0 +1,468 @@
+// License: GPL. For details, see LICENSE file.
+/**
+ * Extracts tag information for the taginfo project.
+ *
+ * Run from the base directory of a JOSM checkout:
+ *
+ * groovy -cp dist/josm-custom.jar scripts/taginfoextract.groovy -t mappaint
+ * groovy -cp dist/josm-custom.jar scripts/taginfoextract.groovy -t presets
+ * groovy -cp dist/josm-custom.jar scripts/taginfoextract.groovy -t external_presets
+ */
+import groovy.json.JsonBuilder
+
+import java.awt.image.BufferedImage
+import java.nio.file.FileSystems
+import java.nio.file.Files
+import java.nio.file.Path
+
+import javax.imageio.ImageIO
+
+import org.openstreetmap.josm.Main
+import org.openstreetmap.josm.data.Version
+import org.openstreetmap.josm.data.coor.LatLon
+import org.openstreetmap.josm.data.osm.Node
+import org.openstreetmap.josm.data.osm.OsmPrimitive
+import org.openstreetmap.josm.data.osm.Way
+import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer
+import org.openstreetmap.josm.data.projection.Projections
+import org.openstreetmap.josm.gui.NavigatableComponent
+import org.openstreetmap.josm.gui.mappaint.Environment
+import org.openstreetmap.josm.gui.mappaint.MultiCascade
+import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference
+import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.SimpleKeyValueCondition
+import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector
+import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser
+import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement
+import org.openstreetmap.josm.gui.mappaint.styleelement.LineElement
+import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement
+import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetReader
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType
+import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem
+import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem.MatchType
+import org.openstreetmap.josm.io.CachedFile
+import org.openstreetmap.josm.tools.Utils
+
+class TagInfoExtract {
+
+    static def options
+    static String image_dir
+    int josm_svn_revision
+    String input_file
+    MapCSSStyleSource style_source
+    FileWriter output_file
+    String base_dir = "."
+    Set tags = []
+
+    private def cached_svnrev
+
+    /**
+     * Check if a certain tag is supported by the style as node / way / area.
+     */
+    abstract class Checker {
+
+        def tag
+        OsmPrimitive osm
+
+        Checker(tag) {
+            this.tag = tag
+        }
+
+        Environment apply_stylesheet(OsmPrimitive osm) {
+            osm.put(tag[0], tag[1])
+            MultiCascade mc = new MultiCascade()
+
+            Environment env = new Environment(osm, mc, null, style_source)
+            for (def r in style_source.rules) {
+                env.clearSelectorMatchingInformation()
+                if (r.selector.matches(env)) {
+                    // ignore selector range
+                    if (env.layer == null) {
+                        env.layer = "default"
+                    }
+                    r.execute(env)
+                }
+            }
+            env.layer = "default"
+            return env
+        }
+
+        /**
+         * Create image file from StyleElement.
+         * @return the URL
+         */
+        def create_image(StyleElement elem_style, type, nc) {
+            def img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB)
+            def g = img.createGraphics()
+            g.setClip(0, 0, 16, 16)
+            def renderer = new StyledMapRenderer(g, nc, false)
+            renderer.getSettings(false)
+            elem_style.paintPrimitive(osm, MapPaintSettings.INSTANCE, renderer, false, false, false)
+            def base_url = options.imgurlprefix ? options.imgurlprefix : image_dir
+            def image_name = "${type}_${tag[0]}=${tag[1]}.png"
+            ImageIO.write(img, "png", new File("${image_dir}/${image_name}"))
+            return "${base_url}/${image_name}"
+        }
+
+        /**
+         * Checks, if tag is supported and find URL for image icon in this case.
+         * @param generate_image if true, create or find a suitable image icon and return URL,
+         * if false, just check if tag is supported and return true or false
+         */
+        abstract def find_url(boolean generate_image)
+    }
+
+    class NodeChecker extends Checker {
+        NodeChecker(tag) {
+            super(tag)
+        }
+
+        def find_url(boolean generate_image) {
+            osm = new Node(LatLon.ZERO)
+            def env = apply_stylesheet(osm)
+            def c = env.mc.getCascade("default")
+            def image = c.get("icon-image")
+            if (image) {
+                if (image instanceof IconReference) {
+                    if (image.iconName != "misc/deprecated.png")
+                        return find_image_url(image.iconName)
+                }
+            }
+        }
+    }
+
+    class WayChecker extends Checker {
+        WayChecker(tag) {
+            super(tag)
+        }
+
+        def find_url(boolean generate_image) {
+            osm = new Way()
+            def nc = new NavigatableComponent()
+            def n1 = new Node(nc.getLatLon(2,8))
+            def n2 = new Node(nc.getLatLon(14,8))
+            ((Way)osm).addNode(n1)
+            ((Way)osm).addNode(n2)
+            def env = apply_stylesheet(osm)
+            def les = LineElement.createLine(env)
+            if (les != null) {
+                if (!generate_image) return true
+                return create_image(les, 'way', nc)
+            }
+        }
+    }
+
+    class AreaChecker extends Checker {
+        AreaChecker(tag) {
+            super(tag)
+        }
+
+        def find_url(boolean generate_image) {
+            osm = new Way()
+            def nc = new NavigatableComponent()
+            def n1 = new Node(nc.getLatLon(2,2))
+            def n2 = new Node(nc.getLatLon(14,2))
+            def n3 = new Node(nc.getLatLon(14,14))
+            def n4 = new Node(nc.getLatLon(2,14))
+            ((Way)osm).addNode(n1)
+            ((Way)osm).addNode(n2)
+            ((Way)osm).addNode(n3)
+            ((Way)osm).addNode(n4)
+            ((Way)osm).addNode(n1)
+            def env = apply_stylesheet(osm)
+            def aes = AreaElement.create(env)
+            if (aes != null) {
+                if (!generate_image) return true
+                return create_image(aes, 'area', nc)
+            }
+        }
+    }
+
+    /**
+     * Main method.
+     */
+    static main(def args) {
+        parse_command_line_arguments(args)
+        def script = new TagInfoExtract()
+        if (!options.t || options.t == 'mappaint') {
+            script.run()
+        } else if (options.t == 'presets') {
+            script.run_presets()
+        } else if (options.t == 'external_presets') {
+            script.run_external_presets()
+        } else {
+            System.err.println 'Invalid type ' + options.t
+            if (!options.noexit) {
+                System.exit(1)
+            }
+        }
+
+        if (!options.noexit) {
+            System.exit(0)
+        }
+    }
+
+    /**
+     * Parse command line arguments.
+     */
+    static void parse_command_line_arguments(args) {
+        def cli = new CliBuilder(usage:'taginfoextract.groovy [options] [inputfile]',
+            header:"Options:",
+            footer:"[inputfile]           the file to process (optional, default is 'resource://styles/standard/elemstyles.mapcss')")
+        cli.o(args:1, argName: "file", "output file (json), - prints to stdout (default: -)")
+        cli.t(args:1, argName: "type", "the project type to be generated")
+        cli._(longOpt:'svnrev', args:1, argName:"revision", "corresponding revision of the repository https://svn.openstreetmap.org/ (optional, current revision is read from the local checkout or from the web if not given, see --svnweb)")
+        cli._(longOpt:'imgdir', args:1, argName:"directory", "directory to put the generated images in (default: ./taginfo-img)")
+        cli._(longOpt:'noexit', "don't call System.exit(), for use from Ant script")
+        cli._(longOpt:'svnweb', 'fetch revision of the repository https://svn.openstreetmap.org/ from web and not from the local repository')
+        cli._(longOpt:'imgurlprefix', args:1, argName:'prefix', 'image URLs prefix for generated image files')
+        cli.h(longOpt:'help', "show this help")
+        options = cli.parse(args)
+
+        if (options.h) {
+            cli.usage()
+            System.exit(0)
+        }
+        if (options.arguments().size() > 1) {
+            System.err.println "Error: More than one input file given!"
+            cli.usage()
+            System.exit(-1)
+        }
+        if (options.svnrev) {
+            assert Integer.parseInt(options.svnrev) > 0
+        }
+        image_dir = 'taginfo-img'
+        if (options.imgdir) {
+            image_dir = options.imgdir
+        }
+        def image_dir_file = new File(image_dir)
+        if (!image_dir_file.exists()) {
+            image_dir_file.mkdirs()
+        }
+    }
+
+    void run_presets() {
+        init()
+        def presets = TaggingPresetReader.readAll(input_file, true)
+        def tags = convert_presets(presets, "", true)
+        write_json("JOSM main presets", "Tags supported by the default presets in the OSM editor JOSM", tags)
+    }
+
+    def convert_presets(Iterable<TaggingPreset> presets, String descriptionPrefix, boolean addImages) {
+        def tags = []
+        for (TaggingPreset preset : presets) {
+            for (KeyedItem item : Utils.filteredCollection(preset.data, KeyedItem.class)) {
+                def values
+                switch (MatchType.ofString(item.match)) {
+                    case MatchType.KEY_REQUIRED: values = item.getValues(); break;
+                    case MatchType.KEY_VALUE_REQUIRED: values = item.getValues(); break;
+                    default: values = [];
+                }
+                for (String value : values) {
+                    def tag = [
+                            description: descriptionPrefix + preset.name,
+                            key: item.key,
+                            value: value,
+                            object_types: preset.types.collect {it == TaggingPresetType.CLOSEDWAY ? "area" : it.toString().toLowerCase()},
+                    ]
+                    if (addImages && preset.iconName) tag += [icon_url: find_image_url(preset.iconName)]
+                    tags += tag
+                }
+            }
+        }
+        return tags
+    }
+
+    void run_external_presets() {
+        init()
+        TaggingPresetReader.setLoadIcons(false)
+        def sources = new TaggingPresetPreference.TaggingPresetSourceEditor().loadAndGetAvailableSources()
+        def tags = []
+        for (def source : sources) {
+            if (source.url.startsWith("resource")) {
+                // default presets
+                continue;
+            }
+            try {
+                println "Loading ${source.url}"
+                def presets = TaggingPresetReader.readAll(source.url, false)
+                def t = convert_presets(presets, source.title + " ", false)
+                println "Converting ${t.size()} presets of ${source.title}"
+                tags += t
+            } catch (Exception ex) {
+                System.err.println("Skipping ${source.url} due to error")
+                ex.printStackTrace()
+            }
+        }
+        write_json("JOSM user presets", "Tags supported by the user contributed presets in the OSM editor JOSM", tags)
+    }
+
+    void run() {
+        init()
+        parse_style_sheet()
+        collect_tags()
+
+        def tags = tags.collect {
+            def tag = it
+            def types = []
+            def final_url = null
+
+            def node_url = new NodeChecker(tag).find_url(true)
+            if (node_url) {
+                types += 'node'
+                final_url = node_url
+            }
+            def way_url = new WayChecker(tag).find_url(final_url == null)
+            if (way_url) {
+                types += 'way'
+                if (!final_url) {
+                    final_url = way_url
+                }
+            }
+            def area_url = new AreaChecker(tag).find_url(final_url == null)
+            if (area_url) {
+                types += 'area'
+                if (!final_url) {
+                    final_url = area_url
+                }
+            }
+
+            def obj = [key: tag[0], value: tag[1]]
+            if (types) obj += [object_types: types]
+            if (final_url) obj += [icon_url: final_url]
+            obj
+        }
+
+        write_json("JOSM main mappaint style", "Tags supported by the main mappaint style in the OSM editor JOSM", tags)
+    }
+
+    void write_json(name, description, tags) {
+        def json = new JsonBuilder()
+        def project = [
+                name: name,
+                description: description,
+                project_url: "https://josm.openstreetmap.de/",
+                icon_url: "https://josm.openstreetmap.de/export/7770/josm/trunk/images/logo_16x16x8.png",
+                contact_name: "JOSM developer team",
+                contact_email: "josm-dev@openstreetmap.org",
+        ]
+        json data_format: 1, data_updated: new Date().format("yyyyMMdd'T'hhmmssZ"), project: project, tags: tags
+
+        if (output_file != null) {
+            json.writeTo(output_file)
+            output_file.close()
+        } else {
+            print json.toPrettyString()
+        }
+    }
+
+    /**
+     * Initialize the script.
+     */
+    def init() {
+        Main.initApplicationPreferences()
+        Main.determinePlatformHook()
+        Main.pref.enableSaveOnPut(false)
+        Main.setProjection(Projections.getProjectionByCode("EPSG:3857"))
+        Path tmpdir = Files.createTempDirectory(FileSystems.getDefault().getPath(base_dir), "pref")
+        tmpdir.toFile().deleteOnExit()
+        System.setProperty("josm.home", tmpdir.toString())
+
+        josm_svn_revision = Version.getInstance().getVersion()
+        assert josm_svn_revision != Version.JOSM_UNKNOWN_VERSION
+
+        if (options.arguments().size() == 0 && (!options.t || options.t == 'mappaint')) {
+            input_file = "resource://styles/standard/elemstyles.mapcss"
+        } else if (options.arguments().size() == 0 && options.t == 'presets') {
+            input_file = "resource://data/defaultpresets.xml"
+        } else {
+            input_file = options.arguments()[0]
+        }
+
+        output_file = null
+        if (options.o && options.o != "-") {
+            output_file = new FileWriter(options.o)
+        }
+    }
+
+    /**
+     * Determine full image url (can refer to JOSM or OSM repository).
+     */
+    def find_image_url(String path) {
+        def f = new File("${base_dir}/images/styles/standard/${path}")
+        if (f.exists()) {
+            def rev = osm_svn_revision()
+            return "https://trac.openstreetmap.org/export/${rev}/subversion/applications/share/map-icons/classic.small/${path}"
+        }
+        f = new File("${base_dir}/images/${path}")
+        if (f.exists()) {
+            if (path.startsWith("images/styles/standard/")) {
+                path = path.substring("images/styles/standard/".length())
+                def rev = osm_svn_revision()
+                return "https://trac.openstreetmap.org/export/${rev}/subversion/applications/share/map-icons/classic.small/${path}"
+            } else if (path.startsWith("styles/standard/")) {
+                path = path.substring("styles/standard/".length())
+                def rev = osm_svn_revision()
+                return "https://trac.openstreetmap.org/export/${rev}/subversion/applications/share/map-icons/classic.small/${path}"
+            } else {
+                return "https://josm.openstreetmap.de/export/${josm_svn_revision}/josm/trunk/images/${path}"
+            }
+        }
+        assert false, "Cannot find image url for ${path}"
+    }
+
+    /**
+     * Get revision for the repository https://svn.openstreetmap.org.
+     */
+    def osm_svn_revision() {
+        if (cached_svnrev != null) return cached_svnrev
+        if (options.svnrev) {
+            cached_svnrev = Integer.parseInt(options.svnrev)
+            return cached_svnrev
+        }
+        def xml
+        if (options.svnweb) {
+            xml = "svn info --xml https://svn.openstreetmap.org/applications/share/map-icons/classic.small".execute().text
+        } else {
+            xml = "svn info --xml ${base_dir}/images/styles/standard/".execute().text
+        }
+
+        def svninfo = new XmlParser().parseText(xml)
+        def rev = svninfo.entry.'@revision'[0]
+        cached_svnrev = Integer.parseInt(rev)
+        assert cached_svnrev > 0
+        return cached_svnrev
+    }
+
+    /**
+     * Read the style sheet file and parse the MapCSS code.
+     */
+    def parse_style_sheet() {
+        def file = new CachedFile(input_file)
+        def stream = file.getInputStream()
+        def parser = new MapCSSParser(stream, "UTF-8", MapCSSParser.LexicalState.DEFAULT)
+        style_source = new MapCSSStyleSource("")
+        style_source.url = ""
+        parser.sheet(style_source)
+    }
+
+    /**
+     * Collect all the tag from the style sheet.
+     */
+    def collect_tags() {
+        for (rule in style_source.rules) {
+            def selector = rule.selector
+            if (selector instanceof GeneralSelector) {
+                def conditions = selector.getConditions()
+                for (cond in conditions) {
+                    if (cond instanceof SimpleKeyValueCondition) {
+                        tags.add([cond.k, cond.v])
+                    }
+                }
+            }
+        }
+    }
+}
Index: unk/scripts/sync_editor_imagery_index.groovy
===================================================================
--- /trunk/scripts/sync_editor_imagery_index.groovy	(revision 9879)
+++ 	(revision )
@@ -1,430 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-/**
- * Compare and analyse the differences of the editor imagery index and the JOSM imagery list.
- * The goal is to keep both lists in sync.
- *
- * The editor imagery index project (https://github.com/osmlab/editor-imagery-index)
- * provides also a version in the JOSM format, but the JSON is the original source
- * format, so we read that.
- *
- * How to run:
- * -----------
- *
- * Main JOSM binary needs to be in classpath, e.g.
- *
- * $ groovy -cp ../dist/josm-custom.jar sync_editor-imagery-index.groovy
- *
- * Add option "-h" to show the available command line flags.
- */
-import java.io.FileReader
-import java.util.List
-
-import javax.json.Json
-import javax.json.JsonArray
-import javax.json.JsonObject
-import javax.json.JsonReader
-
-import org.openstreetmap.josm.io.imagery.ImageryReader
-import org.openstreetmap.josm.data.imagery.ImageryInfo
-import org.openstreetmap.josm.tools.Utils
-
-class sync_editor_imagery_index {
-
-    List<ImageryInfo> josmEntries;
-    JsonArray eiiEntries;
-
-    def eiiUrls = new HashMap<String, JsonObject>()
-    def josmUrls = new HashMap<String, ImageryInfo>()
-
-    static String eiiInputFile = 'imagery.json'
-    static String josmInputFile = 'maps.xml'
-    static FileWriter outputFile = null
-    static BufferedWriter outputStream = null
-    static int skipCount = 0;
-    static def skipEntries = [:]
-
-    static def options
-
-    /**
-     * Main method.
-     */
-    static main(def args) {
-        parse_command_line_arguments(args)
-        def script = new sync_editor_imagery_index()
-        script.loadSkip()
-        script.start()
-        script.loadJosmEntries()
-        script.loadEIIEntries()
-        script.checkInOneButNotTheOther()
-        script.checkCommonEntries()
-        script.end()
-        if(outputStream != null) {
-            outputStream.close();
-        }
-        if(outputFile != null) {
-            outputFile.close();
-        }
-    }
-
-    /**
-     * Parse command line arguments.
-     */
-    static void parse_command_line_arguments(args) {
-        def cli = new CliBuilder(width: 160)
-        cli.o(longOpt:'output', args:1, argName: "output", "Output file, - prints to stdout (default: -)")
-        cli.e(longOpt:'eii_input', args:1, argName:"eii_input", "Input file for the editor imagery index (json). Default is $eiiInputFile (current directory).")
-        cli.j(longOpt:'josm_input', args:1, argName:"josm_input", "Input file for the JOSM imagery list (xml). Default is $josmInputFile (current directory).")
-        cli.s(longOpt:'shorten', "shorten the output, so it is easier to read in a console window")
-        cli.n(longOpt:'noskip', argName:"noskip", "don't skip known entries")
-        cli.x(longOpt:'xhtmlbody', argName:"xhtmlbody", "create XHTML body for display in a web page")
-        cli.X(longOpt:'xhtml', argName:"xhtml", "create XHTML for display in a web page")
-        cli.m(longOpt:'nomissingeii', argName:"nomissingeii", "don't show missing editor imagery index entries")
-        cli.h(longOpt:'help', "show this help")
-        options = cli.parse(args)
-
-        if (options.h) {
-            cli.usage()
-            System.exit(0)
-        }
-        if (options.eii_input) {
-            eiiInputFile = options.eii_input
-        }
-        if (options.josm_input) {
-            josmInputFile = options.josm_input
-        }
-        if (options.output && options.output != "-") {
-            outputFile = new FileWriter(options.output)
-            outputStream = new BufferedWriter(outputFile)
-        }
-    }
-
-    void loadSkip() {
-        /* TMS proxies for our wms */
-        skipEntries["-  Czech CUZK:KM tiles proxy - http://osm-{switch:a,b,c}.zby.cz/tiles_cuzk.php/{zoom}/{x}/{y}.png"] = 1
-        skipEntries["-  [CH] Stadt Zürich Luftbild 2011 - http://mapproxy.sosm.ch:8080/tiles/zh_luftbild2011/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
-        skipEntries["-  [CH] Übersichtsplan Zürich - http://mapproxy.sosm.ch:8080/tiles/zh_uebersichtsplan/EPSG900913/{zoom}/{x}/{y}.png?origin=nw"] = 1
-        skipEntries["-  [CH] Kanton Solothurn 25cm (SOGIS 2011-2014) - http://mapproxy.osm.ch:8080/tiles/sogis2014/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
-        /* URL style mismatch */
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://cyberjapandata.gsi.go.jp/xyz/ort/{z}/{x}/{y}.jpg"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://tms.cadastre.openstreetmap.fr/*/tout/{z}/{x}/{y}.png"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://sdi.provincia.bz.it/geoserver/gwc/service/tms/1.0.0/WMTS_TOPOMAP_APB-PAB@GoogleMapsCompatible@png8/{z}/{x}/{-y}.png"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.osm.ch:8080/tiles/AGIS2014/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.osm.ch:8080/tiles/sogis2014/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openmap.lt/ort10lt/g/{z}/{x}/{y}.jpeg"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openstreetmap.lu/tiles/ortho2010/EPSG900913/{z}/{x}/{y}.jpeg"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openstreetmap.lu/tiles/ortho2013/EPSG900913/{z}/{x}/{y}.jpeg"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.sosm.ch:8080/tiles/zh_luftbild2011/EPSG900913/{z}/{x}/{y}.png?origin=nw"] = 1
-        skipEntries["+++ EII-URL uses {z} instead of {zoom}: http://mapproxy.openmap.lt/ort10lt/g/{z}/{x}/{y}.jpeg"] = 1
-
-        skipEntries["+++ EII-URL is not unique: http://geolittoral.application.equipement.gouv.fr/wms/metropole?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=ortholittorale&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  Streets NRW Geofabrik.de - http://tools.geofabrik.de/osmi/view/strassennrw/wxs?REQUEST=GetMap&SERVICE=wms&VERSION=1.1.1&FORMAT=image/png&SRS={proj}&STYLES=&LAYERS=unzugeordnete_strassen,kreisstrassen_ast,kreisstrassen,landesstrassen_ast,landesstrassen,bundesstrassen_ast,bundesstrassen,autobahnen_ast,autobahnen,endpunkte&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  Czech UHUL:ORTOFOTO - http://geoportal2.uhul.cz/cgi-bin/oprl.asp?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS={proj}&LAYERS=Ortofoto_cb&STYLES=default&FORMAT=image/jpeg&TRANSPARENT=TRUE&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  Czech ÚHUL:ORTOFOTO tiles proxy - http://osm-{switch:a,b,c}.zby.cz/tiles_uhul.php/{zoom}/{x}/{y}.jpg"] = 1
-        skipEntries["-  [CH] Kanton Solothurn 25cm (SOGIS 2011-2014) - http://www.sogis1.so.ch/cgi-bin/sogis/sogis_orthofoto.wms?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Orthofoto_SO&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  [CH] Kanton Solothurn Infrarot 12.5cm (SOGIS 2011) - http://www.sogis1.so.ch/cgi-bin/sogis/sogis_ortho.wms?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=Orthofoto11_CIR&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  [CH] Stadt Bern 10cm/25cm (2008) - http://map.bern.ch/arcgis/services/Orthofoto_2008/MapServer/WMSServer?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=0,1&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  [EE] Estonia Basemap (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=pohi_vr2&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  [EE] Estonia Forestry (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=cir_ngr&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  [EE] Estonia Hillshading (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=reljeef&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  [EE] Estonia Ortho (Maaamet) - http://kaart.maaamet.ee/wms/alus-geo?VERSION=1.1.1&REQUEST=GetMap&LAYERS=of10000&SRS={proj}&FORMAT=image/jpeg&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  Hamburg (DK5) - http://gateway.hamburg.de/OGCFassade/HH_WMS_Geobasisdaten.aspx?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=1&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["-  Hamburg (40 cm) - http://gateway.hamburg.de/OGCFassade/HH_WMS_DOP40.aspx?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=0&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 1
-        skipEntries["  name differs: http://wms.openstreetmap.fr/tms/1.0.0/tours_2013/{zoom}/{x}/{y}"] = 3
-        skipEntries["  name differs: http://wms.openstreetmap.fr/tms/1.0.0/tours/{zoom}/{x}/{y}"] = 3
-        skipEntries["  name differs: https://secure.erlangen.de/arcgiser/services/Luftbilder2011/MapServer/WmsServer?FORMAT=image/bmp&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=Erlangen_ratio10_5cm_gk4.jp2&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 3
-        skipEntries["  name differs: http://wms.openstreetmap.fr/tms/1.0.0/iomhaiti/{zoom}/{x}/{y}"] = 3
-        skipEntries["  name differs: http://{switch:a,b,c}.layers.openstreetmap.fr/bano/{zoom}/{x}/{y}.png"] = 3
-        skipEntries["  name differs: http://ooc.openstreetmap.org/os1/{zoom}/{x}/{y}.jpg"] = 3
-        skipEntries["  name differs: http://www.gisnet.lv/cgi-bin/osm_latvia?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=piekraste&SRS={proj}&WIDTH={width}&height={height}&BBOX={bbox}"] = 3
-        skipEntries["  name differs: http://tms.cadastre.openstreetmap.fr/*/tout/{zoom}/{x}/{y}.png"] = 3
-        skipEntries["  name differs: http://{switch:a,b,c}.tiles.mapbox.com/v4/enf.e0b8291e/{zoom}/{x}/{y}.png?access_token=pk.eyJ1Ijoib3BlbnN0cmVldG1hcCIsImEiOiJhNVlHd29ZIn0.ti6wATGDWOmCnCYen-Ip7Q"] = 3
-        skipEntries["  name differs: http://geo.nls.uk/mapdata2/os/25_inch/scotland_1/{zoom}/{x}/{y}.png"] = 3
-        skipEntries["  name differs: http://geo.nls.uk/mapdata3/os/6_inch_gb_1900/{zoom}/{x}/{y}.png"] = 3
-        skipEntries["  name differs: http://geoserver.infobex.hu/Budapest2014/IST/{zoom}/{x}/{y}.jpg"] = 3
-        skipEntries["  name differs: http://mapproxy.openmap.lt/ort10lt/g/{zoom}/{x}/{y}.jpeg"] = 3
-        skipEntries["  name differs: http://e.tile.openstreetmap.hu/ortofoto2000/{zoom}/{x}/{y}.jpg"] = 3
-        skipEntries["  maxzoom differs: [DE] Bavaria (2 m) - http://geodaten.bayern.de/ogc/ogc_dop200_oa.cgi?FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=adv_dop200c&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries County - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=County&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries NPWS Reserve - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=NPWSReserve&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries Parish - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=Parish&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries Suburb - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=Suburb&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Imagery - http://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Imagery/MapServer/tile/{zoom}/{y}/{x}"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Topographic Map - http://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Topo_Map/MapServer/tile/{zoom}/{y}/{x}"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries State Forest - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=StateForest&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Administrative Boundaries LGA - http://maps.six.nsw.gov.au/arcgis/services/public/NSW_Administrative_Boundaries/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=LocalGovernmentArea&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE"] = 3
-        skipEntries["  minzoom differs: [AU] LPI NSW Base Map - http://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Base_Map/MapServer/tile/{zoom}/{y}/{x}"] = 3
-        skipEntries["  country code differs: [LT] ORT10LT (Lithuania) - http://mapproxy.openmap.lt/ort10lt/g/{zoom}/{x}/{y}.jpeg"] = 3
-    }
-
-    void myprintlnfinal(String s) {
-        if(outputStream != null) {
-            outputStream.write(s);
-            outputStream.newLine();
-        } else {
-            println s;
-        }
-    }
-
-    void myprintln(String s) {
-        if(skipEntries.containsKey(s)) {
-            skipCount = skipEntries.get(s)
-            skipEntries.remove(s)
-        }
-        if(skipCount) {
-            skipCount -= 1;
-            if(options.xhtmlbody || options.xhtml) {
-                s = "<pre style=\"margin:3px;color:green\">"+s.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")+"</pre>"
-            }
-            if (!options.noskip) {
-                return;
-            }
-        } else if(options.xhtmlbody || options.xhtml) {
-            String color = s.startsWith("***") ? "black" : (s.startsWith("+ ") ? "blue" : "red")
-            s = "<pre style=\"margin:3px;color:"+color+"\">"+s.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")+"</pre>"
-        }
-        myprintlnfinal(s)
-    }
-
-    void start() {
-        if (options.xhtml) {
-            myprintlnfinal "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
-            myprintlnfinal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/><title>JOSM - EII differences</title></head><body>\n"
-        }
-    }
-
-    void end() {
-        for (def s: skipEntries.keySet()) {
-            myprintln "+++ Obsolete skip entry: " + s
-        }
-        if (options.xhtml) {
-            myprintlnfinal "</body></html>\n"
-        }
-    }
-
-    void loadEIIEntries() {
-        FileReader fr = new FileReader(eiiInputFile)
-        JsonReader jr = Json.createReader(fr)
-        eiiEntries = jr.readArray()
-        jr.close()
-
-        for (def e : eiiEntries) {
-            def url = getUrl(e)
-            if (url.contains("{z}")) {
-                myprintln "+++ EII-URL uses {z} instead of {zoom}: "+url
-                url = url.replace("{z}","{zoom}")
-            }
-            if (eiiUrls.containsKey(url)) {
-                myprintln "+++ EII-URL is not unique: "+url
-            } else {
-                eiiUrls.put(url, e)
-            }
-        }
-        myprintln "*** Loaded ${eiiEntries.size()} entries (EII). ***"
-    }
-
-    void loadJosmEntries() {
-        def reader = new ImageryReader(josmInputFile)
-        josmEntries = reader.parse()
-
-        for (def e : josmEntries) {
-            def url = getUrl(e)
-            if (url.contains("{z}")) {
-                myprintln "+++ JOSM-URL uses {z} instead of {zoom}: "+url
-                url = url.replace("{z}","{zoom}")
-            }
-            if (josmUrls.containsKey(url)) {
-                myprintln "+++ JOSM-URL is not unique: "+url
-            } else {
-              josmUrls.put(url, e)
-            }
-            for (def m : e.getMirrors()) {
-                url = getUrl(m)
-                if (josmUrls.containsKey(url)) {
-                    myprintln "+++ JOSM-Mirror-URL is not unique: "+url
-                } else {
-                  josmUrls.put(url, m)
-                }
-            }
-        }
-        myprintln "*** Loaded ${josmEntries.size()} entries (JOSM). ***"
-    }
-
-    List inOneButNotTheOther(Map m1, Map m2) {
-        def l = []
-        for (def url : m1.keySet()) {
-            if (!m2.containsKey(url)) {
-                def name = getName(m1.get(url))
-                l += "  "+getDescription(m1.get(url))
-            }
-        }
-        l.sort()
-    }
-
-    void checkInOneButNotTheOther() {
-        def l1 = inOneButNotTheOther(eiiUrls, josmUrls)
-        myprintln "*** URLs found in EII but not in JOSM (${l1.size()}): ***"
-        if (!l1.isEmpty()) {
-            for (def l : l1)
-                myprintln "-"+l
-        }
-
-        if (options.nomissingeii)
-            return
-        def l2 = inOneButNotTheOther(josmUrls, eiiUrls)
-        myprintln "*** URLs found in JOSM but not in EII (${l2.size()}): ***"
-        if (!l2.isEmpty()) {
-            for (def l : l2)
-                myprintln "+" + l
-        }
-    }
-
-    void checkCommonEntries() {
-        myprintln "*** Same URL, but different name: ***"
-        for (def url : eiiUrls.keySet()) {
-            def e = eiiUrls.get(url)
-            if (!josmUrls.containsKey(url)) continue
-            def j = josmUrls.get(url)
-            if (!getName(e).equals(getName(j))) {
-                myprintln "  name differs: $url"
-                myprintln "     (IEE):     ${getName(e)}"
-                myprintln "     (JOSM):    ${getName(j)}"
-            }
-        }
-
-        myprintln "*** Same URL, but different type: ***"
-        for (def url : eiiUrls.keySet()) {
-            def e = eiiUrls.get(url)
-            if (!josmUrls.containsKey(url)) continue
-            def j = josmUrls.get(url)
-            if (!getType(e).equals(getType(j))) {
-                myprintln "  type differs: ${getName(j)} - $url"
-                myprintln "     (IEE):     ${getType(e)}"
-                myprintln "     (JOSM):    ${getType(j)}"
-            }
-        }
-
-        myprintln "*** Same URL, but different zoom bounds: ***"
-        for (def url : eiiUrls.keySet()) {
-            def e = eiiUrls.get(url)
-            if (!josmUrls.containsKey(url)) continue
-            def j = josmUrls.get(url)
-
-            Integer eMinZoom = getMinZoom(e)
-            Integer jMinZoom = getMinZoom(j)
-            if (eMinZoom != jMinZoom  && !(eMinZoom == 0 && jMinZoom == null)) {
-                myprintln "  minzoom differs: ${getDescription(j)}"
-                myprintln "     (IEE):     ${eMinZoom}"
-                myprintln "     (JOSM):    ${jMinZoom}"
-            }
-            Integer eMaxZoom = getMaxZoom(e)
-            Integer jMaxZoom = getMaxZoom(j)
-            if (eMaxZoom != jMaxZoom) {
-                myprintln "  maxzoom differs: ${getDescription(j)}"
-                myprintln "     (IEE):     ${eMaxZoom}"
-                myprintln "     (JOSM):    ${jMaxZoom}"
-            }
-        }
-
-        myprintln "*** Same URL, but different country code: ***"
-        for (def url : eiiUrls.keySet()) {
-            def e = eiiUrls.get(url)
-            if (!josmUrls.containsKey(url)) continue
-            def j = josmUrls.get(url)
-            if (!getCountryCode(e).equals(getCountryCode(j))) {
-                myprintln "  country code differs: ${getDescription(j)}"
-                myprintln "     (IEE):     ${getCountryCode(e)}"
-                myprintln "     (JOSM):    ${getCountryCode(j)}"
-            }
-        }
-        myprintln "*** Same URL, but different quality: ***"
-        for (def url : eiiUrls.keySet()) {
-            def e = eiiUrls.get(url)
-            if (!josmUrls.containsKey(url)) {
-              def q = getQuality(e)
-              if("best".equals(q)) {
-                myprintln "  quality best entry not in JOSM for ${getDescription(e)}"
-              }
-              continue
-            }
-            def j = josmUrls.get(url)
-            if (!getQuality(e).equals(getQuality(j))) {
-                myprintln "  quality differs: ${getDescription(j)}"
-                myprintln "     (IEE):     ${getQuality(e)}"
-                myprintln "     (JOSM):    ${getQuality(j)}"
-            }
-        }
-    }
-
-    /**
-     * Utility functions that allow uniform access for both ImageryInfo and JsonObject.
-     */
-    static String getUrl(Object e) {
-        if (e instanceof ImageryInfo) return e.url
-        return e.getString("url")
-    }
-    static String getName(Object e) {
-        if (e instanceof ImageryInfo) return e.name
-        return e.getString("name")
-    }
-    static String getType(Object e) {
-        if (e instanceof ImageryInfo) return e.getImageryType().getTypeString()
-        return e.getString("type")
-    }
-    static Integer getMinZoom(Object e) {
-        if (e instanceof ImageryInfo) {
-            int mz = e.getMinZoom()
-            return mz == 0 ? null : mz
-        } else {
-            def ext = e.getJsonObject("extent")
-            if (ext == null) return null
-            def num = ext.getJsonNumber("min_zoom")
-            if (num == null) return null
-            return num.intValue()
-        }
-    }
-    static Integer getMaxZoom(Object e) {
-        if (e instanceof ImageryInfo) {
-            int mz = e.getMaxZoom()
-            return mz == 0 ? null : mz
-        } else {
-            def ext = e.getJsonObject("extent")
-            if (ext == null) return null
-            def num = ext.getJsonNumber("max_zoom")
-            if (num == null) return null
-            return num.intValue()
-        }
-    }
-    static String getCountryCode(Object e) {
-        if (e instanceof ImageryInfo) return "".equals(e.getCountryCode()) ? null : e.getCountryCode()
-        return e.getString("country_code", null)
-    }
-    static String getQuality(Object e) {
-        //if (e instanceof ImageryInfo) return "".equals(e.getQuality()) ? null : e.getQuality()
-        if (e instanceof ImageryInfo) return null
-        return e.get("best") ? "best" : null
-    }
-    String getDescription(Object o) {
-        def url = getUrl(o)
-        def cc = getCountryCode(o)
-        if (cc == null) {
-            def j = josmUrls.get(url)
-            if (j != null) cc = getCountryCode(j)
-            if (cc == null) {
-                def e = eiiUrls.get(url)
-                if (e != null) cc = getCountryCode(e)
-            }
-        }
-        if (cc == null) {
-            cc = ''
-        } else {
-            cc = "[$cc] "
-        }
-        def d = cc + getName(o) + " - " + getUrl(o)
-        if (options.shorten) {
-            def MAXLEN = 140
-            if (d.length() > MAXLEN) d = d.substring(0, MAXLEN-1) + "..."
-        }
-        return d
-    }
-
-}
Index: unk/scripts/taginfoextract.groovy
===================================================================
--- /trunk/scripts/taginfoextract.groovy	(revision 9879)
+++ 	(revision )
@@ -1,468 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-/**
- * Extracts tag information for the taginfo project.
- *
- * Run from the base directory of a JOSM checkout:
- *
- * groovy -cp dist/josm-custom.jar scripts/taginfoextract.groovy -t mappaint
- * groovy -cp dist/josm-custom.jar scripts/taginfoextract.groovy -t presets
- * groovy -cp dist/josm-custom.jar scripts/taginfoextract.groovy -t external_presets
- */
-import groovy.json.JsonBuilder
-
-import java.awt.image.BufferedImage
-import java.nio.file.FileSystems
-import java.nio.file.Files
-import java.nio.file.Path
-
-import javax.imageio.ImageIO
-
-import org.openstreetmap.josm.Main
-import org.openstreetmap.josm.data.Version
-import org.openstreetmap.josm.data.coor.LatLon
-import org.openstreetmap.josm.data.osm.Node
-import org.openstreetmap.josm.data.osm.OsmPrimitive
-import org.openstreetmap.josm.data.osm.Way
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings
-import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer
-import org.openstreetmap.josm.data.projection.Projections
-import org.openstreetmap.josm.gui.NavigatableComponent
-import org.openstreetmap.josm.gui.mappaint.Environment
-import org.openstreetmap.josm.gui.mappaint.MultiCascade
-import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference
-import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource
-import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.SimpleKeyValueCondition
-import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector
-import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser
-import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement
-import org.openstreetmap.josm.gui.mappaint.styleelement.LineElement
-import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement
-import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference
-import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset
-import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetReader
-import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType
-import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem
-import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem.MatchType
-import org.openstreetmap.josm.io.CachedFile
-import org.openstreetmap.josm.tools.Utils
-
-class taginfoextract {
-
-    static def options
-    static String image_dir
-    int josm_svn_revision
-    String input_file
-    MapCSSStyleSource style_source
-    FileWriter output_file
-    String base_dir = "."
-    Set tags = []
-
-    private def cached_svnrev
-
-    /**
-     * Check if a certain tag is supported by the style as node / way / area.
-     */
-    abstract class Checker {
-
-        def tag
-        OsmPrimitive osm
-
-        Checker(tag) {
-            this.tag = tag
-        }
-
-        Environment apply_stylesheet(OsmPrimitive osm) {
-            osm.put(tag[0], tag[1])
-            MultiCascade mc = new MultiCascade()
-
-            Environment env = new Environment(osm, mc, null, style_source)
-            for (def r in style_source.rules) {
-                env.clearSelectorMatchingInformation()
-                if (r.selector.matches(env)) {
-                    // ignore selector range
-                    if (env.layer == null) {
-                        env.layer = "default"
-                    }
-                    r.execute(env)
-                }
-            }
-            env.layer = "default"
-            return env
-        }
-
-        /**
-         * Create image file from StyleElement.
-         * @return the URL
-         */
-        def create_image(StyleElement elem_style, type, nc) {
-            def img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB)
-            def g = img.createGraphics()
-            g.setClip(0, 0, 16, 16)
-            def renderer = new StyledMapRenderer(g, nc, false)
-            renderer.getSettings(false)
-            elem_style.paintPrimitive(osm, MapPaintSettings.INSTANCE, renderer, false, false, false)
-            def base_url = options.imgurlprefix ? options.imgurlprefix : image_dir
-            def image_name = "${type}_${tag[0]}=${tag[1]}.png"
-            ImageIO.write(img, "png", new File("${image_dir}/${image_name}"))
-            return "${base_url}/${image_name}"
-        }
-
-        /**
-         * Checks, if tag is supported and find URL for image icon in this case.
-         * @param generate_image if true, create or find a suitable image icon and return URL,
-         * if false, just check if tag is supported and return true or false
-         */
-        abstract def find_url(boolean generate_image)
-    }
-
-    class NodeChecker extends Checker {
-        NodeChecker(tag) {
-            super(tag)
-        }
-
-        def find_url(boolean generate_image) {
-            osm = new Node(LatLon.ZERO)
-            def env = apply_stylesheet(osm)
-            def c = env.mc.getCascade("default")
-            def image = c.get("icon-image")
-            if (image) {
-                if (image instanceof IconReference) {
-                    if (image.iconName != "misc/deprecated.png")
-                        return find_image_url(image.iconName)
-                }
-            }
-        }
-    }
-
-    class WayChecker extends Checker {
-        WayChecker(tag) {
-            super(tag)
-        }
-
-        def find_url(boolean generate_image) {
-            osm = new Way()
-            def nc = new NavigatableComponent()
-            def n1 = new Node(nc.getLatLon(2,8))
-            def n2 = new Node(nc.getLatLon(14,8))
-            ((Way)osm).addNode(n1)
-            ((Way)osm).addNode(n2)
-            def env = apply_stylesheet(osm)
-            def les = LineElement.createLine(env)
-            if (les != null) {
-                if (!generate_image) return true
-                return create_image(les, 'way', nc)
-            }
-        }
-    }
-
-    class AreaChecker extends Checker {
-        AreaChecker(tag) {
-            super(tag)
-        }
-
-        def find_url(boolean generate_image) {
-            osm = new Way()
-            def nc = new NavigatableComponent()
-            def n1 = new Node(nc.getLatLon(2,2))
-            def n2 = new Node(nc.getLatLon(14,2))
-            def n3 = new Node(nc.getLatLon(14,14))
-            def n4 = new Node(nc.getLatLon(2,14))
-            ((Way)osm).addNode(n1)
-            ((Way)osm).addNode(n2)
-            ((Way)osm).addNode(n3)
-            ((Way)osm).addNode(n4)
-            ((Way)osm).addNode(n1)
-            def env = apply_stylesheet(osm)
-            def aes = AreaElement.create(env)
-            if (aes != null) {
-                if (!generate_image) return true
-                return create_image(aes, 'area', nc)
-            }
-        }
-    }
-
-    /**
-     * Main method.
-     */
-    static main(def args) {
-        parse_command_line_arguments(args)
-        def script = new taginfoextract()
-        if (!options.t || options.t == 'mappaint') {
-            script.run()
-        } else if (options.t == 'presets') {
-            script.run_presets()
-        } else if (options.t == 'external_presets') {
-            script.run_external_presets()
-        } else {
-            System.err.println 'Invalid type ' + options.t
-            if (!options.noexit) {
-                System.exit(1)
-            }
-        }
-
-        if (!options.noexit) {
-            System.exit(0)
-        }
-    }
-
-    /**
-     * Parse command line arguments.
-     */
-    static void parse_command_line_arguments(args) {
-        def cli = new CliBuilder(usage:'taginfoextract.groovy [options] [inputfile]',
-            header:"Options:",
-            footer:"[inputfile]           the file to process (optional, default is 'resource://styles/standard/elemstyles.mapcss')")
-        cli.o(args:1, argName: "file", "output file (json), - prints to stdout (default: -)")
-        cli.t(args:1, argName: "type", "the project type to be generated")
-        cli._(longOpt:'svnrev', args:1, argName:"revision", "corresponding revision of the repository https://svn.openstreetmap.org/ (optional, current revision is read from the local checkout or from the web if not given, see --svnweb)")
-        cli._(longOpt:'imgdir', args:1, argName:"directory", "directory to put the generated images in (default: ./taginfo-img)")
-        cli._(longOpt:'noexit', "don't call System.exit(), for use from Ant script")
-        cli._(longOpt:'svnweb', 'fetch revision of the repository https://svn.openstreetmap.org/ from web and not from the local repository')
-        cli._(longOpt:'imgurlprefix', args:1, argName:'prefix', 'image URLs prefix for generated image files')
-        cli.h(longOpt:'help', "show this help")
-        options = cli.parse(args)
-
-        if (options.h) {
-            cli.usage()
-            System.exit(0)
-        }
-        if (options.arguments().size() > 1) {
-            System.err.println "Error: More than one input file given!"
-            cli.usage()
-            System.exit(-1)
-        }
-        if (options.svnrev) {
-            assert Integer.parseInt(options.svnrev) > 0
-        }
-        image_dir = 'taginfo-img'
-        if (options.imgdir) {
-            image_dir = options.imgdir
-        }
-        def image_dir_file = new File(image_dir)
-        if (!image_dir_file.exists()) {
-            image_dir_file.mkdirs()
-        }
-    }
-
-    void run_presets() {
-        init()
-        def presets = TaggingPresetReader.readAll(input_file, true)
-        def tags = convert_presets(presets, "", true)
-        write_json("JOSM main presets", "Tags supported by the default presets in the OSM editor JOSM", tags)
-    }
-
-    def convert_presets(Iterable<TaggingPreset> presets, String descriptionPrefix, boolean addImages) {
-        def tags = []
-        for (TaggingPreset preset : presets) {
-            for (KeyedItem item : Utils.filteredCollection(preset.data, KeyedItem.class)) {
-                def values
-                switch (MatchType.ofString(item.match)) {
-                    case MatchType.KEY_REQUIRED: values = item.getValues(); break;
-                    case MatchType.KEY_VALUE_REQUIRED: values = item.getValues(); break;
-                    default: values = [];
-                }
-                for (String value : values) {
-                    def tag = [
-                            description: descriptionPrefix + preset.name,
-                            key: item.key,
-                            value: value,
-                            object_types: preset.types.collect {it == TaggingPresetType.CLOSEDWAY ? "area" : it.toString().toLowerCase()},
-                    ]
-                    if (addImages && preset.iconName) tag += [icon_url: find_image_url(preset.iconName)]
-                    tags += tag
-                }
-            }
-        }
-        return tags
-    }
-
-    void run_external_presets() {
-        init()
-        TaggingPresetReader.setLoadIcons(false)
-        def sources = new TaggingPresetPreference.TaggingPresetSourceEditor().loadAndGetAvailableSources()
-        def tags = []
-        for (def source : sources) {
-            if (source.url.startsWith("resource")) {
-                // default presets
-                continue;
-            }
-            try {
-                println "Loading ${source.url}"
-                def presets = TaggingPresetReader.readAll(source.url, false)
-                def t = convert_presets(presets, source.title + " ", false)
-                println "Converting ${t.size()} presets of ${source.title}"
-                tags += t
-            } catch (Exception ex) {
-                System.err.println("Skipping ${source.url} due to error")
-                ex.printStackTrace()
-            }
-        }
-        write_json("JOSM user presets", "Tags supported by the user contributed presets in the OSM editor JOSM", tags)
-    }
-
-    void run() {
-        init()
-        parse_style_sheet()
-        collect_tags()
-
-        def tags = tags.collect {
-            def tag = it
-            def types = []
-            def final_url = null
-
-            def node_url = new NodeChecker(tag).find_url(true)
-            if (node_url) {
-                types += 'node'
-                final_url = node_url
-            }
-            def way_url = new WayChecker(tag).find_url(final_url == null)
-            if (way_url) {
-                types += 'way'
-                if (!final_url) {
-                    final_url = way_url
-                }
-            }
-            def area_url = new AreaChecker(tag).find_url(final_url == null)
-            if (area_url) {
-                types += 'area'
-                if (!final_url) {
-                    final_url = area_url
-                }
-            }
-
-            def obj = [key: tag[0], value: tag[1]]
-            if (types) obj += [object_types: types]
-            if (final_url) obj += [icon_url: final_url]
-            obj
-        }
-
-        write_json("JOSM main mappaint style", "Tags supported by the main mappaint style in the OSM editor JOSM", tags)
-    }
-
-    void write_json(name, description, tags) {
-        def json = new JsonBuilder()
-        def project = [
-                name: name,
-                description: description,
-                project_url: "https://josm.openstreetmap.de/",
-                icon_url: "https://josm.openstreetmap.de/export/7770/josm/trunk/images/logo_16x16x8.png",
-                contact_name: "JOSM developer team",
-                contact_email: "josm-dev@openstreetmap.org",
-        ]
-        json data_format: 1, data_updated: new Date().format("yyyyMMdd'T'hhmmssZ"), project: project, tags: tags
-
-        if (output_file != null) {
-            json.writeTo(output_file)
-            output_file.close()
-        } else {
-            print json.toPrettyString()
-        }
-    }
-
-    /**
-     * Initialize the script.
-     */
-    def init() {
-        Main.initApplicationPreferences()
-        Main.determinePlatformHook()
-        Main.pref.enableSaveOnPut(false)
-        Main.setProjection(Projections.getProjectionByCode("EPSG:3857"))
-        Path tmpdir = Files.createTempDirectory(FileSystems.getDefault().getPath(base_dir), "pref")
-        tmpdir.toFile().deleteOnExit()
-        System.setProperty("josm.home", tmpdir.toString())
-
-        josm_svn_revision = Version.getInstance().getVersion()
-        assert josm_svn_revision != Version.JOSM_UNKNOWN_VERSION
-
-        if (options.arguments().size() == 0 && (!options.t || options.t == 'mappaint')) {
-            input_file = "resource://styles/standard/elemstyles.mapcss"
-        } else if (options.arguments().size() == 0 && options.t == 'presets') {
-            input_file = "resource://data/defaultpresets.xml"
-        } else {
-            input_file = options.arguments()[0]
-        }
-
-        output_file = null
-        if (options.o && options.o != "-") {
-            output_file = new FileWriter(options.o)
-        }
-    }
-
-    /**
-     * Determine full image url (can refer to JOSM or OSM repository).
-     */
-    def find_image_url(String path) {
-        def f = new File("${base_dir}/images/styles/standard/${path}")
-        if (f.exists()) {
-            def rev = osm_svn_revision()
-            return "https://trac.openstreetmap.org/export/${rev}/subversion/applications/share/map-icons/classic.small/${path}"
-        }
-        f = new File("${base_dir}/images/${path}")
-        if (f.exists()) {
-            if (path.startsWith("images/styles/standard/")) {
-                path = path.substring("images/styles/standard/".length())
-                def rev = osm_svn_revision()
-                return "https://trac.openstreetmap.org/export/${rev}/subversion/applications/share/map-icons/classic.small/${path}"
-            } else if (path.startsWith("styles/standard/")) {
-                path = path.substring("styles/standard/".length())
-                def rev = osm_svn_revision()
-                return "https://trac.openstreetmap.org/export/${rev}/subversion/applications/share/map-icons/classic.small/${path}"
-            } else {
-                return "https://josm.openstreetmap.de/export/${josm_svn_revision}/josm/trunk/images/${path}"
-            }
-        }
-        assert false, "Cannot find image url for ${path}"
-    }
-
-    /**
-     * Get revision for the repository https://svn.openstreetmap.org.
-     */
-    def osm_svn_revision() {
-        if (cached_svnrev != null) return cached_svnrev
-        if (options.svnrev) {
-            cached_svnrev = Integer.parseInt(options.svnrev)
-            return cached_svnrev
-        }
-        def xml
-        if (options.svnweb) {
-            xml = "svn info --xml https://svn.openstreetmap.org/applications/share/map-icons/classic.small".execute().text
-        } else {
-            xml = "svn info --xml ${base_dir}/images/styles/standard/".execute().text
-        }
-
-        def svninfo = new XmlParser().parseText(xml)
-        def rev = svninfo.entry.'@revision'[0]
-        cached_svnrev = Integer.parseInt(rev)
-        assert cached_svnrev > 0
-        return cached_svnrev
-    }
-
-    /**
-     * Read the style sheet file and parse the MapCSS code.
-     */
-    def parse_style_sheet() {
-        def file = new CachedFile(input_file)
-        def stream = file.getInputStream()
-        def parser = new MapCSSParser(stream, "UTF-8", MapCSSParser.LexicalState.DEFAULT)
-        style_source = new MapCSSStyleSource("")
-        style_source.url = ""
-        parser.sheet(style_source)
-    }
-
-    /**
-     * Collect all the tag from the style sheet.
-     */
-    def collect_tags() {
-        for (rule in style_source.rules) {
-            def selector = rule.selector
-            if (selector instanceof GeneralSelector) {
-                def conditions = selector.getConditions()
-                for (cond in conditions) {
-                    if (cond instanceof SimpleKeyValueCondition) {
-                        tags.add([cond.k, cond.v])
-                    }
-                }
-            }
-        }
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 9879)
+++ /trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 9880)
@@ -202,9 +202,5 @@
             double distanceFirst = firstPosition.distance(refPoint);
             double distanceSecond = secondPosition.distance(refPoint);
-            double difference =  distanceFirst - distanceSecond;
-
-            if (difference > 0.0) return 1;
-            if (difference < 0.0) return -1;
-            return 0;
+            return Double.compare(distanceFirst, distanceSecond);
         }
     }
Index: /trunk/src/org/openstreetmap/josm/data/osm/NodePositionComparator.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/NodePositionComparator.java	(revision 9879)
+++ /trunk/src/org/openstreetmap/josm/data/osm/NodePositionComparator.java	(revision 9880)
@@ -20,13 +20,6 @@
             return 0;
 
-        double dLat = n1.getCoor().lat() - n2.getCoor().lat();
-        if (dLat > 0)
-            return 1;
-        if (dLat < 0)
-            return -1;
-        double dLon = n1.getCoor().lon() - n2.getCoor().lon();
-        if (dLon == 0)
-            return 0;
-        return dLon > 0 ? 1 : -1;
+        int dLat = Double.compare(n1.getCoor().lat(), n2.getCoor().lat());
+        return dLat != 0 ? dLat : Double.compare(n1.getCoor().lon(), n2.getCoor().lon());
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 9879)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 9880)
@@ -1478,6 +1478,6 @@
 
         Pair<Timezone, Offset> splitOutTimezone() {
-            // In hours, rounded to two decimal places
-            double tz = (withoutDayOffset().getSeconds() * 100L / 3600.0) / 100.0;
+            // In hours
+            double tz = withoutDayOffset().getSeconds() / 3600.0;
 
             // Due to imprecise clocks we might get a "+3:28" timezone, which should obviously be 3:30 with
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java	(revision 9879)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java	(revision 9880)
@@ -100,9 +100,5 @@
         @Override
         public int compare(TrackLength l0, TrackLength l1) {
-            if (l0.value < l1.value)
-                return -1;
-            else if (l0.value > l1.value)
-                return 1;
-            return 0;
+            return Double.compare(l0.value, l1.value);
         }
     }
Index: /trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java	(revision 9879)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java	(revision 9880)
@@ -11,4 +11,5 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -28,4 +29,5 @@
     @BeforeClass
     public static void setUp() {
+        JOSMFixture.createUnitTestFixture().init();
         DateUtilsTest.setTimeZone(TimeZone.getTimeZone("UTC"));
     }
