Index: /trunk/data/maps.xsd
===================================================================
--- /trunk/data/maps.xsd	(revision 13535)
+++ /trunk/data/maps.xsd	(revision 13536)
@@ -20,7 +20,16 @@
         <!-- exclude white space characters and characters that are not valid for file names in Windows -->
         <xs:restriction base="xs:string">
-            <xs:pattern value='[^\s/\\:*?"&lt;>|]+' />
+            <xs:pattern value='[^\s/\\:*?"&lt;>|,;]+' />
         </xs:restriction>
     </xs:simpleType>
+
+    <xs:complexType name="oldid">
+        <xs:simpleContent>
+            <xs:extension base="tns:id">
+                <!-- date of the retirement of this ID -->
+                <xs:attribute name="date" type="xs:date" use="required" />
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
 
     <xs:simpleType name="type">
@@ -623,4 +632,6 @@
                             <!-- A unique id for the imagery source -->
                             <xs:element name="id" minOccurs="0" maxOccurs="1" type="tns:id" />
+                            <!-- Historic id for the imagery source -->
+                            <xs:element name="oldid" minOccurs="0" maxOccurs="unbounded" type="tns:oldid" />
                             <!-- The type. Can be tms, wms and html. In addition, there are the special types bing and scanex 
                                 with hardcoded behaviour. -->
@@ -666,4 +677,5 @@
                                     <xs:all>
                                         <xs:element name="type" minOccurs="1" maxOccurs="1" type="tns:type" />
+                                        <xs:element name="id" minOccurs="0" maxOccurs="1" type="tns:id" />
                                         <xs:element name="url" minOccurs="1" maxOccurs="1" type="xs:string" />
                                         <xs:element name="projections" minOccurs="0" maxOccurs="1" type="tns:projections" />
@@ -707,4 +719,5 @@
                     <xs:attribute name="last-check" type="xs:date" use="optional" />
                     <xs:attribute name="eli-best" type="xs:boolean" use="optional" />
+                    <xs:attribute name="overlay" type="xs:boolean" use="optional" />
                 </xs:complexType>
             </xs:element>
Index: /trunk/scripts/SyncEditorLayerIndex.groovy
===================================================================
--- /trunk/scripts/SyncEditorLayerIndex.groovy	(revision 13535)
+++ /trunk/scripts/SyncEditorLayerIndex.groovy	(revision 13536)
@@ -272,6 +272,8 @@
         stream.write "<imagery xmlns=\"http://josm.openstreetmap.de/maps-1.0\">\n"
         for (def e : entries) {
-            def best = "eli-best".equals(getQuality(e))
-            stream.write "    <entry"+(best ? " eli-best=\"true\"" : "" )+">\n"
+            stream.write("    <entry"
+                + ("eli-best".equals(getQuality(e)) ? " eli-best=\"true\"" : "" )
+                + (getOverlay(e) ? " overlay=\"true\"" : "" )
+                + ">\n")
             stream.write "        <name>${cdata(getName(e), true)}</name>\n"
             stream.write "        <id>${getId(e)}</id>\n"
@@ -608,4 +610,13 @@
                 }
             }
+            et = getOverlay(e)
+            jt = getOverlay(j)
+            if (!et.equals(jt)) {
+                if (!jt) {
+                    myprintln "! Missing JOSM overlay flag: ${getDescription(j)}"
+                } else if (!options.nomissingeli) {
+                    myprintln "+ Missing ELI overlay flag: ${getDescription(j)}"
+                }
+            }
         }
         myprintln "*** Mismatching shapes: ***"
@@ -936,4 +947,9 @@
             && e.get("properties").getBoolean("best")) ? "eli-best" : null
     }
+    static Boolean getOverlay(Object e) {
+        if (e instanceof ImageryInfo) return e.isOverlay()
+        return (e.get("properties").containsKey("overlay")
+            && e.get("properties").getBoolean("overlay"))
+    }
     static String getIcon(Object e) {
         if (e instanceof ImageryInfo) return e.getIcon()
Index: /trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 13535)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 13536)
@@ -27,8 +27,10 @@
 import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.StructUtils;
 import org.openstreetmap.josm.data.StructUtils.StructEntry;
 import org.openstreetmap.josm.io.Capabilities;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.spi.preferences.IPreferences;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -199,4 +201,14 @@
       */
     private boolean bestMarked;
+    /**
+      * marked as overlay
+      * @since 13536
+      */
+    private boolean overlay;
+    /**
+      * list of old IDs, only for loading, not handled anywhere else
+      * @since 13536
+      */
+    private Collection<String> oldIds;
     /** mirrors of different type for this entry */
     private List<ImageryInfo> mirrors;
