Index: src/org/openstreetmap/josm/Main.java
===================================================================
--- src/org/openstreetmap/josm/Main.java	(revision 9687)
+++ src/org/openstreetmap/josm/Main.java	(working copy)
@@ -1045,7 +1045,10 @@
                 continue;
             }
             AbstractModifiableLayer odl = (AbstractModifiableLayer) l;
-            if ((odl.requiresSaveToFile() || (odl.requiresUploadToServer() && !odl.isUploadDiscouraged())) && odl.isModified()) {
+            if (odl.isModified() &&
+                    ((!odl.isSavable() && !odl.isUploadable()) ||
+                     odl.requiresSaveToFile() ||
+                     (odl.requiresUploadToServer() && !odl.isUploadDiscouraged()))) {
                 layersWithUnmodifiedChanges.add(odl);
             }
         }
Index: src/org/openstreetmap/josm/gui/io/ActionFlagsTableCell.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/ActionFlagsTableCell.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/io/ActionFlagsTableCell.java	(working copy)
@@ -56,7 +56,6 @@
 
         ActionMap am = getActionMap();
         for (final JCheckBox b : checkBoxes) {
-            add(b, GBC.eol().fill(GBC.HORIZONTAL));
             b.setPreferredSize(new Dimension(b.getPreferredSize().width, 19));
             b.addActionListener(al);
             am.put(b.getText(), new AbstractAction() {
@@ -67,12 +66,6 @@
                 }
             });
         }
-
-        setToolTipText(tr("<html>"+
-            "Select which actions to perform for this layer, if you click the leftmost button.<br/>"+
-            "Check \"upload\" to upload the changes to the OSM server.<br/>"+
-            "Check \"Save\" to save the layer to the file specified on the left."+
-            "</html>"));
     }
 
     protected void updateCheckboxes(Object v) {
@@ -90,8 +83,30 @@
         }
     }
 
+    private void updatePanel(SaveLayerInfo info) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        sb.append(tr("Select which actions to perform for this layer, if you click the leftmost button."));
+        removeAll();
+        if (info != null) {
+            if (info.isUploadable()) {
+                sb.append("<br/>");
+                sb.append(tr("Check \"Upload\" to upload the changes to the OSM server."));
+                add(checkBoxes[0], GBC.eol().fill(GBC.HORIZONTAL));
+            }
+            if (info.isSavable()) {
+                sb.append("<br/>");
+                sb.append(tr("Check \"Save\" to save the layer to the file specified on the left."));
+                add(checkBoxes[1], GBC.eol().fill(GBC.HORIZONTAL));
+            }
+        }
+        sb.append("</html>");
+        setToolTipText(sb.toString());
+    }
+
     @Override
     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+        updatePanel((SaveLayerInfo) value);
         updateCheckboxes(value);
         return this;
     }
@@ -137,6 +152,7 @@
 
     @Override
     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+        updatePanel((SaveLayerInfo) value);
         updateCheckboxes(value);
         return this;
     }
Index: src/org/openstreetmap/josm/gui/io/LayerNameAndFilePathTableCell.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/LayerNameAndFilePathTableCell.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/io/LayerNameAndFilePathTableCell.java	(working copy)
@@ -80,11 +80,13 @@
         SaveLayerInfo info = (SaveLayerInfo) value;
         StringBuilder sb = new StringBuilder();
         sb.append("<html>")
-          .append(addLblLayerName(info))
-          .append("<br>");
-        add(btnFileChooser, GBC.std());
-        sb.append(addLblFilename(info))
-          .append("</html>");
+          .append(addLblLayerName(info));
+        if (info.isSavable()) {
+            add(btnFileChooser, GBC.std());
+            sb.append("<br>")
+              .append(addLblFilename(info));
+        }
+        sb.append("</html>");
         setToolTipText(sb.toString());
         return this;
     }
@@ -98,15 +100,17 @@
 
         StringBuilder sb = new StringBuilder();
         sb.append("<html>")
-          .append(addLblLayerName(info))
-          .append("<br/>");
+          .append(addLblLayerName(info));
 
