Ticket #20913: 20913-v3.1.2.patch

File 20913-v3.1.2.patch, 51.9 KB (added by GerdP, 3 years ago)

converted patch

  • src/org/openstreetmap/josm/actions/SaveAction.java

     
    1414import javax.swing.JPanel;
    1515import javax.swing.SwingConstants;
    1616
    17 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
     17import org.openstreetmap.josm.data.gpx.GpxConstants;
    1818import org.openstreetmap.josm.gui.ExtendedDialog;
    1919import org.openstreetmap.josm.gui.MainApplication;
     20import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    2021import org.openstreetmap.josm.gui.layer.GpxLayer;
    2122import org.openstreetmap.josm.gui.layer.Layer;
    2223import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
    2324import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    24 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2525import org.openstreetmap.josm.gui.layer.SaveToFile;
    2626import org.openstreetmap.josm.gui.util.GuiHelper;
    2727import org.openstreetmap.josm.spi.preferences.Config;
     
    3737    private static final SaveAction instance = new SaveAction();
    3838
    3939    private final PropertyChangeListener updateOnRequireSaveChange = evt -> {
    40         if (OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
     40        if (AbstractModifiableLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
    4141            updateEnabledState();
    4242        }
    4343    };
    4444
    45     private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState();
    46 
    4745    /**
    4846     * Construct the action with "Save" as label.
    4947     */
     
    6866            @Override
    6967            public void layerAdded(LayerAddEvent e) {
    7068                Layer l = e.getAddedLayer();
    71                 if (l instanceof OsmDataLayer) {
     69                if (l instanceof AbstractModifiableLayer) {
    7270                    l.addPropertyChangeListener(updateOnRequireSaveChange);
    7371                }
    74                 if (l instanceof GpxLayer) {
    75                     ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx);
    76                 }
    7772                super.layerAdded(e);
    7873            }
    7974
     
    8075            @Override
    8176            public void layerRemoving(LayerRemoveEvent e) {
    8277                Layer l = e.getRemovedLayer();
    83                 if (l instanceof OsmDataLayer) {
     78                if (l instanceof AbstractModifiableLayer) {
    8479                    l.removePropertyChangeListener(updateOnRequireSaveChange);
    8580                }
    86                 if (l instanceof GpxLayer) {
    87                     ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx);
    88                 }
    8981                super.layerRemoving(e);
    9082            }
    9183        };
     
    113105        }
    114106
    115107        // Ask for overwrite in case of GpxLayer
    116         if (f != null && layer instanceof GpxLayer && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
     108        if (f != null
     109                && layer instanceof GpxLayer
     110                && (((GpxLayer) layer).data == null
     111                || !GpxConstants.JOSM_CREATOR_NAME.equals(((GpxLayer) layer).data.creator))
     112                && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
     113
    117114            JPanel p = new JPanel(new GridBagLayout());
    118             JLabel label = new JLabel(tr("File {0} exists. Overwrite?", f.getName()));
     115            JLabel label = new JLabel("<html>"
     116                    + tr("The file \"{0}\" will be modified.<br>Would you like to overwrite the existing file?", f.getName())
     117                    + "</html>");
    119118            label.setHorizontalAlignment(SwingConstants.CENTER);
    120             JCheckBox remember = new JCheckBox(tr("Remember choice"));
     119            JCheckBox remember = new JCheckBox(tr("Always overwrite GPX files without asking"));
    121120            remember.setHorizontalAlignment(SwingConstants.CENTER);
    122121            p.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 10));
    123122            p.add(remember, GBC.eop().fill(GBC.HORIZONTAL));
     
    124123            ExtendedDialog dialog = new ExtendedDialog(
    125124                    MainApplication.getMainFrame(),
    126125                    tr("Overwrite"),
    127                     tr("Overwrite"), tr("Cancel"))
    128                 .setButtonIcons("save_as", "cancel")
     126                    tr("Overwrite"), tr("Save As..."), tr("Cancel"))
     127                .setButtonIcons("save", "save_as", "cancel")
    129128                .setContent(p);
    130             if (dialog.showDialog().getValue() != 1) {
     129            int val = dialog.showDialog().getValue();
     130            if (val == 1) {
     131                Config.getPref().putBoolean("gpx.export.overwrite", remember.isSelected());
     132            } else if (val == 2) {
    131133                f = null;
    132             } else if (remember.isSelected()) {
    133                 Config.getPref().putBoolean("gpx.export.overwrite", true);
     134            } else {
     135                return null;
    134136            }
    135137        }
    136138        return f == null ? layer.createAndOpenSaveFileChooser() : f;
  • src/org/openstreetmap/josm/actions/SessionSaveAsAction.java

     
    1919import java.util.Map;
    2020import java.util.Set;
    2121import java.util.stream.Collectors;
     22import java.util.stream.Stream;
    2223
    2324import javax.swing.BorderFactory;
    2425import javax.swing.JCheckBox;
     
    3233import javax.swing.border.EtchedBorder;
    3334import javax.swing.filechooser.FileFilter;
    3435
     36import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3537import org.openstreetmap.josm.gui.ExtendedDialog;
    3638import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    3739import org.openstreetmap.josm.gui.MainApplication;
     
    3840import org.openstreetmap.josm.gui.MapFrame;
    3941import org.openstreetmap.josm.gui.MapFrameListener;
    4042import org.openstreetmap.josm.gui.Notification;
     43import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    4144import org.openstreetmap.josm.gui.layer.Layer;
    4245import org.openstreetmap.josm.gui.util.WindowGeometry;
    4346import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
     
    6063    private transient Map<Layer, SessionLayerExporter> exporters;
    6164    private transient MultiMap<Layer, Layer> dependencies;
    6265
     66    private static final BooleanProperty SAVE_LOCAL_FILES_PROPERTY = new BooleanProperty("session.savelocal", true);
     67
    6368    /**
    6469     * Constructs a new {@code SessionSaveAsAction}.
    6570     */
     
    158163                .filter(layer -> exporters.get(layer) != null && exporters.get(layer).shallExport())
    159164                .collect(Collectors.toList());
    160165
     166        Stream<Layer> layersToSaveStream = layersOut.stream()
     167                .filter(layer -> layer.isSavable()
     168                        && layer instanceof AbstractModifiableLayer
     169                        && ((AbstractModifiableLayer) layer).requiresSaveToFile()
     170                        && exporters.get(layer) != null
     171                        && !exporters.get(layer).requiresZip());
     172
     173        if (SAVE_LOCAL_FILES_PROPERTY.get()) {
     174            // individual files must be saved before the session file as the location may change
     175
     176            if (layersToSaveStream
     177                .map(layer -> SaveAction.getInstance().doSave(layer, true))
     178                .collect(Collectors.toList()) //force evaluation of all elements
     179                .contains(false)) {
     180
     181                new Notification(tr("Not all local files referenced by the session file could be saved."
     182                        + "<br>Make sure you save them before closing JOSM."))
     183                    .setIcon(JOptionPane.WARNING_MESSAGE)
     184                    .setDuration(Notification.TIME_LONG)
     185                    .show();
     186            }
     187
     188        } else if (layersToSaveStream.anyMatch(l -> true)) {
     189            new Notification(tr("Not all local files referenced by the session file are saved yet."
     190                    + "<br>Make sure you save them before closing JOSM."))
     191                .setIcon(JOptionPane.INFORMATION_MESSAGE)
     192                .setDuration(Notification.TIME_LONG)
     193                .show();
     194        }
     195
    161196        int active = -1;
    162197        Layer activeLayer = getLayerManager().getActiveLayer();
    163198        if (activeLayer != null) {
     
    250285        }
    251286
    252287        protected final Component build() {
     288            JPanel op = new JPanel(new GridBagLayout());
    253289            JPanel ip = new JPanel(new GridBagLayout());
    254290            for (Layer layer : layers) {
    255291                JPanel wrapper = new JPanel(new GridBagLayout());
     
    272308            p.add(sp, GBC.eol().fill());
    273309            final JTabbedPane tabs = new JTabbedPane();
    274310            tabs.addTab(tr("Layers"), p);
    275             return tabs;
     311            op.add(tabs, GBC.eol().fill());
     312            JCheckBox chkSaveLocal = new JCheckBox(tr("Save all local files to disk"), SAVE_LOCAL_FILES_PROPERTY.get());
     313            chkSaveLocal.addChangeListener(l -> {
     314                SAVE_LOCAL_FILES_PROPERTY.put(chkSaveLocal.isSelected());
     315            });
     316            op.add(chkSaveLocal);
     317            return op;
    276318        }
    277319
    278320        protected final Component getDisabledExportPanel(Layer layer) {
  • src/org/openstreetmap/josm/data/gpx/GpxConstants.java

     
    100100    String META_BOUNDS = META_PREFIX + "bounds";
    101101
    102102    /**
     103     * The creator element that will be written when exporting a GPX file
     104     */
     105    String JOSM_CREATOR_NAME = "JOSM GPX export";
     106
     107    /**
    103108     * Namespace for the XSD
    104109     */
    105110    String XML_URI_XSD = "http://www.w3.org/2001/XMLSchema-instance";
  • src/org/openstreetmap/josm/data/gpx/GpxData.java

     
    4343 *
    4444 * @author Raphael Mack &lt;ramack@raphael-mack.de&gt;
    4545 */
    46 public class GpxData extends WithAttributes implements Data {
     46public class GpxData extends WithAttributes implements Data, IGpxLayerPrefs {
    4747
    4848    /**
    4949     * Constructs a new GpxData.
     
    6767     * A boolean flag indicating if the data was read from the OSM server.
    6868     */
    6969    public boolean fromServer;
     70    /**
     71     * A boolean flag indicating if the data was read from a session file.
     72     */
     73    public boolean fromSession;
    7074
    7175    /**
    7276     * Creator metadata for this file (usually software)
     
    10221026     * @return Modifiable map
    10231027     * @since 15496
    10241028     */
     1029    @Override
    10251030    public Map<String, String> getLayerPrefs() {
    10261031        return layerPrefs;
    10271032    }
     
    11361141            suppressedInvalidate = true;
    11371142        } else {
    11381143            if (setModified) {
    1139                 modified = true;
     1144                setModified(true);
    11401145            }
    11411146            if (listeners.hasListeners()) {
    11421147                GpxDataChangeEvent e = new GpxDataChangeEvent(this);
     
    11781183         * @param e The event
    11791184         */
    11801185        void gpxDataChanged(GpxDataChangeEvent e);
     1186
     1187        /**
     1188         * Called when the modified state of the data changed
     1189         * @param modified the new modified state
     1190         */
     1191        default void modifiedStateChanged(boolean modified) {
     1192            // Override if needed
     1193        }
    11811194    }
    11821195
    11831196    /**
     
    12161229     * @param value modified flag
    12171230     * @since 15496
    12181231     */
     1232    @Override
    12191233    public void setModified(boolean value) {
    1220         modified = value;
     1234        if (!initializing && modified != value) {
     1235            modified = value;
     1236            if (listeners.hasListeners()) {
     1237                listeners.fireEvent(l -> l.modifiedStateChanged(modified));
     1238            }
     1239        }
    12211240    }
    12221241
    12231242    /**
  • src/org/openstreetmap/josm/data/gpx/IGpxLayerPrefs.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.util.Map;
     5
     6/**
     7 * Interface containing the layer preferences.
     8 * Implemented by GpxLayer and MarkerLayer
     9 * @since xxx
     10 */
     11public interface IGpxLayerPrefs {
     12
     13    /**
     14     * The layer specific prefs formerly saved in the preferences, e.g. drawing options.
     15     * NOT the track specific settings (e.g. color, width)
     16     * @return Modifiable map
     17     */
     18    Map<String, String> getLayerPrefs();
     19
     20    /**
     21     * Sets the modified flag to the value.
     22     * @param value modified flag
     23     */
     24    void setModified(boolean value);
     25
     26}
  • src/org/openstreetmap/josm/gui/MainFrame.java

     
    2525import javax.swing.JPanel;
    2626
    2727import org.openstreetmap.josm.data.UserIdentityManager;
     28import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    2829import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
    2930import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
    3031import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
     
    4546    private final transient LayerStateChangeListener updateTitleOnLayerStateChange = (layer, newValue) -> onLayerChange(layer);
    4647
    4748    private final transient PropertyChangeListener updateTitleOnSaveChange = evt -> {
    48         if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP)
     49        if (evt.getPropertyName().equals(AbstractModifiableLayer.REQUIRES_SAVE_TO_DISK_PROP)
    4950                || evt.getPropertyName().equals(OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP)) {
    50             OsmDataLayer layer = (OsmDataLayer) evt.getSource();
     51            AbstractModifiableLayer layer = (AbstractModifiableLayer) evt.getSource();
    5152            onLayerChange(layer);
    5253        }
    5354    };
     
    186187        getRootPane().putClientProperty("Window.documentModified", dirty);
    187188    }
    188189
    189     private void onLayerChange(OsmDataLayer layer) {
     190    private void onLayerChange(AbstractModifiableLayer layer) {
    190191        if (layer == MainApplication.getLayerManager().getEditLayer()) {
    191192            refreshTitle();
    192193        }
  • src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java

     
    147147            }
    148148            if (data.gpxLayer != null) {
    149149                MainApplication.getLayerManager().addLayer(data.gpxLayer);
     150                MainApplication.getLayerManager().setActiveLayer(data.gpxLayer);
    150151            }
    151152            data.postLayerTask.run();
    152153        });
  • src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java

     
    1616 */
    1717public abstract class AbstractModifiableLayer extends Layer implements DownloadFromServer, UploadToServer, SaveToFile, Lockable {
    1818
     19    /** Property used to know if this layer has to be saved on disk */
     20    public static final String REQUIRES_SAVE_TO_DISK_PROP = AbstractModifiableLayer.class.getName() + ".requiresSaveToDisk";
     21    static final String IS_DIRTY_SYMBOL = "*";
     22
    1923    /**
    2024     * Constructs a new {@code ModifiableLayer}.
    2125     * @param name Layer name
     
    5559    }
    5660
    5761    /**
     62     * Determines if this layer is "dirty", i.e. requires save or upload
     63     * @return if this layer is "dirty"
     64     * @since 17626 in {@link OsmDataLayer}
     65     * @since xxx in {@link AbstractModifiableLayer}
     66     */
     67    public boolean isDirty() {
     68        // Override if needed
     69        return requiresSaveToFile() || (requiresUploadToServer() && !isUploadDiscouraged());
     70    }
     71
     72    /**
    5873     * Determines if data managed by this layer has been modified.
    5974     * @return true if data has been modified; false, otherwise
    6075     */
     
    130145        // Override if needed;
    131146        return null;
    132147    }
     148
    133149}
  • src/org/openstreetmap/josm/gui/layer/GpxLayer.java

     
    3333import org.openstreetmap.josm.data.SystemOfMeasurement;
    3434import org.openstreetmap.josm.data.gpx.GpxConstants;
    3535import org.openstreetmap.josm.data.gpx.GpxData;
     36import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeEvent;
     37import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
    3638import org.openstreetmap.josm.data.gpx.GpxDataContainer;
    37 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
    3839import org.openstreetmap.josm.data.gpx.IGpxTrack;
    3940import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    4041import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     
    5859import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction;
    5960import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    6061import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
     62import org.openstreetmap.josm.gui.util.GuiHelper;
    6163import org.openstreetmap.josm.gui.widgets.HtmlPanel;
    6264import org.openstreetmap.josm.tools.ImageProvider;
    6365import org.openstreetmap.josm.tools.Logging;
     
    8587    /**
    8688     * Added as field to be kept as reference.
    8789     */
    88     private final GpxDataChangeListener dataChangeListener = e -> this.invalidate();
     90    private final GpxDataChangeListener dataChangeListener = new GpxDataChangeListener() {
     91        @Override
     92        public void gpxDataChanged(GpxDataChangeEvent e) {
     93            invalidate();
     94        }
     95
     96        @Override
     97        public void modifiedStateChanged(boolean modified) {
     98            GuiHelper.runInEDT(() -> propertyChangeSupport.firePropertyChange(REQUIRES_SAVE_TO_DISK_PROP, !modified, modified));
     99        }
     100    };
    89101    /**
    90102     * The MarkerLayer imported from the same file.
    91103     */
     
    375387    }
    376388
    377389    @Override
     390    public String getLabel() {
     391        return isDirty() ? super.getLabel() + ' ' + IS_DIRTY_SYMBOL : super.getLabel();
     392    }
     393
     394    @Override
    378395    public void visitBoundingBox(BoundingXYVisitor v) {
    379396        if (data != null) {
    380397            v.visit(data.recalculateBounds());
     
    554571
    555572    @Override
    556573    public boolean requiresSaveToFile() {
    557         return isModified() && isLocalFile();
     574        return data != null && isModified() && (isLocalFile() || data.fromSession);
    558575    }
    559576
    560577    @Override
     
    621638
    622639    @Override
    623640    public synchronized void destroy() {
     641        if (linkedMarkerLayer != null && MainApplication.getLayerManager().containsLayer(linkedMarkerLayer)) {
     642            linkedMarkerLayer.data.transferLayerPrefs(data.getLayerPrefs());
     643        }
    624644        data.clear();
    625645        data = null;
    626646        super.destroy();
  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

     
    148148    private static final int HATCHED_SIZE = 15;
    149149    // U+2205 EMPTY SET
    150150    private static final String IS_EMPTY_SYMBOL = "\u2205";
    151     private static final String IS_DIRTY_SYMBOL = "*";
    152     /** Property used to know if this layer has to be saved on disk */
    153     public static final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
    154151    /** Property used to know if this layer has to be uploaded */
    155152    public static final String REQUIRES_UPLOAD_TO_SERVER_PROP = OsmDataLayer.class.getName() + ".requiresUploadToServer";
    156153
     
    10721069        return getAssociatedFile() != null && requiresSaveToFile;
    10731070    }
    10741071
    1075     /**
    1076      * Determines if this layer is "dirty", i.e., requires save or upload
    1077      * @return if this layer is "dirty"
    1078      * @since 17626
    1079      */
    1080     public boolean isDirty() {
    1081         return requiresSaveToFile() || (requiresUploadToServer() && !isUploadDiscouraged());
    1082     }
    1083 
    10841072    @Override
    10851073    public String getLabel() {
    10861074        String label = super.getLabel();
  • src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java

     
    189189     */
    190190    public WayPoint convertToWayPoint() {
    191191        WayPoint wpt = new WayPoint(getCoor());
    192         wpt.setTimeInMillis((long) (time * 1000));
     192        if (time != 0) {
     193            wpt.setTimeInMillis((long) (time * 1000));
     194        }
    193195        if (text != null) {
    194196            wpt.getExtensions().add("josm", "text", text);
    195197        } else if (dataProvider != null) {
  • src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java

     
    2020import java.util.ArrayList;
    2121import java.util.Collection;
    2222import java.util.Comparator;
     23import java.util.HashMap;
    2324import java.util.List;
     25import java.util.Map;
    2426import java.util.Optional;
    2527
    2628import javax.swing.AbstractAction;
     
    3739import org.openstreetmap.josm.data.gpx.GpxData;
    3840import org.openstreetmap.josm.data.gpx.GpxExtension;
    3941import org.openstreetmap.josm.data.gpx.GpxLink;
     42import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs;
    4043import org.openstreetmap.josm.data.gpx.WayPoint;
    4144import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    4245import org.openstreetmap.josm.data.preferences.IntegerProperty;
     
    5659import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    5760import org.openstreetmap.josm.io.audio.AudioPlayer;
    5861import org.openstreetmap.josm.spi.preferences.Config;
     62import org.openstreetmap.josm.tools.ColorHelper;
    5963import org.openstreetmap.josm.tools.ImageProvider;
    6064import org.openstreetmap.josm.tools.Logging;
    6165import org.openstreetmap.josm.tools.Utils;
     
    7680    /**
    7781     * A list of markers.
    7882     */
    79     public final List<Marker> data;
     83    public final MarkerData data;
    8084    private boolean mousePressed;
    8185    public GpxLayer fromLayer;
    8286    private Marker currentMarker;
     
    100104    public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) {
    101105        super(name);
    102106        this.setAssociatedFile(associatedFile);
    103         this.data = new ArrayList<>();
     107        this.data = new MarkerData();
    104108        this.fromLayer = fromLayer;
    105109        double firstTime = -1.0;
    106110        String lastLinkedFile = "";
    107111
     112        if (fromLayer == null || fromLayer.data == null) {
     113            data.ownLayerPrefs = indata.getLayerPrefs();
     114        }
     115
     116        String cs = GPXSettingsPanel.tryGetDataPrefLocal(data, "markers.color");
    108117        Color c = null;
    109         String cs = GPXSettingsPanel.tryGetLayerPrefLocal(indata, "markers.color");
    110118        if (cs != null) {
    111             try {
    112                 c = Color.decode(cs);
    113             } catch (NumberFormatException ex) {
     119            c = ColorHelper.html2color(cs);
     120            if (c == null) {
    114121                Logging.warn("Could not read marker color: " + cs);
    115122            }
    116123        }
     
    459466     * @return <code>true</code> if text should be shown, <code>false</code> otherwise.
    460467     */
    461468    private boolean isTextOrIconShown() {
    462         return Boolean.parseBoolean(GPXSettingsPanel.getLayerPref(fromLayer, "markers.show-text"));
     469        return Boolean.parseBoolean(GPXSettingsPanel.getDataPref(data, "markers.show-text"));
    463470    }
    464471
    465472    @Override
     
    475482    @Override
    476483    public void setColor(Color color) {
    477484        setPrivateColors(color);
    478         if (fromLayer != null) {
    479             String cs = null;
    480             if (color != null) {
    481                 cs = String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue());
    482             }
    483             GPXSettingsPanel.putLayerPrefLocal(fromLayer, "markers.color", cs);
     485        String cs = null;
     486        if (color != null) {
     487            cs = ColorHelper.color2html(color);
    484488        }
     489        GPXSettingsPanel.putDataPrefLocal(data, "markers.color", cs);
    485490        invalidate();
    486491    }
    487492
     
    533538
    534539        @Override
    535540        public void actionPerformed(ActionEvent e) {
    536             GPXSettingsPanel.putLayerPrefLocal(layer.fromLayer, "markers.show-text", Boolean.toString(!layer.isTextOrIconShown()));
     541            GPXSettingsPanel.putDataPrefLocal(layer.data, "markers.show-text", Boolean.toString(!layer.isTextOrIconShown()));
    537542            layer.invalidate();
    538543        }
    539544
     
    617622            invalidate();
    618623        }
    619624    }
     625
     626    /**
     627     * the data of a MarkerLayer
     628     */
     629    public class MarkerData extends ArrayList<Marker> implements IGpxLayerPrefs {
     630
     631        private Map<String, String> ownLayerPrefs;
     632
     633        @Override
     634        public Map<String, String> getLayerPrefs() {
     635            if (ownLayerPrefs == null && fromLayer != null && fromLayer.data != null) {
     636                return fromLayer.data.getLayerPrefs();
     637            }
     638            // fallback to own layerPrefs if the corresponding gpxLayer has already been deleted
     639            // by the user or never existed when loaded from a session file
     640            if (ownLayerPrefs == null) {
     641                ownLayerPrefs = new HashMap<>();
     642            }
     643            return ownLayerPrefs;
     644        }
     645
     646        /**
     647         * Transfers the layerPrefs from the GpxData to MarkerData (when GpxData is deleted)
     648         * @param gpxLayerPrefs the layerPrefs from the GpxData object
     649         */
     650        public void transferLayerPrefs(Map<String, String> gpxLayerPrefs) {
     651            ownLayerPrefs = new HashMap<>(gpxLayerPrefs);
     652        }
     653
     654        @Override
     655        public void setModified(boolean value) {
     656            if (fromLayer != null && fromLayer.data != null) {
     657                fromLayer.data.setModified(value);
     658            }
     659        }
     660
     661    }
    620662}
  • src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java

     
    2929import org.apache.commons.jcs3.access.exception.InvalidArgumentException;
    3030import org.openstreetmap.josm.actions.ExpertToggleAction;
    3131import org.openstreetmap.josm.data.gpx.GpxData;
     32import org.openstreetmap.josm.data.gpx.IGpxLayerPrefs;
    3233import org.openstreetmap.josm.gui.MainApplication;
    3334import org.openstreetmap.josm.gui.layer.GpxLayer;
    3435import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
     
    173174     * @return the value
    174175     */
    175176    public static String getLayerPref(GpxLayer layer, String key) {
     177        GpxData data = layer != null ? layer.data : null;
     178        return getDataPref(data, key);
     179    }
     180
     181    /**
     182     * Reads the preference for the given layer or the default preference if not available
     183     * @param data the data. Can be <code>null</code>, default preference will be returned then
     184     * @param key the drawing key to be read, without "draw.rawgps."
     185     * @return the value
     186     */
     187    public static String getDataPref(IGpxLayerPrefs data, String key) {
    176188        Object d = DEFAULT_PREFS.get(key);
    177189        String ds;
    178190        if (d != null) {
     
    181193            Logging.warn("No default value found for layer preference \"" + key + "\".");
    182194            ds = null;
    183195        }
    184         return Optional.ofNullable(tryGetLayerPrefLocal(layer, key)).orElse(Config.getPref().get("draw.rawgps." + key, ds));
     196        return Optional.ofNullable(tryGetDataPrefLocal(data, key)).orElse(Config.getPref().get("draw.rawgps." + key, ds));
    185197    }
    186198
    187199    /**
     
    191203     * @return the integer value
    192204     */
    193205    public static int getLayerPrefInt(GpxLayer layer, String key) {
    194         String s = getLayerPref(layer, key);
     206        GpxData data = layer != null ? layer.data : null;
     207        return getDataPrefInt(data, key);
     208    }
     209
     210    /**
     211     * Reads the integer preference for the given data or the default preference if not available
     212     * @param data the data. Can be <code>null</code>, default preference will be returned then
     213     * @param key the drawing key to be read, without "draw.rawgps."
     214     * @return the integer value
     215     */
     216    public static int getDataPrefInt(IGpxLayerPrefs data, String key) {
     217        String s = getDataPref(data, key);
    195218        if (s != null) {
    196219            try {
    197220                return Integer.parseInt(s);
     
    214237     * @return the value or <code>null</code> if not found
    215238     */
    216239    public static String tryGetLayerPrefLocal(GpxLayer layer, String key) {
    217         return layer != null ? tryGetLayerPrefLocal(layer.data, key) : null;
     240        return layer != null ? tryGetDataPrefLocal(layer.data, key) : null;
    218241    }
    219242
    220243    /**
     
    223246     * @param key the drawing key to be read, without "draw.rawgps."
    224247     * @return the value or <code>null</code> if not found
    225248     */
    226     public static String tryGetLayerPrefLocal(GpxData data, String key) {
     249    public static String tryGetDataPrefLocal(IGpxLayerPrefs data, String key) {
    227250        return data != null ? data.getLayerPrefs().get(key) : null;
    228251    }
    229252
     
    237260        String v = value == null ? null : value.toString();
    238261        if (layers != null) {
    239262            for (GpxLayer l : layers) {
    240                 putLayerPrefLocal(l.data, key, v);
     263                putDataPrefLocal(l.data, key, v);
    241264            }
    242265        } else {
    243266            Config.getPref().put("draw.rawgps." + key, v);
     
    252275     */
    253276    public static void putLayerPrefLocal(GpxLayer layer, String key, String value) {
    254277        if (layer == null || layer.data == null) return;
    255         putLayerPrefLocal(layer.data, key, value);
     278        putDataPrefLocal(layer.data, key, value);
    256279    }
    257280
    258281    /**
     
    261284     * @param key the drawing key to be written, without "draw.rawgps."
    262285     * @param value the value or <code>null</code> to remove key
    263286     */
    264     public static void putLayerPrefLocal(GpxData data, String key, String value) {
     287    public static void putDataPrefLocal(IGpxLayerPrefs data, String key, String value) {
     288        if (data == null) return;
     289        data.setModified(true);
    265290        if (Utils.isBlank(value) ||
    266291                (getLayerPref(null, key).equals(value) && DEFAULT_PREFS.get(key) != null && DEFAULT_PREFS.get(key).toString().equals(value))) {
    267292            data.getLayerPrefs().remove(key);
  • src/org/openstreetmap/josm/io/GpxWriter.java

     
    148148
    149149        validprefixes = namespaces.stream().map(n -> n.getPrefix()).collect(Collectors.toList());
    150150
     151        data.creator = JOSM_CREATOR_NAME;
    151152        out.println("<?xml version='1.0' encoding='UTF-8'?>");
    152         out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"");
    153153
     154        out.print("<gpx version=\"1.1\" creator=\"");
     155        out.print(JOSM_CREATOR_NAME);
     156        out.println("\" xmlns=\"http://www.topografix.com/GPX/1/1\"");
     157
    154158        StringBuilder schemaLocations = new StringBuilder("http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd");
    155159
    156160        for (XMLNamespace n : namespaces) {
  • src/org/openstreetmap/josm/io/session/GpxTracksSessionExporter.java

     
    66import java.io.PrintWriter;
    77import java.io.Writer;
    88import java.nio.charset.StandardCharsets;
     9import java.time.Instant;
    910
    1011import org.openstreetmap.josm.gui.layer.GpxLayer;
    1112import org.openstreetmap.josm.io.GpxWriter;
     
    1617 */
    1718public class GpxTracksSessionExporter extends GenericSessionExporter<GpxLayer> {
    1819
     20    private Instant metaTime;
     21
    1922    /**
    2023     * Constructs a new {@code GpxTracksSessionExporter}.
    2124     * @param layer GPX layer to export
     
    3639    protected void addDataFile(OutputStream out) {
    3740        Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
    3841        GpxWriter w = new GpxWriter(new PrintWriter(writer));
     42        if (metaTime != null) {
     43            w.setMetaTime(metaTime);
     44        }
    3945        w.write(layer.data);
    4046        w.flush();
    4147    }
     48
     49    protected void setMetaTime(Instant metaTime) {
     50        this.metaTime = metaTime;
     51    }
    4252}
  • src/org/openstreetmap/josm/io/session/GpxTracksSessionImporter.java

     
    5454                } else {
    5555                    importData = GpxImporter.loadLayers(in, support.getFile(fileStr), support.getLayerName(), progressMonitor);
    5656                }
     57                if (importData.getGpxLayer() != null && importData.getGpxLayer().data != null) {
     58                    importData.getGpxLayer().data.fromSession = true;
     59                }
    5760
    5861                support.addPostLayersTask(importData.getPostLayerTask());
    5962                return getLayer(importData);
  • src/org/openstreetmap/josm/io/session/MarkerSessionExporter.java

     
    99import java.io.PrintWriter;
    1010import java.io.Writer;
    1111import java.nio.charset.StandardCharsets;
     12import java.time.Instant;
    1213import java.util.Collection;
    1314import java.util.Collections;
    1415
     
    3233 */
    3334public class MarkerSessionExporter extends AbstractSessionExporter<MarkerLayer> {
    3435
     36    private Instant metaTime;
     37
    3538    /**
    3639     * Constructs a new {@code MarkerSessionExporter}.
    3740     * @param layer marker layer to export
     
    8689    protected void addDataFile(OutputStream out) {
    8790        Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
    8891        MarkerWriter w = new MarkerWriter(new PrintWriter(writer));
     92        if (metaTime != null) {
     93            w.setMetaTime(metaTime);
     94        }
    8995        w.write(layer);
    9096        w.flush();
    9197    }
    9298
     99    protected void setMetaTime(Instant metaTime) {
     100        this.metaTime = metaTime;
     101    }
     102
    93103    /**
    94104     * Writes GPX file from marker data.
    95105     */
     
    109119         */
    110120        public void write(MarkerLayer layer) {
    111121            GpxData data = new GpxData();
     122            layer.data.getLayerPrefs().forEach((k, v) -> {
     123                if (k != null && k.indexOf("markers.") == 0) {
     124                    data.getLayerPrefs().put(k, v);
     125                }
     126            });
    112127            data.put(GpxData.META_DESC, "exported JOSM marker layer");
    113128            for (Marker m : layer.data) {
    114129                data.waypoints.add(m.convertToWayPoint());
  • src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java

     
    55
    66import java.io.IOException;
    77import java.io.InputStream;
    8 import java.util.List;
    98
    109import javax.xml.xpath.XPath;
    1110import javax.xml.xpath.XPathConstants;
     
    1413import javax.xml.xpath.XPathFactory;
    1514
    1615import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
    17 import org.openstreetmap.josm.gui.layer.GpxLayer;
    1816import org.openstreetmap.josm.gui.layer.Layer;
    1917import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    2018import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     
    5048
    5149                support.addPostLayersTask(importData.getPostLayerTask());
    5250
    53                 GpxLayer gpxLayer = null;
    54                 List<SessionReader.LayerDependency> deps = support.getLayerDependencies();
    55                 if (!deps.isEmpty()) {
    56                     Layer layer = deps.get(0).getLayer();
    57                     if (layer instanceof GpxLayer) {
    58                         gpxLayer = (GpxLayer) layer;
    59                     }
    60                 }
    61 
    62                 MarkerLayer markerLayer = importData.getMarkerLayer();
    63                 if (markerLayer != null) {
    64                     markerLayer.fromLayer = gpxLayer;
    65                 }
    66 
    67                 return markerLayer;
     51                return importData.getMarkerLayer();
    6852            }
    6953        } catch (XPathExpressionException e) {
    7054            throw new IllegalDataException(e);
  • src/org/openstreetmap/josm/io/session/SessionReader.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.Color;
    76import java.awt.GraphicsEnvironment;
    87import java.io.BufferedInputStream;
    98import java.io.File;
     
    4645import org.openstreetmap.josm.io.Compression;
    4746import org.openstreetmap.josm.io.IllegalDataException;
    4847import org.openstreetmap.josm.tools.CheckParameterUtil;
    49 import org.openstreetmap.josm.tools.ColorHelper;
    5048import org.openstreetmap.josm.tools.JosmRuntimeException;
    5149import org.openstreetmap.josm.tools.Logging;
    5250import org.openstreetmap.josm.tools.MultiMap;
     
    619617                    Logging.warn(ex);
    620618                }
    621619            }
    622             String colorString = el.getAttribute("color");
    623             if (colorString != null) {
    624                 try {
    625                     Color color = ColorHelper.html2color(colorString);
    626                     layer.setColor(color);
    627                 } catch (RuntimeException ex) {
    628                     Logging.warn("Cannot parse color " + colorString);
    629                 }
    630             }
    631620            layer.setName(names.get(entry.getKey()));
    632621            layers.add(layer);
    633622        }
  • src/org/openstreetmap/josm/io/session/SessionWriter.java

     
    4343import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
    4444import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    4545import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
    46 import org.openstreetmap.josm.tools.ColorHelper;
    4746import org.openstreetmap.josm.tools.JosmRuntimeException;
    4847import org.openstreetmap.josm.tools.Logging;
    4948import org.openstreetmap.josm.tools.MultiMap;
     
    241240            if (!Utils.equalsEpsilon(layer.getOpacity(), 1.0)) {
    242241                el.setAttribute("opacity", Double.toString(layer.getOpacity()));
    243242            }
    244             if (layer.getColor() != null) {
    245                 el.setAttribute("color", ColorHelper.color2html(layer.getColor()));
    246             }
    247243            Set<Layer> deps = dependencies.get(layer);
    248244            final String depends = deps == null ? "" : deps.stream().map(depLayer -> {
    249245                int depIndex = layers.indexOf(depLayer);
  • test/data/sessions/data_export.gpx

     
     1<?xml version='1.0' encoding='UTF-8'?>
     2<gpx version="1.1" creator="JOSM GPX export" xmlns="http://www.topografix.com/GPX/1/1"
     3    xmlns:josm="http://josm.openstreetmap.de/gpx-extensions-1.1"
     4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd">
     6  <metadata>
     7    <time>2021-10-16T18:27:12.351Z</time>
     8    <bounds minlat="42.72659" minlon="-0.00749" maxlat="42.72665" maxlon="-0.00747"/>
     9    <extensions>
     10      <josm:layerPreferences>
     11        <josm:entry key="markers.color" value="#34567812"/>
     12      </josm:layerPreferences>
     13    </extensions>
     14  </metadata>
     15  <wpt lat="42.72665" lon="-0.00747">
     16    <time>2021-01-01T10:15:30Z</time>
     17  </wpt>
     18  <wpt lat="42.72659" lon="-0.00749"/>
     19</gpx>
     20 No newline at end of file
  • test/data/sessions/gpx_markers.jos

     
    1515        <layer index="1" name="GPX layer name" type="tracks" version="0.1" visible="true">
    1616            <file>layers/01/data.gpx</file>
    1717        </layer>
    18         <layer color="#34567812" index="2" name="Marker layer name" opacity="0.5" type="markers" version="0.1" visible="true">
     18        <layer index="2" name="Marker layer name" opacity="0.5" type="markers" version="0.1" visible="true">
    1919            <file>layers/02/data.gpx</file>
    2020        </layer>
    2121    </layers>
  • test/data/sessions/markers.gpx

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
     
     1<?xml version='1.0' encoding='UTF-8'?>
     2<gpx version="1.1" creator="JOSM GPX export" xmlns="http://www.topografix.com/GPX/1/1"
     3    xmlns:josm="http://josm.openstreetmap.de/gpx-extensions-1.1"
     4    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd">
     6  <metadata>
     7    <desc>exported JOSM marker layer</desc>
     8    <time>2021-10-16T18:27:12.351Z</time>
     9    <bounds minlat="42.72659" minlon="-0.00749" maxlat="42.72665" maxlon="-0.00747"/>
     10    <extensions>
     11      <josm:layerPreferences>
     12        <josm:entry key="markers.color" value="#34567812"/>
     13      </josm:layerPreferences>
     14    </extensions>
     15  </metadata>
     16  <wpt lat="42.72665" lon="-0.00747">
     17    <time>2021-01-01T10:15:30Z</time>
     18  </wpt>
     19  <wpt lat="42.72659" lon="-0.00749"/>
     20</gpx>
     21 No newline at end of file
  • test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java

     
    503503        col.add("josm", "from-server", "true");
    504504        EqualsVerifier.forClass(GpxData.class).usingGetClass()
    505505            .suppress(Warning.NONFINAL_FIELDS)
    506             .withIgnoredFields("creator", "fromServer", "storageFile", "initializing", "updating",
     506            .withIgnoredFields("creator", "fromServer", "fromSession", "storageFile", "initializing", "updating",
    507507                    "suppressedInvalidate", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans", "modified")
    508508            .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
    509509            .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create())
  • test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java

     
    88import java.util.ArrayList;
    99import java.util.HashMap;
    1010
     11import org.junit.jupiter.api.Test;
    1112import org.junit.jupiter.api.extension.RegisterExtension;
    12 import org.junit.jupiter.api.Test;
    1313import org.openstreetmap.josm.TestUtils;
    1414import org.openstreetmap.josm.testutils.JOSMTestRules;
    1515import org.openstreetmap.josm.tools.ListenerList;
     
    3131    public JOSMTestRules test = new JOSMTestRules();
    3232
    3333    /**
    34      * Tests weather the track can read and write colors.
     34     * Tests whether the track can read and write colors.
    3535     */
    3636    @Test
    3737    void testColors() {
  • test/unit/org/openstreetmap/josm/io/session/SessionWriterTest.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.io.session;
    33
     4import static org.junit.jupiter.api.Assertions.assertEquals;
     5import static org.junit.jupiter.api.Assertions.fail;
     6
    47import java.awt.Color;
    58import java.io.File;
    69import java.io.IOException;
    7 import java.io.InputStream;
    810import java.nio.charset.StandardCharsets;
    911import java.nio.file.Files;
    1012import java.nio.file.Path;
    1113import java.nio.file.Paths;
     14import java.time.Instant;
    1215import java.util.Arrays;
    1316import java.util.Collections;
    1417import java.util.HashMap;
    1518import java.util.List;
    1619import java.util.Map;
     20import java.util.stream.Collectors;
     21import java.util.zip.ZipEntry;
    1722import java.util.zip.ZipFile;
    1823
    1924import org.junit.jupiter.api.BeforeEach;
     
    4247
    4348import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    4449
    45 import static org.junit.jupiter.api.Assertions.assertEquals;
    46 
    4750/**
    4851 * Unit tests for Session writing.
    4952 */
     
    108111        MainApplication.getLayerManager().addLayer(createOsmLayer());
    109112    }
    110113
    111     private byte[] testWrite(List<Layer> layers, final boolean zip) throws IOException {
     114    private Map<String, byte[]> testWrite(List<Layer> layers, final boolean zip) throws IOException {
    112115        Map<Layer, SessionLayerExporter> exporters = new HashMap<>();
    113116        if (zip) {
    114117            SessionWriter.registerSessionLayerExporter(OsmDataLayer.class, OsmHeadlessJozExporter.class);
     
    118121            SessionWriter.registerSessionLayerExporter(GpxLayer.class, GpxHeadlessJosExporter.class);
    119122        }
    120123        for (final Layer l : layers) {
    121             exporters.put(l, SessionWriter.getSessionLayerExporter(l));
     124            SessionLayerExporter s = SessionWriter.getSessionLayerExporter(l);
     125            exporters.put(l, s);
     126            if (s instanceof GpxTracksSessionExporter) {
     127                ((GpxTracksSessionExporter) s).setMetaTime(Instant.parse("2021-10-16T18:27:12.351Z"));
     128            } else if (s instanceof MarkerSessionExporter) {
     129                ((MarkerSessionExporter) s).setMetaTime(Instant.parse("2021-10-16T18:27:12.351Z"));
     130            }
    122131        }
    123132        SessionWriter sw = new SessionWriter(layers, -1, exporters, new MultiMap<Layer, Layer>(), zip);
    124133        File file = new File(System.getProperty("java.io.tmpdir"), getClass().getName()+(zip ? ".joz" : ".jos"));
     
    127136            if (!zip) {
    128137                return null;
    129138            }
    130             try (ZipFile zipFile = new ZipFile(file);
    131                  InputStream input = zipFile.getInputStream(zipFile.getEntry("session.jos"))) {
    132                 return Utils.readBytesFromStream(input);
     139            try (ZipFile zipFile = new ZipFile(file)) {
     140                return Collections.list(zipFile.entries()).stream().collect(Collectors.toMap(ZipEntry::getName, e -> {
     141                    try {
     142                        return Utils.readBytesFromStream(zipFile.getInputStream(e));
     143                    } catch (IOException ex) {
     144                        fail(ex);
     145                    }
     146                    return null;
     147                }));
    133148            }
    134149        } finally {
    135150            if (file.exists()) {
     
    146161
    147162    private GpxLayer createGpxLayer() {
    148163        GpxData data = new GpxData();
    149         data.waypoints.add(new WayPoint(new LatLon(42.72665, -0.00747)));
     164        WayPoint wp = new WayPoint(new LatLon(42.72665, -0.00747));
     165        wp.setInstant(Instant.parse("2021-01-01T10:15:30.00Z"));
     166        data.waypoints.add(wp);
    150167        data.waypoints.add(new WayPoint(new LatLon(42.72659, -0.00749)));
    151168        GpxLayer layer = new GpxLayer(data, "GPX layer name");
    152169        layer.setAssociatedFile(new File("data.gpx"));
     
    232249    @Test
    233250    void testWriteGpxAndMarkerJoz() throws IOException {
    234251        GpxLayer gpx = createGpxLayer();
    235         byte[] bytes = testWrite(Arrays.asList(gpx, createMarkerLayer(gpx)), true);
     252        Map<String, byte[]> bytes = testWrite(Arrays.asList(gpx, createMarkerLayer(gpx)), true);
     253
    236254        Path path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/gpx_markers.jos");
    237255        String expected = new String(Files.readAllBytes(path), StandardCharsets.UTF_8).replace("\r", "");
    238         String actual = new String(bytes, StandardCharsets.UTF_8).replace("\r", "");
     256        String actual = new String(bytes.get("session.jos"), StandardCharsets.UTF_8).replace("\r", "");
    239257        assertEquals(expected, actual);
     258
     259        path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/data_export.gpx");
     260        expected = new String(Files.readAllBytes(path), StandardCharsets.UTF_8).replace("\r", "");
     261        actual = new String(bytes.get("layers/01/data.gpx"), StandardCharsets.UTF_8).replace("\r", "");
     262        assertEquals(expected, actual);
     263
     264        path = Paths.get(TestUtils.getTestDataRoot() + "/sessions/markers.gpx");
     265        expected = new String(Files.readAllBytes(path), StandardCharsets.UTF_8).replace("\r", "");
     266        actual = new String(bytes.get("layers/02/data.gpx"), StandardCharsets.UTF_8).replace("\r", "");
     267        assertEquals(expected, actual);
    240268    }
    241269
    242270    /**