@@ -245,4 +257,5 @@
         @StructEntry boolean bestMarked;
         @StructEntry boolean modTileFeatures;
+        @StructEntry boolean overlay;
         // TODO: disabled until change of layers is implemented
         // @StructEntry String default_layers;
@@ -271,4 +284,5 @@
             date = i.date;
             bestMarked = i.bestMarked;
+            overlay = i.overlay;
             logo_image = i.attributionImage;
             logo_url = i.attributionImageURL;
@@ -439,4 +453,5 @@
         date = e.date;
         bestMarked = e.bestMarked;
+        overlay = e.overlay;
         termsOfUseText = e.terms_of_use_text;
         termsOfUseURL = e.terms_of_use_url;
@@ -494,4 +509,5 @@
         this.date = i.date;
         this.bestMarked = i.bestMarked;
+        this.overlay = i.overlay;
         // do not copy field {@code mirrors}
         this.icon = i.icon;
@@ -526,4 +542,5 @@
                 Objects.equals(this.modTileFeatures, other.modTileFeatures) &&
                 Objects.equals(this.bestMarked, other.bestMarked) &&
+                Objects.equals(this.overlay, other.overlay) &&
                 Objects.equals(this.isGeoreferenceValid, other.isGeoreferenceValid) &&
                 Objects.equals(this.cookies, other.cookies) &&
@@ -901,4 +918,25 @@
 
     /**
+     * Return the sorted list of activated Imagery IDs
+     * @since 13536
+     */
+    static public Collection<String> getActiveIds() {
+        ArrayList<String> ids = new ArrayList<String>();
+        IPreferences pref = Config.getPref();
+        if (pref != null) {
+            List<ImageryPreferenceEntry> entries = StructUtils.getListOfStructs(
+                pref, "imagery.entries", null, ImageryPreferenceEntry.class);
+            if (entries != null) {
+                for (ImageryPreferenceEntry prefEntry : entries) {
+                    if(prefEntry.id != null && prefEntry.id.length() != 0)
+                        ids.add(prefEntry.id);
+                }
+                Collections.sort(ids);
+            }
+        }
+        return ids;
+    }
+    
+    /**
      * Returns a tool tip text for display.
      * @return The text
@@ -917,4 +955,8 @@
             html = true;
         }
+        if (overlay) {
+            res.append("<br>").append(tr("This imagery is an overlay."));
+            html = true;
+        }
         String desc = getDescription();
         if (desc != null && !desc.isEmpty()) {
@@ -1215,4 +1257,13 @@
 
     /**
+     * Returns the overlay indication.
+     * @return <code>true</code> if it is and overlay.
+     * @since 13536
+     */
+    public boolean isOverlay() {
+        return overlay;
+    }
+
+    /**
      * Sets an indicator that in other editors it is marked as best imagery
      * @param bestMarked <code>true</code> if it is marked as best in other editors.
@@ -1221,4 +1272,36 @@
     public void setBestMarked(boolean bestMarked) {
         this.bestMarked = bestMarked;
+    }
+
+    /**
+     * Sets overlay indication
+     * @param overlay <code>true</code> if it is an overlay.
+     * @since 13536
+     */
+    public void setOverlay(boolean overlay) {
+        this.overlay = overlay;
+    }
+
+    /**
+     * Adds an old Id.
+     *
+     * @param id the Id to be added
+     * @since 13536
+     */
+    public void addOldId(String id) {
+       if (oldIds == null) {
+           oldIds = new ArrayList<>();
+       }
+       oldIds.add(id);
+    }
+
+    /**
+     * Get old Ids.
+     *
+     * @return collection of ids
+     * @since 13536
+     */
+    public Collection<String> getOldIds() {
+        return oldIds;
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 13535)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 13536)
@@ -50,5 +50,5 @@
 
     private static final String[] DEFAULT_LAYER_SITES = {
-        Main.getJOSMWebsite()+"/maps"
+        Main.getJOSMWebsite()+"/maps%<?ids=>"
     };
 
@@ -224,4 +224,15 @@
                 }
                 idMap.put(i.getId(), i);
+                Collection<String> old = i.getOldIds();
+                if(old != null) {
+                    for (String id : old) {
+                        if (idMap.containsKey(id)) {
+                            Logging.error("Old Id ''{0}'' is not unique - used by ''{1}'' and ''{2}''!",
+                                    i.getId(), i.getName(), idMap.get(i.getId()).getName());
+                        } else {
+                            idMap.put(id, i);
+                        }
+                    }
+                }
             }
         }
Index: /trunk/src/org/openstreetmap/josm/io/CachedFile.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/CachedFile.java	(revision 13535)
+++ /trunk/src/org/openstreetmap/josm/io/CachedFile.java	(revision 13536)
@@ -75,4 +75,5 @@
     protected File cacheFile;
     protected boolean initialized;
