Index: trunk/src/org/openstreetmap/josm/data/ImageData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/ImageData.java	(revision 18590)
+++ trunk/src/org/openstreetmap/josm/data/ImageData.java	(revision 18591)
@@ -11,4 +11,5 @@
 import org.openstreetmap.josm.data.gpx.GpxImageEntry;
 import org.openstreetmap.josm.data.osm.QuadBuckets;
+import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry;
 import org.openstreetmap.josm.tools.ListenerList;
@@ -42,4 +43,5 @@
     private final ListenerList<ImageDataUpdateListener> listeners = ListenerList.create();
     private final QuadBuckets<ImageEntry> geoImages = new QuadBuckets<>();
+    private Layer layer;
 
     /**
@@ -327,5 +329,5 @@
 
     /**
-     * Remove the image from the list and optionnally trigger update listener
+     * Remove the image from the list and optionally trigger update listener
      * @param img the {@link ImageEntry} to remove
      * @param fireUpdateEvent if {@code true}, notifies listeners of image update
@@ -377,4 +379,22 @@
 
     /**
+     * Set the layer for use with {@link org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog#displayImages(Layer, List)}
+     * @param layer The layer to use for organization
+     * @since 18591
+     */
+    public void setLayer(Layer layer) {
+        this.layer = layer;
+    }
+
+    /**
+     * Get the layer that this data is associated with. May be {@code null}.
+     * @return The layer this data is associated with.
+     * @since 18591
+     */
+    public Layer getLayer() {
+        return this.layer;
+    }
+
+    /**
      * Add a listener that listens to image data changes
      * @param listener the {@link ImageDataUpdateListener}
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 18590)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 18591)
@@ -172,4 +172,5 @@
         this.useThumbs = useThumbs;
         this.data.addImageDataUpdateListener(this);
+        this.data.setLayer(this);
     }
 
@@ -232,4 +233,5 @@
                 } else {
                     data.setSelectedImage(img);
+                    ImageViewerDialog.getInstance().displayImages(GeoImageLayer.this, Collections.singletonList(img));
                 }
             }
@@ -522,7 +524,4 @@
      */
     public void showCurrentPhoto() {
-        if (data.getSelectedImage() != null) {
-            clearOtherCurrentPhotos();
-        }
         updateBufferAndRepaint();
     }
@@ -630,16 +629,4 @@
 
     /**
-     * Clears the currentPhoto of the other GeoImageLayer's. Otherwise there could be multiple selected photos.
-     */
-    private void clearOtherCurrentPhotos() {
-        for (GeoImageLayer layer:
-                 MainApplication.getLayerManager().getLayersOfType(GeoImageLayer.class)) {
-            if (layer != this) {
-                layer.getImageData().clearSelectedImage();
-            }
-        }
-    }
-
-    /**
      * Registers a map mode for which the functionality of this layer should be available.
      * @param mapMode Map mode to be registered
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 18590)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 18591)
@@ -154,8 +154,8 @@
     @Override
     public void selectImage(ImageViewerDialog imageViewerDialog, IImageEntry<?> entry) {
-        IImageEntry.super.selectImage(imageViewerDialog, entry);
         if (entry instanceof ImageEntry) {
             this.dataSet.setSelectedImage((ImageEntry) entry);
         }
+        imageViewerDialog.displayImages(this.dataSet.getLayer(), Collections.singletonList(entry));
     }
 
@@ -212,5 +212,5 @@
     }
 
-    private ImageReadParam withSubsampling(ImageReader reader, Dimension target) {
+    private static ImageReadParam withSubsampling(ImageReader reader, Dimension target) {
         try {
             ImageReadParam param = reader.getDefaultReadParam();
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 18590)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 18591)
@@ -12,4 +12,5 @@
 import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.WindowEvent;
@@ -22,5 +23,8 @@
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -37,4 +41,5 @@
 import javax.swing.JToggleButton;
 import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
 
 import org.openstreetmap.josm.actions.JosmAction;
@@ -53,7 +58,9 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.layer.MainLayerManager;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
 import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
+import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.imagery.Vector3D;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -121,4 +128,6 @@
     private JButton btnDeleteFromDisk;
     private JToggleButton tbCentre;
+    /** The layer tab (used to select images when multiple layers provide images, makes for easy switching) */
+    private JPanel layers;
 
     private ImageViewerDialog() {
@@ -153,4 +162,6 @@
     private void build() {
         JPanel content = new JPanel(new BorderLayout());
+        this.layers = new JPanel(new GridBagLayout());
+        content.add(layers, BorderLayout.NORTH);
 
         content.add(imgDisplay, BorderLayout.CENTER);
@@ -212,4 +223,63 @@
 
         createLayout(content, false, null);
+    }
+
+    private void updateLayers() {
+        if (this.tabbedEntries.size() <= 1) {
+            this.layers.setVisible(false);
+            this.layers.removeAll();
+        } else {
+            this.layers.setVisible(true);
+            // Remove all old components
+            this.layers.removeAll();
+            MainLayerManager layerManager = MainApplication.getLayerManager();
+            List<Layer> invalidLayers = this.tabbedEntries.keySet().stream().filter(layer -> !layerManager.containsLayer(layer))
+                    .collect(Collectors.toList());
+            // `null` is for anything using the old methods, without telling us what layer it comes from.
+            invalidLayers.remove(null);
+            // We need to do multiple calls to avoid ConcurrentModificationExceptions
+            invalidLayers.forEach(this.tabbedEntries::remove);
+            addButtonsForImageLayers();
+        }
+        this.revalidate();
+    }
+
+    /**
+     * Add the buttons for image layers
+     */
+    private void addButtonsForImageLayers() {
+        final IImageEntry<?> current;
+        synchronized (this) {
+            current = this.currentEntry;
+        }
+        List<JButton> layerButtons = new ArrayList<>(this.tabbedEntries.size());
+        if (this.tabbedEntries.containsKey(null)) {
+            List<IImageEntry<?>> nullEntries = this.tabbedEntries.get(null);
+            JButton layerButton = createImageLayerButton(null, nullEntries);
+            layerButtons.add(layerButton);
+            layerButton.setEnabled(!nullEntries.contains(current));
+        }
+        for (Map.Entry<Layer, List<IImageEntry<?>>> entry :
+                this.tabbedEntries.entrySet().stream().filter(entry -> entry.getKey() != null)
+                        .sorted(Comparator.comparing(entry -> entry.getKey().getName())).collect(Collectors.toList())) {
+            JButton layerButton = createImageLayerButton(entry.getKey(), entry.getValue());
+            layerButtons.add(layerButton);
+            layerButton.setEnabled(!entry.getValue().contains(current));
+        }
+        layerButtons.forEach(this.layers::add);
+    }
+
+    /**
+     * Create a button for a specific layer and its entries
+     *
+     * @param layer     The layer to switch to
+     * @param entries   The entries to display
+     * @return The button to use to switch to the specified layer
+     */
+    private static JButton createImageLayerButton(Layer layer, List<IImageEntry<?>> entries) {
+        final JButton layerButton = new JButton();
+        layerButton.addActionListener(new ImageActionListener(layer, entries));
+        layerButton.setText(layer != null ? layer.getLabel() : tr("Default"));
+        return layerButton;
     }
 
@@ -307,4 +377,7 @@
         }
 
