Index: trunk/scripts/sync_editor_imagery_index.groovy
===================================================================
--- trunk/scripts/sync_editor_imagery_index.groovy	(revision 9657)
+++ trunk/scripts/sync_editor_imagery_index.groovy	(revision 9658)
@@ -45,5 +45,5 @@
 
     static def options
-    
+
     /**
      * Main method.
@@ -53,8 +53,10 @@
         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();
@@ -69,5 +71,5 @@
      */
     static void parse_command_line_arguments(args) {
-        def cli = new CliBuilder()
+        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).")
@@ -75,4 +77,6 @@
         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")
@@ -99,8 +103,8 @@
             return;
         /* 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
+        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
@@ -116,16 +120,16 @@
 
         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["-  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
@@ -135,13 +139,12 @@
         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/{z}/{x}/{y}.png"] = 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["  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["  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["  name differs: http://tms.cadastre.openstreetmap.fr/*/tout/{zoom}/{x}/{y}.png"] = 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
@@ -156,12 +159,5 @@
     }
 
-    void myprintln(String s) {
-        if(skipEntries.containsKey(s)) {
-            skipCount = skipEntries.get(s)
-        }
-        if(skipCount) {
-            skipCount -= 1;
-            return
-        }
+    void myprintlnfinal(String s) {
         if(outputStream != null) {
             outputStream.write(s);
@@ -169,4 +165,39 @@
         } 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>"
+            } else {
+                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"
         }
     }
@@ -199,8 +230,20 @@
         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)
+                }
             }
         }
@@ -222,9 +265,7 @@
         def l1 = inOneButNotTheOther(eiiUrls, josmUrls)
         myprintln "*** URLs found in EII but not in JOSM (${l1.size()}): ***"
-        if (l1.isEmpty()) {
-            myprintln "  -"
-        } else {
+        if (!l1.isEmpty()) {
             for (def l : l1)
-                myprintln l
+                myprintln "-"+l
         }
 
@@ -233,9 +274,7 @@
         def l2 = inOneButNotTheOther(josmUrls, eiiUrls)
         myprintln "*** URLs found in JOSM but not in EII (${l2.size()}): ***"
-        if (l2.isEmpty()) {
-            myprintln "  -"
-        } else {
+        if (!l2.isEmpty()) {
             for (def l : l2)
-                myprintln l
+                myprintln "+" + l
         }
     }
Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 9657)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 9658)
@@ -145,5 +145,4 @@
         }
     }
-
 
     /** original name of the imagery entry in case of translation call, for multiple languages English when possible */
@@ -186,4 +185,6 @@
     /** country code of the imagery (for country specific imagery) */
     private String countryCode = "";
+    /** mirrors of different type for this entry */
+    private List<ImageryInfo> mirrors = null;
     /** icon used in menu */
     private String icon;
@@ -1029,3 +1030,47 @@
     }
 
+    /**
+     * Adds a mirror entry. Mirror entries are completed with the data from the master entry
+     * and only describe another method to access identical data.
+     *
+     * @param entry the mirror to be added
+     * @since 9658
+     */
+    public void addMirror(ImageryInfo entry) {
+       if (mirrors == null) {
+           mirrors = new ArrayList<>();
+       }
+       mirrors.add(entry);
+    }
+
+    /**
+     * Returns the mirror entries. Entries are completed with master entry data.
+     *
+     * @return the list of mirrors
+     * @since 9658
+     */
+    public List<ImageryInfo> getMirrors() {
+       List<ImageryInfo> l = new ArrayList<>();
+       if (mirrors != null) {
+           for (ImageryInfo i : mirrors) {
+               ImageryInfo n = new ImageryInfo(this);
+               if (i.defaultMaxZoom != 0) {
+                   n.defaultMaxZoom = i.defaultMaxZoom;
+               }
+               if (i.defaultMinZoom != 0) {
+                   n.defaultMinZoom = i.defaultMinZoom;
+               }
+               if (i.serverProjections != null) {
+                   n.serverProjections = i.serverProjections;
+               }
+               n.url = i.url;
+               n.imageryType = i.imageryType;
+               if (i.getTileSize() != 0) {
+                   n.setTileSize(i.getTileSize());
+               }
+               l.add(n);
+           }
+       }
+       return l;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 9657)
+++ trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 9658)
@@ -40,5 +40,8 @@
         ENTRY,              // inside an entry
         ENTRY_ATTRIBUTE,    // note we are inside an entry attribute to collect the character data