-        add(btnFileChooser, GBC.std());
-        add(tfFilename, GBC.eol().fill(GBC.HORIZONTAL).insets(1, 0, 0, 0));
-        tfFilename.selectAll();
+        if (info.isSavable()) {
+            add(btnFileChooser, GBC.std());
+            add(tfFilename, GBC.eol().fill(GBC.HORIZONTAL).insets(1, 0, 0, 0));
+            tfFilename.selectAll();
 
-        sb.append(tfFilename.getToolTipText())
-          .append("</html>");
+            sb.append("<br>")
+              .append(tfFilename.getToolTipText());
+        }
+        sb.append("</html>");
         setToolTipText(sb.toString());
         return this;
     }
Index: src/org/openstreetmap/josm/gui/io/SaveLayerInfo.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/SaveLayerInfo.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/io/SaveLayerInfo.java	(working copy)
@@ -46,6 +46,24 @@
     }
 
     /**
+     * Replies true if the layer can be saved to a file
+     *
+     * @return {@code true} if the layer can be saved to a file; {@code false} otherwise
+     */
+    public boolean isSavable() {
+        return layer.isSavable();
+    }
+
+    /**
+     * Replies true if the layer can be uploaded to a server
+     *
+     * @return {@code true} if the layer can be uploaded to a server; {@code false} otherwise
+     */
+    public boolean isUploadable() {
+        return layer.isUploadable();
+    }
+
+    /**
      * Replies true if preconditions should be checked before saving; false, otherwise
      *
      * @return true if preconditions should be checked before saving; false, otherwise
@@ -80,7 +98,7 @@
      * @param doSaveToFile true to save; false, to skip saving
      */
     public void setDoSaveToFile(boolean doSaveToFile) {
-        this.doSaveToFile = doSaveToFile;
+        this.doSaveToFile = isSavable() ? doSaveToFile : false;
     }
 
     /**
@@ -93,13 +111,12 @@
     }
 
     /**
-     * Sets whether this layer should be uploaded to a file
+     * Sets whether this layer should be uploaded to a server
      *
      * @param doUploadToServer {@code true} to upload; {@code false}, to skip uploading
      */
-
     public void setDoUploadToServer(boolean doUploadToServer) {
-        this.doUploadToServer = doUploadToServer;
+        this.doUploadToServer = isUploadable() ? doUploadToServer : false;
     }
 
     /**
Index: src/org/openstreetmap/josm/gui/io/SaveLayersModel.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/SaveLayersModel.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/io/SaveLayersModel.java	(working copy)
@@ -102,15 +102,18 @@
 
     @Override
     public void setValueAt(Object value, int row, int column) {
+        final SaveLayerInfo info = this.layerInfo.get(row);
         switch(column) {
         case columnFilename:
-            this.layerInfo.get(row).setFile((File) value);
-            this.layerInfo.get(row).setDoSaveToFile(true);
+            info.setFile((File) value);
+            if (info.isSavable()) {
+                info.setDoSaveToFile(true);
+            }
             break;
         case columnActions:
             boolean[] values = (boolean[]) value;
-            this.layerInfo.get(row).setDoUploadToServer(values[0]);
-            this.layerInfo.get(row).setDoSaveToFile(values[1]);
+            info.setDoUploadToServer(values[0]);
+            info.setDoSaveToFile(values[1]);
             break;
         }
         fireTableDataChanged();
Index: src/org/openstreetmap/josm/gui/io/SaveLayersTableColumnModel.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/SaveLayersTableColumnModel.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/io/SaveLayersTableColumnModel.java	(working copy)
@@ -46,7 +46,9 @@
                     sb.append(tr("Layer ''{0}'' has modifications which should be uploaded to the server.", info.getName()));
 
                 } else {
-                    panel.add(pnlEmpty, defaultCellStyle);
+                    if (info.isUploadable()) {
+                        panel.add(pnlEmpty, defaultCellStyle);
+                    }
                     if (info.getLayer().requiresUploadToServer()) {
                         sb.append(tr("Layer ''{0}'' has modifications which are discouraged to be uploaded.", info.getName()));
                     } else {
@@ -53,14 +55,16 @@
                         sb.append(tr("Layer ''{0}'' has no modifications to be uploaded.", info.getName()));
                     }
                 }
+
                 sb.append("<br/>");
-
                 if (info.getLayer().requiresSaveToFile()) {
                     panel.add(needsSave, defaultCellStyle);
                     sb.append(tr("Layer ''{0}'' has modifications which should be saved to its associated file ''{1}''.",
                             info.getName(), info.getFile().toString()));
                 } else {
-                    panel.add(pnlEmpty, defaultCellStyle);
+                    if (info.isSavable()) {
+                        panel.add(pnlEmpty, defaultCellStyle);
+                    }
                     sb.append(tr("Layer ''{0}'' has no modifications to be saved.", info.getName()));
                 }
             }
Index: src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java	(working copy)
@@ -9,7 +9,7 @@
  * A modifiable layer.
  * @since 7358
  */