+        /**
+         * Update the icon for this action
+         */
         public void updateIcon() {
             if (this.last != null) {
@@ -350,4 +423,23 @@
             }
             return super.getSupplier();
+        }
+    }
+
+    /**
+     * A listener that is called to change the viewing layer
+     */
+    private static class ImageActionListener implements ActionListener {
+
+        private final Layer layer;
+        private final List<IImageEntry<?>> entries;
+
+        ImageActionListener(Layer layer, List<IImageEntry<?>> entries) {
+            this.layer = layer;
+            this.entries = entries;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            ImageViewerDialog.getInstance().displayImages(this.layer, this.entries);
         }
     }
@@ -552,4 +644,6 @@
     }
 
+    /** Used for tabbed panes */
+    private final transient Map<Layer, List<IImageEntry<?>>> tabbedEntries = new HashMap<>();
     private transient IImageEntry<? extends IImageEntry<?>> currentEntry;
 
@@ -579,4 +673,14 @@
      */
     public void displayImages(List<IImageEntry<?>> entries) {
+        this.displayImages((Layer) null, entries);
+    }
+
+    /**
+     * Displays images for the given layer.
+     * @param layer The layer to use for the tab ui
+     * @param entries image entries
+     * @since 18591
+     */
+    public void displayImages(Layer layer, List<IImageEntry<?>> entries) {
         boolean imageChanged;
         IImageEntry<?> entry = entries != null && entries.size() == 1 ? entries.get(0) : null;
@@ -599,8 +703,23 @@
         }
 
+        if (entries == null || entries.isEmpty() || entries.stream().allMatch(Objects::isNull)) {
+            this.tabbedEntries.remove(layer);
+        } else {
+            this.tabbedEntries.put(layer, entries);
+        }
+        this.updateLayers();
         if (entry != null) {
             this.updateButtonsNonNullEntry(entry, imageChanged);
+        } else if (this.tabbedEntries.isEmpty()) {
+            this.updateButtonsNullEntry(entries);
+            return;
         } else {
-            this.updateButtonsNullEntry(entries);
+            Map.Entry<Layer, List<IImageEntry<?>>> realEntry =
+                    this.tabbedEntries.entrySet().stream().filter(mapEntry -> mapEntry.getValue().size() == 1).findFirst().orElse(null);
+            if (realEntry == null) {
+                this.updateButtonsNullEntry(entries);
+            } else {
+                this.displayImages(realEntry.getKey(), realEntry.getValue());
+            }
             return;
         }
@@ -731,4 +850,5 @@
             btnCollapse.setVisible(!isDocked);
         }
+        this.updateLayers();
     }
 
@@ -798,4 +918,17 @@
     }
 
+    /**
+     * Reload the image. Call this if you load a low-resolution image first, and then get a high-resolution image, or
+     * if you know that the image has changed on disk.
+     * @since 18591
+     */
+    public void refresh() {
+        if (SwingUtilities.isEventDispatchThread()) {
+            this.updateButtonsNonNullEntry(currentEntry, true);
+        } else {
+            GuiHelper.runInEDT(this::refresh);
+        }
+    }
+
     private void registerOnLayer(Layer layer) {
         if (layer instanceof GeoImageLayer) {
@@ -820,10 +953,13 @@
     @Override
     public void selectedImageChanged(ImageData data) {
-        displayImages(new ArrayList<>(data.getSelectedImages()));
+        if (this.currentEntry != data.getSelectedImage() && this.currentEntry instanceof ImageEntry &&
+                !data.getSelectedImages().contains(this.currentEntry)) {
+            displayImages(data.getLayer(), new ArrayList<>(data.getSelectedImages()));
+        }
     }
 
     @Override
     public void imageDataUpdated(ImageData data) {
-        displayImages(new ArrayList<>(data.getSelectedImages()));
+        displayImages(data.getLayer(), new ArrayList<>(data.getSelectedImages()));
     }
 }