+    protected String parameter;
 
     public static final long DEFAULT_MAXTIME = -1L;
@@ -173,5 +174,16 @@
     }
 
+    /**
+     * Sets additional URL parameter (used e.g. for maps)
+     * @param parameter the URL parameter
+     * @since 13536
+     */
+    public void setParam(String parameter) {
+        this.parameter = parameter;
+    }
+
     public String getName() {
+        if(parameter != null)
+            return name.replaceAll("%<(.*)>", "");
         return name;
     }
@@ -284,5 +296,5 @@
         }
         if (cacheFile == null)
-            throw new IOException("Unable to get cache file for "+name);
+            throw new IOException("Unable to get cache file for "+getName());
         return cacheFile;
     }
@@ -298,5 +310,5 @@
      * @param extension  the extension of the file we're looking for
      * @param namepart the name part
-     * @return The zip entry path of the matching file. Null if this cached file
+     * @return The zip entry path of the matching file. <code>null</code> if this cached file
      * doesn't represent a zip file or if there was no matching
      * file in the ZIP file.
@@ -312,5 +324,5 @@
      * @param extension  the extension of the file we're looking for
      * @param namepart the name part
-     * @return InputStream to the matching file. Null if this cached file
+     * @return InputStream to the matching file. <code>null</code> if this cached file
      * doesn't represent a zip file or if there was no matching
      * file in the ZIP file.
@@ -380,5 +392,5 @@
             url = new URL(name);
             if (!"file".equals(url.getProtocol())) {
-                String prefKey = getPrefKey(url, destDir);
+                String prefKey = getPrefKey(url, destDir, null);
                 List<String> localPath = new ArrayList<>(Config.getPref().getList(prefKey));
                 if (localPath.size() == 2) {
@@ -403,16 +415,22 @@
      * @return Preference key
      */
-    private static String getPrefKey(URL url, String destDir) {
+    private static String getPrefKey(URL url, String destDir, String parameter) {
         StringBuilder prefKey = new StringBuilder("mirror.");
         if (destDir != null) {
             prefKey.append(destDir).append('.');
         }
-        prefKey.append(url.toString());
+        if (parameter != null) {
+            prefKey.append(url.toString().replaceAll("%<(.*)>", ""));
+        } else {
+            prefKey.append(url.toString());
+        }
         return prefKey.toString().replaceAll("=", "_");
     }
 
     private File checkLocal(URL url) throws IOException {
-        String prefKey = getPrefKey(url, destDir);
+        String prefKey = getPrefKey(url, destDir, parameter);
         String urlStr = url.toExternalForm();
+        if (parameter != null)
+            urlStr = urlStr.replaceAll("%<(.*)>", "");
         long age = 0L;
         long maxAgeMillis = maxAge;
@@ -460,4 +478,16 @@
         }
 
+        if(parameter != null) {
+            String u = url.toExternalForm();
+            String uc;
+            if("".equals(parameter)) {
+                uc = u.replaceAll("%<(.*)>", "");
+            } else {
+                uc = u.replaceAll("%<(.*)>", "$1"+parameter);
+            }
+            if(!uc.equals(u))
+                url = new URL(uc);
+        }
+
         String a = urlStr.replaceAll("[^A-Za-z0-9_.-]", "_");
         String localPath = "mirror_" + a;
Index: /trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 13535)
+++ /trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 13536)
@@ -85,4 +85,5 @@
         try {
             cachedFile = new CachedFile(source);
+            cachedFile.setParam(Utils.join(",", ImageryInfo.getActiveIds()));
             cachedFile.setFastFail(fastFail);
             try (BufferedReader in = cachedFile
@@ -168,4 +169,8 @@
                         entry.setBestMarked(true);
                     }
+                    String overlay = atts.getValue("overlay");
+                    if (TRUE.equals(overlay)) {
+                        entry.setOverlay(true);
+                    }
                 }
                 break;
@@ -174,4 +179,5 @@
                         "type",
                         "url",
+                        "id",
                         MIN_ZOOM,
                         MAX_ZOOM,
@@ -189,4 +195,5 @@
                         "name",
                         "id",
+                        "oldid",
                         "type",
                         "description",
@@ -328,4 +335,7 @@
                         }
                         break;
+                    case "id":
+                        mirrorEntry.setId(accumulator.toString());
+                        break;
                     case "url":
                         mirrorEntry.setUrl(accumulator.toString());
@@ -379,4 +389,7 @@
                 case "id":
                     entry.setId(accumulator.toString());
+                    break;
+                case "oldid":
+                    entry.addOldId(accumulator.toString());
                     break;
                 case "type":
