Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 7185)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 7186)
@@ -10,4 +10,5 @@
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -146,9 +147,11 @@
         }
     }
-
+    
     /** name of the imagery entry (gets translated by josm usually) */
     private String name;
     /** original name of the imagery entry in case of translation call */
     private String origName;
+    /** id for this imagery entry, optional at the moment */
+    private String id;
     private String url = null;
     private boolean defaultEntry = false;
@@ -176,4 +179,5 @@
     public static class ImageryPreferenceEntry {
         @pref String name;
+        @pref String id;
         @pref String type;
         @pref String url;
@@ -207,4 +211,5 @@
         public ImageryPreferenceEntry(ImageryInfo i) {
             name = i.name;
+            id = i.id;
             type = i.imageryType.getTypeString();
             url = i.url;
@@ -249,5 +254,10 @@
         @Override
         public String toString() {
-            return "ImageryPreferenceEntry [name=" + name + "]";
+            String s = "ImageryPreferenceEntry [name=" + name;
+            if (id != null) {
+                s += " id=" + id;
+            }
+            s += "]";
+            return s;
         }
     }
@@ -308,4 +318,5 @@
         CheckParameterUtil.ensureParameterNotNull(e.url, "url");
         name = e.name;
+        id = e.id;
         url = e.url;
         cookies = e.cookies;
@@ -347,4 +358,5 @@
     public ImageryInfo(ImageryInfo i) {
         this.name = i.name;
+        this.id = i.id;
         this.url = i.url;
         this.defaultEntry = i.defaultEntry;
@@ -380,5 +392,76 @@
         return true;
     }
-
+    
+    /**
+     * Check if this object equals another ImageryInfo with respect to the properties
+     * that get written to the preference file.
+     * 
+     * The field {@link #pixelPerDegree} is ignored.
+     * 
+     * @param other the ImageryInfo object to compare to
+     * @return true if they are equal
+     */
+    public boolean equalsPref(ImageryInfo other) {
+        if (other == null) {
+            return false;
+        }
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (!Objects.equals(this.id, other.id)) {
+            return false;
+        }
+        if (!Objects.equals(this.url, other.url)) {
+            return false;
+        }
+        if (!Objects.equals(this.cookies, other.cookies)) {
+            return false;
+        }
+        if (!Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired)) {
+            return false;
+        }
+        if (this.imageryType != other.imageryType) {
+            return false;
+        }
+        if (this.defaultMaxZoom != other.defaultMaxZoom) {
+            return false;
+        }
+        if (this.defaultMinZoom != other.defaultMinZoom) {
+            return false;
+        }
+        if (!Objects.equals(this.bounds, other.bounds)) {
+            return false;
+        }
+        if (!Objects.equals(this.serverProjections, other.serverProjections)) {
+            return false;
+        }
+        if (!Objects.equals(this.attributionText, other.attributionText)) {
+            return false;
+        }
+        if (!Objects.equals(this.attributionLinkURL, other.attributionLinkURL)) {
+            return false;
+        }
+        if (!Objects.equals(this.attributionImage, other.attributionImage)) {
+            return false;
+        }
+        if (!Objects.equals(this.attributionImageURL, other.attributionImageURL)) {
+            return false;
+        }
+        if (!Objects.equals(this.termsOfUseText, other.termsOfUseText)) {
+            return false;
+        }
+        if (!Objects.equals(this.termsOfUseURL, other.termsOfUseURL)) {
+            return false;
+        }
+        if (!Objects.equals(this.countryCode, other.countryCode)) {
+            return false;
+        }
+        if (!Objects.equals(this.icon, other.icon)) {
+            return false;
+        }
+        return true;
+    }
+
+    
     @Override
     public int hashCode() {
@@ -594,4 +677,24 @@
 
     /**
+     * Gets the entry id.
+     * 
+     * Id can be null. This gets the configured id as is. Due to a user error,
+     * this may not be unique. Use {@link ImageryLayerInfo#getUniqueId} to ensure
+     * a unique value.
+     * @return the id
+     */
+    public String getId() {
+        return this.id;
+    }
+
+    /**
+     * Sets the entry id.
+     * @param id the entry id
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
      * Returns the entry URL.
      * @return The entry URL
Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 7185)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 7186)
@@ -7,7 +7,11 @@
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-
+import java.util.Set;
+import java.util.TreeSet;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
@@ -22,6 +26,8 @@
 
     public static final ImageryLayerInfo instance = new ImageryLayerInfo();
-    List<ImageryInfo> layers = new ArrayList<>();
-    static List<ImageryInfo> defaultLayers = new ArrayList<>();
+    private final List<ImageryInfo> layers = new ArrayList<>();
+    private final Map<String, ImageryInfo> layerIds = new HashMap<>();
+    private final static List<ImageryInfo> defaultLayers = new ArrayList<>();
+    private final static Map<String, ImageryInfo> defaultLayerIds = new HashMap<>();
 
     private static final String[] DEFAULT_LAYER_SITES = {
@@ -38,8 +44,9 @@
     public void clear() {
         layers.clear();
+        layerIds.clear();
     }
 
     public void load() {
-        boolean addedDefault = !layers.isEmpty();
+        clear();
         List<ImageryPreferenceEntry> entries = Main.pref.getListOfStructs("imagery.entries", null, ImageryPreferenceEntry.class);
         if (entries != null) {
@@ -54,7 +61,5 @@
             Collections.sort(layers);
         }
-        if (addedDefault) {
-            save();
-        }
+        loadDefaults(false);
     }
 
@@ -70,4 +75,5 @@
     public void loadDefaults(boolean clearCache) {
         defaultLayers.clear();
+        defaultLayerIds.clear();
         for (String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES))) {
             if (clearCache) {
@@ -80,20 +86,51 @@
             } catch (IOException ex) {
                 Main.error(ex, false);
-                continue;
             } catch (SAXException ex) {
                 Main.error(ex);
-                continue;
             }
         }
         while (defaultLayers.remove(null));
-
-        Collection<String> defaults = Main.pref.getCollection("imagery.layers.default");
-        List<String> defaultsSave = new ArrayList<>();
+        Collections.sort(defaultLayers);
+        buildIdMap(defaultLayers, defaultLayerIds);
+        updateEntriesFromDefaults();
+        buildIdMap(layers, layerIds);
+    }
+    
+    /**
+     * Build the mapping of unique ids to {@link ImageryInfo}s.
+     * @param lst input list
+     * @param idMap output map
+     */
+    private static void buildIdMap(List<ImageryInfo> lst, Map<String, ImageryInfo> idMap) {
+        idMap.clear();
+        Set<String> notUnique = new HashSet<>();
+        for (ImageryInfo i : lst) {
+            if (i.getId() != null) {
+                if (idMap.containsKey(i.getId())) {
+                    notUnique.add(i.getId());
+                    Main.error("Id ''{0}'' is not unique - used by ''{1}'' and ''{2}''!",
+                            i.getId(), i.getName(), idMap.get(i.getId()).getName());
+                    continue;
+                }
+                idMap.put(i.getId(), i);
+            }
+        }
+        for (String i : notUnique) {
+            idMap.remove(i);
+        }
+    }
+    
+    /**
+     * Update user entries according to the list of default entries.
+     */
+    public void updateEntriesFromDefaults() {
+        // add new default entries to the user selection
+        boolean changed = false;
+        Collection<String> knownDefaults = Main.pref.getCollection("imagery.layers.default");
+        Collection<String> newKnownDefaults = new TreeSet<>(knownDefaults);
         for (ImageryInfo def : defaultLayers) {
             if (def.isDefaultEntry()) {
-                defaultsSave.add(def.getUrl());
-
                 boolean isKnownDefault = false;
-                for (String url : defaults) {
+                for (String url : knownDefaults) {
                     if (isSimilar(url, def.getUrl())) {
                         isKnownDefault = true;
@@ -103,6 +140,7 @@
                 boolean isInUserList = false;
                 if (!isKnownDefault) {
+                    newKnownDefaults.add(def.getUrl());
                     for (ImageryInfo i : layers) {
-                        if (isSimilar(def.getUrl(), i.getUrl())) {
+                        if (isSimilar(def, i)) {
                             isInUserList = true;
                             break;
@@ -112,17 +150,62 @@
                 if (!isKnownDefault && !isInUserList) {
                     add(new ImageryInfo(def));
-                }
-            }
-        }
-
-        Collections.sort(defaultLayers);
-        Main.pref.putCollection("imagery.layers.default", defaultsSave.isEmpty() ? defaults : defaultsSave);
-    }
-
+                    changed = true;
+                }
+            }
+        }
+        Main.pref.putCollection("imagery.layers.default", newKnownDefaults);
+
+        // Add ids to user entries without id.
+        // Only do this the first time for each id, so the user can have
+        // custom entries that don't get updated automatically
+        Collection<String> addedIds = Main.pref.getCollection("imagery.layers.addedIds");
+        Collection<String> newAddedIds = new TreeSet<>(addedIds);
+        for (ImageryInfo info : layers) {
+            for (ImageryInfo def : defaultLayers) {
+                if (isSimilar(def, info)) {
+                    if (def.getId() != null && !addedIds.contains(def.getId())) {
+                        if (!defaultLayerIds.containsKey(def.getId())) {
+                            // ignore ids used more than once (have been purged from the map)
+                            continue;
+                        }
+                        newAddedIds.add(def.getId());
+                        if (info.getId() == null) {
+                            info.setId(def.getId());
+                            changed = true;
+                        }
+                    }
+                }
+            }
+        }
+        Main.pref.putCollection("imagery.layers.migration.addedIds", newAddedIds);
+        
+        // automatically update user entries with same id as a default entry
+        for (int i=0; i<layers.size(); i++) {
+            ImageryInfo info = layers.get(i);
+            if (info.getId() == null) {
+                continue;
+            }
+            ImageryInfo matchingDefault = defaultLayerIds.get(info.getId());
+            if (matchingDefault != null && !matchingDefault.equalsPref(info)) {
+                layers.set(i, matchingDefault);
+                changed = true;
+            }
+        }
+        
+        if (changed) {
+            save();
+        }
+    }
+
+    private boolean isSimilar(ImageryInfo iiA, ImageryInfo iiB) {
+        if (iiA.getId() != null && iiB.getId() != null) return iiA.getId().equals(iiB.getId());
+        return isSimilar(iiA.getUrl(), iiB.getUrl());
+    }
+    
     // some additional checks to respect extended URLs in preferences (legacy workaround)
     private boolean isSimilar(String a, String b) {
         return Objects.equals(a, b) || (a != null && b != null && !a.isEmpty() && !b.isEmpty() && (a.contains(b) || b.contains(a)));
     }
-
+    
     public void add(ImageryInfo info) {
         layers.add(info);
@@ -161,3 +244,18 @@
         Collections.sort(instance.layers);
     }
+    
+    /**
+     * Get unique id for ImageryInfo.
+     * 
+     * This takes care, that no id is used twice (due to a user error)
+     * @param info the ImageryInfo to look up
+     * @return null, if there is no id or the id is used twice,
+     * the corresponding id otherwise
+     */
+    public String getUniqueId(ImageryInfo info) {
+        if (info.getId() != null && layerIds.get(info.getId()) == info) {
+            return info.getId();
+        }
+        return null;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/imagery/Shape.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/Shape.java	(revision 7185)
+++ trunk/src/org/openstreetmap/josm/data/imagery/Shape.java	(revision 7186)
@@ -7,4 +7,5 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 import org.openstreetmap.gui.jmapviewer.Coordinate;
@@ -84,3 +85,27 @@
         coords.add(new Coordinate(LatLon.roundToOsmPrecision(lat), LatLon.roundToOsmPrecision(lon)));
     }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 47 * hash + Objects.hashCode(this.coords);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Shape other = (Shape) obj;
+        if (!Objects.equals(this.coords, other.coords)) {
+            return false;
+        }
+        return true;
+    }
+    
+    
 }
Index: trunk/src/org/openstreetmap/josm/gui/ImageryMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/ImageryMenu.java	(revision 7185)
+++ trunk/src/org/openstreetmap/josm/gui/ImageryMenu.java	(revision 7186)
@@ -80,7 +80,7 @@
     };
 
-    private JMenuItem singleOffset = new JMenuItem(offsetAction);
+    private final JMenuItem singleOffset = new JMenuItem(offsetAction);
     private JMenuItem offsetMenuItem = singleOffset;
-    private MapRectifierWMSmenuAction rectaction = new MapRectifierWMSmenuAction();
+    private final MapRectifierWMSmenuAction rectaction = new MapRectifierWMSmenuAction();
 
     public ImageryMenu(JMenu subMenu) {
Index: trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 7185)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 7186)
@@ -880,6 +880,4 @@
      */
     public static void initialize() {
-        ImageryLayerInfo.instance.clear();
-        ImageryLayerInfo.instance.loadDefaults(false);
         ImageryLayerInfo.instance.load();
         OffsetBookmark.loadBookmarks();
Index: trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 7185)
+++ trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java	(revision 7186)
@@ -112,4 +112,5 @@
                 if (Arrays.asList(new String[] {
                         "name",
+                        "id",
                         "type",
                         "default",
@@ -204,4 +205,7 @@
                     entry.setTranslatedName(accumulator.toString());
                     break;
+                case "id":
+                    entry.setId(accumulator.toString());
+                    break;
                 case "type":
                     boolean found = false;