-public abstract class AbstractModifiableLayer extends Layer {
+public abstract class AbstractModifiableLayer extends Layer implements UploadToServer, SaveToFile {
 
     /**
      * Constructs a new {@code ModifiableLayer}.
@@ -20,6 +20,18 @@
     }
 
     /**
+     * Determines if the layer is able to upload data and implements the
+     * {@code UploadToServer} interface.
+     *
+     * @return true if the layer is able to upload data; false, otherwise
+     */
+    @Override
+    public boolean isUploadable() {
+        // Override if needed
+        return false;
+    }
+
+    /**
      * Determines if the data managed by this layer needs to be uploaded to
      * the server because it contains modified data.
      *
@@ -26,6 +38,7 @@
      * @return true if the data managed by this layer needs to be uploaded to
      * the server because it contains modified data; false, otherwise
      */
+    @Override
     public boolean requiresUploadToServer() {
         // Override if needed
         return false;
@@ -39,6 +52,7 @@
      *
      * @return true if the data managed by this layer needs to be saved to a file
      */
+    @Override
     public boolean requiresSaveToFile() {
         // Override if needed
         return false;
@@ -50,6 +64,7 @@
      *
      * @return true if upload is discouraged for this layer; false, otherwise
      */
+    @Override
     public boolean isUploadDiscouraged() {
         // Override if needed
         return false;
@@ -64,6 +79,7 @@
     /**
      * Initializes the layer after a successful save of data to a file.
      */
+    @Override
     public void onPostSaveToFile() {
         // Override if needed
     }
@@ -71,6 +87,7 @@
     /**
      * Initializes the layer after a successful upload to the server.
      */
+    @Override
     public void onPostUploadToServer() {
         // Override if needed
     }
@@ -80,6 +97,7 @@
      * @param monitor The progress monitor
      * @return a new {@code AbstractIOTask} for uploading data, or {@code null} if not applicable
      */
+    @Override
     public AbstractIOTask createUploadTask(ProgressMonitor monitor) {
         // Override if needed
         return null;
@@ -89,6 +107,7 @@
      * Returns the upload dialog for this layer.
      * @return the upload dialog for this layer, or {@code null} if not applicable
      */
+    @Override
     public AbstractUploadDialog getUploadDialog() {
         // Override if needed
         return null;
Index: src/org/openstreetmap/josm/gui/layer/NoteLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(working copy)
@@ -47,7 +47,7 @@
  * A layer to hold Note objects.
  * @since 7522
  */
-public class NoteLayer extends AbstractModifiableLayer implements MouseListener {
+public class NoteLayer extends AbstractModifiableLayer implements MouseListener, UploadToServer, SaveToFile {
 
     private final NoteData noteData;
 
@@ -85,6 +85,11 @@
     }
 
     @Override
+    public boolean isUploadable() {
+        return true;
+    }
+
+    @Override
     public boolean requiresUploadToServer() {
         return isModified();
     }
Index: src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(working copy)
@@ -107,7 +107,7 @@
  * @author imi
  * @since 17
  */
-public class OsmDataLayer extends AbstractModifiableLayer implements Listener, SelectionChangedListener {
+public class OsmDataLayer extends AbstractModifiableLayer implements Listener, SelectionChangedListener, UploadToServer, SaveToFile {
     /** Property used to know if this layer has to be saved on disk */
     public static final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
     /** Property used to know if this layer has to be uploaded */
@@ -832,6 +832,11 @@
     }
 
     @Override
+    public boolean isUploadable() {
+        return true;
+    }
+
+    @Override
     public boolean requiresUploadToServer() {
         return requiresUploadToServer;
     }
Index: src/org/openstreetmap/josm/gui/layer/SaveToFile.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/SaveToFile.java	(revision 0)
+++ src/org/openstreetmap/josm/gui/layer/SaveToFile.java	(working copy)
@@ -0,0 +1,32 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+/**
+ * Interface for layers that can save data to a file.
+ */
+public interface SaveToFile {
+
+    /**
+     * Replies the savable state of the layer (i.e. if it can be saved through
+     * a "File-&gt;Save" dialog).  A layer that implements the
+     * {@code SaveToFile} interface must return {@code true}.
+     *
+     * @return {@code true} if the layer can be saved to a file; {@code false}, otherwise
+     */
+    public boolean isSavable();
+
+    /**
+     * Determines if the data managed by this layer needs to be saved to
+     * a file. Only replies true if a file is assigned to this layer and
+     * if the data managed by this layer has been modified since the last
+     * save operation to the file.
+     *
+     * @return {@code true} if the data managed by this layer needs to be saved to a file; {@code false}, otherwise
+     */
+    public boolean requiresSaveToFile();
+
+    /**
+     * Initializes the layer after a successful save of data to a file.
+     */
+    public void onPostSaveToFile();
+}
Index: src/org/openstreetmap/josm/gui/layer/UploadToServer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/UploadToServer.java	(revision 0)
+++ src/org/openstreetmap/josm/gui/layer/UploadToServer.java	(working copy)
@@ -0,0 +1,57 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import org.openstreetmap.josm.gui.io.AbstractIOTask;
+import org.openstreetmap.josm.gui.io.AbstractUploadDialog;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+/**
+ * Interface for layers that can upload data.
+ */
+public interface UploadToServer {
+
+    /**
+     * Determines if the layer is able to upload data and implements the
+     * {@code UploadToServer} interface.  A layer that implements the
+     * {@code UploadToServer} interface must return {@code true}.
+     *
+     * @return {@code true} if the layer is able to upload data; {@code false}, otherwise
+     */
+    public boolean isUploadable();
+
+    /**
+     * Determines if the data managed by this layer needs to be uploaded to
+     * the server because it contains modified data.
+     *
+     * @return {@code true} if the data managed by this layer needs to be
+     *         uploaded to the server because it contains modified data;
+     *         {@code false}, otherwise
+     */
+    public boolean requiresUploadToServer();
+
+    /**
+     * Determines if upload of data managed by this layer is discouraged.
+     * This feature allows to use "private" data layers.
+     *
+     * @return {@code true} if upload is discouraged for this layer; {@code false}, otherwise
+     */
+    public boolean isUploadDiscouraged();
+
+    /**
+     * Initializes the layer after a successful upload to the server.
+     */
+    public void onPostUploadToServer();
+
+    /**
+     * Creates a new {@code AbstractIOTask} for uploading data.
+     * @param monitor The progress monitor
+     * @return a new {@code AbstractIOTask} for uploading data, or {@code null} if not applicable
+     */
+    public AbstractIOTask createUploadTask(ProgressMonitor monitor);
+
+    /**
+     * Returns the upload dialog for this layer.
+     * @return the upload dialog for this layer, or {@code null} if not applicable
+     */
+    public AbstractUploadDialog getUploadDialog();
+}
Index: src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 9687)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(working copy)
@@ -59,6 +59,7 @@
 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToMarkerLayer;
 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToNextMarker;
 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker;
+import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.JpgImporter;
@@ -68,7 +69,7 @@
 /**
  * Layer displaying geottaged pictures.
  */
-public class GeoImageLayer extends Layer implements PropertyChangeListener, JumpToMarkerLayer {
+public class GeoImageLayer extends AbstractModifiableLayer implements PropertyChangeListener, JumpToMarkerLayer {
 
     private static List<Action> menuAdditions = new LinkedList<>();
 
@@ -370,7 +371,24 @@
         return infoText();
     }
 
+    /**
+     * Determines if data managed by this layer has been modified.  That is
+     * the case if one image has modified GPS data.
+     * @return {@code true} if data has been modified; {@code false}, otherwise
+     */
     @Override
+    public boolean isModified() {
+        if (data != null) {
+            for (ImageEntry e : data) {
+                if (e.hasNewGpsData()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
     public boolean isMergable(Layer other) {
         return other instanceof GeoImageLayer;
     }