-        PROJECTIONS,
+        PROJECTIONS,        // inside projections block of an entry
+        MIRROR,             // inside an mirror entry
+        MIRROR_ATTRIBUTE,   // note we are inside an mirror attribute to collect the character data
+        MIRROR_PROJECTIONS, // inside projections block of an mirror entry
         CODE,
         BOUNDS,
@@ -89,4 +92,6 @@
 
         private ImageryInfo entry;
+        /** In case of mirror parsing this contains the mirror entry */
+        private ImageryInfo mirrorEntry;
         private ImageryBounds bounds;
         private Shape shape;
@@ -130,4 +135,19 @@
                     noTileChecksums = new HashMap<>();
                     metadataHeaders = new HashMap<>();
+                }
+                break;
+            case MIRROR:
+                if (Arrays.asList(new String[] {
+                        "type",
+                        "url",
+                        "min-zoom",
+                        "max-zoom",
+                        "tile-size",
+                }).contains(qName)) {
+                    newState = State.MIRROR_ATTRIBUTE;
+                    lang = atts.getValue("lang");
+                } else if ("projections".equals(qName)) {
+                    projections = new ArrayList<>();
+                    newState = State.MIRROR_PROJECTIONS;
                 }
                 break;
@@ -171,4 +191,8 @@
                     projections = new ArrayList<>();
                     newState = State.PROJECTIONS;
+                } else if ("mirror".equals(qName)) {
+                    projections = new ArrayList<>();
+                    newState = State.MIRROR;
+                    mirrorEntry = new ImageryInfo();
                 } else if ("no-tile-header".equals(qName)) {
                     String name = atts.getValue("name");
@@ -214,4 +238,5 @@
                 break;
             case PROJECTIONS:
+            case MIRROR_PROJECTIONS:
                 if ("code".equals(qName)) {
                     newState = State.CODE;
@@ -259,4 +284,65 @@
                 }
                 break;
+            case MIRROR:
+                if ("mirror".equals(qName)) {
+                    if (mirrorEntry != null) {
+                        entry.addMirror(mirrorEntry);
+                        mirrorEntry = null;
+                    }
+                }
+                break;
+            case MIRROR_ATTRIBUTE:
+                if (mirrorEntry != null) {
+                    switch(qName) {
+                    case "type":
+                        boolean found = false;
+                        for (ImageryType type : ImageryType.values()) {
+                            if (Objects.equals(accumulator.toString(), type.getTypeString())) {
+                                mirrorEntry.setImageryType(type);
+                                found = true;
+                                break;
+                            }
+                        }
+                        if (!found) {
+                            mirrorEntry = null;
+                        }
+                        break;
+                    case "url":
+                        mirrorEntry.setUrl(accumulator.toString());
+                        break;
+                    case "min-zoom":
+                    case "max-zoom":
+                        Integer val = null;
+                        try {
+                            val = Integer.valueOf(accumulator.toString());
+                        } catch (NumberFormatException e) {
+                            val = null;
+                        }
+                        if (val == null) {
+                            mirrorEntry = null;
+                        } else {
+                            if ("min-zoom".equals(qName)) {
+                                mirrorEntry.setDefaultMinZoom(val);
+                            } else {
+                                mirrorEntry.setDefaultMaxZoom(val);
+                            }
+                        }
+                        break;
+                    case "tile-size":
+                        Integer tileSize = null;
+                        try {
+                            tileSize = Integer.valueOf(accumulator.toString());
+                        } catch (NumberFormatException e) {
+                            tileSize = null;
+                        }
+                        if (tileSize == null) {
+                            mirrorEntry = null;
+                        } else {
+                            entry.setTileSize(tileSize.intValue());
+                        }
+                        break;
+                    }
+                }
+                break;
             case ENTRY_ATTRIBUTE:
                 switch(qName) {
@@ -379,7 +465,15 @@
                 projections = null;
                 break;
+            case MIRROR_PROJECTIONS:
+                mirrorEntry.setServerProjections(projections);
+                projections = null;
+                break;
+            /* nothing to do for these or the unknown type:
             case NO_TILE:
-                break;
-
+            case NO_TILESUM:
+            case METADATA:
+            case UNKNOWN:
+                break;
+            */
             }
         }
