Ticket #16796: GpxColors-v1.01.patch

File GpxColors-v1.01.patch, 499.2 KB (added by Bjoeni, 6 years ago)
  • data/gpx-drawing-extensions-1.0.xsd

     
     1<?xml version="1.0" encoding="UTF-8"?>
     2<schema targetNamespace="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0"
     3    elementFormDefault="qualified"
     4    xmlns="http://www.w3.org/2001/XMLSchema"
     5    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     6    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     7    xmlns:gpxd="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0"
     8    xsi:schemaLocation="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0 https://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd">
     9
     10    <xsd:annotation>
     11        <xsd:documentation>
     12            This schema defines drawing extensions for the GPX 1.1 schema (http://www.topografix.com/GPX/1/1/gpx.xsd).
     13            Elements in this schema should be used as child elements of the "extensions" element defined by the GPX schema.
     14        </xsd:documentation>
     15    </xsd:annotation>
     16   
     17    <!-- Elements -->
     18
     19    <xsd:element name="color" type="gpxd:hexColor_type">
     20        <xsd:annotation>
     21            <xsd:documentation>
     22                The color of the element, i.e. #RRGGBB or #RRGGBBAA.
     23                Note that applications should apply possible alpha values to the lines and opacity to the whole track. This means that overlapping parts of the
     24                track with alpha values will look more intense than individual lines, whereas the opacity affects the whole track including overlapping parts.
     25            </xsd:documentation>
     26        </xsd:annotation>
     27    </xsd:element>
     28   
     29    <xsd:element name="opacity" type="gpxd:opacity_type">
     30        <xsd:annotation>
     31            <xsd:documentation>
     32                The opacity of the element between 0.00 and 1.00.
     33            </xsd:documentation>
     34        </xsd:annotation>
     35    </xsd:element>
     36   
     37    <xsd:element name="width" type="xsd:positiveInteger">
     38        <xsd:annotation>
     39            <xsd:documentation>
     40                The width of the line in pixels, applications may use a width relative to this value if required.
     41            </xsd:documentation>
     42        </xsd:annotation>
     43    </xsd:element>
     44   
     45    <xsd:element name="dashPattern" type="gpxd:dashPattern_type">
     46        <xsd:annotation>
     47            <xsd:documentation>
     48                The dash pattern of the line, see gpxd:dashPattern_type. Should always be relative to the width.
     49            </xsd:documentation>
     50        </xsd:annotation>
     51    </xsd:element>
     52   
     53    <!-- Types -->
     54
     55    <xsd:simpleType name="hexColor_type">
     56        <xsd:annotation>
     57            <xsd:documentation>
     58                The hexColor_type must be a # followed by a 6 or 8-digit hex representation of the color (with or without the alpha value).           
     59            </xsd:documentation>
     60        </xsd:annotation>
     61        <xsd:restriction base="xsd:string">
     62            <xsd:pattern value="\#([a-fA-F0-9]{6}|[a-fA-F0-9]{8})" />
     63            <xsd:whiteSpace value="collapse" />
     64        </xsd:restriction>
     65    </xsd:simpleType>
     66
     67    <xsd:simpleType name="opacity_type">
     68        <xsd:annotation>
     69            <xsd:documentation>
     70                The opacity_type must be a decimal value between 0 and 1.
     71            </xsd:documentation>
     72        </xsd:annotation>
     73        <xsd:restriction base="xsd:decimal">
     74            <xsd:minInclusive value="0" />
     75            <xsd:maxInclusive value="1" />
     76        </xsd:restriction>
     77    </xsd:simpleType>
     78   
     79    <xsd:simpleType name="dashPattern_type">
     80        <xsd:annotation>
     81            <xsd:documentation>
     82                The dashPattern_type can be
     83                    - a representation of the pattern as y-n-y-n-... with y being the relative length of the line that is
     84                      visible and n being the relative length of the line that is hidden to create a dashed / dotted line.
     85                      Has to have an even number of segments (at least two) and can contain multi-digit numbers.
     86                    - one of the following predefined values:
     87                      none, dash-long, dash-medium, dash-short, dot-sparse, dot-normal, dot-dense, dash-dot, dash-dot-dot
     88            </xsd:documentation>
     89        </xsd:annotation>
     90        <xsd:restriction base="xsd:string">               <!-- use string based pattern instead of enum because both pattern and enums are allowed -->
     91            <xsd:pattern value="\d+\-\d+(\-\d+\-\d+)*" /> <!-- pattern, see documentation above -->
     92            <xsd:pattern value="none" />                  <!-- 1-0, default value/line -->
     93            <xsd:pattern value="dash-long" />             <!-- 6-2 -->
     94            <xsd:pattern value="dash-medium" />           <!-- 4-4 -->
     95            <xsd:pattern value="dash-short" />            <!-- 2-6 -->
     96            <xsd:pattern value="dot-sparse" />            <!-- 1-4 -->
     97            <xsd:pattern value="dot-normal" />            <!-- 1-2 -->
     98            <xsd:pattern value="dot-dense" />             <!-- 1-1 -->
     99            <xsd:pattern value="dash-dot" />              <!-- 4-2-1-2 -->
     100            <xsd:pattern value="dash-dot-dot" />          <!-- 4-2-1-2-1-2 -->           
     101        </xsd:restriction>
     102    </xsd:simpleType>
     103</schema>
     104 No newline at end of file
  • data/gpx-extensions-1.1.xsd

     
     1<?xml version="1.0" encoding="UTF-8"?>
     2<schema targetNamespace="http://josm.openstreetmap.de/gpx-extensions-1.1"
     3    elementFormDefault="qualified"
     4    xmlns="http://www.w3.org/2001/XMLSchema"
     5    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     6    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     7    xmlns:josm="http://josm.openstreetmap.de/gpx-extensions-1.1"
     8    xsi:schemaLocation="http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd">
     9   
     10    <!-- true, if gpx data has been downloaded from the osm server -->
     11    <!-- it this case, JOSM improves the rendering of clouds of anonymous TrackPoints -->
     12    <element name="from-server" type="boolean"/>
     13   
     14    <!-- the following properties are only set for marker layer export -->
     15    <element name="offset" type="decimal"/>
     16    <element name="sync-offset" type="decimal"/>
     17    <element name="text" type="string" />
     18   
     19    <xsd:element name="layerPreferences" type="josm:preferences_type">
     20        <xsd:annotation>
     21            <xsd:documentation>
     22                The layerPreferences contain the preferences that can be set for the layer, e.g. in the "Customize track drawing" dialog in JOSM.
     23            </xsd:documentation>
     24        </xsd:annotation>
     25    </xsd:element>
     26
     27    <xsd:complexType name="preferences_type">
     28        <xsd:sequence>
     29            <xsd:element name="entry" type="josm:entry_type" minOccurs="0" />
     30        </xsd:sequence>
     31    </xsd:complexType>
     32
     33    <xsd:complexType name="entry_type">
     34        <xsd:attribute name="key" type="xsd:string" use="required" />
     35        <xsd:attribute name="value" type="xsd:string" use="required" />
     36    </xsd:complexType>
     37
     38</schema>
     39 No newline at end of file
  • data/validator/deprecated.mapcss

     
    661661  assertNoMatch: "way roof:color=red roof:colour=red";
    662662  assertMatch: "way roof:color=red roof:colour=green";
    663663}
    664   /* further more universal checks, no autofix */
    665 *[/:color/][!building:color][!roof:color] {
     664/* further more universal checks, no autofix */
     665/* gpxd:color has another warning in unnecessary.mapcss */
     666*[/:color/][!building:color][!roof:color][!gpxd:color] {
    666667  throwWarning: tr("{0} is deprecated", "{0.key}");
    667668  suggestAlternative: ":colour";
    668669  group: tr("deprecated tagging");
  • data/validator/unnecessary.mapcss

     
    167167}
    168168
    169169/* #2760 */
    170 *[/^gpx:/] {
     170*[/^(gpx|gpxx|gpxd):/] {
    171171  throwWarning: tr("{0} should not be uploaded", "{0.key}");
    172172  group: tr("unnecessary tag");
    173173  fixRemove: "{0.key}";
    174174  assertMatch: "node gpx:time=2018-01-01T12:00:00Z";
     175  assertMatch: "node gpxd:color=#FF0000";
    175176  assertNoMatch: "node source=gpx:foo";
    176177}
  • src/org/openstreetmap/josm/actions/SaveAction.java

     
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
     7import java.awt.GridBagLayout;
    78import java.awt.event.KeyEvent;
    89import java.beans.PropertyChangeListener;
    910import java.io.File;
    1011
     12import javax.swing.JCheckBox;
     13import javax.swing.JLabel;
     14import javax.swing.JPanel;
     15import javax.swing.SwingConstants;
     16
     17import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
    1118import org.openstreetmap.josm.gui.ExtendedDialog;
    1219import org.openstreetmap.josm.gui.MainApplication;
    1320import org.openstreetmap.josm.gui.layer.GpxLayer;
     
    1623import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    1724import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    1825import org.openstreetmap.josm.gui.layer.SaveToFile;
     26import org.openstreetmap.josm.gui.util.GuiHelper;
     27import org.openstreetmap.josm.spi.preferences.Config;
     28import org.openstreetmap.josm.tools.GBC;
    1929import org.openstreetmap.josm.tools.Shortcut;
    2030
    2131/**
     
    3242        }
    3343    };
    3444
     45    private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState();
     46
    3547    /**
    3648     * Construct the action with "Save" as label.
    3749     */
    3850    private SaveAction() {
    3951        super(tr("Save"), "save", tr("Save the current data."),
    40                 Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.CTRL));
     52                Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.CTRL),
     53                true);
    4154        setHelpId(ht("/Action/Save"));
    4255    }
    4356
     
    5467        return new LayerChangeAdapter() {
    5568            @Override
    5669            public void layerAdded(LayerAddEvent e) {
    57                 if (e.getAddedLayer() instanceof OsmDataLayer) {
    58                     e.getAddedLayer().addPropertyChangeListener(updateOnRequireSaveChange);
     70                Layer l = e.getAddedLayer();
     71                if (l instanceof OsmDataLayer) {
     72                    l.addPropertyChangeListener(updateOnRequireSaveChange);
    5973                }
     74                if (l instanceof GpxLayer) {
     75                    ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx);
     76                }
    6077                super.layerAdded(e);
    6178            }
    6279
    6380            @Override
    6481            public void layerRemoving(LayerRemoveEvent e) {
    65                 if (e.getRemovedLayer() instanceof OsmDataLayer) {
    66                     e.getRemovedLayer().removePropertyChangeListener(updateOnRequireSaveChange);
     82                Layer l = e.getRemovedLayer();
     83                if (l instanceof OsmDataLayer) {
     84                    l.removePropertyChangeListener(updateOnRequireSaveChange);
    6785                }
     86                if (l instanceof GpxLayer) {
     87                    ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx);
     88                }
    6889                super.layerRemoving(e);
    6990            }
    7091        };
     
    7394    @Override
    7495    protected void updateEnabledState() {
    7596        Layer activeLayer = getLayerManager().getActiveLayer();
    76         setEnabled(activeLayer != null && activeLayer.isSavable()
    77                 && (!(activeLayer.getAssociatedFile() != null
    78                     && activeLayer instanceof SaveToFile && !((SaveToFile) activeLayer).requiresSaveToFile())));
     97        boolean en = activeLayer != null
     98                && activeLayer.isSavable() && (!(activeLayer.getAssociatedFile() != null
     99                && activeLayer instanceof SaveToFile && !((SaveToFile) activeLayer).requiresSaveToFile()));
     100        GuiHelper.runInEDT(() -> setEnabled(en));
    79101    }
    80102
    81103    @Override
     
    85107            f = null;
    86108        }
    87109
    88         // Ask for overwrite in case of GpxLayer: GpxLayers usually are imports
    89         // and modifying is an error most of the time.
    90         if (f != null && layer instanceof GpxLayer) {
     110        // Ask for overwrite in case of GpxLayer
     111        if (f != null && layer instanceof GpxLayer && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
     112            JPanel p = new JPanel(new GridBagLayout());
     113            JLabel label = new JLabel(tr("File {0} exists. Overwrite?", f.getName()));
     114            label.setHorizontalAlignment(SwingConstants.CENTER);
     115            JCheckBox remember = new JCheckBox(tr("Remember choice"));
     116            remember.setHorizontalAlignment(SwingConstants.CENTER);
     117            p.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 10));
     118            p.add(remember, GBC.eop().fill(GBC.HORIZONTAL));
    91119            ExtendedDialog dialog = new ExtendedDialog(
    92120                    MainApplication.getMainFrame(),
    93121                    tr("Overwrite"),
    94122                    tr("Overwrite"), tr("Cancel"))
    95123                .setButtonIcons("save_as", "cancel")
    96                 .setContent(tr("File {0} exists. Overwrite?", f.getName()));
     124                .setContent(p);
    97125            if (dialog.showDialog().getValue() != 1) {
    98126                f = null;
     127            } else if (remember.isSelected()) {
     128                Config.getPref().putBoolean("gpx.export.overwrite", true);
    99129            }
    100130        }
    101131        return f == null ? layer.createAndOpenSaveFileChooser() : f;
  • src/org/openstreetmap/josm/actions/SaveActionBase.java

     
    1919import org.openstreetmap.josm.gui.ExtendedDialog;
    2020import org.openstreetmap.josm.gui.MainApplication;
    2121import org.openstreetmap.josm.gui.io.importexport.FileExporter;
     22import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    2223import org.openstreetmap.josm.gui.layer.Layer;
    23 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    24 import org.openstreetmap.josm.gui.layer.SaveToFile;
    2524import org.openstreetmap.josm.gui.util.GuiHelper;
    2625import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
    2726import org.openstreetmap.josm.spi.preferences.Config;
     
    3534 */
    3635public abstract class SaveActionBase extends DiskAccessAction {
    3736
     37    private boolean quiet;
     38
    3839    /**
    3940     * Constructs a new {@code SaveActionBase}.
    4041     * @param name The action's text as displayed on the menu (if it is added to a menu)
     
    4647        super(name, iconName, tooltip, shortcut);
    4748    }
    4849
     50    /**
     51     * Constructs a new {@code SaveActionBase}.
     52     * @param name The action's text as displayed on the menu (if it is added to a menu)
     53     * @param iconName The filename of the icon to use
     54     * @param tooltip A longer description of the action that will be displayed in the tooltip
     55     * @param shortcut A ready-created shortcut object or {@code null} if you don't want a shortcut
     56     * @param quiet whether the quiet exporter is called
     57     * @since xxx
     58     */
     59    public SaveActionBase(String name, String iconName, String tooltip, Shortcut shortcut, boolean quiet) {
     60        super(name, iconName, tooltip, shortcut);
     61        this.quiet = quiet;
     62    }
     63
    4964    @Override
    5065    public void actionPerformed(ActionEvent e) {
    5166        if (!isEnabled())
    5267            return;
    53         doSave();
     68        doSave(quiet);
    5469    }
    5570
    5671    /**
     
    5873     * @return {@code true} if the save operation succeeds
    5974     */
    6075    public boolean doSave() {
     76        return doSave(false);
     77    }
     78
     79    /**
     80     * Saves the active layer.
     81     * @param quiet If the file is saved without prompting the user
     82     * @return {@code true} if the save operation succeeds
     83     * @since xxx
     84     */
     85    public boolean doSave(boolean quiet) {
    6186        Layer layer = getLayerManager().getActiveLayer();
    6287        if (layer != null && layer.isSavable()) {
    63             return doSave(layer);
     88            return doSave(layer, quiet);
    6489        }
    6590        return false;
    6691    }
     
    7196     * @return {@code true} if the save operation succeeds
    7297     */
    7398    public boolean doSave(Layer layer) {
     99        return doSave(layer, false);
     100    }
     101
     102    /**
     103     * Saves the given layer.
     104     * @param layer layer to save
     105     * @param quiet If the file is saved without prompting the user
     106     * @return {@code true} if the save operation succeeds
     107     * @since xxx
     108     */
     109    public boolean doSave(Layer layer, boolean quiet) {
    74110        if (!layer.checkSaveConditions())
    75111            return false;
    76         final boolean requiresSave = layer instanceof SaveToFile && ((SaveToFile) layer).requiresSaveToFile();
    77         final boolean result = doInternalSave(layer, getFile(layer));
    78         if (!requiresSave) {
    79             updateEnabledState();
    80         }
     112        final boolean result = doInternalSave(layer, getFile(layer), quiet);
     113        updateEnabledState();
    81114        return result;
    82115    }
    83116
     
    86119     * @param layer The layer to save
    87120     * @param file The destination file
    88121     * @param checkSaveConditions if {@code true}, checks preconditions before saving. Set it to {@code false} to skip it
    89      * if preconditions have already been checked (as this check can prompt UI dialog in EDT it may be best in some cases
    90      * to do it earlier).
     122     * and prevent dialogs from being shown.
    91123     * @return {@code true} if the layer has been successfully saved, {@code false} otherwise
    92124     * @since 7204
    93125     */
     
    94126    public static boolean doSave(Layer layer, File file, boolean checkSaveConditions) {
    95127        if (checkSaveConditions && !layer.checkSaveConditions())
    96128            return false;
    97         return doInternalSave(layer, file);
     129        return doInternalSave(layer, file, !checkSaveConditions);
    98130    }
    99131
    100     private static boolean doInternalSave(Layer layer, File file) {
     132    private static boolean doInternalSave(Layer layer, File file, boolean quiet) {
    101133        if (file == null)
    102134            return false;
    103135
     
    106138            boolean canceled = false;
    107139            for (FileExporter exporter : ExtensionFileFilter.getExporters()) {
    108140                if (exporter.acceptFile(file, layer)) {
    109                     exporter.exportData(file, layer);
     141                    if (quiet) {
     142                        exporter.exportDataQuiet(file, layer);
     143                    } else {
     144                        exporter.exportData(file, layer);
     145                    }
    110146                    exported = true;
    111147                    canceled = exporter.isCanceled();
    112148                    break;
     
    124160                layer.setName(file.getName());
    125161            }
    126162            layer.setAssociatedFile(file);
    127             if (layer instanceof OsmDataLayer) {
    128                 ((OsmDataLayer) layer).onPostSaveToFile();
     163            if (layer instanceof AbstractModifiableLayer) {
     164                ((AbstractModifiableLayer) layer).onPostSaveToFile();
    129165            }
    130166        } catch (IOException | InvalidPathException e) {
    131167            showAndLogException(e);
  • src/org/openstreetmap/josm/actions/SaveAsAction.java

     
    2424    public SaveAsAction() {
    2525        super(tr("Save As..."), "save_as", tr("Save the current data to a new file."),
    2626            Shortcut.registerShortcut("system:saveas", tr("File: {0}", tr("Save As...")),
    27             KeyEvent.VK_S, Shortcut.CTRL_SHIFT));
     27            KeyEvent.VK_S, Shortcut.CTRL_SHIFT), false);
    2828        setHelpId(ht("/Action/SaveAs"));
    2929    }
    3030
  • src/org/openstreetmap/josm/actions/ShowStatusReportAction.java

     
    293293        text.append(reportHeader);
    294294
    295295        Preferences.main().getAllSettings().forEach((key, setting) -> {
    296             if (key.startsWith("marker.show")
    297                     || "file-open.history".equals(key)
     296            if ("file-open.history".equals(key)
    298297                    || "download.overpass.query".equals(key)
    299298                    || "download.overpass.queries".equals(key)
    300299                    || key.contains("username")
  • src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java

     
    2525import org.openstreetmap.josm.actions.GpxExportAction;
    2626import org.openstreetmap.josm.actions.IPrimitiveAction;
    2727import org.openstreetmap.josm.data.gpx.GpxData;
    28 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
     28import org.openstreetmap.josm.data.gpx.GpxTrack;
    2929import org.openstreetmap.josm.data.gpx.WayPoint;
    3030import org.openstreetmap.josm.data.osm.IPrimitive;
    3131import org.openstreetmap.josm.data.osm.Node;
     
    154154                WayConnectionType wayConnectionType = wct.get(i);
    155155                if (!wayConnectionType.isOnewayLoopBackwardPart && !wayConnectionType.direction.isRoundabout()) {
    156156                    if (!wayConnectionType.linkPrev && !trkseg.isEmpty()) {
    157                         gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));
     157                        gpxData.addTrack(new GpxTrack(trk, trkAttr));
    158158                        trkAttr.clear();
    159159                        trk.clear();
    160160                        trkseg.clear();
     
    179179                    }
    180180                }
    181181            }
    182             gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));
     182            gpxData.addTrack(new GpxTrack(trk, trkAttr));
    183183
    184184            String lprefix = relations.iterator().next().getName();
    185185            if (lprefix == null || relations.size() > 1)
  • src/org/openstreetmap/josm/data/Preferences.java

     
    2121import java.util.HashMap;
    2222import java.util.HashSet;
    2323import java.util.Iterator;
     24import java.util.List;
    2425import java.util.Map;
    2526import java.util.Map.Entry;
    2627import java.util.Optional;
     
    2930import java.util.TreeMap;
    3031import java.util.concurrent.TimeUnit;
    3132import java.util.function.Predicate;
     33import java.util.stream.Collectors;
    3234import java.util.stream.Stream;
    3335
    3436import javax.swing.JOptionPane;
     
    8183 */
    8284public class Preferences extends AbstractPreferences {
    8385
     86    /** remove if key equals */
    8487    private static final String[] OBSOLETE_PREF_KEYS = {
    8588        "remotecontrol.https.enabled", /* remove entry after Dec. 2019 */
    8689        "remotecontrol.https.port", /* remove entry after Dec. 2019 */
    8790    };
    8891
     92    /** remove if key starts with */
     93    private static final String[] OBSOLETE_PREF_KEYS_START = {
     94            //only remove layer specific prefs
     95            "draw.rawgps.layer.wpt.",
     96            "draw.rawgps.layer.audiowpt.",
     97            "draw.rawgps.lines.force.",
     98            "draw.rawgps.lines.alpha-blend.",
     99            "draw.rawgps.lines.",
     100            "markers.show ", //uses space as separator
     101            "marker.makeautomarker.",
     102            "clr.layer.",
     103
     104            //remove both layer specific and global prefs
     105            "draw.rawgps.colors",
     106            "draw.rawgps.direction",
     107            "draw.rawgps.alternatedirection",
     108            "draw.rawgps.linewidth",
     109            "draw.rawgps.max-line-length.local",
     110            "draw.rawgps.max-line-length",
     111            "draw.rawgps.large",
     112            "draw.rawgps.large.size",
     113            "draw.rawgps.hdopcircle",
     114            "draw.rawgps.min-arrow-distance",
     115            "draw.rawgps.colorTracksTune",
     116            "draw.rawgps.colors.dynamic",
     117            "draw.rawgps.lines.local",
     118            "draw.rawgps.heatmap"
     119    };
     120
     121    /** keep subkey even if it starts with any of {@link #OBSOLETE_PREF_KEYS_START} */
     122    private static final List<String> KEEP_PREF_KEYS = Arrays.asList(
     123            "draw.rawgps.lines.alpha-blend",
     124            "draw.rawgps.lines.arrows",
     125            "draw.rawgps.lines.arrows.fast",
     126            "draw.rawgps.lines.arrows.min-distance",
     127            "draw.rawgps.lines.force",
     128            "draw.rawgps.lines.max-length",
     129            "draw.rawgps.lines.max-length.local",
     130            "draw.rawgps.lines.width"
     131    );
     132
     133    /** rename keys that equal */
     134    private final static Map<String, String> UPDATE_PREF_KEYS = getUpdatePrefKeys();
     135
     136    private static Map<String, String> getUpdatePrefKeys() {
     137        HashMap<String, String> m = new HashMap<>();
     138        m.put("draw.rawgps.direction", "draw.rawgps.lines.arrows");
     139        m.put("draw.rawgps.alternatedirection", "draw.rawgps.lines.arrows.fast");
     140        m.put("draw.rawgps.min-arrow-distance", "draw.rawgps.lines.arrows.min-distance");
     141        m.put("draw.rawgps.linewidth", "draw.rawgps.lines.width");
     142        m.put("draw.rawgps.max-line-length.local", "draw.rawgps.lines.max-length.local");
     143        m.put("draw.rawgps.max-line-length", "draw.rawgps.lines.max-length");
     144        m.put("draw.rawgps.large", "draw.rawgps.points.large");
     145        m.put("draw.rawgps.large.alpha", "draw.rawgps.points.large.alpha");
     146        m.put("draw.rawgps.large.size", "draw.rawgps.points.large.size");
     147        m.put("draw.rawgps.hdopcircle", "draw.rawgps.points.hdopcircle");
     148        m.put("draw.rawgps.layer.wpt.pattern", "draw.rawgps.markers.pattern");
     149        m.put("draw.rawgps.layer.audiowpt.pattern", "draw.rawgps.markers.audio.pattern");
     150        m.put("draw.rawgps.colors", "draw.rawgps.colormode");
     151        m.put("draw.rawgps.colorTracksTune", "draw.rawgps.colormode.velocity.tune");
     152        m.put("draw.rawgps.colors.dynamic", "draw.rawgps.colormode.dynamic-range");
     153        m.put("draw.rawgps.heatmap.line-extra", "draw.rawgps.colormode.heatmap.line-extra");
     154        m.put("draw.rawgps.heatmap.colormap", "draw.rawgps.colormode.heatmap.colormap");
     155        m.put("draw.rawgps.heatmap.use-points", "draw.rawgps.colormode.heatmap.use-points");
     156        m.put("draw.rawgps.heatmap.gain", "draw.rawgps.colormode.heatmap.gain");
     157        m.put("draw.rawgps.heatmap.lower-limit", "draw.rawgps.colormode.heatmap.lower-limit");
     158        m.put("draw.rawgps.date-coloring-min-dt", "draw.rawgps.colormode.time.min-distance");
     159        return Collections.unmodifiableMap(m);
     160    }
     161
    89162    private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50);
    90163
    91164    private final IBaseDirectories dirs;
     165    boolean modifiedDefault;
    92166
    93167    /**
    94168     * Determines if preferences file is saved each time a property is changed.
     
    415489        reader.parse();
    416490        settingsMap.clear();
    417491        settingsMap.putAll(reader.getSettings());
    418         removeObsolete(reader.getVersion());
     492        removeAndUpdateObsolete(reader.getVersion());
    419493    }
    420494
    421495    /**
     
    520594            }
    521595            return;
    522596        }
     597        File def = getDefaultsCacheFile();
     598        if (def.exists()) {
     599            try {
     600                loadDefaults();
     601            } catch (IOException | XMLStreamException | SAXException e) {
     602                Logging.error(e);
     603                Logging.warn(tr("Failed to load defaults cache file: {0}", def));
     604                defaultsMap.clear();
     605                if (!def.delete()) {
     606                    Logging.warn(tr("Failed to delete faulty defaults cache file: {0}", def));
     607                }
     608            }
     609        }
    523610        try {
    524611            load();
    525612            initSuccessful = true;
     
    545632                Logging.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile()));
    546633            }
    547634        }
    548         File def = getDefaultsCacheFile();
    549         if (def.exists()) {
    550             try {
    551                 loadDefaults();
    552             } catch (IOException | XMLStreamException | SAXException e) {
    553                 Logging.error(e);
    554                 Logging.warn(tr("Failed to load defaults cache file: {0}", def));
    555                 defaultsMap.clear();
    556                 if (!def.delete()) {
    557                     Logging.warn(tr("Failed to delete faulty defaults cache file: {0}", def));
    558                 }
    559             }
    560         }
    561635    }
    562636
    563637    /**
     
    755829    }
    756830
    757831    /**
    758      * Removes obsolete preference settings. If you throw out a once-used preference
     832     * Removes and updates obsolete preference settings. If you throw out a once-used preference
    759833     * setting, add it to the list here with an expiry date (written as comment). If you
    760834     * see something with an expiry date in the past, remove it from the list.
    761835     * @param loadedVersion JOSM version when the preferences file was written
    762836     */
    763     private void removeObsolete(int loadedVersion) {
     837    private void removeAndUpdateObsolete(int loadedVersion) {
     838        Logging.trace("Update obsolete preference keys for version {0}", Integer.toString(loadedVersion));
     839        for (Entry<String, String> e : UPDATE_PREF_KEYS.entrySet()) {
     840            String oldkey = e.getKey();
     841            String newkey = e.getValue();
     842            if (settingsMap.containsKey(oldkey)) {
     843                Setting<?> value = settingsMap.remove(oldkey);
     844                settingsMap.putIfAbsent(newkey, value);
     845                Logging.info(tr("Updated preference setting {0} to {1}", oldkey, newkey));
     846            }
     847        }
     848
    764849        Logging.trace("Remove obsolete preferences for version {0}", Integer.toString(loadedVersion));
    765850        for (String key : OBSOLETE_PREF_KEYS) {
    766851            if (settingsMap.containsKey(key)) {
    767852                settingsMap.remove(key);
    768                 Logging.info(tr("Preference setting {0} has been removed since it is no longer used.", key));
     853                Logging.info(tr("Removed preference setting {0} since it is no longer used", key));
    769854            }
     855            if (defaultsMap.containsKey(key)) {
     856                defaultsMap.remove(key);
     857                Logging.info(tr("Removed preference default {0} since it is no longer used", key));
     858                modifiedDefault = true;
     859            }
    770860        }
     861        for (String key : OBSOLETE_PREF_KEYS_START) {
     862            settingsMap.entrySet().stream()
     863            .filter(e -> e.getKey().startsWith(key))
     864            .collect(Collectors.toSet())
     865            .forEach(e -> {
     866                String k = e.getKey();
     867                if (!KEEP_PREF_KEYS.contains(k)) {
     868                    settingsMap.remove(k);
     869                    Logging.info(tr("Removed preference setting {0} since it is no longer used", k));
     870                }
     871            });
     872            defaultsMap.entrySet().stream()
     873            .filter(e -> e.getKey().startsWith(key))
     874            .collect(Collectors.toSet())
     875            .forEach(e -> {
     876                String k = e.getKey();
     877                if (!KEEP_PREF_KEYS.contains(k)) {
     878                    defaultsMap.remove(k);
     879                    Logging.info(tr("Removed preference default {0} since it is no longer used", k));
     880                    modifiedDefault = true;
     881                }
     882            });
     883        }
     884        if (modifiedDefault) {
     885            try {
     886                saveDefaults();
     887                Logging.info(tr("Saved updated default preferences."));
     888            } catch (IOException ex) {
     889                Logging.log(Logging.LEVEL_WARN, tr("Failed to save default preferences."), ex);
     890            }
     891            modifiedDefault = false;
     892        }
    771893    }
    772894
    773895    /**
  • src/org/openstreetmap/josm/data/gpx/Extensions.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.data.gpx;
    3 
    4 import java.util.LinkedHashMap;
    5 
    6 /**
    7  * Data class for extensions in a GPX-File.
    8  */
    9 public class Extensions extends LinkedHashMap<String, String> {
    10 
    11     private static final long serialVersionUID = 1L;
    12 
    13     /**
    14      * Constructs a new {@code Extensions}.
    15      */
    16     public Extensions() {
    17         super();
    18     }
    19 }
  • src/org/openstreetmap/josm/data/gpx/GpxConstants.java

    Property changes on: src/org/openstreetmap/josm/data/gpx/Extensions.java
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    33
     4import java.awt.Color;
    45import java.util.Arrays;
    56import java.util.Collection;
    67import java.util.Collections;
    78import java.util.List;
     9import java.util.Map;
     10import java.util.TreeMap;
    811
    912import org.openstreetmap.josm.data.Bounds;
    1013import org.openstreetmap.josm.spi.preferences.Config;
     
    9598     * @see GpxData#getMetaBounds()
    9699     */
    97100    String META_BOUNDS = META_PREFIX + "bounds";
     101
    98102    /**
    99      * A constant for the metadata hash map: the extension data. This is a {@link Extensions} object
    100      * @see GpxData#addExtension(String, String)
    101      * @see GpxData#get(String)
     103     * Namespace for the XSD
    102104     */
    103     String META_EXTENSIONS = META_PREFIX + "extensions";
     105    String XML_URI_XSD = "http://www.w3.org/2001/XMLSchema-instance";
    104106
    105107    /**
    106      * A namespace for josm GPX extensions
     108     * Namespace for JOSM GPX extensions
    107109     */
    108     String JOSM_EXTENSIONS_NAMESPACE_URI = Config.getUrls().getXMLBase() + "/gpx-extensions-1.0";
     110    String XML_URI_EXTENSIONS_JOSM = Config.getUrls().getXMLBase() + "/gpx-extensions-1.1";
     111    /**
     112     * Location of the XSD schema for JOSM GPX extensions
     113     */
     114    String XML_XSD_EXTENSIONS_JOSM = Config.getUrls().getXMLBase() + "/gpx-extensions-1.1.xsd";
    109115
     116    /**
     117     * Namespace for GPX drawing extensions
     118     */
     119    String XML_URI_EXTENSIONS_DRAWING = Config.getUrls().getXMLBase() + "/gpx-drawing-extensions-1.0";
     120    /**
     121     * Location of the XSD schema for GPX drawing extensions
     122     */
     123    String XML_XSD_EXTENSIONS_DRAWING = Config.getUrls().getXMLBase() + "/gpx-drawing-extensions-1.0.xsd";
     124
     125    /**
     126     * Namespace for Garmin GPX extensions
     127     */
     128    String XML_URI_EXTENSIONS_GARMIN = "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
     129    /**
     130     * Location of the XSD schema for GPX drawing extensions
     131     */
     132    String XML_XSD_EXTENSIONS_GARMIN = "http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd";
     133
    110134    /** Elevation (in meters) of the point. */
    111135    String PT_ELE = "ele";
    112136
     
    154178     */
    155179    List<String> WPT_KEYS = Collections.unmodifiableList(Arrays.asList(PT_ELE, PT_TIME, PT_MAGVAR, PT_GEOIDHEIGHT,
    156180            GPX_NAME, GPX_CMT, GPX_DESC, GPX_SRC, META_LINKS, PT_SYM, PT_TYPE,
    157             PT_FIX, PT_SAT, PT_HDOP, PT_VDOP, PT_PDOP, PT_AGEOFDGPSDATA, PT_DGPSID, META_EXTENSIONS));
     181            PT_FIX, PT_SAT, PT_HDOP, PT_VDOP, PT_PDOP, PT_AGEOFDGPSDATA, PT_DGPSID));
    158182
    159183    /**
    160184     * Ordered list of all possible route and track keys.
    161185     */
    162186    List<String> RTE_TRK_KEYS = Collections.unmodifiableList(Arrays.asList(
    163             GPX_NAME, GPX_CMT, GPX_DESC, GPX_SRC, META_LINKS, "number", PT_TYPE, META_EXTENSIONS));
     187            GPX_NAME, GPX_CMT, GPX_DESC, GPX_SRC, META_LINKS, "number", PT_TYPE));
    164188
    165189    /**
     190     * Map with all supported Garmin colors
     191     */
     192    Map<String, Color> GARMIN_COLORS = getGarminColors();
     193
     194    /**
     195     * Helper method for {@link #GARMIN_COLORS}
     196     * @return Map with all supported Garmin colors
     197     */
     198    static Map<String, Color> getGarminColors() {
     199        TreeMap<String, Color> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
     200        m.put("Black", Color.BLACK);
     201        m.put("DarkRed", new Color(139, 0, 0));
     202        m.put("DarkGreen", new Color(0, 100, 0));
     203        m.put("DarkYellow", new Color(255, 170, 0));
     204        m.put("DarkBlue", new Color(0, 0, 139));
     205        m.put("DarkMagenta", new Color(139, 0, 139));
     206        m.put("DarkCyan", new Color(0, 139, 139));
     207        m.put("LightGray", Color.LIGHT_GRAY);
     208        m.put("DarkGray", Color.DARK_GRAY);
     209        m.put("Red", Color.RED);
     210        m.put("Green", Color.GREEN);
     211        m.put("Yellow", Color.YELLOW);
     212        m.put("Blue", Color.BLUE);
     213        m.put("Magenta", Color.MAGENTA);
     214        m.put("Cyan", Color.CYAN);
     215        m.put("White", Color.WHITE);
     216        m.put("Transparent", new Color(0, 0, 0, 255));
     217        return Collections.unmodifiableMap(m);
     218    }
     219
     220    /**
     221     * Enum with color formats that can be written by JOSM
     222     */
     223    enum ColorFormat {
     224        /** Drawing extension format */
     225        GPXD,
     226        /** Garmin track extension format */
     227        GPXX
     228    }
     229
     230    /**
     231     * Map with all supported extension abbreviations for easier readability in OSM layers
     232     */
     233    Map<String, String> EXTENSION_ABBREVIATIONS = getExtensionAbbreviations();
     234
     235    /**
     236     * Helper method for {@link #EXTENSION_ABBREVIATIONS}
     237     * @return Map with all supported extension abbreviations
     238     */
     239    static Map<String, String> getExtensionAbbreviations() {
     240        TreeMap<String, String> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
     241        m.put("gpx:extension:gpxx:TrackExtensions:DisplayColor", "gpxx:DisplayColor");
     242        m.put("gpx:extension:gpxd:color", "gpxd:color");
     243        return m;
     244    }
     245
     246    /**
    166247     * Possible fix values. NMEA 0183 Version 4.00
    167248     */
    168249    Collection<String> FIX_VALUES = Collections.unmodifiableList(
  • src/org/openstreetmap/josm/data/gpx/GpxData.java

     
    1515import java.util.LongSummaryStatistics;
    1616import java.util.Map;
    1717import java.util.NoSuchElementException;
     18import java.util.Objects;
     19import java.util.Optional;
    1820import java.util.Set;
    1921import java.util.stream.Collectors;
    2022import java.util.stream.Stream;
     
    2325import org.openstreetmap.josm.data.Data;
    2426import org.openstreetmap.josm.data.DataSource;
    2527import org.openstreetmap.josm.data.coor.EastNorth;
    26 import org.openstreetmap.josm.data.gpx.GpxTrack.GpxTrackChangeListener;
     28import org.openstreetmap.josm.data.gpx.IGpxTrack.GpxTrackChangeListener;
    2729import org.openstreetmap.josm.data.projection.ProjectionRegistry;
    2830import org.openstreetmap.josm.gui.MainApplication;
    2931import org.openstreetmap.josm.gui.layer.GpxLayer;
     
    4042public class GpxData extends WithAttributes implements Data {
    4143
    4244    /**
     45     * Constructs a new GpxData.
     46     */
     47    public GpxData() {}
     48
     49    /**
     50     * Constructs a new GpxData that is currently being initialized, so no listeners will be fired until {@link #endUpdate()} is called.
     51     * @param initializing true
     52     * @since xxx
     53     */
     54    public GpxData(boolean initializing) {
     55        this.initializing = initializing;
     56    }
     57
     58    /**
    4359     * The disk file this layer is stored in, if it is a local layer. May be <code>null</code>.
    4460     */
    4561    public File storageFile;
     
    6581     * Addidionaly waypoints for this file.
    6682     */
    6783    private final ArrayList<WayPoint> privateWaypoints = new ArrayList<>();
    68     private final GpxTrackChangeListener proxy = e -> fireInvalidate();
     84    /**
     85     * All namespaces read from the original file
     86     */
     87    private final List<XMLNamespace> namespaces = new ArrayList<>();
     88    /**
     89     * The layer specific prefs formerly saved in the preferences, e.g. drawing options.
     90     * NOT the track specific settings (e.g. color, width)
     91     */
     92    private final Map<String, String> layerPrefs = new HashMap<>();
    6993
     94    private final GpxTrackChangeListener proxy = e -> invalidate();
     95    private boolean modified, updating, initializing;
     96    private boolean suppressedInvalidate;
     97
    7098    /**
    7199     * Tracks. Access is discouraged, use {@link #getTracks()} to read.
    72100     * @see #getTracks()
    73101     */
    74     public final Collection<GpxTrack> tracks = new ListeningCollection<GpxTrack>(privateTracks, this::fireInvalidate) {
     102    public final Collection<GpxTrack> tracks = new ListeningCollection<GpxTrack>(privateTracks, this::invalidate) {
    75103
    76104        @Override
    77105        protected void removed(GpxTrack cursor) {
     
    90118     * Routes. Access is discouraged, use {@link #getTracks()} to read.
    91119     * @see #getRoutes()
    92120     */
    93     public final Collection<GpxRoute> routes = new ListeningCollection<>(privateRoutes, this::fireInvalidate);
     121    public final Collection<GpxRoute> routes = new ListeningCollection<>(privateRoutes, this::invalidate);
    94122
    95123    /**
    96124     * Waypoints. Access is discouraged, use {@link #getTracks()} to read.
    97125     * @see #getWaypoints()
    98126     */
    99     public final Collection<WayPoint> waypoints = new ListeningCollection<>(privateWaypoints, this::fireInvalidate);
     127    public final Collection<WayPoint> waypoints = new ListeningCollection<>(privateWaypoints, this::invalidate);
    100128
    101129    /**
    102130     * All data sources (bounds of downloaded bounds) of this GpxData.<br>
     
    108136
    109137    private final ListenerList<GpxDataChangeListener> listeners = ListenerList.create();
    110138
    111     static class TimestampConfictException extends Exception {}
    112 
    113139    private List<GpxTrackSegmentSpan> segSpans;
    114140
    115141    /**
     
    156182        other.privateRoutes.forEach(this::addRoute);
    157183        other.privateWaypoints.forEach(this::addWaypoint);
    158184        dataSources.addAll(other.dataSources);
    159         fireInvalidate();
     185        invalidate();
    160186    }
    161187
    162188    private void cutOverlapping(GpxTrack trk, boolean connect) {
    163         List<GpxTrackSegment> segsOld = new ArrayList<>(trk.getSegments());
    164         List<GpxTrackSegment> segsNew = new ArrayList<>();
    165         for (GpxTrackSegment seg : segsOld) {
     189        List<IGpxTrackSegment> segsOld = new ArrayList<>(trk.getSegments());
     190        List<IGpxTrackSegment> segsNew = new ArrayList<>();
     191        for (IGpxTrackSegment seg : segsOld) {
    166192            GpxTrackSegmentSpan s = GpxTrackSegmentSpan.tryGetFromSegment(seg);
    167193            if (s != null && anySegmentOverlapsWith(s)) {
    168194                List<WayPoint> wpsNew = new ArrayList<>();
     
    203229                                    // splitting needs to be handled here,
    204230                                    // because other high priority tracks between the same waypoints could follow
    205231                                    if (!wpsNew.isEmpty()) {
    206                                         segsNew.add(new ImmutableGpxTrackSegment(wpsNew));
     232                                        segsNew.add(new GpxTrackSegment(wpsNew));
    207233                                    }
    208234                                    if (!segsNew.isEmpty()) {
    209                                         privateTracks.add(new ImmutableGpxTrack(segsNew, trk.getAttributes()));
     235                                        privateTracks.add(new GpxTrack(segsNew, trk.getAttributes()));
    210236                                    }
    211237                                    segsNew = new ArrayList<>();
    212238                                    wpsNew = new ArrayList<>();
     
    221247                        if (split) {
    222248                            //track has to be split, because we have an overlapping short track in the middle
    223249                            if (!wpsNew.isEmpty()) {
    224                                 segsNew.add(new ImmutableGpxTrackSegment(wpsNew));
     250                                segsNew.add(new GpxTrackSegment(wpsNew));
    225251                            }
    226252                            if (!segsNew.isEmpty()) {
    227                                 privateTracks.add(new ImmutableGpxTrack(segsNew, trk.getAttributes()));
     253                                privateTracks.add(new GpxTrack(segsNew, trk.getAttributes()));
    228254                            }
    229255                            segsNew = new ArrayList<>();
    230256                            wpsNew = new ArrayList<>();
     
    238264                    }
    239265                }
    240266                if (!wpsNew.isEmpty()) {
    241                     segsNew.add(new ImmutableGpxTrackSegment(wpsNew));
     267                    segsNew.add(new GpxTrackSegment(wpsNew));
    242268                }
    243269            } else {
    244270                segsNew.add(seg);
     
    247273        if (segsNew.equals(segsOld)) {
    248274            privateTracks.add(trk);
    249275        } else if (!segsNew.isEmpty()) {
    250             privateTracks.add(new ImmutableGpxTrack(segsNew, trk.getAttributes()));
     276            privateTracks.add(new GpxTrack(segsNew, trk.getAttributes()));
    251277        }
    252278    }
    253279
    254280    private void connectTracks(WayPoint prevWp, GpxTrackSegmentSpan span, Map<String, Object> attr) {
    255281        if (prevWp != null && !span.lastEquals(prevWp)) {
    256             privateTracks.add(new ImmutableGpxTrack(Arrays.asList(Arrays.asList(new WayPoint(prevWp), span.getFirstWp())), attr));
     282            privateTracks.add(new GpxTrack(Arrays.asList(Arrays.asList(new WayPoint(prevWp), span.getFirstWp())), attr));
    257283        }
    258284    }
    259285
     
    309335                || (other.firstTime.before(lastTime) && firstTime.before(other.lastTime));
    310336        }
    311337
    312         static GpxTrackSegmentSpan tryGetFromSegment(GpxTrackSegment seg) {
     338        static GpxTrackSegmentSpan tryGetFromSegment(IGpxTrackSegment seg) {
    313339            WayPoint b = getNextWpWithTime(seg, true);
    314340            if (b != null) {
    315341                WayPoint e = getNextWpWithTime(seg, false);
     
    320346            return null;
    321347        }
    322348
    323         private static WayPoint getNextWpWithTime(GpxTrackSegment seg, boolean forward) {
     349        private static WayPoint getNextWpWithTime(IGpxTrackSegment seg, boolean forward) {
    324350            List<WayPoint> wps = new ArrayList<>(seg.getWayPoints());
    325351            for (int i = forward ? 0 : wps.size() - 1; i >= 0 && i < wps.size(); i += forward ? 1 : -1) {
    326352                if (wps.get(i).hasDate()) {
     
    340366        if (segSpans == null) {
    341367            segSpans = new ArrayList<>();
    342368            for (GpxTrack trk : privateTracks) {
    343                 for (GpxTrackSegment seg : trk.getSegments()) {
     369                for (IGpxTrackSegment seg : trk.getSegments()) {
    344370                    GpxTrackSegmentSpan s = GpxTrackSegmentSpan.tryGetFromSegment(seg);
    345371                    if (s != null) {
    346372                        segSpans.add(s);
     
    373399     * Get stream of track segments.
    374400     * @return {@code Stream<GPXTrack>}
    375401     */
    376     private synchronized Stream<GpxTrackSegment> getTrackSegmentsStream() {
     402    public synchronized Stream<IGpxTrackSegment> getTrackSegmentsStream() {
    377403        return getTracks().stream().flatMap(trk -> trk.getSegments().stream());
    378404    }
    379405
     
    397423        }
    398424        privateTracks.add(track);
    399425        track.addListener(proxy);
    400         fireInvalidate();
     426        invalidate();
    401427    }
    402428
    403429    /**
     
    410436            throw new IllegalArgumentException(MessageFormat.format("The track was not in this data: {0}", track));
    411437        }
    412438        track.removeListener(proxy);
    413         fireInvalidate();
     439        invalidate();
    414440    }
    415441
    416442    /**
     
    420446     * @since 13210
    421447     */
    422448    public synchronized void combineTracksToSegmentedTrack() {
    423         List<GpxTrackSegment> segs = getTrackSegmentsStream()
    424                 .collect(Collectors.toCollection(ArrayList<GpxTrackSegment>::new));
     449        List<IGpxTrackSegment> segs = getTrackSegmentsStream()
     450                .collect(Collectors.toCollection(ArrayList<IGpxTrackSegment>::new));
    425451        Map<String, Object> attrs = new HashMap<>(privateTracks.get(0).getAttributes());
    426452
    427453        // do not let the name grow if split / combine operations are called iteratively
     
    431457        }
    432458
    433459        clearTracks();
    434         addTrack(new ImmutableGpxTrack(segs, attrs));
     460        addTrack(new GpxTrack(segs, attrs));
    435461    }
    436462
    437463    /**
     
    467493            .flatMap(trk -> trk.getSegments().stream().map(seg -> {
    468494                    HashMap<String, Object> attrs = new HashMap<>(trk.getAttributes());
    469495                    ensureUniqueName(attrs, counts, srcLayerName);
    470                     return new ImmutableGpxTrack(Arrays.asList(seg), attrs);
     496                    return new GpxTrack(Arrays.asList(seg), attrs);
    471497                }))
    472498            .collect(Collectors.toCollection(ArrayList<GpxTrack>::new));
    473499
     
    538564            throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", route));
    539565        }
    540566        privateRoutes.add(route);
    541         fireInvalidate();
     567        invalidate();
    542568    }
    543569
    544570    /**
     
    550576        if (!privateRoutes.removeIf(r -> r == route)) {
    551577            throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", route));
    552578        }
    553         fireInvalidate();
     579        invalidate();
    554580    }
    555581
    556582    /**
     
    572598            throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", waypoint));
    573599        }
    574600        privateWaypoints.add(waypoint);
    575         fireInvalidate();
     601        invalidate();
    576602    }
    577603
    578604    /**
     
    584610        if (!privateWaypoints.removeIf(w -> w == waypoint)) {
    585611            throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", waypoint));
    586612        }
    587         fireInvalidate();
     613        invalidate();
    588614    }
    589615
    590616    /**
     
    600626     * @return The stream
    601627     * @see #getTracks()
    602628     * @see GpxTrack#getSegments()
    603      * @see GpxTrackSegment#getWayPoints()
     629     * @see IGpxTrackSegment#getWayPoints()
    604630     * @since 12156
    605631     */
    606632    public synchronized Stream<WayPoint> getTrackPoints() {
     
    767793        double py = p.north();
    768794        double rx = 0.0, ry = 0.0, sx, sy, x, y;
    769795        for (GpxTrack track : privateTracks) {
    770             for (GpxTrackSegment seg : track.getSegments()) {
     796            for (IGpxTrackSegment seg : track.getSegments()) {
    771797                WayPoint r = null;
    772798                for (WayPoint wpSeg : seg.getWayPoints()) {
    773799                    EastNorth en = wpSeg.getEastNorth(ProjectionRegistry.getProjection());
     
    882908
    883909        private Iterator<GpxTrack> itTracks;
    884910        private int idxTracks;
    885         private Iterator<GpxTrackSegment> itTrackSegments;
     911        private Iterator<IGpxTrackSegment> itTrackSegments;
    886912        private final Iterator<GpxRoute> itRoutes;
    887913
    888914        private Line next;
    889915        private final boolean[] trackVisibility;
    890916        private Map<String, Object> trackAttributes;
     917        private GpxTrack curTrack;
    891918
    892919        /**
    893920         * Constructs a new {@code LinesIterator}.
     
    921948        private Line getNext() {
    922949            if (itTracks != null) {
    923950                if (itTrackSegments != null && itTrackSegments.hasNext()) {
    924                     return new Line(itTrackSegments.next(), trackAttributes);
     951                    return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor());
    925952                } else {
    926953                    while (itTracks.hasNext()) {
    927                         GpxTrack nxtTrack = itTracks.next();
    928                         trackAttributes = nxtTrack.getAttributes();
     954                        curTrack = itTracks.next();
     955                        trackAttributes = curTrack.getAttributes();
    929956                        idxTracks++;
    930957                        if (trackVisibility != null && !trackVisibility[idxTracks])
    931958                            continue;
    932                         itTrackSegments = nxtTrack.getSegments().iterator();
     959                        itTrackSegments = curTrack.getSegments().iterator();
    933960                        if (itTrackSegments.hasNext()) {
    934                             return new Line(itTrackSegments.next(), trackAttributes);
     961                            return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor());
    935962                        }
    936963                    }
    937964                    // if we get here, all the Tracks are finished; Continue with Routes
     
    956983        return Collections.unmodifiableCollection(dataSources);
    957984    }
    958985
     986    /**
     987     * The layer specific prefs formerly saved in the preferences, e.g. drawing options.
     988     * NOT the track specific settings (e.g. color, width)
     989     * @return Modifiable map
     990     * @since xxx
     991     */
     992    public Map<String, String> getLayerPrefs() {
     993        return layerPrefs;
     994    }
     995
     996    /**
     997     * All XML namespaces read from the original file
     998     * @return Modifiable list
     999     * @since xxx
     1000     */
     1001    public List<XMLNamespace> getNamespaces() {
     1002        return namespaces;
     1003    }
     1004
    9591005    @Override
    9601006    public synchronized int hashCode() {
    9611007        final int prime = 31;
    962         int result = 1;
     1008        int result = prime + super.hashCode();
     1009        result = prime * result + ((namespaces == null) ? 0 : namespaces.hashCode());
     1010        result = prime * result + ((layerPrefs == null) ? 0 : layerPrefs.hashCode());
    9631011        result = prime * result + ((dataSources == null) ? 0 : dataSources.hashCode());
    9641012        result = prime * result + ((privateRoutes == null) ? 0 : privateRoutes.hashCode());
    9651013        result = prime * result + ((privateTracks == null) ? 0 : privateTracks.hashCode());
     
    9731021            return true;
    9741022        if (obj == null)
    9751023            return false;
     1024        if (!super.equals(obj))
     1025            return false;
    9761026        if (getClass() != obj.getClass())
    9771027            return false;
    9781028        GpxData other = (GpxData) obj;
     
    9811031                return false;
    9821032        } else if (!dataSources.equals(other.dataSources))
    9831033            return false;
     1034        if (layerPrefs == null) {
     1035            if (other.layerPrefs != null)
     1036                return false;
     1037        } else if (!layerPrefs.equals(other.layerPrefs))
     1038            return false;
    9841039        if (privateRoutes == null) {
    9851040            if (other.privateRoutes != null)
    9861041                return false;
     
    9961051                return false;
    9971052        } else if (!privateWaypoints.equals(other.privateWaypoints))
    9981053            return false;
     1054        if (namespaces == null) {
     1055            if (other.namespaces != null)
     1056                return false;
     1057        } else if (!namespaces.equals(other.namespaces))
     1058            return false;
    9991059        return true;
    10001060    }
    10011061
     1062    @Override
     1063    public void put(String key, Object value) {
     1064        super.put(key, value);
     1065        invalidate();
     1066    }
     1067
    10021068    /**
    10031069     * Adds a listener that gets called whenever the data changed.
    10041070     * @param listener The listener
     
    10251091        listeners.removeListener(listener);
    10261092    }
    10271093
    1028     private void fireInvalidate() {
    1029         if (listeners.hasListeners()) {
    1030             GpxDataChangeEvent e = new GpxDataChangeEvent(this);
    1031             listeners.fireEvent(l -> l.gpxDataChanged(e));
     1094    /**
     1095     * Fires event listeners and sets the modified flag to true.
     1096     */
     1097    public void invalidate() {
     1098        fireInvalidate(true);
     1099    }
     1100
     1101    private void fireInvalidate(boolean setModified) {
     1102        if (updating || initializing) {
     1103            suppressedInvalidate = true;
     1104        } else {
     1105            if (setModified) {
     1106                modified = true;
     1107            }
     1108            if (listeners.hasListeners()) {
     1109                GpxDataChangeEvent e = new GpxDataChangeEvent(this);
     1110                listeners.fireEvent(l -> l.gpxDataChanged(e));
     1111            }
    10321112        }
    10331113    }
    10341114
    10351115    /**
     1116     * Begins updating this GpxData and prevents listeners from being fired.
     1117     * @since xxx
     1118     */
     1119    public void beginUpdate() {
     1120        updating = true;
     1121    }
     1122
     1123    /**
     1124     * Finishes updating this GpxData and fires listeners if required.
     1125     * @since xxx
     1126     */
     1127    public void endUpdate() {
     1128        boolean setModified = updating;
     1129        updating = initializing = false;
     1130        if (suppressedInvalidate) {
     1131            fireInvalidate(setModified);
     1132            suppressedInvalidate = false;
     1133        }
     1134    }
     1135
     1136    /**
    10361137     * A listener that listens to GPX data changes.
    10371138     * @author Michael Zangl
    10381139     * @since 12156
     
    10671168            return source;
    10681169        }
    10691170    }
     1171
     1172    /**
     1173     * @return whether anything has been modified (e.g. colors)
     1174     * @since xxx
     1175     */
     1176    public boolean isModified() {
     1177        return modified;
     1178    }
     1179
     1180    /**
     1181     * Sets the modified flag to the value.
     1182     * @param value modified flag
     1183     * @since xxx
     1184     */
     1185    public void setModified(boolean value) {
     1186        modified = value;
     1187    }
     1188
     1189    /**
     1190     * A class containing prefix, URI and location of a namespace
     1191     * @since xxx
     1192     */
     1193    public static class XMLNamespace {
     1194        private final String uri, prefix;
     1195        private String location;
     1196
     1197        /**
     1198         * Creates a schema with prefix and URI, tries to determine prefix from URI
     1199         * @param fallbackPrefix the namespace prefix, if not determined from URI
     1200         * @param uri the namespace URI
     1201         */
     1202        public XMLNamespace(String fallbackPrefix, String uri) {
     1203            this.prefix = Optional.ofNullable(GpxExtension.findPrefix(uri)).orElse(fallbackPrefix);
     1204            this.uri = uri;
     1205        }
     1206
     1207        /**
     1208         * Creates a schema with prefix, URI and location.
     1209         * Does NOT try to determine prefix from URI!
     1210         * @param prefix
     1211         * @param uri
     1212         * @param location
     1213         */
     1214        public XMLNamespace(String prefix, String uri, String location) {
     1215            this.prefix = prefix;
     1216            this.uri = uri;
     1217            this.location = location;
     1218        }
     1219
     1220        /**
     1221         * @return the URI of the namesapce
     1222         */
     1223        public String getURI() {
     1224            return uri;
     1225        }
     1226
     1227        /**
     1228         * @return the prefix of the namespace, determined from URI if possible
     1229         */
     1230        public String getPrefix() {
     1231            return prefix;
     1232        }
     1233
     1234        /**
     1235         * @return the location of the schema
     1236         */
     1237        public String getLocation() {
     1238            return location;
     1239        }
     1240
     1241        /**
     1242         * Sets the location of the schema
     1243         * @param location the location of the schema
     1244         */
     1245        public void setLocation(String location) {
     1246            this.location = location;
     1247        }
     1248
     1249        @Override
     1250        public int hashCode() {
     1251            return Objects.hash(prefix, uri, location);
     1252        }
     1253
     1254        @Override
     1255        public boolean equals(Object obj) {
     1256            if (this == obj)
     1257                return true;
     1258            if (obj == null)
     1259                return false;
     1260            if (getClass() != obj.getClass())
     1261                return false;
     1262            XMLNamespace other = (XMLNamespace) obj;
     1263            if (prefix == null) {
     1264                if (other.prefix != null)
     1265                    return false;
     1266            } else if (!prefix.equals(other.prefix))
     1267                return false;
     1268            if (uri == null) {
     1269                if (other.uri != null)
     1270                    return false;
     1271            } else if (!uri.equals(other.uri))
     1272                return false;
     1273            if (location == null) {
     1274                if (other.location != null)
     1275                    return false;
     1276            } else if (!location.equals(other.location))
     1277                return false;
     1278            return true;
     1279        }
     1280    }
    10701281}
  • src/org/openstreetmap/josm/data/gpx/GpxExtension.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.util.Objects;
     5import java.util.Optional;
     6
     7import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace;
     8import org.xml.sax.Attributes;
     9
     10/**
     11 * A GpxExtension that has attributes and child extensions (implements {@link IWithAttributes}).
     12 * @since xxx
     13 */
     14public class GpxExtension extends WithAttributes implements IWithAttributes, GpxConstants {
     15    private final String qualifiedName, prefix, key;
     16    private IWithAttributes parent;
     17    private String value;
     18    private boolean visible = true;
     19
     20    /**
     21     * Constructs a new {@link GpxExtension}.
     22     * @param prefix the prefix
     23     * @param key the key
     24     * @param value the value
     25     */
     26    public GpxExtension(String prefix, String key, String value) {
     27        this.prefix = Optional.ofNullable(prefix).orElse("");
     28        this.key = key;
     29        this.value = value;
     30        this.qualifiedName = (this.prefix.isEmpty() ? "" : this.prefix + ":") + key;
     31    }
     32
     33    /**
     34     * Creates a new {@link GpxExtension}
     35     *
     36     * @param namespaceURI the URI of the XML namespace, used to determine supported extensions
     37     *                     (josm, gpxx, gpxd) regardless of the prefix that could legally vary from file to file.
     38     * @param qName the qualified name of the XML element including prefix
     39     * @param atts the attributes
     40     */
     41    public GpxExtension(String namespaceURI, String qName, Attributes atts) {
     42        qualifiedName = qName;
     43        int dot = qName.indexOf(':');
     44        String p = findPrefix(namespaceURI);
     45        if (p == null) {
     46            if (dot != -1) {
     47                prefix = qName.substring(0, dot);
     48            } else {
     49                prefix = "";
     50            }
     51        } else {
     52            prefix = p;
     53        }
     54        key = qName.substring(dot + 1);
     55        for (int i = 0; i < atts.getLength(); i++) {
     56            attr.put(atts.getLocalName(i), atts.getValue(i));
     57        }
     58    }
     59
     60    /**
     61     * Finds the default prefix used by JOSM for the given namespaceURI as the document is free specify another one.
     62     * @param namespaceURI
     63     * @return the prefix
     64     */
     65    public static String findPrefix(String namespaceURI) {
     66        if (XML_URI_EXTENSIONS_DRAWING.equals(namespaceURI))
     67            return "gpxd";
     68
     69        if (XML_URI_EXTENSIONS_GARMIN.equals(namespaceURI))
     70            return "gpxx";
     71
     72        if (XML_URI_EXTENSIONS_JOSM.equals(namespaceURI))
     73            return "josm";
     74
     75        return null;
     76    }
     77
     78    /**
     79     * Finds the namespace for the given default prefix, if supported with schema location
     80     * @param prefix the prefix used by JOSM
     81     * @return the {@link XMLNamespace} element, location and URI can be <code>null</code> if not found.
     82     */
     83    public static XMLNamespace findNamespace(String prefix) {
     84        switch (prefix) {
     85        case "gpxx":
     86            return new XMLNamespace("gpxx", XML_URI_EXTENSIONS_GARMIN, XML_XSD_EXTENSIONS_GARMIN);
     87        case "gpxd":
     88            return new XMLNamespace("gpxd", XML_URI_EXTENSIONS_DRAWING, XML_XSD_EXTENSIONS_DRAWING);
     89        case "josm":
     90            return new XMLNamespace("josm", XML_URI_EXTENSIONS_JOSM, XML_XSD_EXTENSIONS_JOSM);
     91        }
     92        return null;
     93    }
     94
     95    /**
     96     * @return the qualified name of the XML element
     97     */
     98    public String getQualifiedName() {
     99        return qualifiedName;
     100    }
     101
     102    /**
     103     * @return the prefix of the XML namespace
     104     */
     105    public String getPrefix() {
     106        return prefix;
     107    }
     108
     109    /**
     110     * @return the key (local element name) of the extension
     111     */
     112    public String getKey() {
     113        return key;
     114    }
     115
     116    /**
     117     * @return the flattened extension key of this extension, used for conversion to OSM layers
     118     */
     119    public String getFlatKey() {
     120        String ret = "";
     121        if (parent != null && parent instanceof GpxExtension) {
     122            GpxExtension ext = (GpxExtension) parent;
     123            ret = ext.getFlatKey() + ":";
     124        }
     125        return ret + getKey();
     126    }
     127
     128    /**
     129     * Searches recursively for the extension with the given key in all children
     130     * @param sPrefix the prefix to look for
     131     * @param sKey the key to look for
     132     * @return the extension if found, otherwise <code>null</code>
     133     */
     134    public GpxExtension findExtension(String sPrefix, String sKey) {
     135        if (prefix.equalsIgnoreCase(sPrefix) && key.equalsIgnoreCase(sKey)) {
     136            return this;
     137        } else {
     138            for (GpxExtension child : getExtensions()) {
     139                GpxExtension ext = child.findExtension(sPrefix, sKey);
     140                if (ext != null) {
     141                    return ext;
     142                }
     143            }
     144            return null;
     145        }
     146    }
     147
     148    /**
     149     * @return the value of the extension
     150     */
     151    public String getValue() {
     152        return value;
     153    }
     154
     155    /**
     156     * @param value the value to set
     157     */
     158    public void setValue(String value) {
     159        this.value = value;
     160    }
     161
     162    /**
     163     * Removes this extension from its parent and all then-empty parents
     164     * @throws IllegalStateException if parent not set
     165     */
     166    public void remove() {
     167        if (parent == null)
     168            throw new IllegalStateException("Extension " + qualifiedName + " has no parent, can't remove it.");
     169
     170        parent.getExtensions().remove(this);
     171        if (parent instanceof GpxExtension) {
     172            GpxExtension gpx = ((GpxExtension) parent);
     173            if ((gpx.getValue() == null || gpx.getValue().trim().isEmpty())
     174                    && gpx.getAttributes().isEmpty()
     175                    && gpx.getExtensions().isEmpty()) {
     176                gpx.remove();
     177            }
     178        }
     179    }
     180
     181    /**
     182     * Hides this extension and all then-empty parents so it isn't written
     183     * @see #isVisible()
     184     */
     185    public void hide() {
     186        visible = false;
     187        if (parent != null && parent instanceof GpxExtension) {
     188            GpxExtension gpx = (GpxExtension) parent;
     189            if ((gpx.getValue() == null || gpx.getValue().trim().isEmpty())
     190                    && gpx.getAttributes().isEmpty()
     191                    && !gpx.getExtensions().isVisible()) {
     192                gpx.hide();
     193            }
     194        }
     195    }
     196
     197    /**
     198     * Shows this extension and all parents so it can be written
     199     * @see #isVisible()
     200     */
     201    public void show() {
     202        visible = true;
     203        if (parent != null && parent instanceof GpxExtension) {
     204            ((GpxExtension) parent).show();
     205        }
     206    }
     207
     208    /**
     209     * @return if this extension should be written, used for hiding colors during export without removing them
     210     */
     211    public boolean isVisible() {
     212        return visible;
     213    }
     214
     215    /**
     216     * @return the parent element of this extension, can be another extension or gpx elements (data, track, segment, ...)
     217     */
     218    public IWithAttributes getParent() {
     219        return parent;
     220    }
     221
     222    /**
     223     * Sets the parent for this extension
     224     * @param parent the parent
     225     * @throws IllegalStateException if parent already set
     226     */
     227    public void setParent(IWithAttributes parent) {
     228        if (this.parent != null)
     229            throw new IllegalStateException("Parent of extension " + qualifiedName + " is already set");
     230
     231        this.parent = parent;
     232    }
     233
     234    @Override
     235    public int hashCode() {
     236        return Objects.hash(prefix, key, value, attr, parent, visible, super.hashCode());
     237    }
     238
     239    @Override
     240    public boolean equals(Object obj) {
     241        if (this == obj)
     242            return true;
     243        if (obj == null)
     244            return false;
     245        if (!super.equals(obj))
     246            return false;
     247        if (!(obj instanceof GpxExtension))
     248            return false;
     249        GpxExtension other = (GpxExtension) obj;
     250        if (visible != other.visible)
     251            return false;
     252        if (prefix == null) {
     253            if (other.prefix != null)
     254                return false;
     255        } else if (!prefix.equals(other.prefix))
     256            return false;
     257        if (key == null) {
     258            if (other.key != null)
     259                return false;
     260        } else if (!key.equals(other.key))
     261            return false;
     262        if (value == null) {
     263            if (other.value != null)
     264                return false;
     265        } else if (!value.equals(other.value))
     266            return false;
     267        if (attr == null) {
     268            if (other.attr != null)
     269                return false;
     270        } else if (!attr.equals(other.attr))
     271            return false;
     272        if (parent == null) {
     273            if (other.parent != null)
     274                return false;
     275        } else if (!parent.equals(other.parent))
     276            return false;
     277        return true;
     278    }
     279}
  • src/org/openstreetmap/josm/data/gpx/GpxExtensionCollection.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.util.ArrayList;
     5import java.util.Collection;
     6import java.util.Objects;
     7import java.util.Optional;
     8import java.util.Stack;
     9import java.util.stream.Collectors;
     10import java.util.stream.Stream;
     11
     12import org.apache.commons.jcs.access.exception.InvalidArgumentException;
     13import org.openstreetmap.josm.io.GpxReader;
     14import org.xml.sax.Attributes;
     15
     16/**
     17 * Class extending <code>ArrayList&lt;GpxExtension&gt;</code>.
     18 * Can be used to collect {@link GpxExtension}s while reading GPX files, see {@link GpxReader}
     19 * @since xxx
     20 */
     21public class GpxExtensionCollection extends ArrayList<GpxExtension> {
     22
     23    private Stack<GpxExtension> childStack = new Stack<>();
     24    private IWithAttributes parent;
     25
     26    /**
     27     * Constructs a new {@link GpxExtensionCollection}
     28     */
     29    public GpxExtensionCollection() {}
     30
     31    /**
     32     * Constructs a new {@link GpxExtensionCollection} with the given parent
     33     * @param parent the parent extending {@link IWithAttributes}
     34     */
     35    public GpxExtensionCollection(IWithAttributes parent) {
     36        this.parent = parent;
     37    }
     38
     39    /**
     40     * Adds a child extension to the last extension and pushes it to the stack.
     41     * @param namespaceURI the URI of the XML namespace, used to determine supported
     42     *                     extensions (josm, gpxx, gpxd) regardless of the prefix.
     43     * @param qName the qualified name of the XML element including prefix
     44     * @param atts the attributes
     45     */
     46    public void openChild(String namespaceURI, String qName, Attributes atts) {
     47        GpxExtension child = new GpxExtension(namespaceURI, qName, atts);
     48        if (!childStack.isEmpty()) {
     49            childStack.lastElement().getExtensions().add(child);
     50        } else {
     51            this.add(child);
     52        }
     53        childStack.add(child);
     54    }
     55
     56    /**
     57     * Sets the value for the last child and pops it from the stack, so the next one will be added to its parent.
     58     * The qualified name is verified.
     59     * @param qName the qualified name
     60     * @param value the value
     61     */
     62    public void closeChild(String qName, String value) {
     63        if (childStack.isEmpty())
     64            throw new InvalidArgumentException("Can't close child " + qName + ", no element in stack.");
     65
     66        GpxExtension child = childStack.pop();
     67
     68        String childQN = child.getQualifiedName();
     69
     70        if (!childQN.equals(qName))
     71            throw new InvalidArgumentException("Can't close child " + qName + ", must close " + childQN + " first.");
     72
     73        child.setValue(value);
     74    }
     75
     76    @Override
     77    public boolean add(GpxExtension gpx) {
     78        gpx.setParent(parent);
     79        return super.add(gpx);
     80    }
     81
     82    /**
     83     * Creates and adds a new {@link GpxExtension} from the given parameters.
     84     * @param prefix the prefix
     85     * @param key the key/tag
     86     * @return the added GpxExtension
     87     */
     88    public GpxExtension add(String prefix, String key) {
     89        return add(prefix, key, null);
     90    }
     91
     92    /**
     93     * Creates and adds a new {@link GpxExtension} from the given parameters.
     94     * @param prefix the prefix
     95     * @param key the key/tag
     96     * @param value the value, can be <code>null</code>
     97     * @return the added GpxExtension
     98     */
     99    public GpxExtension add(String prefix, String key, String value) {
     100        GpxExtension gpx = new GpxExtension(prefix, key, value);
     101        add(gpx);
     102        return gpx;
     103    }
     104
     105    /**
     106     * Creates and adds a new {@link GpxExtension}, if it hasn't been added yet. Shows it if it has.
     107     * @param prefix the prefix
     108     * @param key the key/tag
     109     * @return the added or found GpxExtension
     110     * @see GpxExtension#show()
     111     */
     112    public GpxExtension addIfNotPresent(String prefix, String key) {
     113        GpxExtension gpx = get(prefix, key);
     114        if (gpx != null) {
     115            gpx.show();
     116            return gpx;
     117        }
     118        return add(prefix, key);
     119    }
     120
     121    /**
     122     * Creates and adds a new {@link GpxExtension} or updates its value and shows it if already present.
     123     * @param prefix the prefix
     124     * @param key the key/tag
     125     * @param value the value
     126     * @return the added or found GpxExtension
     127     * @see GpxExtension#show()
     128     */
     129    public GpxExtension addOrUpdate(String prefix, String key, String value) {
     130        GpxExtension gpx = get(prefix, key);
     131        if (gpx != null) {
     132            gpx.show();
     133            gpx.setValue(value);
     134            return gpx;
     135        } else {
     136            return add(prefix, key, value);
     137        }
     138    }
     139
     140    @Override
     141    public boolean addAll(Collection<? extends GpxExtension> extensions) {
     142        extensions.forEach(e -> e.setParent(parent));
     143        return super.addAll(extensions);
     144    }
     145
     146    /**
     147     * Adds an extension from a flat chain without prefix, e.g. when converting from OSM
     148     * @param chain the full key chain, e.g. ["extension", "gpxx", "TrackExtensions", "DisplayColor"]
     149     * @param value the value
     150     */
     151    public void addFlat(String[] chain, String value) {
     152        if (chain.length >= 3 && "extension".equals(chain[0])) {
     153            String prefix = "other".equals(chain[1]) ? "" : chain[1];
     154            GpxExtensionCollection previous = this;
     155            for (int i = 2; i < chain.length; i++) {
     156                if (i != 2 || !"segment".equals(chain[2])) {
     157                    previous = previous.add(prefix, chain[i], i == chain.length - 1 ? value : null).getExtensions();
     158                }
     159            }
     160        }
     161    }
     162
     163    /**
     164     * Gets the extension with the given prefix and key
     165     * @param prefix the prefix
     166     * @param key the key/tag
     167     * @return the {@link GpxExtension} if found or <code>null</code>
     168     */
     169    public GpxExtension get(String prefix, String key) {
     170        return stream(prefix, key).findAny().orElse(null);
     171    }
     172
     173    /**
     174     * Gets all extensions with the given prefix and key
     175     * @param prefix the prefix
     176     * @param key the key/tag
     177     * @return a {@link GpxExtensionCollection} with the extensions, empty collection if none found
     178     */
     179    public GpxExtensionCollection getAll(String prefix, String key) {
     180        GpxExtensionCollection copy = new GpxExtensionCollection(this.parent);
     181        copy.addAll(stream(prefix, key).collect(Collectors.toList()));
     182        return copy;
     183    }
     184
     185    /**
     186     * Gets a stream with all extensions with the given prefix and key
     187     * @param prefix the prefix
     188     * @param key the key/tag
     189     * @return the <code>Stream&lt;{@link GpxExtension}&gt;</code>
     190     */
     191    public Stream<GpxExtension> stream(String prefix, String key) {
     192        return stream().filter(e -> Objects.equals(prefix, e.getPrefix()) && Objects.equals(key, e.getKey()));
     193    }
     194
     195    /**
     196     * Searches recursively for the extension with the given prefix and key in all children
     197     * @param prefix the prefix to look for
     198     * @param key the key to look for
     199     * @return the extension if found, otherwise <code>null</code>
     200     */
     201    public GpxExtension find(String prefix, String key) {
     202        for (GpxExtension child : this) {
     203            GpxExtension ext = child.findExtension(prefix, key);
     204            if (ext != null) {
     205                return ext;
     206            }
     207        }
     208        return null;
     209    }
     210
     211    /**
     212     * Searches and removes recursively all extensions with the given prefix and key in all children
     213     * @param prefix the prefix to look for
     214     * @param key the key to look for
     215      */
     216    public void findAndRemove(String prefix, String key) {
     217        Optional.ofNullable(find(prefix, key)).ifPresent(GpxExtension::remove);
     218    }
     219
     220    /**
     221     * Removes all {@link GpxExtension}s with the given prefix and key in direct children
     222     * @param prefix the prefix
     223     * @param key the key/tag
     224     */
     225    public void remove(String prefix, String key) {
     226        stream(prefix, key)
     227        .collect(Collectors.toList()) //needs to be collected to avoid concurrent modification
     228        .forEach(e -> super.remove(e));
     229    }
     230
     231    /**
     232     * Removes all extensions with the given prefix in direct children
     233     * @param prefix the prefix
     234     */
     235    public void removeAllWithPrefix(String prefix) {
     236        stream()
     237        .filter(e -> Objects.equals(prefix, e.getPrefix()))
     238        .collect(Collectors.toList()) //needs to be collected to avoid concurrent modification
     239        .forEach(e -> super.remove(e));
     240    }
     241
     242    /**
     243     * Gets all prefixes of direct (writable) children
     244     * @return stream with the prefixes
     245     */
     246    public Stream<String> getPrefixesStream() {
     247        return stream()
     248                .filter(GpxExtension::isVisible)
     249                .map(GpxExtension::getPrefix)
     250                .distinct();
     251    }
     252
     253    /**
     254     * @return <code>true</code> if this collection contains writable extensions
     255     */
     256    public boolean isVisible() {
     257        return stream().anyMatch(GpxExtension::isVisible);
     258    }
     259
     260    @Override
     261    public void clear() {
     262        childStack.clear();
     263        super.clear();
     264    }
     265
     266}
  • src/org/openstreetmap/josm/data/gpx/GpxImageCorrelation.java

     
    3939
    4040        for (GpxTrack trk : selectedGpx.tracks) {
    4141            List<List<WayPoint>> segs = new ArrayList<>();
    42             for (GpxTrackSegment seg : trk.getSegments()) {
     42            for (IGpxTrackSegment seg : trk.getSegments()) {
    4343                List<WayPoint> wps = new ArrayList<>(seg.getWayPoints());
    4444                if (!wps.isEmpty()) {
    4545                    //remove waypoints at the beginning of the track/segment without timestamps
  • src/org/openstreetmap/josm/data/gpx/GpxTrack.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    33
     4import java.awt.Color;
     5import java.util.ArrayList;
    46import java.util.Collection;
     7import java.util.Collections;
     8import java.util.HashMap;
     9import java.util.List;
    510import java.util.Map;
     11import java.util.Map.Entry;
     12import java.util.Optional;
    613
    714import org.openstreetmap.josm.data.Bounds;
     15import org.openstreetmap.josm.tools.ListenerList;
     16import org.openstreetmap.josm.tools.Logging;
    817
    918/**
    10  * Read-only gpx track. Implementations doesn't have to be immutable, but should always be thread safe.
    11  * @since 444
     19 * GPX track.
     20 * Note that the color attributes are not immutable and may be modified by the user.
     21 * @since xxx
    1222 */
    13 public interface GpxTrack extends IWithAttributes {
     23public class GpxTrack extends WithAttributes implements IGpxTrack {
    1424
    15     /**
    16      * Returns the track segments.
    17      * @return the track segments
    18      */
    19     Collection<GpxTrackSegment> getSegments();
     25    private final List<IGpxTrackSegment> segments;
     26    private final double length;
     27    private final Bounds bounds;
     28    private Color colorCache;
     29    private final ListenerList<IGpxTrack.GpxTrackChangeListener> listeners = ListenerList.create();
     30    private static final HashMap<Color, String> closestGarminColorCache = new HashMap<>();
     31    private ColorFormat colorFormat;
    2032
    2133    /**
    22      * Returns the track attributes.
    23      * @return the track attributes
     34     * Constructs a new {@code GpxTrack}.
     35     * @param trackSegs track segments
     36     * @param attributes track attributes
    2437     */
    25     Map<String, Object> getAttributes();
     38    public GpxTrack(Collection<Collection<WayPoint>> trackSegs, Map<String, Object> attributes) {
     39        List<IGpxTrackSegment> newSegments = new ArrayList<>();
     40        for (Collection<WayPoint> trackSeg: trackSegs) {
     41            if (trackSeg != null && !trackSeg.isEmpty()) {
     42                newSegments.add(new GpxTrackSegment(trackSeg));
     43            }
     44        }
     45        this.segments = Collections.unmodifiableList(newSegments);
     46        this.length = calculateLength();
     47        this.bounds = calculateBounds();
     48        this.attr = new HashMap<>(attributes);
     49    }
    2650
    2751    /**
    28      * Returns the track bounds.
    29      * @return the track bounds
     52     * Constructs a new {@code GpxTrack} from {@code GpxTrackSegment} objects.
     53     * @param trackSegs The segments to build the track from.  Input is not deep-copied,
     54     *                 which means the caller may reuse the same segments to build
     55     *                 multiple GpxTrack instances from.  This should not be
     56     *                 a problem, since this object cannot modify {@code this.segments}.
     57     * @param attributes Attributes for the GpxTrack, the input map is copied.
    3058     */
    31     Bounds getBounds();
     59    public GpxTrack(List<IGpxTrackSegment> trackSegs, Map<String, Object> attributes) {
     60        this.attr = new HashMap<>(attributes);
     61        this.segments = Collections.unmodifiableList(trackSegs);
     62        this.length = calculateLength();
     63        this.bounds = calculateBounds();
     64    }
    3265
     66    private double calculateLength() {
     67        double result = 0.0; // in meters
     68
     69        for (IGpxTrackSegment trkseg : segments) {
     70            result += trkseg.length();
     71        }
     72        return result;
     73    }
     74
     75    private Bounds calculateBounds() {
     76        Bounds result = null;
     77        for (IGpxTrackSegment segment: segments) {
     78            Bounds segBounds = segment.getBounds();
     79            if (segBounds != null) {
     80                if (result == null) {
     81                    result = new Bounds(segBounds);
     82                } else {
     83                    result.extend(segBounds);
     84                }
     85            }
     86        }
     87        return result;
     88    }
     89
     90    @Override
     91    public void setColor(Color color) {
     92        setColorExtension(color);
     93        colorCache = color;
     94    }
     95
     96    private void setColorExtension(Color color) {
     97        getExtensions().findAndRemove("gpxx",  "DisplayColor");
     98        if (color == null) {
     99            getExtensions().findAndRemove("gpxd",  "color");
     100        } else {
     101            getExtensions().addOrUpdate("gpxd", "color", String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue()));
     102        }
     103        fireInvalidate();
     104    }
     105
     106    @Override
     107    public Color getColor() {
     108        if (colorCache == null) {
     109            colorCache = getColorFromExtension();
     110        }
     111        return colorCache;
     112    }
     113
     114    private Color getColorFromExtension() {
     115        GpxExtension gpxd = getExtensions().find("gpxd", "color");
     116        if (gpxd != null) {
     117            colorFormat = ColorFormat.GPXD;
     118            String cs = gpxd.getValue();
     119            try {
     120                return Color.decode(cs);
     121            } catch (NumberFormatException ex) {
     122                Logging.warn("Could not read gpxd color: " + cs);
     123            }
     124        } else {
     125            GpxExtension gpxx = getExtensions().find("gpxx", "DisplayColor");
     126            if (gpxx != null) {
     127                colorFormat = ColorFormat.GPXX;
     128                String cs = gpxx.getValue();
     129                if (cs != null) {
     130                    Color cc = GARMIN_COLORS.get(cs);
     131                    if (cc != null) {
     132                        return cc;
     133                    }
     134                }
     135                Logging.warn("Could not read garmin color: " + cs);
     136            }
     137        }
     138        return null;
     139    }
     140
    33141    /**
    34      * Returns the track length.
    35      * @return the track length
     142     * Converts the color to the given format, if present.
     143     * @param cFormat can be a {@link GpxConstants.ColorFormat}
    36144     */
    37     double length();
     145    public void convertColor(ColorFormat cFormat) {
     146        Color c = getColor();
     147        if (c == null) return;
    38148
    39     /**
    40      * Add a listener that listens to changes in the GPX track.
    41      * @param l The listener
    42      */
    43     default void addListener(GpxTrackChangeListener l) {
    44         // nop
     149        if (cFormat != this.colorFormat) {
     150            if (cFormat == null) {
     151                // just hide the extensions, don't actually remove them
     152                Optional.ofNullable(getExtensions().find("gpxx", "DisplayColor")).ifPresent(GpxExtension::hide);
     153                Optional.ofNullable(getExtensions().find("gpxd", "color")).ifPresent(GpxExtension::hide);
     154            } else if (cFormat == ColorFormat.GPXX) {
     155                getExtensions().findAndRemove("gpxd", "color");
     156                String colorString = null;
     157                if (closestGarminColorCache.containsKey(c)) {
     158                    colorString = closestGarminColorCache.get(c);
     159                } else {
     160                    //find closest garmin color
     161                    double closestDiff = -1;
     162                    for (Entry<String, Color> e : GARMIN_COLORS.entrySet()) {
     163                        double diff = colorDist(e.getValue(), c);
     164                        if (closestDiff < 0 || diff < closestDiff) {
     165                            colorString = e.getKey();
     166                            closestDiff = diff;
     167                            if (closestDiff == 0) break;
     168                        }
     169                    }
     170                }
     171                closestGarminColorCache.put(c, colorString);
     172                getExtensions().addIfNotPresent("gpxx", "TrackExtensions").getExtensions().addOrUpdate("gpxx", "DisplayColor", colorString);
     173            } else if (cFormat == ColorFormat.GPXD) {
     174                setColor(c);
     175            }
     176            colorFormat = cFormat;
     177        }
    45178    }
    46179
     180    private double colorDist(Color c1, Color c2) {
     181        // Simple Euclidean distance between two colors
     182        return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(), 2)
     183                + Math.pow(c1.getGreen() - c2.getGreen(), 2)
     184                + Math.pow(c1.getBlue() - c2.getBlue(), 2));
     185    }
     186
     187    @Override
     188    public void put(String key, Object value) {
     189        super.put(key, value);
     190        fireInvalidate();
     191    }
     192
     193    private void fireInvalidate() {
     194        listeners.fireEvent(l -> l.gpxDataChanged(new IGpxTrack.GpxTrackChangeEvent(this)));
     195    }
     196
     197    @Override
     198    public Bounds getBounds() {
     199        return bounds == null ? null : new Bounds(bounds);
     200    }
     201
     202    @Override
     203    public double length() {
     204        return length;
     205    }
     206
     207    @Override
     208    public Collection<IGpxTrackSegment> getSegments() {
     209        return segments;
     210    }
     211
     212    @Override
     213    public int hashCode() {
     214        return 31 * super.hashCode() + ((segments == null) ? 0 : segments.hashCode());
     215    }
     216
     217    @Override
     218    public boolean equals(Object obj) {
     219        if (this == obj)
     220            return true;
     221        if (obj == null)
     222            return false;
     223        if (!super.equals(obj))
     224            return false;
     225        if (getClass() != obj.getClass())
     226            return false;
     227        GpxTrack other = (GpxTrack) obj;
     228        if (segments == null) {
     229            if (other.segments != null)
     230                return false;
     231        } else if (!segments.equals(other.segments))
     232            return false;
     233        return true;
     234    }
     235
     236    @Override
     237    public void addListener(IGpxTrack.GpxTrackChangeListener l) {
     238        listeners.addListener(l);
     239    }
     240
     241    @Override
     242    public void removeListener(IGpxTrack.GpxTrackChangeListener l) {
     243        listeners.removeListener(l);
     244    }
     245
    47246    /**
    48      * Remove a listener that listens to changes in the GPX track.
    49      * @param l The listener
     247     * Resets the color cache
    50248     */
    51     default void removeListener(GpxTrackChangeListener l) {
    52         // nop
     249    public void invalidate() {
     250        colorCache = null;
    53251    }
    54252
    55253    /**
    56254     * A listener that listens to GPX track changes.
    57      * @author Michael Zangl
    58      * @since 12156
     255     * @deprecated use {@link IGpxTrack.GpxTrackChangeListener} instead
    59256     */
     257    @Deprecated
    60258    @FunctionalInterface
    61259    interface GpxTrackChangeListener {
    62         /**
    63          * Called when the gpx data changed.
    64          * @param e The event
    65          */
    66260        void gpxDataChanged(GpxTrackChangeEvent e);
    67261    }
    68262
    69263    /**
    70264     * A track change event for the current track.
    71      * @author Michael Zangl
    72      * @since 12156
     265     * @deprecated use {@link IGpxTrack.GpxTrackChangeEvent} instead
    73266     */
    74     class GpxTrackChangeEvent {
    75         private final GpxTrack source;
    76 
    77         GpxTrackChangeEvent(GpxTrack source) {
    78             super();
    79             this.source = source;
     267    @Deprecated
     268    static class GpxTrackChangeEvent extends IGpxTrack.GpxTrackChangeEvent {
     269        GpxTrackChangeEvent(IGpxTrack source) {
     270            super(source);
    80271        }
     272    }
    81273
    82         /**
    83          * Get the track that was changed.
    84          * @return The track.
    85          */
    86         public GpxTrack getSource() {
    87             return source;
    88         }
    89     }
    90274}
  • src/org/openstreetmap/josm/data/gpx/GpxTrackSegment.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    33
     4import java.util.ArrayList;
    45import java.util.Collection;
     6import java.util.Collections;
     7import java.util.List;
    58
    69import org.openstreetmap.josm.data.Bounds;
    710
    811/**
    9  * Read-only gpx track segments. Implementations doesn't have to be immutable, but should always be thread safe.
    10  * @since 2907
     12 * A gpx track segment consisting of multiple waypoints.
     13 * @since xxx
    1114 */
    12 public interface GpxTrackSegment {
     15public class GpxTrackSegment extends WithAttributes implements IGpxTrackSegment {
    1316
    14     /**
    15      * Returns the segment bounds.
    16      * @return the segment bounds
    17      */
    18     Bounds getBounds();
     17    private final List<WayPoint> wayPoints;
     18    private final Bounds bounds;
     19    private final double length;
    1920
    2021    /**
    21      * Returns the segment waypoints.
    22      * @return the segment waypoints
     22     * Constructs a new {@code GpxTrackSegment}.
     23     * @param wayPoints list of waypoints
    2324     */
    24     Collection<WayPoint> getWayPoints();
     25    public GpxTrackSegment(Collection<WayPoint> wayPoints) {
     26        this.wayPoints = Collections.unmodifiableList(new ArrayList<>(wayPoints));
     27        this.bounds = calculateBounds();
     28        this.length = calculateLength();
     29    }
    2530
    26     /**
    27      * Returns the segment length.
    28      * @return the segment length
    29      */
    30     double length();
     31    private Bounds calculateBounds() {
     32        Bounds result = null;
     33        for (WayPoint wpt: wayPoints) {
     34            if (result == null) {
     35                result = new Bounds(wpt.getCoor());
     36            } else {
     37                result.extend(wpt.getCoor());
     38            }
     39        }
     40        return result;
     41    }
    3142
    32     /**
    33      * Returns the number of times this track has been changed
    34      * @return Number of times this track has been changed. Always 0 for read-only segments
    35      */
    36     int getUpdateCount();
     43    private double calculateLength() {
     44        double result = 0.0; // in meters
     45        WayPoint last = null;
     46        for (WayPoint tpt : wayPoints) {
     47            if (last != null) {
     48                Double d = last.getCoor().greatCircleDistance(tpt.getCoor());
     49                if (!d.isNaN() && !d.isInfinite()) {
     50                    result += d;
     51                }
     52            }
     53            last = tpt;
     54        }
     55        return result;
     56    }
     57
     58    @Override
     59    public Bounds getBounds() {
     60        return bounds == null ? null : new Bounds(bounds);
     61    }
     62
     63    @Override
     64    public Collection<WayPoint> getWayPoints() {
     65        return Collections.unmodifiableList(wayPoints);
     66    }
     67
     68    @Override
     69    public double length() {
     70        return length;
     71    }
     72
     73    @Override
     74    public int getUpdateCount() {
     75        return 0;
     76    }
     77
     78    @Override
     79    public int hashCode() {
     80        final int prime = 31;
     81        int result = prime + super.hashCode();
     82        result = prime * result + ((wayPoints == null) ? 0 : wayPoints.hashCode());
     83        return result;
     84    }
     85
     86    @Override
     87    public boolean equals(Object obj) {
     88        if (this == obj)
     89            return true;
     90        if (obj == null)
     91            return false;
     92        if (!super.equals(obj))
     93            return false;
     94        if (getClass() != obj.getClass())
     95            return false;
     96        GpxTrackSegment other = (GpxTrackSegment) obj;
     97        if (wayPoints == null) {
     98            if (other.wayPoints != null)
     99                return false;
     100        } else if (!wayPoints.equals(other.wayPoints))
     101            return false;
     102        return true;
     103    }
    37104}
  • src/org/openstreetmap/josm/data/gpx/IGpxTrack.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.awt.Color;
     5import java.util.Collection;
     6
     7import org.openstreetmap.josm.data.Bounds;
     8
     9/**
     10 * Gpx track. Implementations don't have to be immutable, but should always be thread safe.
     11 * @since xxx
     12 */
     13public interface IGpxTrack extends IWithAttributes {
     14
     15    /**
     16     * Returns the track segments.
     17     * @return the track segments
     18     */
     19    Collection<IGpxTrackSegment> getSegments();
     20
     21    /**
     22     * Returns the track bounds.
     23     * @return the track bounds
     24     */
     25    Bounds getBounds();
     26
     27    /**
     28     * Returns the track length.
     29     * @return the track length
     30     */
     31    double length();
     32
     33    /**
     34     * Gets the color of this track.
     35     * @return The color, <code>null</code> if not set or not supported by the implementation.
     36     * @since xxx
     37     */
     38    default Color getColor() {
     39        return null;
     40    }
     41
     42    /**
     43     * Sets the color of this track. Not necessarily supported by all implementations.
     44     * @param color
     45     * @since xxx
     46     */
     47    default void setColor(Color color) {}
     48
     49    /**
     50     * Add a listener that listens to changes in the GPX track.
     51     * @param l The listener
     52     */
     53    default void addListener(GpxTrackChangeListener l) {
     54        // nop
     55    }
     56
     57    /**
     58     * Remove a listener that listens to changes in the GPX track.
     59     * @param l The listener
     60     */
     61    default void removeListener(GpxTrackChangeListener l) {
     62        // nop
     63    }
     64
     65    /**
     66     * A listener that listens to GPX track changes.
     67     * @author Michael Zangl
     68     * @since xxx
     69     */
     70    @FunctionalInterface
     71    interface GpxTrackChangeListener {
     72        /**
     73         * Called when the gpx data changed.
     74         * @param e The event
     75         */
     76        void gpxDataChanged(GpxTrackChangeEvent e);
     77    }
     78
     79    /**
     80     * A track change event for the current track.
     81     * @author Michael Zangl
     82     * @since xxx
     83     */
     84    class GpxTrackChangeEvent {
     85        private final IGpxTrack source;
     86
     87        GpxTrackChangeEvent(IGpxTrack source) {
     88            super();
     89            this.source = source;
     90        }
     91
     92        /**
     93         * Get the track that was changed.
     94         * @return The track.
     95         */
     96        public IGpxTrack getSource() {
     97            return source;
     98        }
     99    }
     100}
  • src/org/openstreetmap/josm/data/gpx/IGpxTrackSegment.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.util.Collection;
     5
     6import org.openstreetmap.josm.data.Bounds;
     7
     8/**
     9 * Read-only gpx track segments. Implementations don't have to be immutable, but should always be thread safe.
     10 * @since xxx
     11 */
     12public interface IGpxTrackSegment extends IWithAttributes {
     13
     14    /**
     15     * Returns the segment bounds.
     16     * @return the segment bounds
     17     */
     18    Bounds getBounds();
     19
     20    /**
     21     * Returns the segment waypoints.
     22     * @return the segment waypoints
     23     */
     24    Collection<WayPoint> getWayPoints();
     25
     26    /**
     27     * Returns the segment length.
     28     * @return the segment length
     29     */
     30    double length();
     31
     32    /**
     33     * Returns the number of times this track has been changed
     34     * @return Number of times this track has been changed. Always 0 for read-only segments
     35     */
     36    int getUpdateCount();
     37}
  • src/org/openstreetmap/josm/data/gpx/IWithAttributes.java

     
    22package org.openstreetmap.josm.data.gpx;
    33
    44import java.util.Collection;
     5import java.util.Map;
    56
    67/**
    78 * Object with attributes (in the context of GPX data).
     
    5051    void put(String key, Object value);
    5152
    5253    /**
    53      * Add a key / value pair that is not part of the GPX schema as an extension.
    54      *
    55      * @param key the key
    56      * @param value the value
     54     * Returns the attributes
     55     * @return the attributes
    5756     */
    58     void addExtension(String key, String value);
     57    Map<String, Object> getAttributes();
    5958
     59    /**
     60     * Returns the extensions
     61     * @return the extensions
     62     */
     63    GpxExtensionCollection getExtensions();
     64
    6065}
  • src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    33
    4 import java.util.ArrayList;
    54import java.util.Collection;
    6 import java.util.Collections;
    7 import java.util.HashMap;
    85import java.util.List;
    96import java.util.Map;
    107
    11 import org.openstreetmap.josm.data.Bounds;
    12 
    138/**
    14  * Immutable GPX track.
     9 * GPX track, NOT immutable
    1510 * @since 2907
     11 * @deprecated Use {@link GpxTrack} instead!
    1612 */
    17 public class ImmutableGpxTrack extends WithAttributes implements GpxTrack {
     13@Deprecated
     14public class ImmutableGpxTrack extends GpxTrack {
    1815
    19     private final List<GpxTrackSegment> segments;
    20     private final double length;
    21     private final Bounds bounds;
    22 
    2316    /**
    2417     * Constructs a new {@code ImmutableGpxTrack}.
    2518     * @param trackSegs track segments
     
    2619     * @param attributes track attributes
    2720     */
    2821    public ImmutableGpxTrack(Collection<Collection<WayPoint>> trackSegs, Map<String, Object> attributes) {
    29         List<GpxTrackSegment> newSegments = new ArrayList<>();
    30         for (Collection<WayPoint> trackSeg: trackSegs) {
    31             if (trackSeg != null && !trackSeg.isEmpty()) {
    32                 newSegments.add(new ImmutableGpxTrackSegment(trackSeg));
    33             }
    34         }
    35         this.attr = Collections.unmodifiableMap(new HashMap<>(attributes));
    36         this.segments = Collections.unmodifiableList(newSegments);
    37         this.length = calculateLength();
    38         this.bounds = calculateBounds();
     22        super(trackSegs, attributes);
    3923    }
    4024
    4125    /**
     
    4731     * @param attributes Attributes for the GpxTrack, the input map is copied.
    4832     * @since 13210
    4933     */
    50     public ImmutableGpxTrack(List<GpxTrackSegment> segments, Map<String, Object> attributes) {
    51         this.attr = Collections.unmodifiableMap(new HashMap<>(attributes));
    52         this.segments = Collections.unmodifiableList(segments);
    53         this.length = calculateLength();
    54         this.bounds = calculateBounds();
     34    public ImmutableGpxTrack(List<IGpxTrackSegment> segments, Map<String, Object> attributes) {
     35        super(segments, attributes);
    5536    }
    56 
    57     private double calculateLength() {
    58         double result = 0.0; // in meters
    59 
    60         for (GpxTrackSegment trkseg : segments) {
    61             result += trkseg.length();
    62         }
    63         return result;
    64     }
    65 
    66     private Bounds calculateBounds() {
    67         Bounds result = null;
    68         for (GpxTrackSegment segment: segments) {
    69             Bounds segBounds = segment.getBounds();
    70             if (segBounds != null) {
    71                 if (result == null) {
    72                     result = new Bounds(segBounds);
    73                 } else {
    74                     result.extend(segBounds);
    75                 }
    76             }
    77         }
    78         return result;
    79     }
    80 
    81     @Override
    82     public Map<String, Object> getAttributes() {
    83         return attr;
    84     }
    85 
    86     @Override
    87     public Bounds getBounds() {
    88         return bounds == null ? null : new Bounds(bounds);
    89     }
    90 
    91     @Override
    92     public double length() {
    93         return length;
    94     }
    95 
    96     @Override
    97     public Collection<GpxTrackSegment> getSegments() {
    98         return segments;
    99     }
    100 
    101     @Override
    102     public int hashCode() {
    103         return 31 * super.hashCode() + ((segments == null) ? 0 : segments.hashCode());
    104     }
    105 
    106     @Override
    107     public boolean equals(Object obj) {
    108         if (this == obj)
    109             return true;
    110         if (!super.equals(obj))
    111             return false;
    112         if (getClass() != obj.getClass())
    113             return false;
    114         ImmutableGpxTrack other = (ImmutableGpxTrack) obj;
    115         if (segments == null) {
    116             if (other.segments != null)
    117                 return false;
    118         } else if (!segments.equals(other.segments))
    119             return false;
    120         return true;
    121     }
    12237}
  • src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    33
    4 import java.util.ArrayList;
    54import java.util.Collection;
    6 import java.util.Collections;
    7 import java.util.List;
    85
    9 import org.openstreetmap.josm.data.Bounds;
    10 
    116/**
    127 * A gpx track segment consisting of multiple waypoints, that cannot be changed.
     8 * @deprecated Use {@link GpxTrackSegment} instead!
    139 */
    14 public class ImmutableGpxTrackSegment implements GpxTrackSegment {
     10@Deprecated
     11public class ImmutableGpxTrackSegment extends GpxTrackSegment {
    1512
    16     private final List<WayPoint> wayPoints;
    17     private final Bounds bounds;
    18     private final double length;
    19 
    2013    /**
    2114     * Constructs a new {@code ImmutableGpxTrackSegment}.
    2215     * @param wayPoints list of waypoints
    2316     */
    2417    public ImmutableGpxTrackSegment(Collection<WayPoint> wayPoints) {
    25         this.wayPoints = Collections.unmodifiableList(new ArrayList<>(wayPoints));
    26         this.bounds = calculateBounds();
    27         this.length = calculateLength();
     18        super(wayPoints);
    2819    }
    29 
    30     private Bounds calculateBounds() {
    31         Bounds result = null;
    32         for (WayPoint wpt: wayPoints) {
    33             if (result == null) {
    34                 result = new Bounds(wpt.getCoor());
    35             } else {
    36                 result.extend(wpt.getCoor());
    37             }
    38         }
    39         return result;
    40     }
    41 
    42     private double calculateLength() {
    43         double result = 0.0; // in meters
    44         WayPoint last = null;
    45         for (WayPoint tpt : wayPoints) {
    46             if (last != null) {
    47                 Double d = last.getCoor().greatCircleDistance(tpt.getCoor());
    48                 if (!d.isNaN() && !d.isInfinite()) {
    49                     result += d;
    50                 }
    51             }
    52             last = tpt;
    53         }
    54         return result;
    55     }
    56 
    57     @Override
    58     public Bounds getBounds() {
    59         return bounds == null ? null : new Bounds(bounds);
    60     }
    61 
    62     @Override
    63     public Collection<WayPoint> getWayPoints() {
    64         return Collections.unmodifiableList(wayPoints);
    65     }
    66 
    67     @Override
    68     public double length() {
    69         return length;
    70     }
    71 
    72     @Override
    73     public int getUpdateCount() {
    74         return 0;
    75     }
    76 
    77     @Override
    78     public int hashCode() {
    79         return 31 + ((wayPoints == null) ? 0 : wayPoints.hashCode());
    80     }
    81 
    82     @Override
    83     public boolean equals(Object obj) {
    84         if (this == obj)
    85             return true;
    86         if (obj == null)
    87             return false;
    88         if (getClass() != obj.getClass())
    89             return false;
    90         ImmutableGpxTrackSegment other = (ImmutableGpxTrackSegment) obj;
    91         if (wayPoints == null) {
    92             if (other.wayPoints != null)
    93                 return false;
    94         } else if (!wayPoints.equals(other.wayPoints))
    95             return false;
    96         return true;
    97     }
    9820}
  • src/org/openstreetmap/josm/data/gpx/Line.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.data.gpx;
    3 
    4 import java.util.Collection;
    5 import java.util.Iterator;
    6 import java.util.Map;
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import java.awt.Color;
     5import java.util.Collection;
     6import java.util.Iterator;
     7import java.util.Map;
    78import java.util.Objects;
    89
    910/**
     
    1011 * Line represents a linear collection of GPX waypoints with the ordered/unordered distinction.
    1112 * @since 14451
    1213 */
    13 public class Line implements Collection<WayPoint> {
    14     private final Collection<WayPoint> waypoints;
    15     private final boolean unordered;
     14public class Line implements Collection<WayPoint> {
     15    private final Collection<WayPoint> waypoints;
     16    private final boolean unordered;
     17    private final Color color;
     18
     19    /**
     20     * Constructs a new {@code Line}.
     21     * @param waypoints collection of waypoints
     22     * @param attributes track/route attributes
     23     * @param color color of the track
     24     * @since xxx
     25     */
     26    public Line(Collection<WayPoint> waypoints, Map<String, Object> attributes, Color color) {
     27        this.color = color;
     28        this.waypoints = Objects.requireNonNull(waypoints);
     29        unordered = attributes.isEmpty() && waypoints.stream().allMatch(x -> x.get(GpxConstants.PT_TIME) == null);
     30    }
    1631
    1732    /**
    1833     * Constructs a new {@code Line}.
    19      * @param waypoints collection of waypoints
    20      * @param attributes track/route attributes
    21      */
    22     public Line(Collection<WayPoint> waypoints, Map<String, Object> attributes) {
    23         this.waypoints = Objects.requireNonNull(waypoints);
    24         unordered = attributes.isEmpty() && waypoints.stream().allMatch(x -> x.get(GpxConstants.PT_TIME) == null);
    25     }
    26 
    27     /**
     34     * @param trackSegment track segment
     35     * @param trackAttributes track attributes
     36     * @param color color of the track
     37     * @since xxx
     38     */
     39    public Line(IGpxTrackSegment trackSegment, Map<String, Object> trackAttributes, Color color) {
     40        this(trackSegment.getWayPoints(), trackAttributes, color);
     41    }
     42
     43    /**
    2844     * Constructs a new {@code Line}.
    29      * @param trackSegment track segment
    30      * @param trackAttributes track attributes
    31      */
    32     public Line(GpxTrackSegment trackSegment, Map<String, Object> trackAttributes) {
    33         this(trackSegment.getWayPoints(), trackAttributes);
    34     }
    35 
    36     /**
    37      * Constructs a new {@code Line}.
    38      * @param route route
    39      */
    40     public Line(GpxRoute route) {
    41         this(route.routePoints, route.attr);
    42     }
    43 
    44     /**
     45     * @param route route
     46     */
     47    public Line(GpxRoute route) {
     48        this(route.routePoints, route.attr, null);
     49    }
     50
     51    /**
    4552     * Determines if waypoints are ordered.
    4653     * @return {@code true} if waypoints are ordered
    4754     */
    4855    public boolean isUnordered() {
    49         return unordered;
     56        return unordered;
     57    }
     58
     59    /**
     60     * Returns the track/route color
     61     * @return the color
     62     * @since xxx
     63     */
     64    public Color getColor() {
     65        return color;
     66    }
     67
     68    @Override
     69    public int size() {
     70        return waypoints.size();
    5071    }
    5172
    5273    @Override
    53     public int size() {
    54         return waypoints.size();
    55     }
    56 
    57     @Override
    5874    public boolean isEmpty() {
    5975        return waypoints.isEmpty();
    6076    }
  • src/org/openstreetmap/josm/data/gpx/WithAttributes.java

     
    44import java.util.Collection;
    55import java.util.HashMap;
    66import java.util.Map;
     7import java.util.Objects;
    78
    89/**
    910 * Default implementation for IWithAttributes.
     
    2122    public Map<String, Object> attr = new HashMap<>(0);
    2223
    2324    /**
     25     * The "exts" collection contains all extensions.
     26     */
     27    private final GpxExtensionCollection exts = new GpxExtensionCollection(this);
     28
     29    /**
    2430     * Returns the Object value to which the specified key is mapped,
    2531     * or {@code null} if this map contains no mapping for the key.
    2632     *
     
    6470
    6571    /**
    6672     * Put a key / value pair as a new attribute.
    67      *
    6873     * Overrides key / value pair with the same key (if present).
    6974     *
    7075     * @param key the key
     
    7580        attr.put(key, value);
    7681    }
    7782
    78     /**
    79      * Add a key / value pair that is not part of the GPX schema as an extension.
    80      *
    81      * @param key the key
    82      * @param value the value
    83      */
    8483    @Override
    85     public void addExtension(String key, String value) {
    86         if (!attr.containsKey(META_EXTENSIONS)) {
    87             attr.put(META_EXTENSIONS, new Extensions());
    88         }
    89         Extensions ext = (Extensions) attr.get(META_EXTENSIONS);
    90         ext.put(key, value);
     84    public Map<String, Object> getAttributes() {
     85        return attr;
    9186    }
    9287
    9388    @Override
     89    public GpxExtensionCollection getExtensions() {
     90        return exts;
     91    }
     92
     93    @Override
    9494    public int hashCode() {
    95         return 31 + ((attr == null) ? 0 : attr.hashCode());
     95        return Objects.hash(attr, exts);
    9696    }
    9797
    9898    @Override
     
    109109                return false;
    110110        } else if (!attr.equals(other.attr))
    111111            return false;
     112        if (exts == null) {
     113            if (other.exts != null)
     114                return false;
     115        } else if (!exts.equals(other.exts))
     116            return false;
    112117        return true;
    113118    }
    114119}
  • src/org/openstreetmap/josm/data/osm/DataSet.java

     
    3232import org.openstreetmap.josm.data.conflict.ConflictCollection;
    3333import org.openstreetmap.josm.data.coor.EastNorth;
    3434import org.openstreetmap.josm.data.coor.LatLon;
     35import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace;
    3536import org.openstreetmap.josm.data.osm.DataSelectionListener.SelectionAddEvent;
    3637import org.openstreetmap.josm.data.osm.DataSelectionListener.SelectionChangeEvent;
    3738import org.openstreetmap.josm.data.osm.DataSelectionListener.SelectionRemoveEvent;
     
    170171    private String remark;
    171172
    172173    /**
     174     * Used to temporarily store namespaces from the GPX file in case the user converts back and forth.
     175     * Will not be saved to .osm files, but that's not necessary because GPX files won't automatically be overridden after that.
     176     */
     177    private List<XMLNamespace> GPXNamespaces;
     178
     179    /**
    173180     * Constructs a new {@code DataSet}.
    174181     */
    175182    public DataSet() {
     
    11941201    }
    11951202
    11961203    /**
     1204     * Gets the GPX (XML) namespaces if this DataSet was created from a GPX file
     1205     * @return the GPXNamespaces or <code>null</code>
     1206     */
     1207    public List<XMLNamespace> getGPXNamespaces() {
     1208        return GPXNamespaces;
     1209    }
     1210
     1211    /**
     1212     * Sets the GPX (XML) namespaces
     1213     * @param GPXNamespaces the GPXNamespaces to set
     1214     */
     1215    public void setGPXNamespaces(List<XMLNamespace> GPXNamespaces) {
     1216        this.GPXNamespaces = GPXNamespaces;
     1217    }
     1218
     1219    /**
    11971220     * @return true if this Dataset contains no primitives
    11981221     * @since 14835
    11991222     */
  • src/org/openstreetmap/josm/data/preferences/NamedColorProperty.java

     
    2222
    2323    public static final String COLOR_CATEGORY_GENERAL = "general";
    2424    public static final String COLOR_CATEGORY_MAPPAINT = "mappaint";
    25     public static final String COLOR_CATEGORY_LAYER = "layer";
    2625
    2726    private final String category;
    2827    private final String source;
     
    3130    /**
    3231     * Construct a new {@code NamedColorProperty}.
    3332     * @param category a category, can be any identifier, but the following values are recognized by
    34      * the GUI preferences: {@link #COLOR_CATEGORY_GENERAL}, {@link #COLOR_CATEGORY_MAPPAINT} and
    35      * {@link #COLOR_CATEGORY_LAYER}
     33     * the GUI preferences: {@link #COLOR_CATEGORY_GENERAL} and {@link #COLOR_CATEGORY_MAPPAINT}
    3634     * @param source a filename or similar associated with the color, can be null if not applicable
    3735     * @param name a short description of the color
    3836     * @param defaultValue the default value, can be null
  • src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.Color;
    76import java.awt.Component;
    87import java.awt.Dimension;
    98import java.awt.Font;
     
    1716import java.util.ArrayList;
    1817import java.util.Arrays;
    1918import java.util.List;
    20 import java.util.Objects;
     19import java.util.Optional;
    2120import java.util.concurrent.CopyOnWriteArrayList;
    2221
    2322import javax.swing.AbstractAction;
     
    4241import org.openstreetmap.josm.actions.MergeLayerAction;
    4342import org.openstreetmap.josm.data.coor.EastNorth;
    4443import org.openstreetmap.josm.data.imagery.OffsetBookmark;
    45 import org.openstreetmap.josm.data.preferences.AbstractProperty;
    4644import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener;
    4745import org.openstreetmap.josm.data.preferences.BooleanProperty;
    4846import org.openstreetmap.josm.gui.MainApplication;
     
    611609                label.setFont(label.getFont().deriveFont(Font.BOLD));
    612610            }
    613611            if (Config.getPref().getBoolean("dialog.layer.colorname", true)) {
    614                 AbstractProperty<Color> prop = layer.getColorProperty();
    615                 Color c = prop == null ? null : prop.get();
    616                 if (c == null || model.getLayers().stream()
    617                         .map(Layer::getColorProperty)
    618                         .filter(Objects::nonNull)
    619                         .map(AbstractProperty::get)
    620                         .noneMatch(oc -> oc != null && !oc.equals(c))) {
    621                     /* not more than one color, don't use coloring */
    622                     label.setForeground(UIManager.getColor(isSelected ? "Table.selectionForeground" : "Table.foreground"));
    623                 } else {
    624                     label.setForeground(c);
    625                 }
     612                label.setForeground(Optional
     613                        .ofNullable(layer.getColor())
     614                        .orElse(UIManager.getColor(isSelected ? "Table.selectionForeground" : "Table.foreground")));
    626615            }
    627616            label.setIcon(layer.getIcon());
    628617            label.setToolTipText(layer.getToolTipText());
  • src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java

     
    593593                    List<Layer> layers = layerSupplier.get();
    594594                    for (Layer l : layers) {
    595595                        if (l instanceof GpxLayer) {
    596                             l.getColorProperty().put(color);
     596                            l.setColor(color);
    597597                        }
    598598                    }
    599599                    highlightColor(color);
     
    602602            add(colorPanel, GBC.std().weight(1, 1).fill().insets(5));
    603603            panels.put(color, colorPanel);
    604604
    605             List<Color> colors = layerSupplier.get().stream().map(l -> l.getColorProperty().get()).distinct().collect(Collectors.toList());
     605            List<Color> colors = layerSupplier.get().stream().map(l -> l.getColor()).distinct().collect(Collectors.toList());
    606606            if (colors.size() == 1) {
    607607                highlightColor(colors.get(0));
    608608            }
     
    611611        @Override
    612612        public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
    613613            List<Color> colors = layers.stream().filter(l -> l instanceof GpxLayer)
    614                     .map(l -> ((GpxLayer) l).getColorProperty().get())
     614                    .map(l -> ((GpxLayer) l).getColor())
    615615                    .distinct()
    616616                    .collect(Collectors.toList());
    617617            if (colors.size() == 1) {
  • src/org/openstreetmap/josm/gui/io/importexport/FileExporter.java

     
    5555    }
    5656
    5757    /**
     58     * Execute the data export without prompting the user. (To be overridden by subclasses.)
     59     *
     60     * @param file target file
     61     * @param layer the layer to export
     62     * @throws IOException in case of an IO error
     63     * @since xxx
     64     */
     65    public void exportDataQuiet(File file, Layer layer) throws IOException {
     66        exportData(file, layer); //backwards compatibility
     67    }
     68
     69    /**
    5870     * Returns the enabled state of this {@code FileExporter}. When enabled, it is listed and usable in "File-&gt;Save" dialogs.
    5971     * @return true if this {@code FileExporter} is enabled
    6072     * @since 5459
  • src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java

     
    2525
    2626import org.openstreetmap.josm.data.gpx.GpxConstants;
    2727import org.openstreetmap.josm.data.gpx.GpxData;
     28import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    2829import org.openstreetmap.josm.gui.ExtendedDialog;
    2930import org.openstreetmap.josm.gui.MainApplication;
    3031import org.openstreetmap.josm.gui.layer.GpxLayer;
     
    7778
    7879    @Override
    7980    public void exportData(File file, Layer layer) throws IOException {
     81        exportData(file, layer, false);
     82    }
     83
     84    @Override
     85    public void exportDataQuiet(File file, Layer layer) throws IOException {
     86        exportData(file, layer, true);
     87    }
     88
     89    private void exportData(File file, Layer layer, boolean quiet) throws IOException {
    8090        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
    8191        if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
    8292            throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer
     
    8999            file = new File(fn);
    90100        }
    91101
     102        GpxData gpxData;
     103        if (quiet) {
     104            gpxData = getGpxData(layer, file);
     105            try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) {
     106                GpxWriter w = new GpxWriter(fo);
     107                w.write(gpxData);
     108                w.close();
     109                fo.flush();
     110            }
     111            return;
     112        }
     113
    92114        // open the dialog asking for options
    93115        JPanel p = new JPanel(new GridBagLayout());
    94116
    95         GpxData gpxData;
    96117        // At this moment, we only need to know the attributes of the GpxData,
    97118        // conversion of OsmDataLayer (if needed) will be done after the dialog is closed.
    98119        if (layer instanceof GpxLayer) {
     
    146167        p.add(new JLabel(tr("Keywords")), GBC.eol());
    147168        JosmTextField keywords = new JosmTextField();
    148169        keywords.setText(gpxData.getString(META_KEYWORDS));
    149         p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL));
     170        p.add(keywords, GBC.eol().fill(GBC.HORIZONTAL));
    150171
     172        boolean sel = Config.getPref().getBoolean("gpx.export.colors", true);
     173        JCheckBox colors = new JCheckBox(tr("Save track colors in GPX file"), sel);
     174        p.add(colors, GBC.eol().fill(GBC.HORIZONTAL));
     175        JCheckBox garmin = new JCheckBox(tr("Use Garmin compatible GPX extensions"),
     176                Config.getPref().getBoolean("gpx.export.colors.garmin", false));
     177        garmin.setEnabled(sel);
     178        p.add(garmin, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 0));
     179
     180        boolean hasPrefs = !gpxData.getLayerPrefs().isEmpty();
     181        JCheckBox layerPrefs = new JCheckBox(tr("Save layer specific preferences"),
     182                hasPrefs && Config.getPref().getBoolean("gpx.export.prefs", true));
     183        layerPrefs.setEnabled(hasPrefs);
     184        p.add(layerPrefs, GBC.eop().fill(GBC.HORIZONTAL));
     185
    151186        ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(),
    152187                tr("Export options"),
    153188                tr("Export and Save"), tr("Cancel"))
     
    154189            .setButtonIcons("exportgpx", "cancel")
    155190            .setContent(p);
    156191
     192        colors.addActionListener(l -> {
     193            garmin.setEnabled(colors.isSelected());
     194        });
     195
     196        garmin.addActionListener(l -> {
     197            if (garmin.isSelected() &&
     198                    !ConditionalOptionPaneUtil.showConfirmationDialog(
     199                            "gpx_color_garmin",
     200                            ed,
     201                            new JLabel(tr("<html>Garmin track extensions only support 16 colors.<br>If you continue, the closest supported track color will be used.</html>")),
     202                            tr("Information"),
     203                            JOptionPane.OK_CANCEL_OPTION,
     204                            JOptionPane.INFORMATION_MESSAGE,
     205                            JOptionPane.OK_OPTION)) {
     206                garmin.setSelected(false);
     207            }
     208        });
     209
    157210        if (ed.showDialog().getValue() != 1) {
    158211            setCanceled(true);
    159212            return;
     
    167220        if (!copyright.getText().isEmpty()) {
    168221            Config.getPref().put("lastCopyright", copyright.getText());
    169222        }
    170 
    171         if (layer instanceof OsmDataLayer) {
    172             gpxData = ((OsmDataLayer) layer).toGpxData();
    173         } else if (layer instanceof GpxLayer) {
    174             gpxData = ((GpxLayer) layer).data;
    175         } else {
    176             gpxData = OsmDataLayer.toGpxData(MainApplication.getLayerManager().getEditDataSet(), file);
     223        Config.getPref().putBoolean("gpx.export.colors", colors.isSelected());
     224        Config.getPref().putBoolean("gpx.export.colors.garmin", garmin.isSelected());
     225        if (hasPrefs) {
     226            Config.getPref().putBoolean("gpx.export.prefs", layerPrefs.isSelected());
    177227        }
     228        ColorFormat cFormat = null;
     229        if (colors.isSelected()) {
     230            cFormat = garmin.isSelected() ? ColorFormat.GPXX : ColorFormat.GPXD;
     231        }
    178232
     233        gpxData = getGpxData(layer, file);
     234
    179235        // add author and copyright details to the gpx data
    180236        if (author.isSelected()) {
    181237            if (!authorName.getText().isEmpty()) {
     
    203259            gpxData.put(META_KEYWORDS, keywords.getText());
    204260        }
    205261
    206         try (OutputStream fo = Compression.getCompressedFileOutputStream(file); GpxWriter writer = new GpxWriter(fo)) {
    207             writer.write(gpxData);
     262        try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) {
     263            GpxWriter w = new GpxWriter(fo);
     264            w.write(gpxData, cFormat, layerPrefs.isSelected());
     265            w.close();
     266            fo.flush();
    208267        }
    209268    }
    210269
     270    private static GpxData getGpxData(Layer layer, File file) {
     271        if (layer instanceof OsmDataLayer) {
     272            return ((OsmDataLayer) layer).toGpxData();
     273        } else if (layer instanceof GpxLayer) {
     274            return ((GpxLayer) layer).data;
     275        }
     276        return OsmDataLayer.toGpxData(MainApplication.getLayerManager().getEditDataSet(), file);
     277    }
     278
    211279    private static void enableCopyright(final GpxData data, final JosmTextField copyright, final JButton predefined,
    212280            final JosmTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel,
    213281            final JLabel warning, boolean enable) {
  • src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java

     
    149149            markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer);
    150150            if (markerLayer.data.isEmpty()) {
    151151                markerLayer = null;
     152            } else {
     153                gpxLayer.setLinkedMarkerLayer(markerLayer);
    152154            }
    153155        }
    154156        Runnable postLayerTask = () -> {
  • src/org/openstreetmap/josm/gui/layer/CustomizeColor.java

     
    1818import javax.swing.JMenuItem;
    1919import javax.swing.JOptionPane;
    2020
    21 import org.openstreetmap.josm.data.preferences.AbstractProperty;
    2221import org.openstreetmap.josm.gui.MainApplication;
    2322import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    2423import org.openstreetmap.josm.gui.layer.Layer.LayerAction;
    2524import org.openstreetmap.josm.gui.layer.Layer.MultiLayerAction;
    26 import org.openstreetmap.josm.tools.CheckParameterUtil;
    2725import org.openstreetmap.josm.tools.ImageProvider;
    2826
    2927/**
     
    3331 * of a certain {@link GpxLayer} or {@link org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer}.
    3432 */
    3533public class CustomizeColor extends AbstractAction implements LayerAction, MultiLayerAction {
    36     private final transient List<AbstractProperty<Color>> colors;
     34    private final transient List<Layer> colorLayers;
    3735
    3836    /**
    3937     * Constructs a new {@code CustomizeColor} for a given list of layers.
     
    4240    public CustomizeColor(List<Layer> l) {
    4341        super(tr("Customize Color"));
    4442        new ImageProvider("colorchooser").getResource().attachImageIcon(this, true);
    45         colors = l.stream().map(Layer::getColorProperty).collect(Collectors.toList());
    46         CheckParameterUtil.ensureThat(colors.stream().allMatch(Objects::nonNull), "All layers must have colors.");
     43        colorLayers = l.stream().filter(Objects::nonNull).filter(Layer::hasColor).collect(Collectors.toList());
    4744        putValue("help", ht("/Action/LayerCustomizeColor"));
    4845    }
    4946
     
    5754
    5855    @Override
    5956    public boolean supportLayers(List<Layer> layers) {
    60         return layers.stream().allMatch(l -> l.getColorProperty() != null);
     57        return layers.stream().allMatch(Layer::hasColor);
    6158    }
    6259
    6360    @Override
     
    7269
    7370    @Override
    7471    public void actionPerformed(ActionEvent e) {
    75         Color cl = colors.stream().map(AbstractProperty::get).filter(Objects::nonNull).findAny().orElse(Color.GRAY);
     72        Color cl = colorLayers.stream().filter(Objects::nonNull).map(Layer::getColor).filter(Objects::nonNull).findAny().orElse(Color.GRAY);
    7673        JColorChooser c = new JColorChooser(cl);
    7774        Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
    7875        int answer = JOptionPane.showOptionDialog(
     
    8784        );
    8885        switch (answer) {
    8986        case 0:
    90             colors.stream().forEach(prop -> prop.put(c.getColor()));
     87            colorLayers.stream().forEach(l -> l.setColor(c.getColor()));
    9188            break;
    9289        case 1:
    9390            return;
    9491        case 2:
    95             colors.stream().forEach(prop -> prop.put(null));
     92            colorLayers.stream().forEach(l -> l.setColor(null));
    9693            break;
    9794        }
    9895        // TODO: Make the layer dialog listen to property change events so that this is not needed any more.
  • src/org/openstreetmap/josm/gui/layer/GpxLayer.java

     
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55import static org.openstreetmap.josm.tools.I18n.trn;
    66
     7import java.awt.Color;
    78import java.awt.Dimension;
    89import java.awt.Graphics2D;
    910import java.awt.event.ActionEvent;
     
    1314import java.util.Arrays;
    1415import java.util.Date;
    1516import java.util.List;
     17import java.util.stream.Collectors;
    1618
    1719import javax.swing.AbstractAction;
    1820import javax.swing.Action;
     
    3133import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
    3234import org.openstreetmap.josm.data.gpx.GpxTrack;
    3335import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    34 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
    3536import org.openstreetmap.josm.data.projection.Projection;
    3637import org.openstreetmap.josm.gui.MapView;
    3738import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
     
    4647import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction;
    4748import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction;
    4849import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction;
     50import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
     51import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    4952import org.openstreetmap.josm.gui.widgets.HtmlPanel;
    5053import org.openstreetmap.josm.tools.ImageProvider;
     54import org.openstreetmap.josm.tools.Logging;
    5155import org.openstreetmap.josm.tools.Utils;
    5256import org.openstreetmap.josm.tools.date.DateUtils;
    5357
     
    5458/**
    5559 * A layer that displays data from a Gpx file / the OSM gpx downloads.
    5660 */
    57 public class GpxLayer extends Layer implements ExpertModeChangeListener {
     61public class GpxLayer extends AbstractModifiableLayer implements ExpertModeChangeListener {
    5862
    5963    /** GPX data */
    6064    public GpxData data;
    61     private final boolean isLocalFile;
     65    private boolean isLocalFile;
    6266    private boolean isExpertMode;
     67
    6368    /**
    6469     * used by {@link ChooseTrackVisibilityAction} to determine which tracks to show/hide
    6570     *
     
    7277     * Added as field to be kept as reference.
    7378     */
    7479    private final GpxDataChangeListener dataChangeListener = e -> this.invalidate();
     80    /**
     81     * The MarkerLayer imported from the same file.
     82     */
     83    private MarkerLayer linkedMarkerLayer;
    7584
    7685    /**
    7786     * Constructs a new {@code GpxLayer} without name.
     
    108117    }
    109118
    110119    @Override
    111     protected NamedColorProperty getBaseColorProperty() {
    112         return GpxDrawHelper.DEFAULT_COLOR;
     120    public Color getColor() {
     121        Color[] c = data.getTracks().stream().map(t -> t.getColor()).distinct().toArray(Color[]::new);
     122        return c.length == 1 ? c[0] : null; //only return if exactly one distinct color present
    113123    }
    114124
     125    @Override
     126    public void setColor(Color color) {
     127        data.beginUpdate();
     128        for (GpxTrack trk : data.getTracks()) {
     129            trk.setColor(color);
     130        }
     131        GPXSettingsPanel.putLayerPrefLocal(this, "colormode", "0");
     132        data.endUpdate();
     133    }
     134
     135    @Override
     136    public boolean hasColor() {
     137        return true;
     138    }
     139
    115140    /**
    116141     * Returns a human readable string that shows the timespan of the given track
    117142     * @param trk The GPX track for which timespan is displayed
     
    276301            .append(", ")
    277302            .append(trn("{0} route, ", "{0} routes, ", data.getRoutes().size(), data.getRoutes().size()))
    278303            .append(trn("{0} waypoint", "{0} waypoints", data.getWaypoints().size(), data.getWaypoints().size())).append("<br>")
    279             .append(tr("Length: {0}", SystemOfMeasurement.getSystemOfMeasurement().getDistText(data.length())))
    280             .append("<br></html>");
     304            .append(tr("Length: {0}", SystemOfMeasurement.getSystemOfMeasurement().getDistText(data.length())));
     305
     306        if (Logging.isDebugEnabled() && !data.getLayerPrefs().isEmpty()) {
     307            info.append("<br><br>")
     308                .append(String.join("<br>", data.getLayerPrefs().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.toList())));
     309        }
     310
     311        info.append("<br></html>");
     312
    281313        return info.toString();
    282314    }
    283315
     
    341373        data.storageFile = file;
    342374    }
    343375
     376    /**
     377     * @return the linked MarkerLayer (imported from the same file)
     378     * @since xxx
     379     */
     380    public MarkerLayer getLinkedMarkerLayer() {
     381        return linkedMarkerLayer;
     382    }
     383
     384    /**
     385     * @param linkedMarkerLayer the linked MarkerLayer
     386     * @since xxx
     387     */
     388    public void setLinkedMarkerLayer(MarkerLayer linkedMarkerLayer) {
     389        this.linkedMarkerLayer = linkedMarkerLayer;
     390    }
     391
    344392    @Override
    345393    public void projectionChanged(Projection oldValue, Projection newValue) {
    346394        if (newValue == null) return;
     
    480528    }
    481529
    482530    @Override
     531    public boolean isModified() {
     532        return data.isModified();
     533    }
     534
     535    @Override
     536    public boolean requiresSaveToFile() {
     537        return isModified() && isLocalFile();
     538    }
     539
     540    @Override
     541    public void onPostSaveToFile() {
     542        isLocalFile = true;
     543        data.invalidate();
     544        data.setModified(false);
     545    }
     546
     547    @Override
    483548    public String getChangesetSourceTag() {
    484549        // no i18n for international values
    485550        return "survey";
  • src/org/openstreetmap/josm/gui/layer/Layer.java

     
    2525import org.openstreetmap.josm.actions.SaveAsAction;
    2626import org.openstreetmap.josm.data.ProjectionBounds;
    2727import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    28 import org.openstreetmap.josm.data.preferences.AbstractProperty;
    29 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener;
    30 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
    3128import org.openstreetmap.josm.data.projection.Projection;
    3229import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
    3330import org.openstreetmap.josm.gui.MainApplication;
     
    164161     */
    165162    private File associatedFile;
    166163
    167     private final ValueChangeListener<Object> invalidateListener = change -> invalidate();
    168164    private boolean isDestroyed;
    169165
    170166    /**
     
    198194    public abstract Icon getIcon();
    199195
    200196    /**
    201      * Gets the color property to use for this layer.
    202      * @return The color property.
    203      * @since 10824
     197     * @return whether the layer has / can handle colors.
     198     * @since xxx
    204199     */
    205     public AbstractProperty<Color> getColorProperty() {
    206         NamedColorProperty base = getBaseColorProperty();
    207         if (base != null) {
    208             return base.getChildColor(NamedColorProperty.COLOR_CATEGORY_LAYER, getName(), base.getName());
    209         } else {
    210             return null;
    211         }
     200    public boolean hasColor() {
     201        return false;
    212202    }
    213203
    214204    /**
    215      * Gets the color property that stores the default color for this layer.
    216      * @return The property or <code>null</code> if this layer is not colored.
    217      * @since 10824
     205     * Return the current color of the layer
     206     * @return null when not present or not supported
     207     * @since xxx
    218208     */
    219     protected NamedColorProperty getBaseColorProperty() {
     209    public Color getColor() {
    220210        return null;
    221211    }
    222212
    223     private void addColorPropertyListener() {
    224         AbstractProperty<Color> colorProperty = getColorProperty();
    225         if (colorProperty != null) {
    226             colorProperty.addListener(invalidateListener);
    227         }
     213    /**
     214     * Sets the color for this layer. Nothing happens if not supported by the layer
     215     * @param color the color to be set, <code>null</code> for default
     216     * @since xxx
     217     */
     218    public void setColor(Color color) {
    228219    }
    229220
    230     private void removeColorPropertyListener() {
    231         AbstractProperty<Color> colorProperty = getColorProperty();
    232         if (colorProperty != null) {
    233             colorProperty.removeListener(invalidateListener);
    234         }
    235     }
    236 
    237221    /**
    238222     * @return A small tooltip hint about some statistics for this layer.
    239223     */
     
    302286        }
    303287        isDestroyed = true;
    304288        // Override in subclasses if needed
    305         removeColorPropertyListener();
    306289    }
    307290
    308291    /**
     
    339322     * @param name the name. If null, the name is set to the empty string.
    340323     */
    341324    public void setName(String name) {
    342         if (this.name != null) {
    343             removeColorPropertyListener();
    344         }
    345325        String oldValue = this.name;
    346326        this.name = Optional.ofNullable(name).orElse("");
    347327        if (!this.name.equals(oldValue)) {
    348328            propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name);
    349329        }
    350 
    351         // re-add listener
    352         addColorPropertyListener();
    353330        invalidate();
    354331    }
    355332
     
    539516
    540517        @Override
    541518        public void actionPerformed(ActionEvent e) {
    542             SaveAction.getInstance().doSave(layer);
     519            SaveAction.getInstance().doSave(layer, true);
    543520        }
    544521    }
    545522
  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

     
    3131import java.util.LinkedHashMap;
    3232import java.util.List;
    3333import java.util.Map;
     34import java.util.Map.Entry;
    3435import java.util.Optional;
    3536import java.util.Set;
    3637import java.util.concurrent.CopyOnWriteArrayList;
     
    6061import org.openstreetmap.josm.data.coor.LatLon;
    6162import org.openstreetmap.josm.data.gpx.GpxConstants;
    6263import org.openstreetmap.josm.data.gpx.GpxData;
     64import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
    6365import org.openstreetmap.josm.data.gpx.GpxLink;
    64 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
     66import org.openstreetmap.josm.data.gpx.GpxTrack;
     67import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     68import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    6569import org.openstreetmap.josm.data.gpx.WayPoint;
    6670import org.openstreetmap.josm.data.osm.DataIntegrityProblemException;
    6771import org.openstreetmap.josm.data.osm.DataSelectionListener;
     
    757761     */
    758762    public static GpxData toGpxData(DataSet data, File file) {
    759763        GpxData gpxData = new GpxData();
     764        if (data.getGPXNamespaces() != null) {
     765            gpxData.getNamespaces().addAll(data.getGPXNamespaces());
     766        }
    760767        gpxData.storageFile = file;
    761768        Set<Node> doneNodes = new HashSet<>();
    762769        waysToGpxData(data.getWays(), gpxData, doneNodes);
     
    777784            if (!w.isUsable()) {
    778785                return;
    779786            }
    780             Collection<Collection<WayPoint>> trk = new ArrayList<>();
     787            List<IGpxTrackSegment> trk = new ArrayList<>();
    781788            Map<String, Object> trkAttr = new HashMap<>();
    782789
    783             String name = gpxVal(w, "name");
    784             if (name != null) {
    785                 trkAttr.put("name", name);
     790            GpxExtensionCollection trkExts = new GpxExtensionCollection();
     791            GpxExtensionCollection segExts = new GpxExtensionCollection();
     792            for (Entry<String, String> e : w.getKeys().entrySet()) {
     793                String k = e.getKey().startsWith(GpxConstants.GPX_PREFIX) ? e.getKey().substring(GpxConstants.GPX_PREFIX.length()) : e.getKey();
     794                String v = e.getValue();
     795                if (GpxConstants.RTE_TRK_KEYS.contains(k)) {
     796                    trkAttr.put(k, v);
     797                } else {
     798                    k = GpxConstants.EXTENSION_ABBREVIATIONS.entrySet()
     799                            .stream()
     800                            .filter(s -> s.getValue().equals(e.getKey()))
     801                            .map(s -> s.getKey().substring(GpxConstants.GPX_PREFIX.length()))
     802                            .findAny()
     803                            .orElse(k);
     804                    if (k.startsWith("extension")) {
     805                        String[] chain = k.split(":");
     806                        if (chain.length >= 3 && "segment".equals(chain[2])) {
     807                            segExts.addFlat(chain, v);
     808                        } else {
     809                            trkExts.addFlat(chain, v);
     810                        }
     811                    }
     812
     813                }
    786814            }
    787 
    788             List<WayPoint> trkseg = null;
     815            List<WayPoint> trkseg = new ArrayList<>();
    789816            for (Node n : w.getNodes()) {
    790817                if (!n.isUsable()) {
    791                     trkseg = null;
     818                    if (!trkseg.isEmpty()) {
     819                        trk.add(new GpxTrackSegment(trkseg));
     820                        trkseg.clear();
     821                    }
    792822                    continue;
    793823                }
    794                 if (trkseg == null) {
    795                     trkseg = new ArrayList<>();
    796                     trk.add(trkseg);
    797                 }
    798824                if (!n.isTagged() || containsOnlyGpxTags(n)) {
    799825                    doneNodes.add(n);
    800826                }
    801827                trkseg.add(nodeToWayPoint(n));
    802828            }
    803 
    804             gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));
     829            trk.add(new GpxTrackSegment(trkseg));
     830            trk.forEach(gpxseg -> gpxseg.getExtensions().addAll(segExts));
     831            GpxTrack gpxtrk = new GpxTrack(trk, trkAttr);
     832            gpxtrk.getExtensions().addAll(trkExts);
     833            gpxData.addTrack(gpxtrk);
    805834        });
    806835    }
    807836
  • src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java

     
    7070import org.openstreetmap.josm.data.gpx.GpxTimeOffset;
    7171import org.openstreetmap.josm.data.gpx.GpxTimezone;
    7272import org.openstreetmap.josm.data.gpx.GpxTrack;
    73 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     73import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    7474import org.openstreetmap.josm.data.gpx.WayPoint;
    7575import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    7676import org.openstreetmap.josm.gui.ExtendedDialog;
     
    12681268        long firstGPXDate = -1;
    12691269        // Finds first GPX point
    12701270        outer: for (GpxTrack trk : gpx.tracks) {
    1271             for (GpxTrackSegment segment : trk.getSegments()) {
     1271            for (IGpxTrackSegment segment : trk.getSegments()) {
    12721272                for (WayPoint curWp : segment.getWayPoints()) {
    12731273                    if (curWp.hasDate()) {
    12741274                        firstGPXDate = curWp.getTimeInMillis();
  • src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java

     
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
     7import java.awt.Color;
    78import java.awt.Component;
    89import java.awt.Dimension;
    910import java.awt.GridBagLayout;
     
    1213import java.awt.event.MouseEvent;
    1314import java.awt.event.MouseListener;
    1415import java.io.Serializable;
     16import java.util.ArrayList;
    1517import java.util.Arrays;
    1618import java.util.Comparator;
     19import java.util.List;
    1720import java.util.Map;
     21import java.util.Objects;
    1822import java.util.Optional;
    1923
    2024import javax.swing.AbstractAction;
     25import javax.swing.JColorChooser;
    2126import javax.swing.JComponent;
    2227import javax.swing.JLabel;
     28import javax.swing.JOptionPane;
    2329import javax.swing.JPanel;
    2430import javax.swing.JScrollPane;
    2531import javax.swing.JTable;
    2632import javax.swing.JToggleButton;
    2733import javax.swing.ListSelectionModel;
     34import javax.swing.event.TableModelEvent;
    2835import javax.swing.table.DefaultTableModel;
    2936import javax.swing.table.TableCellRenderer;
    3037import javax.swing.table.TableRowSorter;
    3138
     39import org.apache.commons.jcs.access.exception.InvalidArgumentException;
    3240import org.openstreetmap.josm.data.SystemOfMeasurement;
    3341import org.openstreetmap.josm.data.gpx.GpxConstants;
    3442import org.openstreetmap.josm.data.gpx.GpxTrack;
     
    3543import org.openstreetmap.josm.gui.ExtendedDialog;
    3644import org.openstreetmap.josm.gui.MainApplication;
    3745import org.openstreetmap.josm.gui.layer.GpxLayer;
     46import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    3847import org.openstreetmap.josm.gui.util.WindowGeometry;
    3948import org.openstreetmap.josm.tools.GBC;
    4049import org.openstreetmap.josm.tools.ImageProvider;
     
    5564     * @param layer The associated GPX layer
    5665     */
    5766    public ChooseTrackVisibilityAction(final GpxLayer layer) {
    58         super(tr("Choose visible tracks"));
     67        super(tr("Choose track visibility and colors"));
    5968        new ImageProvider("dialogs/filter").getResource().attachImageIcon(this, true);
    6069        this.layer = layer;
    6170        putValue("help", ht("/Action/ChooseTrackVisibility"));
     
    116125            String time = GpxLayer.getTimespanForTrack(trk);
    117126            TrackLength length = new TrackLength(trk.length());
    118127            String url = (String) Optional.ofNullable(attr.get("url")).orElse("");
    119             tracks[i] = new Object[]{name, desc, time, length, url};
     128            tracks[i] = new Object[]{name, desc, time, length, url, trk};
    120129            i++;
    121130        }
    122131        return tracks;
    123132    }
    124133
     134    private void showColorDialog(List<GpxTrack> tracks) {
     135        Color cl = tracks.stream().filter(Objects::nonNull)
     136                .map(GpxTrack::getColor).filter(Objects::nonNull)
     137                .findAny().orElse(GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get());
     138        JColorChooser c = new JColorChooser(cl);
     139        Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
     140        int answer = JOptionPane.showOptionDialog(
     141                MainApplication.getMainFrame(),
     142                c,
     143                tr("Choose a color"),
     144                JOptionPane.OK_CANCEL_OPTION,
     145                JOptionPane.PLAIN_MESSAGE,
     146                null,
     147                options,
     148                options[0]
     149        );
     150        switch (answer) {
     151        case 0:
     152            tracks.stream().forEach(t -> t.setColor(c.getColor()));
     153            GPXSettingsPanel.putLayerPrefLocal(layer, "colormode", "0"); //set Colormode to none
     154            break;
     155        case 1:
     156            return;
     157        case 2:
     158            tracks.stream().forEach(t -> t.setColor(null));
     159            break;
     160        }
     161        table.repaint();
     162    }
     163
    125164    /**
    126      * Builds an non-editable table whose 5th column will open a browser when double clicked.
     165     * Builds an editable table whose 5th column will open a browser when double clicked.
    127166     * The table will fill its parent.
    128167     * @param content table data
    129168     * @return non-editable table
     
    138177                if (c instanceof JComponent) {
    139178                    JComponent jc = (JComponent) c;
    140179                    jc.setToolTipText(getValueAt(row, col).toString());
     180                    if (content.length > row
     181                            && content[row].length > 5
     182                            && content[row][5] instanceof GpxTrack) {
     183                        Color color = ((GpxTrack) content[row][5]).getColor();
     184                        if (color != null) {
     185                            double brightness = Math.sqrt(Math.pow(color.getRed(), 2) * .241
     186                                    + Math.pow(color.getGreen(), 2) * .691
     187                                    + Math.pow(color.getBlue(), 2) * .068);
     188                            if (brightness > 250) {
     189                                color = color.darker();
     190                            }
     191                            if (isRowSelected(row)) {
     192                                jc.setBackground(color);
     193                                if (brightness <= 130) {
     194                                    jc.setForeground(Color.WHITE);
     195                                } else {
     196                                    jc.setForeground(Color.BLACK);
     197                                }
     198                            } else {
     199                                if (brightness > 200) {
     200                                    color = color.darker(); //brightness >250 is darkened twice on purpose
     201                                }
     202                                jc.setForeground(color);
     203                                jc.setBackground(Color.WHITE);
     204                            }
     205                        } else {
     206                            jc.setForeground(Color.BLACK);
     207                            if (isRowSelected(row)) {
     208                                jc.setBackground(new Color(175, 210, 210));
     209                            } else {
     210                                jc.setBackground(Color.WHITE);
     211                            }
     212                        }
     213                    }
    141214                }
    142215                return c;
    143216            }
     
    144217
    145218            @Override
    146219            public boolean isCellEditable(int rowIndex, int colIndex) {
    147                 return false;
     220                return colIndex <= 1;
    148221            }
     222
     223            @Override
     224            public void tableChanged(TableModelEvent e) {
     225                super.tableChanged(e);
     226                int col = e.getColumn();
     227                int row = e.getFirstRow();
     228                if (row >= 0 && row < content.length && col >= 0 && col <= 1) {
     229                    Object t = content[row][5];
     230                    String val = (String) getValueAt(row, col);
     231                    if (t != null && t instanceof GpxTrack) {
     232                        GpxTrack trk = (GpxTrack) t;
     233                        if (col == 0) {
     234                            trk.put("name", val);
     235                        } else {
     236                            trk.put("desc", val);
     237                        }
     238                    } else {
     239                        throw new InvalidArgumentException("Invalid object in table, must be GpxTrack.");
     240                    }
     241                }
     242            }
    149243        };
    150244        // define how to sort row
    151245        TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<>();
     
    180274        };
    181275        t.addMouseListener(urlOpener);
    182276        t.setFillsViewportHeight(true);
     277        t.putClientProperty("terminateEditOnFocusLost", true);
    183278        return t;
    184279    }
    185280
     
    249344
    250345        msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. " +
    251346                "You can drag select a range of tracks or use CTRL+Click to select specific ones. " +
    252                 "The map is updated live in the background. Open the URLs by double clicking them.</html>")),
     347                "The map is updated live in the background. Open the URLs by double clicking them, " +
     348                "edit name and description by double clicking the cell.</html>")),
    253349                GBC.eop().fill(GBC.HORIZONTAL));
    254350        // build table
    255351        final boolean[] trackVisibilityBackup = layer.trackVisibility.clone();
    256         table = buildTable(buildTableContents());
     352        Object[][] content = buildTableContents();
     353        table = buildTable(content);
    257354        selectVisibleTracksInTable();
    258355        listenToSelectionChanges();
    259356        // make the table scrollable
     
    262359
    263360        int v = 1;
    264361        // build dialog
    265         ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), tr("Set track visibility for {0}", layer.getName()),
    266                 tr("Show all"), tr("Show selected only"), tr("Cancel"));
    267         ed.setButtonIcons("eye", "dialogs/filter", "cancel");
     362        ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(),
     363                tr("Set track visibility for {0}", layer.getName()),
     364                tr("Set color for selected tracks..."), tr("Show all"), tr("Show selected only"), tr("Close")) {
     365            @Override
     366            protected void buttonAction(int buttonIndex, ActionEvent evt) {
     367                if (buttonIndex == 0) {
     368                    List<GpxTrack> trks = new ArrayList<>();
     369                    for (int i : table.getSelectedRows()) {
     370                        Object trk = content[i][5];
     371                        if (trk != null && trk instanceof GpxTrack) {
     372                            trks.add((GpxTrack) trk);
     373                        }
     374                    }
     375                    showColorDialog(trks);
     376                } else {
     377                    super.buttonAction(buttonIndex, evt);
     378                }
     379            }
     380        };
     381        ed.setButtonIcons("colorchooser", "eye", "dialogs/filter", "cancel");
    268382        ed.setContent(msg, false);
    269383        ed.setDefaultButton(2);
    270384        ed.setCancelButton(3);
     
    275389        dateFilter.saveInPrefs();
    276390        v = ed.getValue();
    277391        // cancel for unknown buttons and copy back original settings
    278         if (v != 1 && v != 2) {
     392        if (v != 2 && v != 3) {
    279393            layer.trackVisibility = Arrays.copyOf(trackVisibilityBackup, layer.trackVisibility.length);
    280394            MainApplication.getMap().repaint();
    281395            return;
    282396        }
    283         // set visibility (1 = show all, 2 = filter). If no tracks are selected
     397        // set visibility (2 = show all, 3 = filter). If no tracks are selected
    284398        // set all of them visible and...
    285399        ListSelectionModel s = table.getSelectionModel();
    286         final boolean all = v == 1 || s.isSelectionEmpty();
     400        final boolean all = v == 2 || s.isSelectionEmpty();
    287401        for (int i = 0; i < layer.trackVisibility.length; i++) {
    288402            layer.trackVisibility[table.convertRowIndexToModel(i)] = all || s.isSelectedIndex(i);
    289403        }
     
    290404        // layer has been changed
    291405        layer.invalidate();
    292406        // ...sync with layer visibility instead to avoid having two ways to hide everything
    293         layer.setVisible(v == 1 || !s.isSelectionEmpty());
     407        layer.setVisible(v == 2 || !s.isSelectionEmpty());
    294408    }
    295409}
  • src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java

     
    77import java.awt.event.ActionEvent;
    88import java.awt.event.ActionListener;
    99import java.util.ArrayList;
    10 import java.util.Collection;
    1110import java.util.Date;
    1211import java.util.List;
     12import java.util.Map;
    1313import java.util.Map.Entry;
    1414
    1515import javax.swing.BorderFactory;
     
    2020import javax.swing.JRadioButton;
    2121
    2222import org.openstreetmap.josm.data.gpx.GpxConstants;
    23 import org.openstreetmap.josm.data.gpx.GpxTrack;
    24 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     23import org.openstreetmap.josm.data.gpx.GpxExtension;
     24import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
     25import org.openstreetmap.josm.data.gpx.IGpxTrack;
     26import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2527import org.openstreetmap.josm.data.gpx.WayPoint;
    2628import org.openstreetmap.josm.data.osm.DataSet;
    2729import org.openstreetmap.josm.data.osm.Node;
     30import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2831import org.openstreetmap.josm.data.osm.Way;
    2932import org.openstreetmap.josm.gui.ExtendedDialog;
    3033import org.openstreetmap.josm.gui.MainApplication;
     
    5356    @Override
    5457    public DataSet convert() {
    5558        final DataSet ds = new DataSet();
     59        ds.setGPXNamespaces(layer.data.getNamespaces());
    5660
    5761        List<String> keys = new ArrayList<>(); // note that items in this list don't have the GPX_PREFIX
    5862        String convertTags = Config.getPref().get(GPX_SETTING, "ask");
     
    5963        boolean check = "list".equals(convertTags) || "ask".equals(convertTags);
    6064        boolean none = "no".equals(convertTags); // no need to convert tags when no dialog will be shown anyways
    6165
    62         for (GpxTrack trk : layer.data.getTracks()) {
    63             for (GpxTrackSegment segment : trk.getSegments()) {
     66        for (IGpxTrack trk : layer.data.getTracks()) {
     67            for (IGpxTrackSegment segment : trk.getSegments()) {
    6468                List<Node> nodes = new ArrayList<>();
    6569                for (WayPoint p : segment.getWayPoints()) {
    6670                    Node n = new Node(p.getCoor());
    67                     for (Entry<String, Object> entry : p.attr.entrySet()) {
    68                         String key = entry.getKey();
    69                         Object obj = p.get(key);
    70                         if (check && !keys.contains(key) && (obj instanceof String || obj instanceof Number || obj instanceof Date)) {
    71                             keys.add(key);
    72                         }
    73                         if (!none && (obj instanceof String || obj instanceof Number)) {
    74                             // only convert when required
    75                             n.put(GpxConstants.GPX_PREFIX + key, obj.toString());
    76                         } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) {
    77                             // timestamps should always be converted
    78                             Date date = (Date) obj;
    79                             if (!none) { //... but the tag will only be set when required
    80                                 n.put(GpxConstants.GPX_PREFIX + key, DateUtils.fromDate(date));
    81                             }
    82                             n.setTimestamp(date);
    83                         }
     71                    addAttributes(p.getAttributes(), n, keys, check, none);
     72                    if (!none) {
     73                        addExtensions(p.getExtensions(), n, false, keys, check);
    8474                    }
    8575                    ds.addPrimitive(n);
    8676                    nodes.add(n);
     
    8777                }
    8878                Way w = new Way();
    8979                w.setNodes(nodes);
     80                addAttributes(trk.getAttributes(), w, keys, check, none);
     81                addAttributes(segment.getAttributes(), w, keys, check, none);
     82                if (!none) {
     83                    addExtensions(trk.getExtensions(), w, false, keys, check);
     84                    addExtensions(segment.getExtensions(), w, true, keys, check);
     85                }
    9086                ds.addPrimitive(w);
    9187            }
    9288        }
     
    122118        return ds;
    123119    }
    124120
     121    private static void addAttributes(Map<String, Object> attr, OsmPrimitive p, List<String> keys, boolean check, boolean none) {
     122        for (Entry<String, Object> entry : attr.entrySet()) {
     123            String key = entry.getKey();
     124            Object obj = entry.getValue();
     125            if (check && !keys.contains(key) && (obj instanceof String || obj instanceof Number || obj instanceof Date)) {
     126                keys.add(key);
     127            }
     128            if (!none && (obj instanceof String || obj instanceof Number)) {
     129                // only convert when required
     130                p.put(GpxConstants.GPX_PREFIX + key, obj.toString());
     131            } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) {
     132                // timestamps should always be converted
     133                Date date = (Date) obj;
     134                if (!none) { //... but the tag will only be set when required
     135                    p.put(GpxConstants.GPX_PREFIX + key, DateUtils.fromDate(date));
     136                }
     137                p.setTimestamp(date);
     138            }
     139        }
     140    }
     141
     142    private static void addExtensions(GpxExtensionCollection exts, OsmPrimitive p, boolean seg, List<String> keys, boolean check) {
     143        for (GpxExtension ext : exts) {
     144            String value = ext.getValue();
     145            if (value != null && !value.isEmpty()) {
     146                String extpre = "extension:";
     147                String pre = ext.getPrefix();
     148                if (pre == null || pre.isEmpty()) {
     149                    pre = "other";
     150                }
     151                String segpre = seg ? "segment:" : ""; //needs to be distinguished since both track and segment extensions are applied to the resulting way
     152                String key = ext.getFlatKey();
     153                String fullkey = GpxConstants.GPX_PREFIX + extpre + pre + ":" + segpre + key;
     154                if (GpxConstants.EXTENSION_ABBREVIATIONS.containsKey(fullkey)) {
     155                    fullkey = GpxConstants.EXTENSION_ABBREVIATIONS.get(fullkey);
     156                }
     157                if (check && !keys.contains(fullkey)) {
     158                    keys.add(fullkey);
     159                }
     160                p.put(fullkey, value);
     161            }
     162            addExtensions(ext.getExtensions(), p, seg, keys, check);
     163        }
     164    }
     165
    125166    /**
    126167     * Filters the tags of the given {@link DataSet}
    127168     * @param ds The {@link DataSet}
     
    130171     * @since 14103
    131172     */
    132173    public DataSet filterDataSet(DataSet ds, List<String> listPos) {
    133         Collection<Node> nodes = ds.getNodes();
    134         for (Node n : nodes) {
    135             for (String key : n.keySet()) {
    136                 if (listPos == null || !listPos.contains(key.substring(GpxConstants.GPX_PREFIX.length()))) {
    137                     n.put(key, null);
     174        for (OsmPrimitive p : ds.getPrimitives(p -> p instanceof Node || p instanceof Way)) {
     175            for (String key : p.keySet()) {
     176                String listkey;
     177                if (listPos != null && key.startsWith(GpxConstants.GPX_PREFIX)) {
     178                    listkey = key.substring(GpxConstants.GPX_PREFIX.length());
     179                } else {
     180                    listkey = key;
    138181                }
     182                if (listPos == null || !listPos.contains(listkey)) {
     183                   p.put(key, null);
     184                }
    139185            }
    140186        }
    141187        return ds;
  • src/org/openstreetmap/josm/gui/layer/gpx/CustomizeDrawingAction.java

     
    99import java.awt.event.ActionEvent;
    1010import java.util.LinkedList;
    1111import java.util.List;
     12import java.util.stream.Collectors;
    1213
    1314import javax.swing.AbstractAction;
    1415import javax.swing.Action;
     
    5960
    6061    @Override
    6162    public boolean supportLayers(List<Layer> layers) {
    62         for (Layer layer : layers) {
    63             if (!(layer instanceof GpxLayer)) {
    64                 return false;
    65             }
    66         }
    67         return true;
     63        return layers.stream().allMatch(l -> l instanceof GpxLayer);
    6864    }
    6965
    7066    @Override
     
    7975
    8076    @Override
    8177    public void actionPerformed(ActionEvent e) {
    82         boolean hasLocal = false;
    83         boolean hasNonlocal = false;
    84         for (Layer layer : layers) {
    85             if (layer instanceof GpxLayer) {
    86                 if (((GpxLayer) layer).isLocalFile()) {
    87                     hasLocal = true;
    88                 } else {
    89                     hasNonlocal = true;
    90                 }
    91             }
    92         }
    93         GPXSettingsPanel panel = new GPXSettingsPanel(layers.get(0).getName(), hasLocal, hasNonlocal);
     78        GPXSettingsPanel panel = new GPXSettingsPanel(layers.stream().filter(l -> l instanceof GpxLayer).map(l -> (GpxLayer) l).collect(Collectors.toList()));
    9479        JScrollPane scrollpane = GuiHelper.embedInVerticalScrollPane(panel);
    9580        scrollpane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
    9681        int screenHeight = GuiHelper.getScreenSize().height;
     
    10388        if (answer == JOptionPane.CANCEL_OPTION || answer == JOptionPane.CLOSED_OPTION) {
    10489            return;
    10590        }
    106         for (Layer layer : layers) {
    107             // save preferences for all layers
    108             boolean f = false;
    109             if (layer instanceof GpxLayer) {
    110                 f = ((GpxLayer) layer).isLocalFile();
    111             }
    112             panel.savePreferences(layer.getName(), f);
    113         }
    114         MainApplication.getMap().repaint();
     91        panel.savePreferences();
     92        MainApplication.getMainPanel().repaint();
     93        layers.stream().forEach(Layer::invalidate);
    11594    }
    11695
    11796}
  • src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java

     
    88import org.openstreetmap.josm.actions.DownloadAlongAction;
    99import org.openstreetmap.josm.data.gpx.GpxData;
    1010import org.openstreetmap.josm.data.gpx.GpxTrack;
    11 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     11import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    1212import org.openstreetmap.josm.data.gpx.WayPoint;
    1313import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    1414import org.openstreetmap.josm.gui.help.HelpUtil;
     
    6060        Path2D gpxPath = new Path2D.Double();
    6161        if (near == NEAR_TRACK || near == NEAR_BOTH) {
    6262            for (GpxTrack trk : data.tracks) {
    63                 for (GpxTrackSegment segment : trk.getSegments()) {
     63                for (IGpxTrackSegment segment : trk.getSegments()) {
    6464                    boolean first = true;
    6565                    for (WayPoint p : segment.getWayPoints()) {
    6666                        if (first) {
  • src/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackAction.java

     
    1616import org.openstreetmap.josm.data.coor.LatLon;
    1717import org.openstreetmap.josm.data.gpx.GpxData;
    1818import org.openstreetmap.josm.data.gpx.GpxTrack;
    19 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     19import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2020import org.openstreetmap.josm.data.gpx.WayPoint;
    2121import org.openstreetmap.josm.gui.MainApplication;
    2222import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     
    9292    PrecacheWmsTask createTask() {
    9393        List<LatLon> points = new ArrayList<>();
    9494        for (GpxTrack trk : data.tracks) {
    95             for (GpxTrackSegment segment : trk.getSegments()) {
     95            for (IGpxTrackSegment segment : trk.getSegments()) {
    9696                for (WayPoint p : segment.getWayPoints()) {
    9797                    points.add(p.getCoor());
    9898                }
  • src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java

     
    2727import java.util.Date;
    2828import java.util.LinkedList;
    2929import java.util.List;
     30import java.util.Objects;
    3031import java.util.Random;
    3132
    3233import javax.swing.ImageIcon;
    3334
    3435import org.openstreetmap.josm.data.Bounds;
    35 import org.openstreetmap.josm.data.PreferencesUtils;
    3636import org.openstreetmap.josm.data.SystemOfMeasurement;
    3737import org.openstreetmap.josm.data.SystemOfMeasurement.SoMChangeListener;
    3838import org.openstreetmap.josm.data.coor.LatLon;
     
    5151import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent;
    5252import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent;
    5353import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener;
     54import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    5455import org.openstreetmap.josm.io.CachedFile;
    5556import org.openstreetmap.josm.spi.preferences.Config;
    5657import org.openstreetmap.josm.tools.ColorScale;
     
    6566public class GpxDrawHelper implements SoMChangeListener, MapViewPaintable.LayerPainter, PaintableInvalidationListener, GpxDataChangeListener {
    6667
    6768    /**
    68      * The color that is used for drawing GPX points.
    69      * @since 10824
     69     * The default color property that is used for drawing GPX points.
     70     * @since xxx
    7071     */
    71     public static final NamedColorProperty DEFAULT_COLOR = new NamedColorProperty(marktr("gps point"), Color.magenta);
     72    public static final NamedColorProperty DEFAULT_COLOR_PROPERTY = new NamedColorProperty(marktr("gps point"), Color.magenta);
    7273
    7374    private final GpxData data;
    7475    private final GpxLayer layer;
     
    7879    // use alpha blending for line draw
    7980    private boolean alphaLines;
    8081    // draw direction arrows on the lines
    81     private boolean direction;
     82    private boolean arrows;
    8283    /** width of line for paint **/
    8384    private int lineWidth;
    8485    /** don't draw lines if longer than x meters **/
     
    9091    private int largesize;
    9192    private boolean hdopCircle;
    9293    /** paint direction arrow with alternate math. may be faster **/
    93     private boolean alternateDirection;
     94    private boolean arrowsFast;
    9495    /** don't draw arrows nearer to each other than this **/
    95     private int delta;
     96    private int arrowsDelta;
    9697    private double minTrackDurationForTimeColoring;
    9798
    9899    /** maximum value of displayed HDOP, minimum is 0 */
     
    106107    private Color computeCacheColorUsed;
    107108    private boolean computeCacheColorDynamic;
    108109    private ColorMode computeCacheColored;
    109     private int computeCacheColorTracksTune;
     110    private int computeCacheVelocityTune;
    110111    private int computeCacheHeatMapDrawColorTableIdx;
    111112    private boolean computeCacheHeatMapDrawPointMode;
    112113    private int computeCacheHeatMapDrawGain;
    113114    private int computeCacheHeatMapDrawLowerLimit;
    114115
     116    private Color colorCache;
     117    private Color colorCacheTransparent;
     118
    115119    //// Color-related fields
    116120    /** Mode of the line coloring **/
    117121    private ColorMode colored;
    118122    /** max speed for coloring - allows to tweak line coloring for different speed levels. **/
    119     private int colorTracksTune;
     123    private int velocityTune;
    120124    private boolean colorModeDynamic;
    121125    private Color neutralColor;
    122126    private int largePointAlpha;
     
    145149
    146150    /** heat map parameters **/
    147151
    148     // enabled or not (override by settings)
    149     private boolean heatMapEnabled;
    150152    // draw small extra line
    151153    private boolean heatMapDrawExtraLine;
    152154    // used index for color table (parameter)
     
    268270        setupColors();
    269271    }
    270272
    271     private static String specName(String layerName) {
    272         return "layer " + layerName;
    273     }
    274 
    275273    /**
    276      * Get the default color for gps tracks for specified layer
    277      * @param layerName name of the GpxLayer
    278      * @param ignoreCustom do not use preferences
    279      * @return the color or null if the color is not constant
    280      */
    281     public Color getColor(String layerName, boolean ignoreCustom) {
    282         if (ignoreCustom || getColorMode(layerName) == ColorMode.NONE) {
    283             return DEFAULT_COLOR.getChildColor(
    284                     NamedColorProperty.COLOR_CATEGORY_LAYER,
    285                     layerName,
    286                     DEFAULT_COLOR.getName()).get();
    287         } else {
    288             return null;
    289         }
    290     }
    291 
    292     /**
    293274     * Read coloring mode for specified layer from preferences
    294      * @param layerName name of the GpxLayer
    295275     * @return coloring mode
    296276     */
    297     public ColorMode getColorMode(String layerName) {
     277    public ColorMode getColorMode() {
    298278        try {
    299             int i = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colors", specName(layerName), 0);
     279            int i = optInt("colormode");
     280            if (i == -1) i = 0; //global
    300281            return ColorMode.fromIndex(i);
    301282        } catch (IndexOutOfBoundsException e) {
    302283            Logging.warn(e);
     
    304285        return ColorMode.NONE;
    305286    }
    306287
    307     /** Reads generic color from preferences (usually gray)
    308      * @return the color
    309      **/
    310     public static Color getGenericColor() {
    311         return DEFAULT_COLOR.get();
     288    private String opt(String key) {
     289        return GPXSettingsPanel.getLayerPref(layer, key);
    312290    }
    313291
     292    private boolean optBool(String key) {
     293        return Boolean.parseBoolean(opt(key));
     294    }
     295
     296    private int optInt(String key) {
     297        return GPXSettingsPanel.getLayerPrefInt(layer, key);
     298    }
     299
    314300    /**
    315301     * Read all drawing-related settings from preferences
    316      * @param layerName layer name used to access its specific preferences
    317302     **/
    318     public void readPreferences(String layerName) {
    319         String spec = specName(layerName);
    320         forceLines = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines.force", spec, false);
    321         direction = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.direction", spec, false);
    322         lineWidth = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.linewidth", spec, 0);
    323         alphaLines = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines.alpha-blend", spec, false);
     303    public void readPreferences() {
     304        forceLines = optBool("lines.force");
     305        arrows = optBool("lines.arrows");
     306        arrowsFast = optBool("lines.arrows.fast");
     307        arrowsDelta = optInt("lines.arrows.min-distance");
     308        lineWidth = optInt("lines.width");
     309        alphaLines = optBool("lines.alpha-blend");
    324310
     311        int l = optInt("lines");
    325312        if (!data.fromServer) {
    326             maxLineLength = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.max-line-length.local", spec, -1);
    327             lines = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines.local", spec, true);
     313            maxLineLength = optInt("lines.max-length.local");
     314            lines = l != 0; //draw for -1 (default), 1 (local) and 2 (all)
    328315        } else {
    329             maxLineLength = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.max-line-length", spec, 200);
    330             lines = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines", spec, true);
     316            maxLineLength = optInt("lines.max-length");
     317            lines = l == 2; //draw only for 2 (all)
    331318        }
    332         large = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.large", spec, false);
    333         largesize = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.large.size", spec, 3);
    334         hdopCircle = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.hdopcircle", spec, false);
    335         colored = getColorMode(layerName);
    336         alternateDirection = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.alternatedirection", spec, false);
    337         delta = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.min-arrow-distance", spec, 40);
    338         colorTracksTune = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colorTracksTune", spec, 45);
    339         colorModeDynamic = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.colors.dynamic", spec, false);
     319        large = optBool("points.large");
     320        largesize = optInt("points.large.size");
     321        hdopCircle = optBool("points.hdopcircle");
     322        colored = getColorMode();
     323        velocityTune = optInt("colormode.velocity.tune");
     324        colorModeDynamic = optBool("colormode.dynamic-range");
    340325        /* good HDOP's are between 1 and 3, very bad HDOP's go into 3 digit values */
    341326        hdoprange = Config.getPref().getInt("hdop.range", 7);
    342         minTrackDurationForTimeColoring = Config.getPref().getInt("draw.rawgps.date-coloring-min-dt", 60);
    343         largePointAlpha = Config.getPref().getInt("draw.rawgps.large.alpha", -1) & 0xFF;
     327        minTrackDurationForTimeColoring = optInt("colormode.time.min-distance");
     328        largePointAlpha = optInt("points.large.alpha") & 0xFF;
    344329
    345330        // get heatmap parameters
    346         heatMapEnabled = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.heatmap.enabled", spec, false);
    347         heatMapDrawExtraLine = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.heatmap.line-extra", spec, false);
    348         heatMapDrawColorTableIdx = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.heatmap.colormap", spec, 0);
    349         heatMapDrawPointMode = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.heatmap.use-points", spec, false);
    350         heatMapDrawGain = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.heatmap.gain", spec, 0);
    351         heatMapDrawLowerLimit = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.heatmap.lower-limit", spec, 0);
     331        heatMapDrawExtraLine = optBool("colormode.heatmap.line-extra");
     332        heatMapDrawColorTableIdx = optInt("colormode.heatmap.colormap");
     333        heatMapDrawPointMode = optBool("colormode.heatmap.use-points");
     334        heatMapDrawGain = optInt("colormode.heatmap.gain");
     335        heatMapDrawLowerLimit = optInt("colormode.heatmap.lower-limit");
    352336
    353337        // shrink to range
    354338        heatMapDrawGain = Utils.clamp(heatMapDrawGain, -10, 10);
    355 
    356         neutralColor = getColor(layerName, true);
     339        neutralColor = DEFAULT_COLOR_PROPERTY.get();
    357340        velocityScale.setNoDataColor(neutralColor);
    358341        dateScale.setNoDataColor(neutralColor);
    359342        hdopScale.setNoDataColor(neutralColor);
     
    368351        Bounds clipBounds = graphics.getClipBounds().getLatLonBoundsBox();
    369352        List<WayPoint> visibleSegments = listVisibleSegments(clipBounds);
    370353        if (!visibleSegments.isEmpty()) {
    371             readPreferences(layer.getName());
     354            readPreferences();
    372355            drawAll(graphics.getDefaultGraphics(), graphics.getMapView(), visibleSegments, clipBounds);
    373356            if (graphics.getMapView().getLayerManager().getActiveLayer() == layer) {
    374357                drawColorBar(graphics.getDefaultGraphics(), graphics.getMapView());
     
    430413     * @param clipBounds      the clipping rectangle for the current view
    431414     * @since 14748 : new parameter clipBounds
    432415     */
    433 
    434416    public void drawAll(Graphics2D g, MapView mv, List<WayPoint> visibleSegments, Bounds clipBounds) {
    435417
    436418        final long timeStart = System.currentTimeMillis();
     
    462444        }
    463445
    464446        // global enabled or select via color
    465         boolean useHeatMap = heatMapEnabled || ColorMode.HEATMAP == colored;
     447        boolean useHeatMap = ColorMode.HEATMAP == colored;
    466448
    467449        // default global alpha level
    468450        float layerAlpha = 1.00f;
     
    570552            }
    571553            oldWp = null;
    572554        } else { // color mode not dynamic
    573             velocityScale.setRange(0, colorTracksTune);
     555            velocityScale.setRange(0, velocityTune);
    574556            hdopScale.setRange(0, hdoprange);
    575557            qualityScale.setRange(1, rtkLibQualityColors.length);
    576558        }
     
    594576            }
    595577            for (WayPoint trkPnt : segment) {
    596578                LatLon c = trkPnt.getCoor();
    597                 trkPnt.customColoring = neutralColor;
     579                trkPnt.customColoring = segment.getColor();
    598580                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
    599581                    continue;
    600582                }
     
    642624                    }
    643625                } else { // make sure we reset outdated data
    644626                    trkPnt.drawLine = false;
    645                     color = neutralColor;
     627                    color = segment.getColor();
    646628                }
    647629                if (color != null) {
    648630                    trkPnt.customColoring = color;
     
    700682        /****************************************************************
    701683         ********** STEP 3b - DRAW NICE ARROWS **************************
    702684         ****************************************************************/
    703         if (lines && direction && !alternateDirection) {
     685        if (lines && arrows && !arrowsFast) {
    704686            Point old = null;
    705687            Point oldA = null; // last arrow painted
    706688            for (WayPoint trkPnt : visibleSegments) {
     
    712694                    Point screen = mv.getPoint(trkPnt);
    713695                    // skip points that are on the same screenposition
    714696                    if (old != null
    715                             && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
    716                             || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
     697                            && (oldA == null || screen.x < oldA.x - arrowsDelta || screen.x > oldA.x + arrowsDelta
     698                            || screen.y < oldA.y - arrowsDelta || screen.y > oldA.y + arrowsDelta)) {
    717699                        g.setColor(trkPnt.customColoring);
    718700                        double t = Math.atan2((double) screen.y - old.y, (double) screen.x - old.x) + Math.PI;
    719701                        g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t - PHI)),
     
    730712        /****************************************************************
    731713         ********** STEP 3c - DRAW FAST ARROWS **************************
    732714         ****************************************************************/
    733         if (lines && direction && alternateDirection) {
     715        if (lines && arrows && arrowsFast) {
    734716            Point old = null;
    735717            Point oldA = null; // last arrow painted
    736718            for (WayPoint trkPnt : visibleSegments) {
     
    742724                    Point screen = mv.getPoint(trkPnt);
    743725                    // skip points that are on the same screenposition
    744726                    if (old != null
    745                             && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
    746                             || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
     727                            && (oldA == null || screen.x < oldA.x - arrowsDelta || screen.x > oldA.x + arrowsDelta
     728                            || screen.y < oldA.y - arrowsDelta || screen.y > oldA.y + arrowsDelta)) {
    747729                        g.setColor(trkPnt.customColoring);
    748730                        g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y
    749731                                + dir[trkPnt.dir][1]);
     
    794776                if (large) {
    795777                    // color the large GPS points like the gps lines
    796778                    if (trkPnt.customColoring != null) {
    797                         Color customColoringTransparent = largePointAlpha < 0 ? trkPnt.customColoring :
    798                             new Color((trkPnt.customColoring.getRGB() & 0x00ffffff) | (largePointAlpha << 24), true);
     779                        if (trkPnt.customColoring.equals(colorCache) && colorCacheTransparent != null) {
     780                            g.setColor(colorCacheTransparent);
     781                        } else {
     782                            Color customColoringTransparent = largePointAlpha < 0 ? trkPnt.customColoring :
     783                                new Color((trkPnt.customColoring.getRGB() & 0x00ffffff) | (largePointAlpha << 24), true);
    799784
    800                         g.setColor(customColoringTransparent);
     785                            g.setColor(customColoringTransparent);
     786                            colorCache = trkPnt.customColoring;
     787                            colorCacheTransparent = customColoringTransparent;
     788                        }
    801789                    }
    802790                    g.fillRect(screen.x-halfSize, screen.y-halfSize, largesize, largesize);
    803791                }
     
    815803                    continue;
    816804                }
    817805                if (!trkPnt.drawLine) {
     806                    g.setColor(trkPnt.customColoring);
    818807                    Point screen = mv.getPoint(trkPnt);
    819808                    g.drawRect(screen.x, screen.y, 0, 0);
    820809                }
     
    14801469        // CHECKSTYLE.OFF: BooleanExpressionComplexity
    14811470        if ((computeCacheMaxLineLengthUsed != maxLineLength)
    14821471                || (computeCacheColored != colored)
    1483                 || (computeCacheColorTracksTune != colorTracksTune)
     1472                || (computeCacheVelocityTune != velocityTune)
    14841473                || (computeCacheColorDynamic != colorModeDynamic)
    14851474                || (computeCacheHeatMapDrawColorTableIdx != heatMapDrawColorTableIdx)
    1486                 || (!neutralColor.equals(computeCacheColorUsed)
     1475                || !Objects.equals(neutralColor, computeCacheColorUsed)
    14871476                || (computeCacheHeatMapDrawPointMode != heatMapDrawPointMode)
    1488                 || (computeCacheHeatMapDrawGain != heatMapDrawGain))
     1477                || (computeCacheHeatMapDrawGain != heatMapDrawGain)
    14891478                || (computeCacheHeatMapDrawLowerLimit != heatMapDrawLowerLimit)
    14901479        ) {
    14911480            // CHECKSTYLE.ON: BooleanExpressionComplexity
     
    14931482            computeCacheInSync = false;
    14941483            computeCacheColorUsed = neutralColor;
    14951484            computeCacheColored = colored;
    1496             computeCacheColorTracksTune = colorTracksTune;
     1485            computeCacheVelocityTune = velocityTune;
    14971486            computeCacheColorDynamic = colorModeDynamic;
    14981487            computeCacheHeatMapDrawColorTableIdx = heatMapDrawColorTableIdx;
    14991488            computeCacheHeatMapDrawPointMode = heatMapDrawPointMode;
  • src/org/openstreetmap/josm/gui/layer/gpx/ImportAudioAction.java

     
    2121import org.openstreetmap.josm.data.gpx.GpxConstants;
    2222import org.openstreetmap.josm.data.gpx.GpxData;
    2323import org.openstreetmap.josm.data.gpx.GpxTrack;
    24 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     24import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2525import org.openstreetmap.josm.data.gpx.WayPoint;
    2626import org.openstreetmap.josm.data.projection.ProjectionRegistry;
    2727import org.openstreetmap.josm.gui.HelpAwareOptionPane;
     
    147147        double firstTime = -1.0;
    148148        if (hasTracks) {
    149149            for (GpxTrack track : layer.data.tracks) {
    150                 for (GpxTrackSegment seg : track.getSegments()) {
     150                for (IGpxTrackSegment seg : track.getSegments()) {
    151151                    for (WayPoint w : seg.getWayPoints()) {
    152152                        firstTime = w.getTime();
    153153                        break;
     
    206206        if (layer.data.tracks != null && Config.getPref().getBoolean("marker.audiofromnamedtrackpoints", false)
    207207                && !layer.data.tracks.isEmpty()) {
    208208            for (GpxTrack track : layer.data.tracks) {
    209                 for (GpxTrackSegment seg : track.getSegments()) {
     209                for (IGpxTrackSegment seg : track.getSegments()) {
    210210                    for (WayPoint w : seg.getWayPoints()) {
    211211                        if (w.attr.containsKey(GpxConstants.GPX_NAME) || w.attr.containsKey(GpxConstants.GPX_DESC)) {
    212212                            waypoints.add(w);
     
    227227            WayPoint w2 = null;
    228228
    229229            for (GpxTrack track : layer.data.tracks) {
    230                 for (GpxTrackSegment seg : track.getSegments()) {
     230                for (IGpxTrackSegment seg : track.getSegments()) {
    231231                    for (WayPoint w : seg.getWayPoints()) {
    232232                        if (startTime < w.getTime()) {
    233233                            w2 = w;
     
    263263        if ((Config.getPref().getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && hasTracks) {
    264264            boolean gotOne = false;
    265265            for (GpxTrack track : layer.data.tracks) {
    266                 for (GpxTrackSegment seg : track.getSegments()) {
     266                for (IGpxTrackSegment seg : track.getSegments()) {
    267267                    for (WayPoint w : seg.getWayPoints()) {
    268268                        WayPoint wStart = new WayPoint(w.getCoor());
    269269                        wStart.put(GpxConstants.GPX_NAME, "start");
  • src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java

     
    9393    }
    9494
    9595    @Override
    96     protected TemplateEntryProperty getTextTemplate() {
    97         return TemplateEntryProperty.forAudioMarker(parentLayer.getName());
     96    protected String getTextTemplateKey() {
     97        return "markers.audio.pattern";
    9898    }
    9999
    100100    @Override
     
    103103        GpxLink link = new GpxLink(audioUrl.toString());
    104104        link.type = "audio";
    105105        wpt.put(GpxConstants.META_LINKS, Collections.singleton(link));
    106         wpt.addExtension("offset", Double.toString(offset));
    107         wpt.addExtension("sync-offset", Double.toString(syncOffset));
     106        wpt.getExtensions().add("josm", "offset", Double.toString(offset));
     107        wpt.getExtensions().add("josm", "sync-offset", Double.toString(syncOffset));
    108108        return wpt;
    109109    }
    110110}
  • src/org/openstreetmap/josm/gui/layer/markerlayer/DefaultMarkerProducers.java

     
    99import java.util.Collections;
    1010import java.util.Optional;
    1111
    12 import org.openstreetmap.josm.data.gpx.Extensions;
    1312import org.openstreetmap.josm.data.gpx.GpxConstants;
     13import org.openstreetmap.josm.data.gpx.GpxExtension;
    1414import org.openstreetmap.josm.data.gpx.GpxLink;
    1515import org.openstreetmap.josm.data.gpx.WayPoint;
    1616import org.openstreetmap.josm.tools.Logging;
     
    4545            return Collections.singleton(marker);
    4646        } else if (Utils.hasExtension(urlStr, "wav", "mp3", "aac", "aif", "aiff")) {
    4747            final AudioMarker audioMarker = new AudioMarker(wpt.getCoor(), wpt, url, parentLayer, time, offset);
    48             Extensions exts = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);
    49             if (exts != null && exts.containsKey("offset")) {
     48            GpxExtension offsetExt = wpt.getExtensions().get("josm", "sync-offset");
     49            if (offsetExt != null && offsetExt.getValue() != null) {
    5050                try {
    51                     audioMarker.syncOffset = Double.parseDouble(exts.get("sync-offset"));
     51                    audioMarker.syncOffset = Double.parseDouble(offsetExt.getValue());
    5252                } catch (NumberFormatException nfe) {
    5353                    Logging.warn(nfe);
    5454                }
  • src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java

     
    1818
    1919import javax.swing.ImageIcon;
    2020
     21import org.openstreetmap.josm.data.Preferences;
    2122import org.openstreetmap.josm.data.coor.CachedLatLon;
    2223import org.openstreetmap.josm.data.coor.EastNorth;
    2324import org.openstreetmap.josm.data.coor.ILatLon;
     
    2526import org.openstreetmap.josm.data.gpx.GpxConstants;
    2627import org.openstreetmap.josm.data.gpx.WayPoint;
    2728import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match;
    28 import org.openstreetmap.josm.data.preferences.CachedProperty;
    2929import org.openstreetmap.josm.gui.MapView;
    30 import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
     30import org.openstreetmap.josm.gui.layer.GpxLayer;
     31import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    3132import org.openstreetmap.josm.tools.ImageProvider;
    3233import org.openstreetmap.josm.tools.Logging;
    3334import org.openstreetmap.josm.tools.template_engine.ParseError;
     
    7273 */
    7374public class Marker implements TemplateEngineDataProvider, ILatLon {
    7475
    75     public static final class TemplateEntryProperty extends CachedProperty<TemplateEntry> {
    76         // This class is a bit complicated because it supports both global and per layer settings. I've added per layer settings because
    77         // GPXSettingsPanel had possibility to set waypoint label but then I've realized that markers use different layer then gpx data
    78         // so per layer settings is useless. Anyway it's possible to specify marker layer pattern in Einstein preferences and maybe somebody
    79         // will make gui for it so I'm keeping it here
    80 
    81         private static final Map<String, TemplateEntryProperty> CACHE = new HashMap<>();
    82 
    83         public static TemplateEntryProperty forMarker(String layerName) {
    84             String key = "draw.rawgps.layer.wpt.pattern";
    85             if (layerName != null) {
    86                 key += '.' + layerName;
    87             }
    88             TemplateEntryProperty result = CACHE.get(key);
    89             if (result == null) {
    90                 String defaultValue = layerName == null ? LABEL_PATTERN_AUTO : "";
    91                 TemplateEntryProperty parent = layerName == null ? null : forMarker(null);
    92                 result = new TemplateEntryProperty(key, defaultValue, parent);
    93                 CACHE.put(key, result);
    94             }
    95             return result;
    96         }
    97 
    98         public static TemplateEntryProperty forAudioMarker(String layerName) {
    99             String key = "draw.rawgps.layer.audiowpt.pattern";
    100             if (layerName != null) {
    101                 key += '.' + layerName;
    102             }
    103             TemplateEntryProperty result = CACHE.get(key);
    104             if (result == null) {
    105                 String defaultValue = layerName == null ? "?{ '{name}' | '{desc}' | '{" + Marker.MARKER_FORMATTED_OFFSET + "}' }" : "";
    106                 TemplateEntryProperty parent = layerName == null ? null : forAudioMarker(null);
    107                 result = new TemplateEntryProperty(key, defaultValue, parent);
    108                 CACHE.put(key, result);
    109             }
    110             return result;
    111         }
    112 
    113         private final TemplateEntryProperty parent;
    114 
    115         private TemplateEntryProperty(String key, String defaultValue, TemplateEntryProperty parent) {
    116             super(key, defaultValue);
    117             this.parent = parent;
    118             updateValue(); // Needs to be called because parent wasn't know in super constructor
    119         }
    120 
    121         @Override
    122         protected TemplateEntry fromString(String s) {
    123             try {
    124                 return new TemplateParser(s).parse();
    125             } catch (ParseError e) {
    126                 Logging.debug(e);
    127                 Logging.warn("Unable to parse template engine pattern ''{0}'' for property {1}. Using default (''{2}'') instead",
    128                         s, getKey(), super.getDefaultValueAsString());
    129                 return getDefaultValue();
    130             }
    131         }
    132 
    133         @Override
    134         public String getDefaultValueAsString() {
    135             if (parent == null)
    136                 return super.getDefaultValueAsString();
    137             else
    138                 return parent.getAsString();
    139         }
    140 
    141         @Override
    142         public void preferenceChanged(PreferenceChangeEvent e) {
    143             if (e.getKey().equals(key) || (parent != null && e.getKey().equals(parent.getKey()))) {
    144                 updateValue();
    145             }
    146         }
    147     }
    148 
    14976    /**
    15077     * Plugins can add their Marker creation stuff at the bottom or top of this list
    15178     * (depending on whether they want to override default behaviour or just add new stuff).
     
    216143    public double offset;
    217144
    218145    private String cachedText;
    219     private int textVersion = -1;
     146    private static Map<GpxLayer, String> cachedTemplates = new HashMap<>();
     147    private String cachedDefaultTemplate;
     148
    220149    private CachedLatLon coor;
    221150
    222151    private boolean erroneous;
     
    244173
    245174        this.dataProvider = dataProvider;
    246175        this.text = text;
     176
     177        Preferences.main().addKeyPreferenceChangeListener("draw.rawgps." + getTextTemplateKey(), l -> updateText());
    247178    }
    248179
    249180    /**
     
    257188        WayPoint wpt = new WayPoint(getCoor());
    258189        wpt.setTimeInMillis((long) (time * 1000));
    259190        if (text != null) {
    260             wpt.addExtension("text", text);
     191            wpt.getExtensions().add("josm", "text", text);
    261192        } else if (dataProvider != null) {
    262193            for (String key : dataProvider.getTemplateKeys()) {
    263194                Object value = dataProvider.getTemplateValue(key, false);
     
    372303        }
    373304    }
    374305
    375     protected TemplateEntryProperty getTextTemplate() {
    376         return TemplateEntryProperty.forMarker(parentLayer.getName());
     306    protected String getTextTemplateKey() {
     307        return "markers.pattern";
    377308    }
    378309
     310    private String getTextTemplate() {
     311        String tmpl;
     312        if (cachedTemplates.containsKey(parentLayer.fromLayer)) {
     313            tmpl = cachedTemplates.get(parentLayer.fromLayer);
     314        } else {
     315            tmpl = GPXSettingsPanel.getLayerPref(parentLayer.fromLayer, getTextTemplateKey());
     316            cachedTemplates.put(parentLayer.fromLayer, tmpl);
     317        }
     318        return tmpl;
     319    }
     320
     321    private String getDefaultTextTemplate() {
     322        if (cachedDefaultTemplate == null) {
     323            cachedDefaultTemplate = GPXSettingsPanel.getLayerPref(null,  getTextTemplateKey());
     324        }
     325        return cachedDefaultTemplate;
     326    }
     327
    379328    /**
    380329     * Returns the Text which should be displayed, depending on chosen preference
    381330     * @return Text of the label
    382331     */
    383332    public String getText() {
    384         if (text != null)
     333        if (text != null) {
    385334            return text;
    386         else {
    387             TemplateEntryProperty property = getTextTemplate();
    388             if (property.getUpdateCount() != textVersion) {
    389                 TemplateEntry templateEntry = property.get();
    390                 StringBuilder sb = new StringBuilder();
    391                 templateEntry.appendText(sb, this);
     335        } else if (cachedText == null) {
     336            TemplateEntry template;
     337            String templateString = getTextTemplate();
     338            try {
     339                template = new TemplateParser(templateString).parse();
     340            } catch (ParseError e) {
     341                Logging.debug(e);
     342                String def = getDefaultTextTemplate();
     343                Logging.warn("Unable to parse template engine pattern ''{0}'' for property {1}. Using default (''{2}'') instead",
     344                        templateString, getTextTemplateKey(), def);
     345                try {
     346                    template = new TemplateParser(def).parse();
     347                } catch (ParseError e1) {
     348                    Logging.error(e1);
     349                    cachedText = "";
     350                    return "";
     351                }
     352            }
     353            StringBuilder sb = new StringBuilder();
     354            template.appendText(sb, this);
     355            cachedText = sb.toString();
    392356
    393                 cachedText = sb.toString();
    394                 textVersion = property.getUpdateCount();
    395             }
    396             return cachedText;
    397357        }
     358        return cachedText;
    398359    }
    399360
     361    /**
     362     * Called when the template changes
     363     */
     364    public void updateText() {
     365        cachedText = null;
     366        cachedDefaultTemplate = null;
     367        cachedTemplates = new HashMap<>();
     368    }
     369
    400370    @Override
    401371    public Collection<String> getTemplateKeys() {
    402372        Collection<String> result;
  • src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java

     
    2020import java.util.Collection;
    2121import java.util.Comparator;
    2222import java.util.List;
     23import java.util.Optional;
    2324
    2425import javax.swing.AbstractAction;
    2526import javax.swing.Action;
     
    3031import org.openstreetmap.josm.actions.RenameLayerAction;
    3132import org.openstreetmap.josm.data.Bounds;
    3233import org.openstreetmap.josm.data.coor.LatLon;
    33 import org.openstreetmap.josm.data.gpx.Extensions;
    3434import org.openstreetmap.josm.data.gpx.GpxConstants;
    3535import org.openstreetmap.josm.data.gpx.GpxData;
     36import org.openstreetmap.josm.data.gpx.GpxExtension;
    3637import org.openstreetmap.josm.data.gpx.GpxLink;
    3738import org.openstreetmap.josm.data.gpx.WayPoint;
    3839import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     
    4849import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker;
    4950import org.openstreetmap.josm.gui.layer.Layer;
    5051import org.openstreetmap.josm.gui.layer.gpx.ConvertFromMarkerLayerAction;
     52import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel;
    5153import org.openstreetmap.josm.io.audio.AudioPlayer;
    5254import org.openstreetmap.josm.spi.preferences.Config;
    5355import org.openstreetmap.josm.tools.ImageProvider;
     
    7577    public GpxLayer fromLayer;
    7678    private Marker currentMarker;
    7779    public AudioMarker syncAudioMarker;
     80    private Color color, realcolor;
    7881
    79     private static final Color DEFAULT_COLOR = Color.magenta;
    80     private static final NamedColorProperty COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), DEFAULT_COLOR);
     82    /**
     83     * The default color that is used for drawing markers.
     84     */
     85    public static final NamedColorProperty DEFAULT_COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), Color.magenta);
    8186
    8287    /**
    8388     * Constructs a new {@code MarkerLayer}.
     
    9499        double firstTime = -1.0;
    95100        String lastLinkedFile = "";
    96101
     102        Color c = null;
     103        String cs = GPXSettingsPanel.tryGetLayerPrefLocal(indata, "markers.color");
     104        if (cs != null) {
     105            try {
     106                c = Color.decode(cs);
     107            } catch (NumberFormatException ex) {
     108                Logging.warn("Could not read marker color: " + cs);
     109            }
     110        }
     111        setPrivateColors(c);
     112
    97113        for (WayPoint wpt : indata.waypoints) {
    98114            /* calculate time differences in waypoints */
    99115            double time = wpt.getTime();
     
    123139            // audio file) calculate the offset relative to the first marker of
    124140            // that group. This way the user can jump to the corresponding
    125141            // playback positions in a long audio track.
    126             Extensions exts = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);
    127             if (exts != null && exts.containsKey("offset")) {
     142            GpxExtension offsetExt = wpt.getExtensions().get("josm", "offset");
     143            if (offsetExt != null && offsetExt.getValue() != null) {
    128144                try {
    129                     offset = Double.valueOf(exts.get("offset"));
     145                    offset = Double.valueOf(offsetExt.getValue());
    130146                } catch (NumberFormatException nfe) {
    131147                    Logging.warn(nfe);
    132148                }
     
    173189    }
    174190
    175191    @Override
    176     protected NamedColorProperty getBaseColorProperty() {
    177         return COLOR_PROPERTY;
    178     }
    179 
    180     /* for preferences */
    181     public static Color getGenericColor() {
    182         return COLOR_PROPERTY.get();
    183     }
    184 
    185     @Override
    186192    public void paint(Graphics2D g, MapView mv, Bounds box) {
    187193        boolean showTextOrIcon = isTextOrIconShown();
    188         g.setColor(getColorProperty().get());
    189 
     194        g.setColor(realcolor);
    190195        if (mousePressed) {
    191196            boolean mousePressedTmp = mousePressed;
    192197            Point mousePos = mv.getMousePosition(); // Get mouse position only when necessary (it's the slowest part of marker layer painting)
     
    450455     * @return <code>true</code> if text should be shown, <code>false</code> otherwise.
    451456     */
    452457    private boolean isTextOrIconShown() {
    453         String current = Config.getPref().get("marker.show "+getName(), "show");
    454         return "show".equalsIgnoreCase(current);
     458        return Boolean.parseBoolean(GPXSettingsPanel.getLayerPref(fromLayer, "markers.show-text"));
    455459    }
    456460
     461    @Override
     462    public boolean hasColor() {
     463        return true;
     464    }
     465
     466    @Override
     467    public Color getColor() {
     468        return color;
     469    }
     470
     471    @Override
     472    public void setColor(Color color) {
     473        setPrivateColors(color);
     474        if (fromLayer != null) {
     475            String cs = null;
     476            if (color != null) {
     477                cs = String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue());
     478            }
     479            GPXSettingsPanel.putLayerPrefLocal(fromLayer, "markers.color", cs);
     480        }
     481        invalidate();
     482    }
     483
     484    private void setPrivateColors(Color color) {
     485        this.color = color;
     486        this.realcolor = Optional.ofNullable(color).orElse(DEFAULT_COLOR_PROPERTY.get());
     487    }
     488
    457489    private final class MarkerMouseAdapter extends MouseAdapter {
    458490        @Override
    459491        public void mousePressed(MouseEvent e) {
     
    503535
    504536        @Override
    505537        public void actionPerformed(ActionEvent e) {
    506             Config.getPref().put("marker.show "+layer.getName(), layer.isTextOrIconShown() ? "hide" : "show");
     538            GPXSettingsPanel.putLayerPrefLocal(layer.fromLayer, "markers.show-text", Boolean.toString(!layer.isTextOrIconShown()));
    507539            layer.invalidate();
    508540        }
    509541
  • src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java

     
    1818import org.openstreetmap.josm.data.coor.EastNorth;
    1919import org.openstreetmap.josm.data.coor.LatLon;
    2020import org.openstreetmap.josm.data.gpx.GpxTrack;
    21 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     21import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2222import org.openstreetmap.josm.data.gpx.WayPoint;
    2323import org.openstreetmap.josm.data.projection.ProjectionRegistry;
    2424import org.openstreetmap.josm.gui.MainApplication;
     
    327327        WayPoint w2 = null;
    328328
    329329        for (GpxTrack track : trackLayer.data.getTracks()) {
    330             for (GpxTrackSegment trackseg : track.getSegments()) {
     330            for (IGpxTrackSegment trackseg : track.getSegments()) {
    331331                for (WayPoint w: trackseg.getWayPoints()) {
    332332                    if (audioTime < w.getTime()) {
    333333                        w2 = w;
  • src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java

     
    100100         */
    101101        public String getDisplay() {
    102102            switch (info.getCategory()) {
    103                 case NamedColorProperty.COLOR_CATEGORY_LAYER:
    104                     String v = null;
    105                     if (info.getSource() != null) {
    106                         v = info.getSource();
    107                     }
    108                     if (!info.getName().isEmpty()) {
    109                         if (v == null) {
    110                             v = tr(I18n.escape(info.getName()));
    111                         } else {
    112                             v += " - " + tr(I18n.escape(info.getName()));
    113                         }
    114                     }
    115                     return tr("Layer: {0}", v);
    116103                case NamedColorProperty.COLOR_CATEGORY_MAPPAINT:
    117104                    if (info.getSource() != null)
    118105                        return tr("Paint style {0}: {1}", tr(I18n.escape(info.getSource())), tr(info.getName()));
     
    251238        switch (category) {
    252239            case NamedColorProperty.COLOR_CATEGORY_GENERAL: return 1;
    253240            case NamedColorProperty.COLOR_CATEGORY_MAPPAINT: return 2;
    254             case NamedColorProperty.COLOR_CATEGORY_LAYER: return 3;
    255             default: return 4;
     241            default: return 3;
    256242        }
    257243    }
    258244
     
    379365        gui.getDisplayPreference().addSubTab(this, tr("Colors"), panel);
    380366    }
    381367
     368    @SuppressWarnings("PMD.UnusedFormalParameter")
    382369    private static boolean isRemoveColor(ColorEntry ce) {
    383         return NamedColorProperty.COLOR_CATEGORY_LAYER.equals(ce.info.getCategory());
     370        return false;
     371        //COLOR_CATEGORY_LAYER is no longer supported and was the only one that could be removed.
     372        //Maybe this is useful for other categories in the future.
     373        //return NamedColorProperty.COLOR_CATEGORY_LAYER.equals(ce.info.getCategory());
    384374    }
    385375
    386376    /**
     
    390380        PaintColors.values();
    391381        ConflictColors.getColors();
    392382        Severity.getColors();
    393         MarkerLayer.getGenericColor();
    394         GpxDrawHelper.getGenericColor();
     383        MarkerLayer.DEFAULT_COLOR_PROPERTY.get();
     384        GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get();
    395385        OsmDataLayer.getOutsideColor();
    396386        MapScaler.getColor();
    397387        MapStatus.getColors();
  • src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java

     
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55import static org.openstreetmap.josm.tools.I18n.trc;
    66
    7 import java.awt.Color;
    87import java.awt.Component;
    98import java.awt.Dimension;
    109import java.awt.GridBagLayout;
    1110import java.awt.event.ActionListener;
     11import java.util.Collections;
    1212import java.util.Enumeration;
     13import java.util.HashMap;
     14import java.util.List;
     15import java.util.Map;
     16import java.util.Optional;
    1317
    1418import javax.swing.AbstractButton;
    1519import javax.swing.BorderFactory;
     
    2226import javax.swing.JRadioButton;
    2327import javax.swing.JSlider;
    2428
     29import org.apache.commons.jcs.access.exception.InvalidArgumentException;
    2530import org.openstreetmap.josm.actions.ExpertToggleAction;
    26 import org.openstreetmap.josm.data.PreferencesUtils;
    27 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
     31import org.openstreetmap.josm.data.gpx.GpxData;
    2832import org.openstreetmap.josm.gui.MainApplication;
     33import org.openstreetmap.josm.gui.layer.GpxLayer;
    2934import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
    3035import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
    31 import org.openstreetmap.josm.gui.layer.markerlayer.Marker.TemplateEntryProperty;
    3236import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener;
    3337import org.openstreetmap.josm.gui.widgets.JosmComboBox;
    3438import org.openstreetmap.josm.gui.widgets.JosmTextField;
     
    6771    private final JRadioButton colorTypeQuality = new JRadioButton(tr("Quality (RTKLib only, if available)"));
    6872    private final JRadioButton colorTypeTime = new JRadioButton(tr("Track date"));
    6973    private final JRadioButton colorTypeHeatMap = new JRadioButton(tr("Heat Map (dark = few, bright = many)"));
    70     private final JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized for named layers)"));
     74    private final JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized in the layer manager)"));
    7175    private final JRadioButton colorTypeGlobal = new JRadioButton(tr("Use global settings"));
    7276    private final JosmComboBox<String> colorTypeVelocityTune = new JosmComboBox<>(new String[] {tr("Car"), tr("Bicycle"), tr("Foot")});
    7377    private final JosmComboBox<String> colorTypeHeatMapTune = new JosmComboBox<>(new String[] {
     
    9397    private final JCheckBox useGpsAntialiasing = new JCheckBox(tr("Smooth GPX graphics (antialiasing)"));
    9498    private final JCheckBox drawLineWithAlpha = new JCheckBox(tr("Draw with Opacity (alpha blending) "));
    9599
    96     private String layerName;
    97     private final boolean local; // flag to display LocalOnly checkbox
    98     private final boolean nonlocal; // flag to display AllLines checkbox
     100    private final List<GpxLayer> layers;
     101    private final GpxLayer firstLayer;
     102    private final boolean global; // global settings vs. layer specific settings
     103    private final boolean hasLocalFile; // flag to display LocalOnly checkbooks
     104    private final boolean hasNonLocalFile; // flag to display AllLines checkbox
    99105
     106    private final static Map<String, Object> DEFAULT_PREFS = getDefaultPrefs();
     107
     108    private static Map<String, Object> getDefaultPrefs() {
     109        HashMap<String, Object> m = new HashMap<>();
     110        m.put("colormode", -1);
     111        m.put("colormode.dynamic-range", false);
     112        m.put("colormode.heatmap.colormap", 0);
     113        m.put("colormode.heatmap.gain", 0);
     114        m.put("colormode.heatmap.line-extra", false); //Einstein only
     115        m.put("colormode.heatmap.lower-limit", 0);
     116        m.put("colormode.heatmap.use-points", false);
     117        m.put("colormode.time.min-distance", 60); //Einstein only
     118        m.put("colormode.velocity.tune", 45);
     119        m.put("lines", -1);
     120        m.put("lines.alpha-blend", false);
     121        m.put("lines.arrows", false);
     122        m.put("lines.arrows.fast", false);
     123        m.put("lines.arrows.min-distance", 40);
     124        m.put("lines.force", false);
     125        m.put("lines.max-length", 200);
     126        m.put("lines.max-length.local", -1);
     127        m.put("lines.width", 0);
     128        m.put("markers.color", "");
     129        m.put("markers.show-text", true);
     130        m.put("markers.pattern", Marker.LABEL_PATTERN_AUTO);
     131        m.put("markers.audio.pattern", "?{ '{name}' | '{desc}' | '{" + Marker.MARKER_FORMATTED_OFFSET + "}' }");
     132        m.put("points.hdopcircle", false);
     133        m.put("points.large", false);
     134        m.put("points.large.alpha", -1); //Einstein only
     135        m.put("points.large.size", 3); //Einstein only
     136        return Collections.unmodifiableMap(m);
     137    }
     138
    100139    /**
    101      * Constructs a new {@code GPXSettingsPanel} for a given layer name.
    102      * @param layerName The GPX layer name
    103      * @param local flag to display LocalOnly checkbox
    104      * @param nonlocal flag to display AllLines checkbox
     140     * Constructs a new {@code GPXSettingsPanel} for the given layers.
     141     * @param layers the GPX layers
    105142     */
    106     public GPXSettingsPanel(String layerName, boolean local, boolean nonlocal) {
     143    public GPXSettingsPanel(List<GpxLayer> layers) {
    107144        super(new GridBagLayout());
    108         this.local = local;
    109         this.nonlocal = nonlocal;
    110         this.layerName = "layer "+layerName;
     145        this.layers = layers;
     146        if (layers == null || layers.isEmpty()) {
     147            throw new InvalidArgumentException("At least one layer required");
     148        }
     149        firstLayer = layers.get(0);
     150        global = false;
     151        hasLocalFile = layers.stream().anyMatch(GpxLayer::isLocalFile);
     152        hasNonLocalFile = layers.stream().anyMatch(l -> !l.isLocalFile());
    111153        initComponents();
    112154        loadPreferences();
    113155    }
     
    117159     */
    118160    public GPXSettingsPanel() {
    119161        super(new GridBagLayout());
     162        layers = null;
     163        firstLayer = null;
     164        global = hasLocalFile = hasNonLocalFile = true;
    120165        initComponents();
    121         local = false;
    122         nonlocal = false;
    123166        loadPreferences(); // preferences -> controls
    124167    }
    125168
     169    /**
     170     * Reads the preference for the given layer or the default preference if not available
     171     * @param layer the GpxLayer. Can be <code>null</code>, default preference will be returned then
     172     * @param key the drawing key to be read, without "draw.rawgps."
     173     * @return the value
     174     */
     175    public static String getLayerPref(GpxLayer layer, String key) {
     176        Object d = DEFAULT_PREFS.get(key);
     177        String ds;
     178        if (d != null) {
     179            ds = d.toString();
     180        } else {
     181            Logging.warn("No default value found for layer preference \"" + key + "\".");
     182            ds = null;
     183        }
     184        return Optional.ofNullable(tryGetLayerPrefLocal(layer, key)).orElse(Config.getPref().get("draw.rawgps." + key, ds));
     185    }
     186
     187    /**
     188     * Reads the integer preference for the given layer or the default preference if not available
     189     * @param layer the GpxLayer. Can be <code>null</code>, default preference will be returned then
     190     * @param key the drawing key to be read, without "draw.rawgps."
     191     * @return the integer value
     192     */
     193    public static int getLayerPrefInt(GpxLayer layer, String key) {
     194        String s = getLayerPref(layer, key);
     195        if (s != null) {
     196            try {
     197                return Integer.parseInt(s);
     198            } catch (NumberFormatException ex) {
     199                Object d = DEFAULT_PREFS.get(key);
     200                if (d instanceof Integer) {
     201                    return (int) d;
     202                } else {
     203                    Logging.warn("No valid default value found for layer preference \"" + key + "\".");
     204                }
     205            }
     206        }
     207        return 0;
     208    }
     209
     210    /**
     211     * Try to read the preference for the given layer
     212     * @param layer the GpxLayer
     213     * @param key the drawing key to be read, without "draw.rawgps."
     214     * @return the value or <code>null</code> if not found
     215     */
     216    public static String tryGetLayerPrefLocal(GpxLayer layer, String key) {
     217        return layer != null ? tryGetLayerPrefLocal(layer.data, key) : null;
     218    }
     219
     220    /**
     221     * Try to read the preference for the given GpxData
     222     * @param data the GpxData
     223     * @param key the drawing key to be read, without "draw.rawgps."
     224     * @return the value or <code>null</code> if not found
     225     */
     226    public static String tryGetLayerPrefLocal(GpxData data, String key) {
     227        return data != null ? data.getLayerPrefs().get(key) : null;
     228    }
     229
     230    /**
     231     * Puts the preference for the given layers or the default preference if layers is <code>null</code>
     232     * @param layers List of <code>GpxLayer</code> to put the drawingOptions
     233     * @param key the drawing key to be written, without "draw.rawgps."
     234     * @param value (can be <code>null</code> to remove option)
     235     */
     236    public static void putLayerPref(List<GpxLayer> layers, String key, Object value) {
     237        String v = value == null ? null : value.toString();
     238        if (layers != null) {
     239            for (GpxLayer l : layers) {
     240                putLayerPrefLocal(l.data, key, v);
     241            }
     242        } else {
     243            Config.getPref().put("draw.rawgps." + key, v);
     244        }
     245    }
     246
     247    /**
     248     * Puts the preference for the given layer
     249     * @param layer <code>GpxLayer</code> to put the drawingOptions
     250     * @param key the drawing key to be written, without "draw.rawgps."
     251     * @param value the value or <code>null</code> to remove key
     252     */
     253    public static void putLayerPrefLocal(GpxLayer layer, String key, String value) {
     254        if (layer == null) return;
     255        putLayerPrefLocal(layer.data, key, value);
     256    }
     257
     258    /**
     259     * Puts the preference for the given layer
     260     * @param data <code>GpxData</code> to put the drawingOptions. Must not be <code>null</code>
     261     * @param key the drawing key to be written, without "draw.rawgps."
     262     * @param value the value or <code>null</code> to remove key
     263     */
     264    public static void putLayerPrefLocal(GpxData data, String key, String value) {
     265        if (value == null || value.trim().isEmpty() || (getLayerPref(null, key).equals(value) && DEFAULT_PREFS.get(key) != null && DEFAULT_PREFS.get(key).toString().equals(value))) {
     266            data.getLayerPrefs().remove(key);
     267        } else {
     268            data.getLayerPrefs().put(key, value);
     269        }
     270    }
     271
     272    private String pref(String key) {
     273        return getLayerPref(firstLayer, key);
     274    }
     275
     276    private boolean prefBool(String key) {
     277        return Boolean.parseBoolean(pref(key));
     278    }
     279
     280    private int prefInt(String key) {
     281        return getLayerPrefInt(firstLayer, key);
     282    }
     283
     284    private int prefIntLocal(String key) {
     285        try {
     286            return Integer.parseInt(tryGetLayerPrefLocal(firstLayer, key));
     287        } catch (NumberFormatException ex) {
     288            return -1;
     289        }
     290
     291    }
     292
     293    private void putPref(String key, Object value) {
     294        putLayerPref(layers, key, value);
     295    }
     296
    126297    // CHECKSTYLE.OFF: ExecutableStatementCountCheck
    127298    private void initComponents() {
    128299        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    129300
    130         // makeAutoMarkers
    131         makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer."));
    132         ExpertToggleAction.addVisibilitySwitcher(makeAutoMarkers);
    133         add(makeAutoMarkers, GBC.eol().insets(20, 0, 0, 5));
     301        if (global) {
     302            // makeAutoMarkers
     303            makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer."));
     304            ExpertToggleAction.addVisibilitySwitcher(makeAutoMarkers);
     305            add(makeAutoMarkers, GBC.eol().insets(20, 0, 0, 5));
     306        }
    134307
    135308        // drawRawGpsLines
    136309        ButtonGroup gpsLinesGroup = new ButtonGroup();
    137         if (layerName != null) {
     310        if (!global) {
    138311            gpsLinesGroup.add(drawRawGpsLinesGlobal);
    139312        }
    140313        gpsLinesGroup.add(drawRawGpsLinesNone);
     
    145318
    146319        JLabel label = new JLabel(tr("Draw lines between raw GPS points"));
    147320        add(label, GBC.eol().insets(20, 0, 0, 0));
    148         if (layerName != null) {
     321        if (!global) {
    149322            add(drawRawGpsLinesGlobal, GBC.eol().insets(40, 0, 0, 0));
    150323        }
    151324        add(drawRawGpsLinesNone, GBC.eol().insets(40, 0, 0, 0));
    152         if (layerName == null || local) {
     325        if (hasLocalFile) {
    153326            add(drawRawGpsLinesLocal, GBC.eol().insets(40, 0, 0, 0));
    154327        }
    155         if (layerName == null || nonlocal) {
     328        if (hasNonLocalFile) {
    156329            add(drawRawGpsLinesAll, GBC.eol().insets(40, 0, 0, 0));
    157330        }
    158331        ExpertToggleAction.addVisibilitySwitcher(label);
     
    242415
    243416        // colorTracks
    244417        ButtonGroup colorGroup = new ButtonGroup();
    245         if (layerName != null) {
     418        if (!global) {
    246419            colorGroup.add(colorTypeGlobal);
    247420        }
    248421        colorGroup.add(colorTypeNone);
     
    253426        colorGroup.add(colorTypeTime);
    254427        colorGroup.add(colorTypeHeatMap);
    255428
    256         colorTypeNone.setToolTipText(tr("All points and track segments will have the same color. Can be customized in Layer Manager."));
     429        colorTypeNone.setToolTipText(tr("All points and track segments will have their own color. Can be customized in Layer Manager."));
    257430        colorTypeVelocity.setToolTipText(tr("Colors points and track segments by velocity."));
    258431        colorTypeDirection.setToolTipText(tr("Colors points and track segments by direction."));
    259432        colorTypeDilution.setToolTipText(
     
    272445        add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0));
    273446
    274447        add(new JLabel(tr("Track and Point Coloring")), GBC.eol().insets(20, 0, 0, 0));
    275         if (layerName != null) {
     448        if (!global) {
    276449            add(colorTypeGlobal, GBC.eol().insets(40, 0, 0, 0));
    277450        }
    278451        add(colorTypeNone, GBC.eol().insets(40, 0, 0, 0));
     
    331504            if (null != dim) {
    332505                // get image size of environment
    333506                final int iconSize = (int) dim.getHeight();
    334                 final Color color;
    335                 // ask the GPX draw for the correct color of that layer ( if there is one )
    336                 if (null != layerName) {
    337                     color = GpxDrawHelper.DEFAULT_COLOR.getChildColor(
    338                             NamedColorProperty.COLOR_CATEGORY_LAYER, layerName, GpxDrawHelper.DEFAULT_COLOR.getName()).get();
    339                 } else {
    340                     color = GpxDrawHelper.DEFAULT_COLOR.getDefaultValue();
    341                 }
    342                 colorTypeHeatIconLabel.setIcon(GpxDrawHelper.getColorMapImageIcon(color, colorTypeHeatMapTune.getSelectedIndex(), iconSize));
     507                colorTypeHeatIconLabel.setIcon(GpxDrawHelper.getColorMapImageIcon(
     508                        GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get(),
     509                        colorTypeHeatMapTune.getSelectedIndex(),
     510                        iconSize));
    343511            }
    344512        });
    345513
     
    353521        add(colorDynamic, GBC.eop().insets(40, 0, 0, 0));
    354522        ExpertToggleAction.addVisibilitySwitcher(colorDynamic);
    355523
    356         if (layerName == null) {
     524        if (global) {
    357525            // Setting waypoints for gpx layer doesn't make sense - waypoints are shown in marker layer that has different name - so show
    358526            // this only for global config
    359527
     
    363531            label.setLabelFor(waypointLabel);
    364532            add(waypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
    365533            waypointLabel.addActionListener(e -> updateWaypointPattern(waypointLabel, waypointLabelPattern));
    366             updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, TemplateEntryProperty.forMarker(layerName));
    367534            add(waypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5));
    368535            ExpertToggleAction.addVisibilitySwitcher(label);
    369536            ExpertToggleAction.addVisibilitySwitcher(waypointLabel);
     
    379546            label.setLabelFor(audioWaypointLabel);
    380547            add(audioWaypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
    381548            audioWaypointLabel.addActionListener(e -> updateWaypointPattern(audioWaypointLabel, audioWaypointLabelPattern));
    382             updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, TemplateEntryProperty.forAudioMarker(layerName));
    383549            add(audioWaypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5));
    384550            ExpertToggleAction.addVisibilitySwitcher(label);
    385551            ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabel);
     
    395561     */
    396562    public final void loadPreferences() {
    397563        makeAutoMarkers.setSelected(Config.getPref().getBoolean("marker.makeautomarkers", true));
    398         if (layerName != null && Config.getPref().get("draw.rawgps.lines."+layerName).isEmpty()
    399                 && Config.getPref().get("draw.rawgps.lines.local."+layerName).isEmpty()) {
    400             // no line preferences for layer is found
     564        int lines = global ? prefInt("lines") : prefIntLocal("lines");
     565        if (lines == 2 && hasNonLocalFile) {
     566            drawRawGpsLinesAll.setSelected(true);
     567        } else if ((lines == 1 && hasLocalFile) || (lines == -1 && global)) {
     568            drawRawGpsLinesLocal.setSelected(true);
     569        } else if (lines == 0) {
     570            drawRawGpsLinesNone.setSelected(true);
     571        } else if (lines == -1) {
    401572            drawRawGpsLinesGlobal.setSelected(true);
    402573        } else {
    403             Boolean lf = PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines.local", layerName, true);
    404             if (PreferencesUtils.getBoolean(Config.getPref(), "draw.rawgps.lines", layerName, true)) {
    405                 drawRawGpsLinesAll.setSelected(true);
    406             } else if (lf) {
    407                 drawRawGpsLinesLocal.setSelected(true);
    408             } else {
    409                 drawRawGpsLinesNone.setSelected(true);
    410             }
     574            Logging.warn("Unknown line type: " + lines);
    411575        }
    412 
    413         drawRawGpsMaxLineLengthLocal.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(),
    414                 "draw.rawgps.max-line-length.local", layerName, -1)));
    415         drawRawGpsMaxLineLength.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(),
    416                 "draw.rawgps.max-line-length", layerName, 200)));
    417         drawLineWidth.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(),
    418                 "draw.rawgps.linewidth", layerName, 0)));
    419         drawLineWithAlpha.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    420                 "draw.rawgps.lines.alpha-blend", layerName, false));
    421         forceRawGpsLines.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    422                 "draw.rawgps.lines.force", layerName, false));
    423         drawGpsArrows.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    424                 "draw.rawgps.direction", layerName, false));
    425         drawGpsArrowsFast.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    426                 "draw.rawgps.alternatedirection", layerName, false));
    427         drawGpsArrowsMinDist.setText(Integer.toString(PreferencesUtils.getInteger(Config.getPref(),
    428                 "draw.rawgps.min-arrow-distance", layerName, 40)));
    429         hdopCircleGpsPoints.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    430                 "draw.rawgps.hdopcircle", layerName, false));
    431         largeGpsPoints.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    432                 "draw.rawgps.large", layerName, false));
     576        drawRawGpsMaxLineLengthLocal.setText(pref("lines.max-length.local"));
     577        drawRawGpsMaxLineLength.setText(pref("lines.max-length"));
     578        drawLineWidth.setText(pref("lines.width"));
     579        drawLineWithAlpha.setSelected(prefBool("lines.alpha-blend"));
     580        forceRawGpsLines.setSelected(prefBool("lines.force"));
     581        drawGpsArrows.setSelected(prefBool("lines.arrows"));
     582        drawGpsArrowsFast.setSelected(prefBool("lines.arrows.fast"));
     583        drawGpsArrowsMinDist.setText(pref("lines.arrows.min-distance"));
     584        hdopCircleGpsPoints.setSelected(prefBool("points.hdopcircle"));
     585        largeGpsPoints.setSelected(prefBool("points.large"));
    433586        useGpsAntialiasing.setSelected(Config.getPref().getBoolean("mappaint.gpx.use-antialiasing", false));
    434587
    435588        drawRawGpsLinesActionListener.actionPerformed(null);
    436 
    437         if (layerName != null && Config.getPref().get("draw.rawgps.colors."+layerName).isEmpty()) {
     589        if (!global && prefIntLocal("colormode") == -1) {
    438590            colorTypeGlobal.setSelected(true);
    439591            colorDynamic.setSelected(false);
    440592            colorDynamic.setEnabled(false);
     
    442594            colorTypeHeatMapGain.setValue(0);
    443595            colorTypeHeatMapLowerLimit.setValue(0);
    444596        } else {
    445             int colorType = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colors", layerName, 0);
     597            int colorType = prefInt("colormode");
    446598            switch (colorType) {
    447             case 0: colorTypeNone.setSelected(true); break;
     599            case -1: case 0: colorTypeNone.setSelected(true); break;
    448600            case 1: colorTypeVelocity.setSelected(true); break;
    449601            case 2: colorTypeDilution.setSelected(true); break;
    450602            case 3: colorTypeDirection.setSelected(true); break;
     
    453605            case 6: colorTypeQuality.setSelected(true); break;
    454606            default: Logging.warn("Unknown color type: " + colorType);
    455607            }
    456             int ccts = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colorTracksTune", layerName, 45);
     608            int ccts = prefInt("colormode.velocity.tune");
    457609            colorTypeVelocityTune.setSelectedIndex(ccts == 10 ? 2 : (ccts == 20 ? 1 : 0));
    458             colorTypeHeatMapTune.setSelectedIndex(PreferencesUtils.getInteger(Config.getPref(),
    459                     "draw.rawgps.heatmap.colormap", layerName, 0));
    460             colorDynamic.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    461                     "draw.rawgps.colors.dynamic", layerName, false));
    462             colorTypeHeatMapPoints.setSelected(PreferencesUtils.getBoolean(Config.getPref(),
    463                     "draw.rawgps.heatmap.use-points", layerName, false));
    464             colorTypeHeatMapGain.setValue(PreferencesUtils.getInteger(Config.getPref(),
    465                     "draw.rawgps.heatmap.gain", layerName, 0));
    466             colorTypeHeatMapLowerLimit.setValue(PreferencesUtils.getInteger(Config.getPref(),
    467                     "draw.rawgps.heatmap.lower-limit", layerName, 0));
     610            colorTypeHeatMapTune.setSelectedIndex(prefInt("colormode.heatmap.colormap"));
     611            colorDynamic.setSelected(prefBool("colormode.dynamic-range"));
     612            colorTypeHeatMapPoints.setSelected(prefBool("colormode.heatmap.use-points"));
     613            colorTypeHeatMapGain.setValue(prefInt("colormode.heatmap.gain"));
     614            colorTypeHeatMapLowerLimit.setValue(prefInt("colormode.heatmap.lower-limit"));
    468615        }
     616        updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, pref("markers.pattern"));
     617        updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, pref("markers.audio.pattern"));
     618
    469619    }
    470620
    471621    /**
    472      * Save preferences from UI controls, globally or for a specified layer.
    473      * @param layerName The GPX layer name. Can be {@code null}, in that case, global preferences are written
    474      * @param locLayer {@code true} if the GPX layer is a local one. Ignored if {@code layerName} is null
     622     * Save preferences from UI controls, globally or for the specified layers.
    475623     * @return {@code true} when restart is required, {@code false} otherwise
    476624     */
    477     public boolean savePreferences(String layerName, boolean locLayer) {
    478         String layerNameDot = ".layer "+layerName;
    479         if (layerName == null) {
    480             layerNameDot = "";
     625    public boolean savePreferences() {
     626        if (global) {
     627            Config.getPref().putBoolean("marker.makeautomarkers", makeAutoMarkers.isSelected());
     628            putPref("markers.pattern", waypointLabelPattern.getText());
     629            putPref("markers.audio.pattern", audioWaypointLabelPattern.getText());
    481630        }
    482         Config.getPref().putBoolean("marker.makeautomarkers"+layerNameDot, makeAutoMarkers.isSelected());
    483         if (drawRawGpsLinesGlobal.isSelected()) {
    484             Config.getPref().put("draw.rawgps.lines" + layerNameDot, null);
    485             Config.getPref().put("draw.rawgps.max-line-length" + layerNameDot, null);
    486             Config.getPref().put("draw.rawgps.lines.local" + layerNameDot, null);
    487             Config.getPref().put("draw.rawgps.max-line-length.local" + layerNameDot, null);
    488             Config.getPref().put("draw.rawgps.lines.force"+layerNameDot, null);
    489             Config.getPref().put("draw.rawgps.direction"+layerNameDot, null);
    490             Config.getPref().put("draw.rawgps.alternatedirection"+layerNameDot, null);
    491             Config.getPref().put("draw.rawgps.min-arrow-distance"+layerNameDot, null);
     631        boolean g;
     632        if (!global && ((g = drawRawGpsLinesGlobal.isSelected()) || drawRawGpsLinesNone.isSelected())) {
     633            if (g) {
     634                putPref("lines", null);
     635            } else {
     636                putPref("lines", 0);
     637            }
     638            putPref("lines.max-length", null);
     639            putPref("lines.max-length.local", null);
     640            putPref("lines.force", null);
     641            putPref("lines.arrows", null);
     642            putPref("lines.arrows.fast", null);
     643            putPref("lines.arrows.min-distance", null);
    492644        } else {
    493             if (layerName == null || !locLayer) {
    494                 Config.getPref().putBoolean("draw.rawgps.lines" + layerNameDot, drawRawGpsLinesAll.isSelected());
    495                 Config.getPref().put("draw.rawgps.max-line-length" + layerNameDot, drawRawGpsMaxLineLength.getText());
     645            if (drawRawGpsLinesLocal.isSelected()) {
     646                putPref("lines", 1);
     647            } else if (drawRawGpsLinesAll.isSelected()) {
     648                putPref("lines", 2);
    496649            }
    497             if (layerName == null || locLayer) {
    498                 Config.getPref().putBoolean("draw.rawgps.lines.local" + layerNameDot,
    499                         drawRawGpsLinesAll.isSelected() || drawRawGpsLinesLocal.isSelected());
    500                 Config.getPref().put("draw.rawgps.max-line-length.local" + layerNameDot,
    501                         drawRawGpsMaxLineLengthLocal.getText());
    502             }
    503             Config.getPref().putBoolean("draw.rawgps.lines.force"+layerNameDot, forceRawGpsLines.isSelected());
    504             Config.getPref().putBoolean("draw.rawgps.direction"+layerNameDot, drawGpsArrows.isSelected());
    505             Config.getPref().putBoolean("draw.rawgps.alternatedirection"+layerNameDot, drawGpsArrowsFast.isSelected());
    506             Config.getPref().put("draw.rawgps.min-arrow-distance"+layerNameDot, drawGpsArrowsMinDist.getText());
     650            putPref("lines.max-length", drawRawGpsMaxLineLength.getText());
     651            putPref("lines.max-length.local", drawRawGpsMaxLineLengthLocal.getText());
     652            putPref("lines.force", forceRawGpsLines.isSelected());
     653            putPref("lines.arrows", drawGpsArrows.isSelected());
     654            putPref("lines.arrows.fast", drawGpsArrowsFast.isSelected());
     655            putPref("lines.arrows.min-distance", drawGpsArrowsMinDist.getText());
    507656        }
    508657
    509         Config.getPref().putBoolean("draw.rawgps.hdopcircle"+layerNameDot, hdopCircleGpsPoints.isSelected());
    510         Config.getPref().putBoolean("draw.rawgps.large"+layerNameDot, largeGpsPoints.isSelected());
    511         Config.getPref().put("draw.rawgps.linewidth"+layerNameDot, drawLineWidth.getText());
    512         Config.getPref().putBoolean("draw.rawgps.lines.alpha-blend"+layerNameDot, drawLineWithAlpha.isSelected());
     658        putPref("points.hdopcircle", hdopCircleGpsPoints.isSelected());
     659        putPref("points.large", largeGpsPoints.isSelected());
     660        putPref("lines.width", drawLineWidth.getText());
     661        putPref("lines.alpha-blend", drawLineWithAlpha.isSelected());
    513662
    514663        Config.getPref().putBoolean("mappaint.gpx.use-antialiasing", useGpsAntialiasing.isSelected());
    515664
    516         TemplateEntryProperty.forMarker(layerName).put(waypointLabelPattern.getText());
    517         TemplateEntryProperty.forAudioMarker(layerName).put(audioWaypointLabelPattern.getText());
    518 
    519665        if (colorTypeGlobal.isSelected()) {
    520             Config.getPref().put("draw.rawgps.colors"+layerNameDot, null);
    521             Config.getPref().put("draw.rawgps.colors.dynamic"+layerNameDot, null);
    522             Config.getPref().put("draw.rawgps.colorTracksTunec"+layerNameDot, null);
     666            putPref("colormode", null);
     667            putPref("colormode.dynamic-range", null);
     668            putPref("colormode.velocity.tune", null);
    523669            return false;
    524670        } else if (colorTypeVelocity.isSelected()) {
    525             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 1);
     671            putPref("colormode", 1);
    526672        } else if (colorTypeDilution.isSelected()) {
    527             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 2);
     673            putPref("colormode", 2);
    528674        } else if (colorTypeDirection.isSelected()) {
    529             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 3);
     675            putPref("colormode", 3);
    530676        } else if (colorTypeTime.isSelected()) {
    531             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 4);
     677            putPref("colormode", 4);
    532678        } else if (colorTypeHeatMap.isSelected()) {
    533             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 5);
     679            putPref("colormode", 5);
    534680        } else if (colorTypeQuality.isSelected()) {
    535             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 6);
     681            putPref("colormode", 6);
    536682        } else {
    537             Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 0);
     683            putPref("colormode", 0);
    538684        }
    539         Config.getPref().putBoolean("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected());
     685        putPref("colormode.dynamic-range", colorDynamic.isSelected());
    540686        int ccti = colorTypeVelocityTune.getSelectedIndex();
    541         Config.getPref().putInt("draw.rawgps.colorTracksTune"+layerNameDot, ccti == 2 ? 10 : (ccti == 1 ? 20 : 45));
    542         Config.getPref().putInt("draw.rawgps.heatmap.colormap"+layerNameDot, colorTypeHeatMapTune.getSelectedIndex());
    543         Config.getPref().putBoolean("draw.rawgps.heatmap.use-points"+layerNameDot, colorTypeHeatMapPoints.isSelected());
    544         Config.getPref().putInt("draw.rawgps.heatmap.gain"+layerNameDot, colorTypeHeatMapGain.getValue());
    545         Config.getPref().putInt("draw.rawgps.heatmap.lower-limit"+layerNameDot, colorTypeHeatMapLowerLimit.getValue());
     687        putPref("colormode.velocity.tune", ccti == 2 ? 10 : (ccti == 1 ? 20 : 45));
     688        putPref("colormode.heatmap.colormap", colorTypeHeatMapTune.getSelectedIndex());
     689        putPref("colormode.heatmap.use-points", colorTypeHeatMapPoints.isSelected());
     690        putPref("colormode.heatmap.gain", colorTypeHeatMapGain.getValue());
     691        putPref("colormode.heatmap.lower-limit", colorTypeHeatMapLowerLimit.getValue());
    546692
     693        if (!global && layers != null && !layers.isEmpty()) {
     694            layers.forEach(l -> l.data.invalidate());
     695        }
     696
    547697        return false;
    548698    }
    549699
    550     /**
    551      * Save preferences from UI controls for initial layer or globally
    552      * @return {@code true} when restart is required, {@code false} otherwise
    553      */
    554     public boolean savePreferences() {
    555         return savePreferences(null, false);
    556     }
    557 
    558     private static void updateWaypointLabelCombobox(JosmComboBox<String> cb, JosmTextField tf, TemplateEntryProperty property) {
    559         String labelPattern = property.getAsString();
     700    private static void updateWaypointLabelCombobox(JosmComboBox<String> cb, JosmTextField tf, String labelPattern) {
    560701        boolean found = false;
    561702        for (int i = 0; i < LABEL_PATTERN_TEMPLATE.length; i++) {
    562703            if (LABEL_PATTERN_TEMPLATE[i].equals(labelPattern)) {
  • src/org/openstreetmap/josm/io/GpxReader.java

     
    1818
    1919import org.openstreetmap.josm.data.Bounds;
    2020import org.openstreetmap.josm.data.coor.LatLon;
    21 import org.openstreetmap.josm.data.gpx.Extensions;
    2221import org.openstreetmap.josm.data.gpx.GpxConstants;
    2322import org.openstreetmap.josm.data.gpx.GpxData;
     23import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace;
     24import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
    2425import org.openstreetmap.josm.data.gpx.GpxLink;
    2526import org.openstreetmap.josm.data.gpx.GpxRoute;
    26 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
     27import org.openstreetmap.josm.data.gpx.GpxTrack;
     28import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     29import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2730import org.openstreetmap.josm.data.gpx.WayPoint;
    2831import org.openstreetmap.josm.tools.Logging;
    2932import org.openstreetmap.josm.tools.UncheckedParseException;
     
    6770    private class Parser extends DefaultHandler {
    6871
    6972        private GpxData data;
    70         private Collection<Collection<WayPoint>> currentTrack;
     73        private Collection<IGpxTrackSegment> currentTrack;
    7174        private Map<String, Object> currentTrackAttr;
    7275        private Collection<WayPoint> currentTrackSeg;
    7376        private GpxRoute currentRoute;
     
    7679        private State currentState = State.INIT;
    7780
    7881        private GpxLink currentLink;
    79         private Extensions currentExtensions;
     82        private GpxExtensionCollection currentExtensionCollection;
     83        private GpxExtensionCollection currentTrackExtensionCollection;
    8084        private Stack<State> states;
    8185        private final Stack<String> elements = new Stack<>();
    8286
     
    8892        public void startDocument() {
    8993            accumulator = new StringBuilder();
    9094            states = new Stack<>();
    91             data = new GpxData();
     95            data = new GpxData(true);
     96            currentExtensionCollection = new GpxExtensionCollection();
     97            currentTrackExtensionCollection = new GpxExtensionCollection();
    9298        }
    9399
     100        @Override
     101        public void startPrefixMapping(String prefix, String uri) throws SAXException {
     102            data.getNamespaces().add(new XMLNamespace(prefix, uri));
     103        }
     104
    94105        private double parseCoord(Attributes atts, String key) {
    95106            String val = atts.getValue(key);
    96107            if (val != null) {
     
    133144                    // unknown version, assume 1.1
    134145                    version = "1.1";
    135146                }
     147                String schemaLocation = atts.getValue(GpxConstants.XML_URI_XSD, "schemaLocation");
     148                if (schemaLocation != null) {
     149                    String[] schemaLocations = schemaLocation.split(" ");
     150                    for (int i = 0; i < schemaLocations.length - 1; i += 2) {
     151                        final String schemaURI = schemaLocations[i];
     152                        final String schemaXSD = schemaLocations[i + 1];
     153                        data.getNamespaces().stream().filter(xml -> xml.getURI().equals(schemaURI)).forEach(xml -> {
     154                            xml.setLocation(schemaXSD);
     155                        });
     156                    }
     157                }
    136158                break;
    137159            case GPX:
    138160                switch (localName) {
     
    159181                case "extensions":
    160182                    states.push(currentState);
    161183                    currentState = State.EXT;
    162                     currentExtensions = new Extensions();
    163184                    break;
    164185                case "gpx":
    165186                    if (atts.getValue("creator") != null && atts.getValue("creator").startsWith("Nokia Sports Tracker")) {
     
    178199                case "extensions":
    179200                    states.push(currentState);
    180201                    currentState = State.EXT;
    181                     currentExtensions = new Extensions();
    182202                    break;
    183203                case "copyright":
    184204                    states.push(currentState);
     
    228248                case "extensions":
    229249                    states.push(currentState);
    230250                    currentState = State.EXT;
    231                     currentExtensions = new Extensions();
    232251                    break;
    233252                default: // Do nothing
    234253                }
    235254                break;
    236255            case TRKSEG:
    237                 if ("trkpt".equals(localName)) {
     256                switch (localName) {
     257                case "trkpt":
    238258                    states.push(currentState);
    239259                    currentState = State.WPT;
    240260                    currentWayPoint = new WayPoint(parseLatLon(atts));
     261                    break;
     262                case "extensions":
     263                    states.push(currentState);
     264                    currentState = State.EXT;
     265                    break;
    241266                }
    242267                break;
    243268            case WPT:
     
    250275                case "extensions":
    251276                    states.push(currentState);
    252277                    currentState = State.EXT;
    253                     currentExtensions = new Extensions();
    254278                    break;
    255279                default: // Do nothing
    256280                }
     
    270294                case "extensions":
    271295                    states.push(currentState);
    272296                    currentState = State.EXT;
    273                     currentExtensions = new Extensions();
    274297                    break;
    275298                default: // Do nothing
    276299                }
    277300                break;
     301            case EXT:
     302                if (states.lastElement() == State.TRK) {
     303                    currentTrackExtensionCollection.openChild(namespaceURI, qName, atts);
     304                } else {
     305                    currentExtensionCollection.openChild(namespaceURI, qName, atts);
     306                }
     307                break;
    278308            default: // Do nothing
    279309            }
    280310            accumulator.setLength(0);
     
    349379                    if ((currentState == State.METADATA && "metadata".equals(localName)) ||
    350380                        (currentState == State.GPX && "gpx".equals(localName))) {
    351381                        convertUrlToLink(data.attr);
    352                         if (currentExtensions != null && !currentExtensions.isEmpty()) {
    353                             data.put(META_EXTENSIONS, currentExtensions);
    354                         }
     382                        data.getExtensions().addAll(currentExtensionCollection);
     383                        currentExtensionCollection.clear();
    355384                        currentState = states.pop();
    356385                    }
    357386                    break;
     
    359388                    // do nothing, has been parsed on startElement
    360389                    break;
    361390                default:
    362                     //TODO: parse extensions
    363391                }
    364392                break;
    365393            case AUTHOR:
     
    464492                case "wpt":
    465493                    currentState = states.pop();
    466494                    convertUrlToLink(currentWayPoint.attr);
    467                     if (currentExtensions != null && !currentExtensions.isEmpty()) {
    468                         currentWayPoint.put(META_EXTENSIONS, currentExtensions);
    469                     }
     495                    currentWayPoint.getExtensions().addAll(currentExtensionCollection);
    470496                    data.waypoints.add(currentWayPoint);
     497                    currentExtensionCollection.clear();
    471498                    break;
    472499                default: // Do nothing
    473500                }
     
    475502            case TRKSEG:
    476503                if ("trkseg".equals(localName)) {
    477504                    currentState = states.pop();
    478                     currentTrack.add(currentTrackSeg);
     505                    if (!currentTrackSeg.isEmpty()) {
     506                        GpxTrackSegment seg = new GpxTrackSegment(currentTrackSeg);
     507                        seg.getExtensions().addAll(currentExtensionCollection);
     508                        currentTrack.add(seg);
     509                    }
     510                    currentExtensionCollection.clear();
    479511                }
    480512                break;
    481513            case TRK:
     
    483515                case "trk":
    484516                    currentState = states.pop();
    485517                    convertUrlToLink(currentTrackAttr);
    486                     data.addTrack(new ImmutableGpxTrack(currentTrack, currentTrackAttr));
     518                    GpxTrack trk = new GpxTrack(new ArrayList<>(currentTrack), currentTrackAttr);
     519                    trk.getExtensions().addAll(currentTrackExtensionCollection);
     520                    data.addTrack(trk);
     521                    currentTrackExtensionCollection.clear();
    487522                    break;
    488523                case "name":
    489524                case "cmt":
     
    501536            case EXT:
    502537                if ("extensions".equals(localName)) {
    503538                    currentState = states.pop();
    504                 } else if (JOSM_EXTENSIONS_NAMESPACE_URI.equals(namespaceURI)) {
    505                     // only interested in extensions written by JOSM
    506                     currentExtensions.put(localName, accumulator.toString());
     539                } else if (currentExtensionCollection != null) {
     540                    String acc = accumulator.toString().trim();
     541                    if (states.lastElement() == State.TRK) {
     542                        currentTrackExtensionCollection.closeChild(qName, acc); //a segment inside the track can have an extension too
     543                    } else {
     544                        currentExtensionCollection.closeChild(qName, acc);
     545                    }
    507546                }
    508547                break;
    509548            default:
     
    519558                default: // Do nothing
    520559                }
    521560            }
     561            accumulator.setLength(0);
    522562        }
    523563
    524564        @Override
     
    525565        public void endDocument() throws SAXException {
    526566            if (!states.empty())
    527567                throw new SAXException(tr("Parse error: invalid document structure for GPX document."));
    528             Extensions metaExt = (Extensions) data.get(META_EXTENSIONS);
    529             if (metaExt != null && "true".equals(metaExt.get("from-server"))) {
    530                 data.fromServer = true;
    531             }
     568
     569            data.getExtensions().stream("josm", "from-server").findAny().ifPresent(ext -> {
     570                data.fromServer = "true".equals(ext.getValue());
     571            });
     572
     573            data.getExtensions().stream("josm", "layerPreferences").forEach(prefs -> {
     574                prefs.getExtensions().stream("josm", "entry").forEach(prefEntry -> {
     575                    Object key = prefEntry.get("key");
     576                    Object val = prefEntry.get("value");
     577                    if (key != null && val != null) {
     578                        data.getLayerPrefs().put(key.toString(), val.toString());
     579                    }
     580                });
     581            });
     582            data.endUpdate();
    532583            gpxData = data;
    533584        }
    534585
  • src/org/openstreetmap/josm/io/GpxWriter.java

     
    88import java.io.OutputStreamWriter;
    99import java.io.PrintWriter;
    1010import java.nio.charset.StandardCharsets;
     11import java.util.ArrayList;
    1112import java.util.Collection;
    1213import java.util.Date;
    1314import java.util.List;
    1415import java.util.Map;
    15 import java.util.Map.Entry;
     16import java.util.Objects;
     17import java.util.stream.Collectors;
    1618
    1719import javax.xml.XMLConstants;
    1820
    1921import org.openstreetmap.josm.data.Bounds;
    2022import org.openstreetmap.josm.data.coor.LatLon;
    21 import org.openstreetmap.josm.data.gpx.Extensions;
    2223import org.openstreetmap.josm.data.gpx.GpxConstants;
    2324import org.openstreetmap.josm.data.gpx.GpxData;
     25import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace;
     26import org.openstreetmap.josm.data.gpx.GpxExtension;
     27import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
    2428import org.openstreetmap.josm.data.gpx.GpxLink;
    2529import org.openstreetmap.josm.data.gpx.GpxRoute;
    2630import org.openstreetmap.josm.data.gpx.GpxTrack;
    27 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     31import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2832import org.openstreetmap.josm.data.gpx.IWithAttributes;
    2933import org.openstreetmap.josm.data.gpx.WayPoint;
    3034import org.openstreetmap.josm.tools.JosmRuntimeException;
     
    5458
    5559    private GpxData data;
    5660    private String indent = "";
     61    private List<String> validprefixes;
    5762
    5863    private static final int WAY_POINT = 0;
    5964    private static final int ROUTE_POINT = 1;
     
    6469     * @param data The data to write
    6570     */
    6671    public void write(GpxData data) {
     72        write(data, ColorFormat.GPXD, true);
     73    }
     74
     75    /**
     76     * Writes the given GPX data.
     77     *
     78     * @param data The data to write
     79     * @param colorFormat determines if colors are saved and which extension is to be used
     80     * @param savePrefs whether layer specific preferences are saved
     81     */
     82    public void write(GpxData data, ColorFormat colorFormat, boolean savePrefs) {
    6783        this.data = data;
    68         // We write JOSM specific meta information into gpx 'extensions' elements.
    69         // In particular it is noted whether the gpx data is from the OSM server
    70         // (so the rendering of clouds of anonymous TrackPoints can be improved)
    71         // and some extra synchronization info for export of AudioMarkers.
    72         // It is checked in advance, if any extensions are used, so we know whether
    73         // a namespace declaration is necessary.
    74         boolean hasExtensions = data.fromServer;
    75         if (!hasExtensions) {
    76             for (WayPoint wpt : data.waypoints) {
    77                 Extensions extensions = (Extensions) wpt.get(META_EXTENSIONS);
    78                 if (extensions != null && !extensions.isEmpty()) {
    79                     hasExtensions = true;
    80                     break;
     84
     85        //Prepare extensions for writing
     86        data.beginUpdate();
     87        data.getTracks().forEach(trk -> trk.convertColor(colorFormat));
     88        data.getExtensions().removeAllWithPrefix("josm");
     89        if (data.fromServer) {
     90            data.getExtensions().add("josm", "from-server", "true");
     91        }
     92        if (savePrefs && !data.getLayerPrefs().isEmpty()) {
     93            GpxExtensionCollection layerExts = data.getExtensions().add("josm", "layerPreferences").getExtensions();
     94            data.getLayerPrefs().entrySet()
     95            .stream()
     96            .sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
     97            .forEach(entry -> {
     98                GpxExtension e = layerExts.add("josm", "entry");
     99                e.put("key", entry.getKey());
     100                e.put("value", entry.getValue());
     101            });
     102        }
     103        data.endUpdate();
     104
     105        Collection<IWithAttributes> all = new ArrayList<>();
     106
     107        all.add(data);
     108        all.addAll(data.getWaypoints());
     109        all.addAll(data.getRoutes());
     110        all.addAll(data.getTracks());
     111        all.addAll(data.getTrackSegmentsStream().collect(Collectors.toList()));
     112
     113        List<XMLNamespace> namespaces = all
     114                .stream()
     115                .flatMap(w -> w.getExtensions().getPrefixesStream())
     116                .distinct()
     117                .map(p -> data.getNamespaces()
     118                        .stream()
     119                        .filter(s -> s.getPrefix().equals(p))
     120                        .findAny()
     121                        .orElse(GpxExtension.findNamespace(p)))
     122                .filter(Objects::nonNull)
     123                .collect(Collectors.toList());
     124
     125        validprefixes = namespaces.stream().map(n -> n.getPrefix()).collect(Collectors.toList());
     126
     127        out.println("<?xml version='1.0' encoding='UTF-8'?>");
     128        out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"");
     129
     130        String schemaLocations = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd";
     131
     132        for (XMLNamespace n : namespaces) {
     133            if (n.getURI() != null && n.getPrefix() != null && !n.getPrefix().isEmpty()) {
     134                out.println(String.format("    xmlns:%s=\"%s\"", n.getPrefix(), n.getURI()));
     135                if (n.getLocation() != null) {
     136                    schemaLocations += " " + n.getURI() + " " + n.getLocation();
    81137                }
    82138            }
    83139        }
    84140
    85         out.println("<?xml version='1.0' encoding='UTF-8'?>");
    86         out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"");
    87         out.println((hasExtensions ? String.format("    xmlns:josm=\"%s\"%n", JOSM_EXTENSIONS_NAMESPACE_URI) : "") +
    88                     "    xmlns:xsi=\""+XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI+"\"");
    89         out.println("    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">");
     141        out.println("    xmlns:xsi=\""+XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI+"\"");
     142        out.println(String.format("    xsi:schemaLocation=\"%s\">", schemaLocations));
    90143        indent = "  ";
    91144        writeMetaData();
    92145        writeWayPoints();
     
    105158                        gpxLink(link);
    106159                    }
    107160                }
    108             } else if (META_EXTENSIONS.equals(key)) {
    109                 Extensions extensions = (Extensions) obj.get(key);
    110                 if (extensions != null) {
    111                     gpxExtensions(extensions);
    112                 }
    113161            } else {
    114162                String value = obj.getString(key);
    115163                if (value != null) {
     
    147195            if (attr.containsKey(META_AUTHOR_EMAIL)) {
    148196                String[] tmp = data.getString(META_AUTHOR_EMAIL).split("@");
    149197                if (tmp.length == 2) {
    150                     inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+'\"');
     198                    inline("email", "id=\"" + encode(tmp[0]) + "\" domain=\"" + encode(tmp[1]) +'\"');
    151199                }
    152200            }
    153201            // write the author link
     
    158206        // write the copyright details
    159207        if (attr.containsKey(META_COPYRIGHT_LICENSE)
    160208                || attr.containsKey(META_COPYRIGHT_YEAR)) {
    161             openAtt("copyright", "author=\""+ data.get(META_COPYRIGHT_AUTHOR) +'\"');
     209            openln("copyright", "author=\""+ encode(data.get(META_COPYRIGHT_AUTHOR).toString()) +'\"');
    162210            if (attr.containsKey(META_COPYRIGHT_YEAR)) {
    163211                simpleTag("year", (String) data.get(META_COPYRIGHT_YEAR));
    164212            }
     
    187235            inline("bounds", b);
    188236        }
    189237
    190         if (data.fromServer) {
    191             openln("extensions");
    192             simpleTag("josm:from-server", "true");
    193             closeln("extensions");
    194         }
    195 
     238        gpxExtensions(data.getExtensions());
    196239        closeln("metadata");
    197240    }
    198241
     
    206249        for (GpxRoute rte : data.getRoutes()) {
    207250            openln("rte");
    208251            writeAttr(rte, RTE_TRK_KEYS);
     252            gpxExtensions(rte.getExtensions());
    209253            for (WayPoint pnt : rte.routePoints) {
    210254                wayPoint(pnt, ROUTE_POINT);
    211255            }
     
    217261        for (GpxTrack trk : data.getTracks()) {
    218262            openln("trk");
    219263            writeAttr(trk, RTE_TRK_KEYS);
    220             for (GpxTrackSegment seg : trk.getSegments()) {
     264            gpxExtensions(trk.getExtensions());
     265            for (IGpxTrackSegment seg : trk.getSegments()) {
    221266                openln("trkseg");
     267                gpxExtensions(seg.getExtensions());
    222268                for (WayPoint pnt : seg.getWayPoints()) {
    223269                    wayPoint(pnt, TRACK_POINT);
    224270                }
     
    233279        out.println();
    234280    }
    235281
     282    private void openln(String tag, String attributes) {
     283        open(tag, attributes);
     284        out.println();
     285    }
     286
    236287    private void open(String tag) {
    237288        out.print(indent + '<' + tag + '>');
    238289        indent += "  ";
    239290    }
    240291
    241     private void openAtt(String tag, String attributes) {
    242         out.println(indent + '<' + tag + ' ' + attributes + '>');
     292    private void open(String tag, String attributes) {
     293        out.print(indent + '<' + tag + (attributes.isEmpty() ? "" : ' ') + attributes + '>');
    243294        indent += "  ";
    244295    }
    245296
    246297    private void inline(String tag, String attributes) {
    247         out.println(indent + '<' + tag + ' ' + attributes + "/>");
     298        out.println(indent + '<' + tag + (attributes.isEmpty() ? "" : ' ') + attributes + "/>");
    248299    }
    249300
    250301    private void close(String tag) {
     
    272323        }
    273324    }
    274325
     326    private void simpleTag(String tag, String content, String attributes) {
     327        if (content != null && !content.isEmpty()) {
     328            open(tag, attributes);
     329            out.print(encode(content));
     330            out.println("</" + tag + '>');
     331            indent = indent.substring(2);
     332        }
     333    }
     334
    275335    /**
    276336     * output link
    277337     * @param link link
     
    278338     */
    279339    private void gpxLink(GpxLink link) {
    280340        if (link != null) {
    281             openAtt("link", "href=\"" + link.uri + '\"');
     341            openln("link", "href=\"" + encode(link.uri) + '\"');
    282342            simpleTag("text", link.text);
    283343            simpleTag("type", link.type);
    284344            closeln("link");
     
    308368        if (pnt != null) {
    309369            LatLon c = pnt.getCoor();
    310370            String coordAttr = "lat=\"" + c.lat() + "\" lon=\"" + c.lon() + '\"';
    311             if (pnt.attr.isEmpty()) {
     371            if (pnt.attr.isEmpty() && pnt.getExtensions().isEmpty()) {
    312372                inline(type, coordAttr);
    313373            } else {
    314                 openAtt(type, coordAttr);
     374                openln(type, coordAttr);
    315375                writeAttr(pnt, WPT_KEYS);
     376                gpxExtensions(pnt.getExtensions());
    316377                closeln(type);
    317378            }
    318379        }
    319380    }
    320381
    321     private void gpxExtensions(Extensions extensions) {
    322         if (extensions != null && !extensions.isEmpty()) {
     382    private void gpxExtensions(GpxExtensionCollection allExtensions) {
     383        if (allExtensions.isVisible()) {
    323384            openln("extensions");
    324             for (Entry<String, String> e : extensions.entrySet()) {
    325                 simpleTag("josm:" + e.getKey(), e.getValue());
    326             }
     385            writeExtension(allExtensions);
    327386            closeln("extensions");
    328387        }
    329388    }
     389
     390    private void writeExtension(List<GpxExtension> extensions) {
     391        for (GpxExtension e : extensions) {
     392            if (validprefixes.contains(e.getPrefix()) && e.isVisible()) {
     393                // this might lead to loss of an unknown extension *after* the file was saved as .osm,
     394                // but otherwise the file is invalid and can't even be parsed by SAX anymore
     395                String k = (e.getPrefix().isEmpty() ? "" : e.getPrefix() + ":") + e.getKey();
     396                String attr = String.join(" ", e.getAttributes().entrySet().stream().map(a -> encode(a.getKey()) + "=\"" + encode(a.getValue().toString()) + "\"").sorted().collect(Collectors.toList()));
     397                if (e.getValue() == null && e.getExtensions().isEmpty()) {
     398                    inline(k, attr);
     399                } else if (e.getExtensions().isEmpty()) {
     400                    simpleTag(k, e.getValue(), attr);
     401                } else {
     402                    openln(k, attr);
     403                    if (e.getValue() != null) {
     404                        out.print(encode(e.getValue()));
     405                    }
     406                    writeExtension(e.getExtensions());
     407                    closeln(k);
     408                }
     409            }
     410        }
     411    }
    330412}
  • src/org/openstreetmap/josm/io/nmea/NmeaReader.java

     
    2020import org.openstreetmap.josm.data.coor.LatLon;
    2121import org.openstreetmap.josm.data.gpx.GpxConstants;
    2222import org.openstreetmap.josm.data.gpx.GpxData;
    23 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
     23import org.openstreetmap.josm.data.gpx.GpxTrack;
    2424import org.openstreetmap.josm.data.gpx.WayPoint;
    2525import org.openstreetmap.josm.io.IGpxReader;
    2626import org.openstreetmap.josm.io.IllegalDataException;
     
    264264                }
    265265            }
    266266            currentTrack.add(ps.waypoints);
    267             data.tracks.add(new ImmutableGpxTrack(currentTrack, Collections.<String, Object>emptyMap()));
     267            data.tracks.add(new GpxTrack(currentTrack, Collections.<String, Object>emptyMap()));
    268268
    269269        } catch (IllegalDataException e) {
    270270            Logging.warn(e);
  • src/org/openstreetmap/josm/io/rtklib/RtkLibPosReader.java

     
    1818import org.openstreetmap.josm.data.coor.LatLon;
    1919import org.openstreetmap.josm.data.gpx.GpxConstants;
    2020import org.openstreetmap.josm.data.gpx.GpxData;
    21 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
     21import org.openstreetmap.josm.data.gpx.GpxTrack;
    2222import org.openstreetmap.josm.data.gpx.WayPoint;
    2323import org.openstreetmap.josm.io.IGpxReader;
    2424import org.openstreetmap.josm.tools.Logging;
     
    114114            } while (line != null);
    115115        }
    116116        currentTrack.add(waypoints);
    117         data.tracks.add(new ImmutableGpxTrack(currentTrack, Collections.<String, Object>emptyMap()));
     117        data.tracks.add(new GpxTrack(currentTrack, Collections.<String, Object>emptyMap()));
    118118        return true;
    119119    }
    120120
  • src/org/openstreetmap/josm/io/session/GenericSessionExporter.java

     
    8181
    8282        @Override
    8383        public void actionPerformed(ActionEvent e) {
    84             SaveAction.getInstance().doSave(layer);
     84            SaveAction.getInstance().doSave(layer, true);
    8585            updateEnabledState();
    8686        }
    8787
  • test/data/tracks/tracks-extensions.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:gpxd="http://josm.openstreetmap.de/gpx-drawing-extensions-1.0"
     5    xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
     6    xmlns:test="TestExtensionNamespace"
     7    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     8    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 http://josm.openstreetmap.de/gpx-drawing-extensions-1.0 http://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd TestExtensionNamespace http://example.com">
     9  <metadata>
     10    <bounds minlat="47.1849004" minlon="8.7719297" maxlat="47.2014997" maxlon="8.7984188"/>
     11    <extensions>
     12      <josm:layerPreferences>
     13        <josm:entry key="colors" value="0"/>
     14      </josm:layerPreferences>
     15    </extensions>
     16  </metadata>
     17  <trk>
     18    <extensions>
     19      <test:tag>Track extension</test:tag>
     20      <gpxx:TrackExtensions>
     21        <gpxx:DisplayColor>Red</gpxx:DisplayColor>
     22      </gpxx:TrackExtensions>
     23    </extensions>
     24    <trkseg>
     25      <extensions>
     26        <test:tag>Segment extension</test:tag>
     27      </extensions>
     28      <trkpt lat="47.19286847859621" lon="8.79732714034617">
     29        <ele>471.86000000000001</ele>
     30        <time>2016-01-03T11:59:58Z</time>
     31      </trkpt>
     32      <trkpt lat="47.192921955138445" lon="8.797342479228973">
     33        <ele>471.43000000000001</ele>
     34        <time>2016-01-03T11:59:59Z</time>
     35      </trkpt>
     36      <trkpt lat="47.19297501258552" lon="8.79734423942864">
     37        <ele>470.69</ele>
     38        <time>2016-01-03T12:00:00Z</time>
     39      </trkpt>
     40      <trkpt lat="47.19302739948034" lon="8.797332588583231">
     41        <ele>470.94</ele>
     42        <time>2016-01-03T12:00:01Z</time>
     43      </trkpt>
     44      <trkpt lat="47.193073416128755" lon="8.797307442873716">
     45        <ele>471.5</ele>
     46        <time>2016-01-03T12:00:02Z</time>
     47      </trkpt>
     48      <trkpt lat="47.193113481625915" lon="8.797272993251681">
     49        <ele>471.67000000000002</ele>
     50        <time>2016-01-03T12:00:03Z</time>
     51      </trkpt>
     52      <trkpt lat="47.19313560985029" lon="8.797232424840331">
     53        <ele>471.58999999999997</ele>
     54        <time>2016-01-03T12:00:04Z</time>
     55      </trkpt>
     56      <trkpt lat="47.19333660788834" lon="8.796738646924496">
     57        <ele>472.44999999999999</ele>
     58        <time>2016-01-03T12:00:15Z</time>
     59      </trkpt>
     60      <trkpt lat="47.193504832684994" lon="8.796404711902142">
     61        <ele>472.54000000000002</ele>
     62        <time>2016-01-03T12:00:22Z</time>
     63      </trkpt>
     64      <trkpt lat="47.19353936612606" lon="8.79635970108211">
     65        <ele>472.45999999999998</ele>
     66        <time>2016-01-03T12:00:23Z</time>
     67      </trkpt>
     68      <trkpt lat="47.19381898641586" lon="8.796017384156585">
     69        <ele>471.38</ele>
     70        <time>2016-01-03T12:00:30Z</time>
     71      </trkpt>
     72      <trkpt lat="47.19386676326394" lon="8.795985281467438">
     73        <ele>471.26999999999998</ele>
     74        <time>2016-01-03T12:00:31Z</time>
     75      </trkpt>
     76      <trkpt lat="47.19391797669232" lon="8.795968098565936">
     77        <ele>471.36000000000001</ele>
     78        <time>2016-01-03T12:00:32Z</time>
     79      </trkpt>
     80      <trkpt lat="47.19474418088794" lon="8.795836586505175">
     81        <ele>473.19999999999999</ele>
     82        <time>2016-01-03T12:00:50Z</time>
     83      </trkpt>
     84      <trkpt lat="47.195586981251836" lon="8.795750420540571">
     85        <ele>475.06</ele>
     86        <time>2016-01-03T12:01:09Z</time>
     87      </trkpt>
     88      <trkpt lat="47.19563358463347" lon="8.795769028365612">
     89        <ele>474.75</ele>
     90        <time>2016-01-03T12:01:10Z</time>
     91      </trkpt>
     92      <trkpt lat="47.19567717052996" lon="8.795795682817698">
     93        <ele>474.62</ele>
     94        <time>2016-01-03T12:01:11Z</time>
     95      </trkpt>
     96      <trkpt lat="47.195715978741646" lon="8.79583046771586">
     97        <ele>474.74000000000001</ele>
     98        <time>2016-01-03T12:01:12Z</time>
     99      </trkpt>
     100      <trkpt lat="47.19575093127787" lon="8.79587598145008">
     101        <ele>474.06</ele>
     102        <time>2016-01-03T12:01:13Z</time>
     103      </trkpt>
     104      <trkpt lat="47.19578169286251" lon="8.795927111059427">
     105        <ele>474</ele>
     106        <time>2016-01-03T12:01:14Z</time>
     107      </trkpt>
     108      <trkpt lat="47.195808347314596" lon="8.795980839058757">
     109        <ele>474.58999999999997</ele>
     110        <time>2016-01-03T12:01:15Z</time>
     111      </trkpt>
     112      <trkpt lat="47.195833241567016" lon="8.796036075800657">
     113        <ele>474.81999999999999</ele>
     114        <time>2016-01-03T12:01:16Z</time>
     115      </trkpt>
     116      <trkpt lat="47.19628603197634" lon="8.797110635787249">
     117        <ele>476.29000000000002</ele>
     118        <time>2016-01-03T12:01:35Z</time>
     119      </trkpt>
     120      <trkpt lat="47.19658995978534" lon="8.797826115041971">
     121        <ele>478.48000000000002</ele>
     122        <time>2016-01-03T12:01:50Z</time>
     123      </trkpt>
     124      <trkpt lat="47.19703386537731" lon="8.798356018960476">
     125        <ele>481.02999999999997</ele>
     126        <time>2016-01-03T12:02:06Z</time>
     127      </trkpt>
     128      <trkpt lat="47.19723687507212" lon="8.79841879941523">
     129        <ele>480</ele>
     130        <time>2016-01-03T12:02:12Z</time>
     131      </trkpt>
     132      <trkpt lat="47.19727886840701" lon="8.79838845692575">
     133        <ele>479.58999999999997</ele>
     134        <time>2016-01-03T12:02:13Z</time>
     135      </trkpt>
     136      <trkpt lat="47.19731541350484" lon="8.798339758068323">
     137        <ele>478.99000000000001</ele>
     138        <time>2016-01-03T12:02:14Z</time>
     139      </trkpt>
     140      <trkpt lat="47.197333350777626" lon="8.798282761126757">
     141        <ele>479.58999999999997</ele>
     142        <time>2016-01-03T12:02:15Z</time>
     143      </trkpt>
     144      <trkpt lat="47.19734919257462" lon="8.798161642625928">
     145        <ele>481.18000000000001</ele>
     146        <time>2016-01-03T12:02:18Z</time>
     147      </trkpt>
     148      <trkpt lat="47.19734827056527" lon="8.798118894919753">
     149        <ele>481.88</ele>
     150        <time>2016-01-03T12:02:20Z</time>
     151      </trkpt>
     152      <trkpt lat="47.19730585813522" lon="8.79802887327969">
     153        <ele>483.11000000000001</ele>
     154        <time>2016-01-03T12:02:24Z</time>
     155      </trkpt>
     156      <trkpt lat="47.19727643765509" lon="8.79797455854714">
     157        <ele>484.49000000000001</ele>
     158        <time>2016-01-03T12:02:27Z</time>
     159      </trkpt>
     160      <trkpt lat="47.19720158725977" lon="8.797768447548151">
     161        <ele>486.88</ele>
     162        <time>2016-01-03T12:02:35Z</time>
     163      </trkpt>
     164      <trkpt lat="47.19708633609116" lon="8.79746401682496">
     165        <ele>488.05000000000001</ele>
     166        <time>2016-01-03T12:02:48Z</time>
     167      </trkpt>
     168      <trkpt lat="47.19707007519901" lon="8.79741758108139">
     169        <ele>487.76999999999998</ele>
     170        <time>2016-01-03T12:02:51Z</time>
     171      </trkpt>
     172      <trkpt lat="47.19702917151153" lon="8.797250529751182">
     173        <ele>488.95999999999998</ele>
     174        <time>2016-01-03T12:02:57Z</time>
     175      </trkpt>
     176      <trkpt lat="47.19699899666011" lon="8.797172158956528">
     177        <ele>488.36000000000001</ele>
     178        <time>2016-01-03T12:03:01Z</time>
     179      </trkpt>
     180      <trkpt lat="47.19691224396229" lon="8.796978034079075">
     181        <ele>488.35000000000002</ele>
     182        <time>2016-01-03T12:03:09Z</time>
     183      </trkpt>
     184      <trkpt lat="47.19690545462072" lon="8.796931263059378">
     185        <ele>487.19</ele>
     186        <time>2016-01-03T12:03:10Z</time>
     187      </trkpt>
     188      <trkpt lat="47.19690595753491" lon="8.796886671334505">
     189        <ele>487.63999999999999</ele>
     190        <time>2016-01-03T12:03:11Z</time>
     191      </trkpt>
     192      <trkpt lat="47.19690511934459" lon="8.796837385743856">
     193        <ele>487.55000000000001</ele>
     194        <time>2016-01-03T12:03:12Z</time>
     195      </trkpt>
     196      <trkpt lat="47.19690562225878" lon="8.796793716028333">
     197        <ele>488.39999999999998</ele>
     198        <time>2016-01-03T12:03:13Z</time>
     199      </trkpt>
     200      <trkpt lat="47.196887601166964" lon="8.796751135960221">
     201        <ele>489.18000000000001</ele>
     202        <time>2016-01-03T12:03:15Z</time>
     203      </trkpt>
     204      <trkpt lat="47.1968892775476" lon="8.796689361333847">
     205        <ele>490.73000000000002</ele>
     206        <time>2016-01-03T12:03:18Z</time>
     207      </trkpt>
     208      <trkpt lat="47.196868155151606" lon="8.79647453315556">
     209        <ele>491.24000000000001</ele>
     210        <time>2016-01-03T12:03:33Z</time>
     211      </trkpt>
     212      <trkpt lat="47.19686932861805" lon="8.796442430466413">
     213        <ele>490.25</ele>
     214        <time>2016-01-03T12:03:34Z</time>
     215      </trkpt>
     216      <trkpt lat="47.19686849042773" lon="8.796406639739871">
     217        <ele>490.00999999999999</ele>
     218        <time>2016-01-03T12:03:35Z</time>
     219      </trkpt>
     220      <trkpt lat="47.196873016655445" lon="8.796368166804314">
     221        <ele>489.54000000000002</ele>
     222        <time>2016-01-03T12:03:36Z</time>
     223      </trkpt>
     224      <trkpt lat="47.19687905162573" lon="8.796329610049725">
     225        <ele>489.66000000000003</ele>
     226        <time>2016-01-03T12:03:37Z</time>
     227      </trkpt>
     228      <trkpt lat="47.196886762976646" lon="8.796280743554235">
     229        <ele>489.06</ele>
     230        <time>2016-01-03T12:03:38Z</time>
     231      </trkpt>
     232      <trkpt lat="47.19695775769651" lon="8.795829126611352">
     233        <ele>490.08999999999997</ele>
     234        <time>2016-01-03T12:03:48Z</time>
     235      </trkpt>
     236      <trkpt lat="47.1969928778708" lon="8.795709516853094">
     237        <ele>489.41000000000003</ele>
     238        <time>2016-01-03T12:03:52Z</time>
     239      </trkpt>
     240      <trkpt lat="47.19698550179601" lon="8.795667607337236">
     241        <ele>489.17000000000002</ele>
     242        <time>2016-01-03T12:03:53Z</time>
     243      </trkpt>
     244      <trkpt lat="47.19697644934058" lon="8.795624608173966">
     245        <ele>488.87</ele>
     246        <time>2016-01-03T12:03:54Z</time>
     247      </trkpt>
     248      <trkpt lat="47.19696890562773" lon="8.795590326189995">
     249        <ele>489.37</ele>
     250        <time>2016-01-03T12:03:55Z</time>
     251      </trkpt>
     252      <trkpt lat="47.19696664251387" lon="8.795558391138911">
     253        <ele>488.94999999999999</ele>
     254        <time>2016-01-03T12:03:56Z</time>
     255      </trkpt>
     256      <trkpt lat="47.19695256091654" lon="8.795536765828729">
     257        <ele>488.93000000000001</ele>
     258        <time>2016-01-03T12:03:57Z</time>
     259      </trkpt>
     260      <trkpt lat="47.196956500411034" lon="8.795507680624723">
     261        <ele>488.88999999999999</ele>
     262        <time>2016-01-03T12:03:58Z</time>
     263      </trkpt>
     264      <trkpt lat="47.19695859588683" lon="8.79548110999167">
     265        <ele>488.86000000000001</ele>
     266        <time>2016-01-03T12:03:59Z</time>
     267      </trkpt>
     268      <trkpt lat="47.19696907326579" lon="8.795452527701855">
     269        <ele>489.19999999999999</ele>
     270        <time>2016-01-03T12:04:00Z</time>
     271      </trkpt>
     272      <trkpt lat="47.19697988592088" lon="8.79541271366179">
     273        <ele>489.29000000000002</ele>
     274        <time>2016-01-03T12:04:01Z</time>
     275      </trkpt>
     276      <trkpt lat="47.19698726199567" lon="8.795355297625065">
     277        <ele>488.95999999999998</ele>
     278        <time>2016-01-03T12:04:02Z</time>
     279      </trkpt>
     280      <trkpt lat="47.19698960892856" lon="8.795302575454116">
     281        <ele>489.10000000000002</ele>
     282        <time>2016-01-03T12:04:03Z</time>
     283      </trkpt>
     284      <trkpt lat="47.19690981321037" lon="8.79494265653193">
     285        <ele>492.19</ele>
     286        <time>2016-01-03T12:04:14Z</time>
     287      </trkpt>
     288      <trkpt lat="47.19696622341871" lon="8.794532027095556">
     289        <ele>492.12</ele>
     290        <time>2016-01-03T12:04:25Z</time>
     291      </trkpt>
     292      <trkpt lat="47.19697737134993" lon="8.79450285807252">
     293        <ele>493.06999999999999</ele>
     294        <time>2016-01-03T12:04:26Z</time>
     295      </trkpt>
     296      <trkpt lat="47.19699212349951" lon="8.794450974091887">
     297        <ele>492.58999999999997</ele>
     298        <time>2016-01-03T12:04:27Z</time>
     299      </trkpt>
     300      <trkpt lat="47.19699405133724" lon="8.794399006292224">
     301        <ele>492.38999999999999</ele>
     302        <time>2016-01-03T12:04:28Z</time>
     303      </trkpt>
     304      <trkpt lat="47.19698860310018" lon="8.794350139796734">
     305        <ele>492.94</ele>
     306        <time>2016-01-03T12:04:29Z</time>
     307      </trkpt>
     308      <trkpt lat="47.19698214903474" lon="8.794298926368356">
     309        <ele>492.47000000000003</ele>
     310        <time>2016-01-03T12:04:30Z</time>
     311      </trkpt>
     312      <trkpt lat="47.19697737134993" lon="8.794246790930629">
     313        <ele>492.50999999999999</ele>
     314        <time>2016-01-03T12:04:31Z</time>
     315      </trkpt>
     316      <trkpt lat="47.19698206521571" lon="8.794189291074872">
     317        <ele>492.73000000000002</ele>
     318        <time>2016-01-03T12:04:32Z</time>
     319      </trkpt>
     320      <trkpt lat="47.196985417976975" lon="8.794133719056845">
     321        <ele>492.63</ele>
     322        <time>2016-01-03T12:04:33Z</time>
     323      </trkpt>
     324      <trkpt lat="47.19698768109083" lon="8.794077644124627">
     325        <ele>493</ele>
     326        <time>2016-01-03T12:04:34Z</time>
     327      </trkpt>
     328      <trkpt lat="47.19699136912823" lon="8.794022323563695">
     329        <ele>493.00999999999999</ele>
     330        <time>2016-01-03T12:04:35Z</time>
     331      </trkpt>
     332      <trkpt lat="47.196987848728895" lon="8.793976558372378">
     333        <ele>493.19999999999999</ele>
     334        <time>2016-01-03T12:04:36Z</time>
     335      </trkpt>
     336      <trkpt lat="47.19698550179601" lon="8.793926937505603">
     337        <ele>493.18000000000001</ele>
     338        <time>2016-01-03T12:04:37Z</time>
     339      </trkpt>
     340      <trkpt lat="47.196980556473136" lon="8.793885698541999">
     341        <ele>493</ele>
     342        <time>2016-01-03T12:04:38Z</time>
     343      </trkpt>
     344      <trkpt lat="47.19698307104409" lon="8.793837334960699">
     345        <ele>493.20999999999998</ele>
     346        <time>2016-01-03T12:04:39Z</time>
     347      </trkpt>
     348      <trkpt lat="47.196992291137576" lon="8.793786959722638">
     349        <ele>493.11000000000001</ele>
     350        <time>2016-01-03T12:04:40Z</time>
     351      </trkpt>
     352      <trkpt lat="47.19699547626078" lon="8.793743625283241">
     353        <ele>493.30000000000001</ele>
     354        <time>2016-01-03T12:04:41Z</time>
     355      </trkpt>
     356      <trkpt lat="47.19700268469751" lon="8.793694507330656">
     357        <ele>493.50999999999999</ele>
     358        <time>2016-01-03T12:04:42Z</time>
     359      </trkpt>
     360      <trkpt lat="47.19700511544943" lon="8.793357806280255">
     361        <ele>493</ele>
     362        <time>2016-01-03T12:04:50Z</time>
     363      </trkpt>
     364      <trkpt lat="47.19702162779868" lon="8.793316148221493">
     365        <ele>492.37</ele>
     366        <time>2016-01-03T12:04:51Z</time>
     367      </trkpt>
     368      <trkpt lat="47.19704459421337" lon="8.793262336403131">
     369        <ele>492.30000000000001</ele>
     370        <time>2016-01-03T12:04:52Z</time>
     371      </trkpt>
     372      <trkpt lat="47.19706906937063" lon="8.793201064690948">
     373        <ele>492.04000000000002</ele>
     374        <time>2016-01-03T12:04:53Z</time>
     375      </trkpt>
     376      <trkpt lat="47.19708985649049" lon="8.793123783543706">
     377        <ele>491.37</ele>
     378        <time>2016-01-03T12:04:54Z</time>
     379      </trkpt>
     380      <trkpt lat="47.19710913486779" lon="8.79304625093937">
     381        <ele>491.06999999999999</ele>
     382        <time>2016-01-03T12:04:55Z</time>
     383      </trkpt>
     384      <trkpt lat="47.19713117927313" lon="8.792974585667253">
     385        <ele>490.39999999999998</ele>
     386        <time>2016-01-03T12:04:56Z</time>
     387      </trkpt>
     388      <trkpt lat="47.197186248376966" lon="8.792809881269932">
     389        <ele>489.75</ele>
     390        <time>2016-01-03T12:04:58Z</time>
     391      </trkpt>
     392      <trkpt lat="47.19720753841102" lon="8.79271550104022">
     393        <ele>489.87</ele>
     394        <time>2016-01-03T12:04:59Z</time>
     395      </trkpt>
     396      <trkpt lat="47.19722296111286" lon="8.792620282620192">
     397        <ele>488.95999999999998</ele>
     398        <time>2016-01-03T12:05:00Z</time>
     399      </trkpt>
     400      <trkpt lat="47.19733930192888" lon="8.792043440043926">
     401        <ele>485.85000000000002</ele>
     402        <time>2016-01-03T12:05:06Z</time>
     403      </trkpt>
     404      <trkpt lat="47.1974522061646" lon="8.791516218334436">
     405        <ele>482.81</ele>
     406        <time>2016-01-03T12:05:11Z</time>
     407      </trkpt>
     408      <trkpt lat="47.197455475106835" lon="8.79138101823628">
     409        <ele>481.99000000000001</ele>
     410        <time>2016-01-03T12:05:12Z</time>
     411      </trkpt>
     412      <trkpt lat="47.197490092366934" lon="8.79095789976418">
     413        <ele>478.92</ele>
     414      </trkpt>
     415      <trkpt lat="47.19753225333989" lon="8.790533943101764">
     416        <ele>476</ele>
     417      </trkpt>
     418      <trkpt lat="47.19762864522636" lon="8.789889458566904">
     419        <ele>474.38</ele>
     420        <time>2016-01-03T12:05:23Z</time>
     421      </trkpt>
     422      <trkpt lat="47.19763744622469" lon="8.789792899042368">
     423        <ele>474.35000000000002</ele>
     424        <time>2016-01-03T12:05:24Z</time>
     425      </trkpt>
     426      <trkpt lat="47.1976438164711" lon="8.789720982313156">
     427        <ele>474.12</ele>
     428        <time>2016-01-03T12:05:25Z</time>
     429      </trkpt>
     430      <trkpt lat="47.19764859415591" lon="8.789367768913507">
     431        <ele>472.52999999999997</ele>
     432        <time>2016-01-03T12:05:33Z</time>
     433      </trkpt>
     434      <trkpt lat="47.19765613786876" lon="8.78931068815291">
     435        <ele>472.75</ele>
     436        <time>2016-01-03T12:05:34Z</time>
     437      </trkpt>
     438      <trkpt lat="47.1976903360337" lon="8.789105415344238">
     439        <ele>472.19</ele>
     440        <time>2016-01-03T12:05:37Z</time>
     441      </trkpt>
     442      <trkpt lat="47.197700729593635" lon="8.78903266042471">
     443        <ele>471.92000000000002</ele>
     444        <time>2016-01-03T12:05:38Z</time>
     445      </trkpt>
     446      <trkpt lat="47.197715900838375" lon="8.78876687027514">
     447        <ele>471.98000000000002</ele>
     448        <time>2016-01-03T12:05:41Z</time>
     449      </trkpt>
     450      <trkpt lat="47.19775462523103" lon="8.788432851433754">
     451        <ele>473.49000000000001</ele>
     452        <time>2016-01-03T12:05:47Z</time>
     453      </trkpt>
     454      <trkpt lat="47.197777507826686" lon="8.78841600380838">
     455        <ele>473.44999999999999</ele>
     456        <time>2016-01-03T12:05:48Z</time>
     457      </trkpt>
     458      <trkpt lat="47.19780064187944" lon="8.788405358791351">
     459        <ele>473.45999999999998</ele>
     460        <time>2016-01-03T12:05:49Z</time>
     461      </trkpt>
     462      <trkpt lat="47.197963669896126" lon="8.788386499509215">
     463        <ele>470.27999999999997</ele>
     464        <time>2016-01-03T12:05:53Z</time>
     465      </trkpt>
     466      <trkpt lat="47.1981308888644" lon="8.788295891135931">
     467        <ele>467.20999999999998</ele>
     468        <time>2016-01-03T12:05:57Z</time>
     469      </trkpt>
     470      <trkpt lat="47.19813633710146" lon="8.788262866437435">
     471        <ele>466.89999999999998</ele>
     472        <time>2016-01-03T12:05:58Z</time>
     473      </trkpt>
     474      <trkpt lat="47.19812661409378" lon="8.788229255005717">
     475        <ele>466.76999999999998</ele>
     476        <time>2016-01-03T12:05:59Z</time>
     477      </trkpt>
     478      <trkpt lat="47.19810306094587" lon="8.788186172023416">
     479        <ele>466.25</ele>
     480        <time>2016-01-03T12:06:00Z</time>
     481      </trkpt>
     482      <trkpt lat="47.19808369874954" lon="8.788127414882183">
     483        <ele>465.23000000000002</ele>
     484        <time>2016-01-03T12:06:01Z</time>
     485      </trkpt>
     486      <trkpt lat="47.19807816669345" lon="8.788059940561652">
     487        <ele>464.12</ele>
     488        <time>2016-01-03T12:06:02Z</time>
     489      </trkpt>
     490      <trkpt lat="47.19808881171048" lon="8.787991376593709">
     491        <ele>463.68000000000001</ele>
     492        <time>2016-01-03T12:06:03Z</time>
     493      </trkpt>
     494      <trkpt lat="47.19811093993485" lon="8.787914346903563">
     495        <ele>462.5</ele>
     496        <time>2016-01-03T12:06:04Z</time>
     497      </trkpt>
     498      <trkpt lat="47.19815100543201" lon="8.78783798776567">
     499        <ele>462.45999999999998</ele>
     500        <time>2016-01-03T12:06:05Z</time>
     501      </trkpt>
     502      <trkpt lat="47.19819492660463" lon="8.787756934762001">
     503        <ele>461.56999999999999</ele>
     504        <time>2016-01-03T12:06:06Z</time>
     505      </trkpt>
     506      <trkpt lat="47.1982860378921" lon="8.787589464336634">
     507        <ele>461.14999999999998</ele>
     508        <time>2016-01-03T12:06:08Z</time>
     509      </trkpt>
     510      <trkpt lat="47.19831814058125" lon="8.7874944973737">
     511        <ele>460.19</ele>
     512        <time>2016-01-03T12:06:09Z</time>
     513      </trkpt>
     514      <trkpt lat="47.1983309648931" lon="8.787389053031802">
     515        <ele>459.83999999999997</ele>
     516        <time>2016-01-03T12:06:10Z</time>
     517      </trkpt>
     518      <trkpt lat="47.198331551626325" lon="8.787284530699253">
     519        <ele>460.49000000000001</ele>
     520        <time>2016-01-03T12:06:11Z</time>
     521      </trkpt>
     522      <trkpt lat="47.198325265198946" lon="8.787185624241829">
     523        <ele>460.23000000000002</ele>
     524        <time>2016-01-03T12:06:12Z</time>
     525      </trkpt>
     526      <trkpt lat="47.19824245199561" lon="8.78635254688561">
     527        <ele>460.56999999999999</ele>
     528        <time>2016-01-03T12:06:22Z</time>
     529      </trkpt>
     530      <trkpt lat="47.198192747309804" lon="8.78586145117879">
     531        <ele>457.79000000000002</ele>
     532        <time>2016-01-03T12:06:27Z</time>
     533      </trkpt>
     534      <trkpt lat="47.19819249585271" lon="8.78536943346262">
     535        <ele>454.66000000000003</ele>
     536        <time>2016-01-03T12:06:31Z</time>
     537      </trkpt>
     538    </trkseg>
     539  </trk>
     540  <trk>
     541    <extensions>
     542      <gpxd:color>#0000FF</gpxd:color>
     543    </extensions>
     544    <trkseg>
     545      <trkpt lat="47.18660652637482" lon="8.796921372413635">
     546        <ele>624.7421875</ele>
     547      </trkpt>
     548      <trkpt lat="47.186644077301025" lon="8.7968248128891">
     549        <ele>626.015625</ele>
     550      </trkpt>
     551      <trkpt lat="47.18669772148132" lon="8.796712160110474">
     552        <ele>627.0625</ele>
     553      </trkpt>
     554      <trkpt lat="47.18676209449768" lon="8.796610236167908">
     555        <ele>627.3671875</ele>
     556      </trkpt>
     557      <trkpt lat="47.1868371963501" lon="8.796519041061401">
     558        <ele>626.90234375</ele>
     559      </trkpt>
     560      <trkpt lat="47.186912298202515" lon="8.796401023864746">
     561        <ele>626.11328125</ele>
     562      </trkpt>
     563      <trkpt lat="47.18698740005493" lon="8.796277642250061">
     564        <ele>625.33203125</ele>
     565      </trkpt>
     566      <trkpt lat="47.18707859516144" lon="8.796148896217346">
     567        <ele>623.90625</ele>
     568      </trkpt>
     569      <trkpt lat="47.18710541725159" lon="8.796095252037048">
     570        <ele>623.66796875</ele>
     571      </trkpt>
     572      <trkpt lat="47.187126874923706" lon="8.796030879020691">
     573        <ele>623.71484375</ele>
     574      </trkpt>
     575      <trkpt lat="47.187132239341736" lon="8.795977234840393">
     576        <ele>623.75</ele>
     577      </trkpt>
     578      <trkpt lat="47.187132239341736" lon="8.795923590660095">
     579        <ele>623.9375</ele>
     580      </trkpt>
     581      <trkpt lat="47.187132239341736" lon="8.795859217643738">
     582        <ele>624.1640625</ele>
     583      </trkpt>
     584      <trkpt lat="47.18711078166962" lon="8.795735836029053">
     585        <ele>625.234375</ele>
     586      </trkpt>
     587      <trkpt lat="47.18710005283356" lon="8.795607089996338">
     588        <ele>626</ele>
     589      </trkpt>
     590      <trkpt lat="47.18709468841553" lon="8.795521259307861">
     591        <ele>626.3515625</ele>
     592      </trkpt>
     593      <trkpt lat="47.18710541725159" lon="8.795440793037415">
     594        <ele>626.14453125</ele>
     595      </trkpt>
     596      <trkpt lat="47.187126874923706" lon="8.795360326766968">
     597        <ele>625.6640625</ele>
     598      </trkpt>
     599      <trkpt lat="47.187164425849915" lon="8.79528522491455">
     600        <ele>624.7890625</ele>
     601      </trkpt>
     602      <trkpt lat="47.1872341632843" lon="8.795183300971985">
     603        <ele>623.23046875</ele>
     604      </trkpt>
     605      <trkpt lat="47.18729317188263" lon="8.795086741447449">
     606        <ele>622.09375</ele>
     607      </trkpt>
     608      <trkpt lat="47.18736290931702" lon="8.794984817504883">
     609        <ele>620.9765625</ele>
     610      </trkpt>
     611      <trkpt lat="47.187416553497314" lon="8.794904351234436">
     612        <ele>620.23828125</ele>
     613      </trkpt>
     614      <trkpt lat="47.18746483325958" lon="8.794845342636108">
     615        <ele>619.42578125</ele>
     616      </trkpt>
     617      <trkpt lat="47.18752384185791" lon="8.79479706287384">
     618        <ele>617.7734375</ele>
     619      </trkpt>
     620      <trkpt lat="47.18756139278412" lon="8.794743418693542">
     621        <ele>616.8671875</ele>
     622      </trkpt>
     623      <trkpt lat="47.1875935792923" lon="8.794695138931274">
     624        <ele>616.09765625</ele>
     625      </trkpt>
     626      <trkpt lat="47.18760967254639" lon="8.794630765914917">
     627        <ele>615.87890625</ele>
     628      </trkpt>
     629      <trkpt lat="47.187620401382446" lon="8.7945556640625">
     630        <ele>615.3984375</ele>
     631      </trkpt>
     632      <trkpt lat="47.187636494636536" lon="8.794485926628113">
     633        <ele>614.76953125</ele>
     634      </trkpt>
     635      <trkpt lat="47.187674045562744" lon="8.794400095939636">
     636        <ele>613.47265625</ele>
     637      </trkpt>
     638      <trkpt lat="47.18769550323486" lon="8.79430890083313">
     639        <ele>612.72265625</ele>
     640      </trkpt>
     641      <trkpt lat="47.18770086765289" lon="8.794185519218445">
     642        <ele>612.4609375</ele>
     643      </trkpt>
     644      <trkpt lat="47.18770086765289" lon="8.79407823085785">
     645        <ele>611.85546875</ele>
     646      </trkpt>
     647      <trkpt lat="47.187684774398804" lon="8.793997764587402">
     648        <ele>611.90625</ele>
     649      </trkpt>
     650      <trkpt lat="47.187663316726685" lon="8.793933391571045">
     651        <ele>612.27734375</ele>
     652      </trkpt>
     653      <trkpt lat="47.18761503696442" lon="8.793869018554688">
     654        <ele>613.66796875</ele>
     655      </trkpt>
     656      <trkpt lat="47.18758285045624" lon="8.79381537437439">
     657        <ele>614.62890625</ele>
     658      </trkpt>
     659      <trkpt lat="47.18756675720215" lon="8.793767094612122">
     660        <ele>615.0703125</ele>
     661      </trkpt>
     662      <trkpt lat="47.18757212162018" lon="8.793713450431824">
     663        <ele>614.72265625</ele>
     664      </trkpt>
     665      <trkpt lat="47.18758821487427" lon="8.793681263923645">
     666        <ele>614.26171875</ele>
     667      </trkpt>
     668      <trkpt lat="47.187641859054565" lon="8.793681263923645">
     669        <ele>612.06640625</ele>
     670      </trkpt>
     671      <trkpt lat="47.18770086765289" lon="8.793708086013794">
     672        <ele>609.6875</ele>
     673      </trkpt>
     674      <trkpt lat="47.18778133392334" lon="8.793751001358032">
     675        <ele>606.83984375</ele>
     676      </trkpt>
     677      <trkpt lat="47.18785107135773" lon="8.79380464553833">
     678        <ele>604.671875</ele>
     679      </trkpt>
     680      <trkpt lat="47.187920808792114" lon="8.793842196464539">
     681        <ele>602.52734375</ele>
     682      </trkpt>
     683      <trkpt lat="47.18800127506256" lon="8.793911933898926">
     684        <ele>600.78515625</ele>
     685      </trkpt>
     686      <trkpt lat="47.18811392784119" lon="8.794013857841492">
     687        <ele>598.078125</ele>
     688      </trkpt>
     689      <trkpt lat="47.188151478767395" lon="8.79406213760376">
     690        <ele>597.1953125</ele>
     691      </trkpt>
     692      <trkpt lat="47.1881890296936" lon="8.794153332710266">
     693        <ele>596.5234375</ele>
     694      </trkpt>
     695      <trkpt lat="47.18821585178375" lon="8.794239163398743">
     696        <ele>595.74609375</ele>
     697      </trkpt>
     698      <trkpt lat="47.1882426738739" lon="8.794351816177368">
     699        <ele>594.82421875</ele>
     700      </trkpt>
     701      <trkpt lat="47.18826413154602" lon="8.794426918029785">
     702        <ele>594.10546875</ele>
     703      </trkpt>
     704      <trkpt lat="47.18829095363617" lon="8.794475197792053">
     705        <ele>593.2109375</ele>
     706      </trkpt>
     707      <trkpt lat="47.18830704689026" lon="8.794523477554321">
     708        <ele>592.6875</ele>
     709      </trkpt>
     710      <trkpt lat="47.18831241130829" lon="8.794614672660828">
     711        <ele>592.5390625</ele>
     712      </trkpt>
     713      <trkpt lat="47.18830704689026" lon="8.79479706287384">
     714        <ele>592.7578125</ele>
     715      </trkpt>
     716      <trkpt lat="47.18830704689026" lon="8.794888257980347">
     717        <ele>592.77734375</ele>
     718      </trkpt>
     719      <trkpt lat="47.18831241130829" lon="8.794941902160645">
     720        <ele>592.63671875</ele>
     721      </trkpt>
     722      <trkpt lat="47.18833386898041" lon="8.794995546340942">
     723        <ele>592.06640625</ele>
     724      </trkpt>
     725      <trkpt lat="47.188366055488586" lon="8.7950599193573">
     726        <ele>591.2578125</ele>
     727      </trkpt>
     728      <trkpt lat="47.188403606414795" lon="8.795113563537598">
     729        <ele>590.46875</ele>
     730      </trkpt>
     731      <trkpt lat="47.188435792922974" lon="8.795167207717896">
     732        <ele>589.7578125</ele>
     733      </trkpt>
     734      <trkpt lat="47.18845188617706" lon="8.795226216316223">
     735        <ele>589.359375</ele>
     736      </trkpt>
     737      <trkpt lat="47.18846261501312" lon="8.79529058933258">
     738        <ele>589.05859375</ele>
     739      </trkpt>
     740      <trkpt lat="47.18845188617706" lon="8.795354962348938">
     741        <ele>589.21875</ele>
     742      </trkpt>
     743      <trkpt lat="47.188430428504944" lon="8.795451521873474">
     744        <ele>589.59375</ele>
     745      </trkpt>
     746      <trkpt lat="47.188408970832825" lon="8.7955641746521">
     747        <ele>589.98046875</ele>
     748      </trkpt>
     749      <trkpt lat="47.188403606414795" lon="8.795655369758606">
     750        <ele>589.51953125</ele>
     751      </trkpt>
     752      <trkpt lat="47.188408970832825" lon="8.795746564865112">
     753        <ele>588.796875</ele>
     754      </trkpt>
     755      <trkpt lat="47.188435792922974" lon="8.795859217643738">
     756        <ele>587.4375</ele>
     757      </trkpt>
     758      <trkpt lat="47.18847870826721" lon="8.795971870422363">
     759        <ele>585.70703125</ele>
     760      </trkpt>
     761      <trkpt lat="47.18850553035736" lon="8.79605233669281">
     762        <ele>584.48046875</ele>
     763      </trkpt>
     764      <trkpt lat="47.18852162361145" lon="8.796132802963257">
     765        <ele>583.328125</ele>
     766      </trkpt>
     767      <trkpt lat="47.18852162361145" lon="8.796197175979614">
     768        <ele>582.69921875</ele>
     769      </trkpt>
     770      <trkpt lat="47.18851625919342" lon="8.79628300666809">
     771        <ele>581.96875</ele>
     772      </trkpt>
     773      <trkpt lat="47.18850016593933" lon="8.796379566192627">
     774        <ele>581.3359375</ele>
     775      </trkpt>
     776      <trkpt lat="47.18848943710327" lon="8.796465396881104">
     777        <ele>580.6796875</ele>
     778      </trkpt>
     779      <trkpt lat="47.18848943710327" lon="8.796524405479431">
     780        <ele>580.33984375</ele>
     781      </trkpt>
     782      <trkpt lat="47.18850016593933" lon="8.796578049659729">
     783        <ele>580.0234375</ele>
     784      </trkpt>
     785      <trkpt lat="47.18852162361145" lon="8.796631693840027">
     786        <ele>579.5078125</ele>
     787      </trkpt>
     788      <trkpt lat="47.1885484457016" lon="8.796685338020325">
     789        <ele>578.890625</ele>
     790      </trkpt>
     791      <trkpt lat="47.18856990337372" lon="8.796749711036682">
     792        <ele>578.34375</ele>
     793      </trkpt>
     794      <trkpt lat="47.18858063220978" lon="8.79681944847107">
     795        <ele>577.984375</ele>
     796      </trkpt>
     797      <trkpt lat="47.18857526779175" lon="8.796899914741516">
     798        <ele>577.90234375</ele>
     799      </trkpt>
     800      <trkpt lat="47.18858599662781" lon="8.796991109848022">
     801        <ele>577.50390625</ele>
     802      </trkpt>
     803      <trkpt lat="47.18859672546387" lon="8.79704475402832">
     804        <ele>577.20703125</ele>
     805      </trkpt>
     806      <trkpt lat="47.188623547554016" lon="8.797119855880737">
     807        <ele>576.6171875</ele>
     808      </trkpt>
     809      <trkpt lat="47.188666462898254" lon="8.797189593315125">
     810        <ele>575.82421875</ele>
     811      </trkpt>
     812      <trkpt lat="47.18870937824249" lon="8.797211050987244">
     813        <ele>575.125</ele>
     814      </trkpt>
     815      <trkpt lat="47.18871474266052" lon="8.797157406806946">
     816        <ele>575.0546875</ele>
     817      </trkpt>
     818      <trkpt lat="47.18872547149658" lon="8.79705548286438">
     819        <ele>574.89453125</ele>
     820      </trkpt>
     821      <trkpt lat="47.18873620033264" lon="8.796937465667725">
     822        <ele>574.75</ele>
     823      </trkpt>
     824      <trkpt lat="47.18875765800476" lon="8.79681408405304">
     825        <ele>574.6484375</ele>
     826      </trkpt>
     827      <trkpt lat="47.18879520893097" lon="8.796685338020325">
     828        <ele>574.26953125</ele>
     829      </trkpt>
     830      <trkpt lat="47.18883812427521" lon="8.796588778495789">
     831        <ele>573.72265625</ele>
     832      </trkpt>
     833      <trkpt lat="47.188907861709595" lon="8.796438574790955">
     834        <ele>572.4296875</ele>
     835      </trkpt>
     836      <trkpt lat="47.18901515007019" lon="8.796240091323853">
     837        <ele>570.828125</ele>
     838      </trkpt>
     839      <trkpt lat="47.18906342983246" lon="8.796154260635376">
     840        <ele>570.1015625</ele>
     841      </trkpt>
     842      <trkpt lat="47.189154624938965" lon="8.795987963676453">
     843        <ele>568.7734375</ele>
     844      </trkpt>
     845      <trkpt lat="47.18921899795532" lon="8.795869946479797">
     846        <ele>567.8125</ele>
     847      </trkpt>
     848      <trkpt lat="47.18926191329956" lon="8.795778751373291">
     849        <ele>567.25390625</ele>
     850      </trkpt>
     851      <trkpt lat="47.18928337097168" lon="8.795714378356934">
     852        <ele>567.09765625</ele>
     853      </trkpt>
     854      <trkpt lat="47.18929409980774" lon="8.795660734176636">
     855        <ele>567.1640625</ele>
     856      </trkpt>
     857      <trkpt lat="47.18929946422577" lon="8.795580267906189">
     858        <ele>567.546875</ele>
     859      </trkpt>
     860      <trkpt lat="47.18929946422577" lon="8.795451521873474">
     861        <ele>567.86328125</ele>
     862      </trkpt>
     863      <trkpt lat="47.18929409980774" lon="8.7953120470047">
     864        <ele>568.2890625</ele>
     865      </trkpt>
     866      <trkpt lat="47.18929409980774" lon="8.795210123062134">
     867        <ele>568.49609375</ele>
     868      </trkpt>
     869      <trkpt lat="47.1893048286438" lon="8.795135021209717">
     870        <ele>568.33203125</ele>
     871      </trkpt>
     872      <trkpt lat="47.18934237957001" lon="8.794904351234436">
     873        <ele>566.84765625</ele>
     874      </trkpt>
     875      <trkpt lat="47.18934774398804" lon="8.794845342636108">
     876        <ele>566.609375</ele>
     877      </trkpt>
     878      <trkpt lat="47.18933701515198" lon="8.794738054275513">
     879        <ele>566.94140625</ele>
     880      </trkpt>
     881      <trkpt lat="47.18933701515198" lon="8.794657588005066">
     882        <ele>566.88671875</ele>
     883      </trkpt>
     884      <trkpt lat="47.18935310840607" lon="8.79457712173462">
     885        <ele>566.27734375</ele>
     886      </trkpt>
     887      <trkpt lat="47.189374566078186" lon="8.794502019882202">
     888        <ele>565.48046875</ele>
     889      </trkpt>
     890      <trkpt lat="47.189428210258484" lon="8.794384002685547">
     891        <ele>563.5</ele>
     892      </trkpt>
     893      <trkpt lat="47.18945503234863" lon="8.7943035364151">
     894        <ele>562.51953125</ele>
     895      </trkpt>
     896      <trkpt lat="47.18947112560272" lon="8.794206976890564">
     897        <ele>561.9453125</ele>
     898      </trkpt>
     899      <trkpt lat="47.18948185443878" lon="8.79407286643982">
     900        <ele>561.85546875</ele>
     901      </trkpt>
     902      <trkpt lat="47.18949794769287" lon="8.793911933898926">
     903        <ele>561.7578125</ele>
     904      </trkpt>
     905      <trkpt lat="47.18950867652893" lon="8.79378855228424">
     906        <ele>561.765625</ele>
     907      </trkpt>
     908      <trkpt lat="47.18952476978302" lon="8.793686628341675">
     909        <ele>561.453125</ele>
     910      </trkpt>
     911      <trkpt lat="47.18955159187317" lon="8.79356861114502">
     912        <ele>560.6015625</ele>
     913      </trkpt>
     914      <trkpt lat="47.18958377838135" lon="8.793466687202454">
     915        <ele>559.65234375</ele>
     916      </trkpt>
     917      <trkpt lat="47.189615964889526" lon="8.793386220932007">
     918        <ele>558.7578125</ele>
     919      </trkpt>
     920      <trkpt lat="47.189658880233765" lon="8.793278932571411">
     921        <ele>557.66015625</ele>
     922      </trkpt>
     923      <trkpt lat="47.18971252441406" lon="8.793187737464905">
     924        <ele>556.35546875</ele>
     925      </trkpt>
     926      <trkpt lat="47.18976080417633" lon="8.793118000030518">
     927        <ele>555.2265625</ele>
     928      </trkpt>
     929      <trkpt lat="47.18983054161072" lon="8.79304826259613">
     930        <ele>553.53125</ele>
     931      </trkpt>
     932      <trkpt lat="47.189921736717224" lon="8.792962431907654">
     933        <ele>551.265625</ele>
     934      </trkpt>
     935      <trkpt lat="47.18997538089752" lon="8.792903423309326">
     936        <ele>549.92578125</ele>
     937      </trkpt>
     938      <trkpt lat="47.1900075674057" lon="8.792855143547058">
     939        <ele>549.140625</ele>
     940      </trkpt>
     941      <trkpt lat="47.19003438949585" lon="8.792780041694641">
     942        <ele>548.48828125</ele>
     943      </trkpt>
     944      <trkpt lat="47.19004511833191" lon="8.792726397514343">
     945        <ele>548.0078125</ele>
     946      </trkpt>
     947      <trkpt lat="47.19005584716797" lon="8.792635202407837">
     948        <ele>547.39453125</ele>
     949      </trkpt>
     950      <trkpt lat="47.19005584716797" lon="8.79256546497345">
     951        <ele>547.15234375</ele>
     952      </trkpt>
     953      <trkpt lat="47.19005048274994" lon="8.792501091957092">
     954        <ele>547.08203125</ele>
     955      </trkpt>
     956      <trkpt lat="47.19001829624176" lon="8.792366981506348">
     957        <ele>547.55078125</ele>
     958      </trkpt>
     959      <trkpt lat="47.18994319438934" lon="8.792104125022888">
     960        <ele>548.69921875</ele>
     961      </trkpt>
     962      <trkpt lat="47.189932465553284" lon="8.792034387588501">
     963        <ele>548.76953125</ele>
     964      </trkpt>
     965      <trkpt lat="47.189932465553284" lon="8.791970014572144">
     966        <ele>548.51953125</ele>
     967      </trkpt>
     968      <trkpt lat="47.18994319438934" lon="8.791900277137756">
     969        <ele>547.88671875</ele>
     970      </trkpt>
     971      <trkpt lat="47.18997001647949" lon="8.79183053970337">
     972        <ele>546.7265625</ele>
     973      </trkpt>
     974      <trkpt lat="47.19000220298767" lon="8.791755437850952">
     975        <ele>545.3984375</ele>
     976      </trkpt>
     977      <trkpt lat="47.19003975391388" lon="8.791680335998535">
     978        <ele>543.87890625</ele>
     979      </trkpt>
     980      <trkpt lat="47.190120220184326" lon="8.791535496711731">
     981        <ele>540.6484375</ele>
     982      </trkpt>
     983      <trkpt lat="47.19023287296295" lon="8.79129409790039">
     984        <ele>536.4296875</ele>
     985      </trkpt>
     986      <trkpt lat="47.19025433063507" lon="8.791224360466003">
     987        <ele>536.046875</ele>
     988      </trkpt>
     989      <trkpt lat="47.19027042388916" lon="8.791149258613586">
     990        <ele>535.765625</ele>
     991      </trkpt>
     992      <trkpt lat="47.19027578830719" lon="8.791015148162842">
     993        <ele>535.71875</ele>
     994      </trkpt>
     995      <trkpt lat="47.19027578830719" lon="8.790768384933472">
     996        <ele>535.55859375</ele>
     997      </trkpt>
     998      <trkpt lat="47.19026505947113" lon="8.790435791015625">
     999        <ele>535.359375</ele>
     1000      </trkpt>
     1001      <trkpt lat="47.1902596950531" lon="8.790162205696106">
     1002        <ele>534.5234375</ele>
     1003      </trkpt>
     1004      <trkpt lat="47.1902596950531" lon="8.789920806884766">
     1005        <ele>534.2578125</ele>
     1006      </trkpt>
     1007      <trkpt lat="47.19025433063507" lon="8.789674043655396">
     1008        <ele>535.1328125</ele>
     1009      </trkpt>
     1010      <trkpt lat="47.19025433063507" lon="8.78955602645874">
     1011        <ele>535.46875</ele>
     1012      </trkpt>
     1013      <trkpt lat="47.19025433063507" lon="8.789491653442383">
     1014        <ele>535.04296875</ele>
     1015      </trkpt>
     1016      <trkpt lat="47.19027042388916" lon="8.789438009262085">
     1017        <ele>534.04296875</ele>
     1018      </trkpt>
     1019      <trkpt lat="47.19030797481537" lon="8.789384365081787">
     1020        <ele>532.234375</ele>
     1021      </trkpt>
     1022      <trkpt lat="47.19035625457764" lon="8.789357542991638">
     1023        <ele>530.2109375</ele>
     1024      </trkpt>
     1025      <trkpt lat="47.190393805503845" lon="8.789357542991638">
     1026        <ele>528.7890625</ele>
     1027      </trkpt>
     1028      <trkpt lat="47.190425992012024" lon="8.789373636245728">
     1029        <ele>527.69140625</ele>
     1030      </trkpt>
     1031      <trkpt lat="47.1904581785202" lon="8.789411187171936">
     1032        <ele>526.76171875</ele>
     1033      </trkpt>
     1034      <trkpt lat="47.19048500061035" lon="8.789459466934204">
     1035        <ele>526.1171875</ele>
     1036      </trkpt>
     1037      <trkpt lat="47.19050645828247" lon="8.789529204368591">
     1038        <ele>525.83984375</ele>
     1039      </trkpt>
     1040      <trkpt lat="47.19052255153656" lon="8.789609670639038">
     1041        <ele>525.51171875</ele>
     1042      </trkpt>
     1043      <trkpt lat="47.19053328037262" lon="8.789727687835693">
     1044        <ele>525.34375</ele>
     1045      </trkpt>
     1046      <trkpt lat="47.19054400920868" lon="8.78982961177826">
     1047        <ele>525.18359375</ele>
     1048      </trkpt>
     1049      <trkpt lat="47.19058692455292" lon="8.790022730827332">
     1050        <ele>524.3515625</ele>
     1051      </trkpt>
     1052      <trkpt lat="47.190635204315186" lon="8.790242671966553">
     1053        <ele>524.41015625</ele>
     1054      </trkpt>
     1055      <trkpt lat="47.190672755241394" lon="8.790398240089417">
     1056        <ele>524.47265625</ele>
     1057      </trkpt>
     1058      <trkpt lat="47.19072639942169" lon="8.79054844379425">
     1059        <ele>524.02734375</ele>
     1060      </trkpt>
     1061      <trkpt lat="47.19078004360199" lon="8.790677189826965">
     1062        <ele>523.09375</ele>
     1063      </trkpt>
     1064      <trkpt lat="47.19083368778229" lon="8.79082202911377">
     1065        <ele>521.92578125</ele>
     1066      </trkpt>
     1067      <trkpt lat="47.190876603126526" lon="8.790961503982544">
     1068        <ele>520.87109375</ele>
     1069      </trkpt>
     1070      <trkpt lat="47.190908789634705" lon="8.791095614433289">
     1071        <ele>520.51953125</ele>
     1072      </trkpt>
     1073      <trkpt lat="47.19093561172485" lon="8.791192173957825">
     1074        <ele>520.2578125</ele>
     1075      </trkpt>
     1076      <trkpt lat="47.19097316265106" lon="8.791267275810242">
     1077        <ele>519.6640625</ele>
     1078      </trkpt>
     1079      <trkpt lat="47.19103217124939" lon="8.791337013244629">
     1080        <ele>518.5703125</ele>
     1081      </trkpt>
     1082      <trkpt lat="47.191112637519836" lon="8.791401386260986">
     1083        <ele>517.04296875</ele>
     1084      </trkpt>
     1085      <trkpt lat="47.19128429889679" lon="8.791524767875671">
     1086        <ele>513.03125</ele>
     1087      </trkpt>
     1088      <trkpt lat="47.191407680511475" lon="8.791610598564148">
     1089        <ele>510.19921875</ele>
     1090      </trkpt>
     1091      <trkpt lat="47.19148814678192" lon="8.791669607162476">
     1092        <ele>508.5</ele>
     1093      </trkpt>
     1094      <trkpt lat="47.1915203332901" lon="8.791696429252625">
     1095        <ele>507.8828125</ele>
     1096      </trkpt>
     1097      <trkpt lat="47.19157934188843" lon="8.791739344596863">
     1098        <ele>506.71875</ele>
     1099      </trkpt>
     1100      <trkpt lat="47.191627621650696" lon="8.79179835319519">
     1101        <ele>506.03125</ele>
     1102      </trkpt>
     1103      <trkpt lat="47.191659808158875" lon="8.791851997375488">
     1104        <ele>505.5390625</ele>
     1105      </trkpt>
     1106      <trkpt lat="47.19168663024902" lon="8.791916370391846">
     1107        <ele>504.96875</ele>
     1108      </trkpt>
     1109      <trkpt lat="47.1917188167572" lon="8.792018294334412">
     1110        <ele>504.28125</ele>
     1111      </trkpt>
     1112      <trkpt lat="47.19175100326538" lon="8.792141675949097">
     1113        <ele>503.6640625</ele>
     1114      </trkpt>
     1115      <trkpt lat="47.19179928302765" lon="8.79229724407196">
     1116        <ele>502.578125</ele>
     1117      </trkpt>
     1118      <trkpt lat="47.19184219837189" lon="8.792458176612854">
     1119        <ele>500.921875</ele>
     1120      </trkpt>
     1121      <trkpt lat="47.191869020462036" lon="8.79257082939148">
     1122        <ele>499.85546875</ele>
     1123      </trkpt>
     1124      <trkpt lat="47.191879749298096" lon="8.792662024497986">
     1125        <ele>499.40234375</ele>
     1126      </trkpt>
     1127      <trkpt lat="47.191890478134155" lon="8.792763948440552">
     1128        <ele>498.96875</ele>
     1129      </trkpt>
     1130      <trkpt lat="47.191895842552185" lon="8.792887330055237">
     1131        <ele>497.8828125</ele>
     1132      </trkpt>
     1133      <trkpt lat="47.191895842552185" lon="8.79306435585022">
     1134        <ele>496.2265625</ele>
     1135      </trkpt>
     1136      <trkpt lat="47.191901206970215" lon="8.793182373046875">
     1137        <ele>494.9609375</ele>
     1138      </trkpt>
     1139      <trkpt lat="47.191906571388245" lon="8.793257474899292">
     1140        <ele>494.13671875</ele>
     1141      </trkpt>
     1142      <trkpt lat="47.191928029060364" lon="8.793343305587769">
     1143        <ele>493.6015625</ele>
     1144      </trkpt>
     1145      <trkpt lat="47.19196021556854" lon="8.793472051620483">
     1146        <ele>492.828125</ele>
     1147      </trkpt>
     1148      <trkpt lat="47.19199282117188" lon="8.793616723269224">
     1149        <ele>492.1015625</ele>
     1150      </trkpt>
     1151      <trkpt lat="47.19200849533081" lon="8.793686628341675">
     1152        <ele>491.76953125</ele>
     1153      </trkpt>
     1154      <trkpt lat="47.19204068183899" lon="8.793852925300598">
     1155        <ele>490.83984375</ele>
     1156      </trkpt>
     1157      <trkpt lat="47.19206213951111" lon="8.793992400169373">
     1158        <ele>490.23828125</ele>
     1159      </trkpt>
     1160      <trkpt lat="47.19207286834717" lon="8.7940514087677">
     1161        <ele>489.96484375</ele>
     1162      </trkpt>
     1163      <trkpt lat="47.19208359718323" lon="8.794147968292236">
     1164        <ele>489.703125</ele>
     1165      </trkpt>
     1166      <trkpt lat="47.192121148109436" lon="8.795000910758972">
     1167        <ele>484.7109375</ele>
     1168      </trkpt>
     1169      <trkpt lat="47.192137241363525" lon="8.79530131816864">
     1170        <ele>482.328125</ele>
     1171      </trkpt>
     1172      <trkpt lat="47.192137241363525" lon="8.795451521873474">
     1173        <ele>481.04296875</ele>
     1174      </trkpt>
     1175      <trkpt lat="47.192137241363525" lon="8.79553735256195">
     1176        <ele>480.3046875</ele>
     1177      </trkpt>
     1178      <trkpt lat="47.192126512527466" lon="8.795607089996338">
     1179        <ele>479.70703125</ele>
     1180      </trkpt>
     1181      <trkpt lat="47.19211041927338" lon="8.795682191848755">
     1182        <ele>478.984375</ele>
     1183      </trkpt>
     1184      <trkpt lat="47.19208896160126" lon="8.795746564865112">
     1185        <ele>478.4609375</ele>
     1186      </trkpt>
     1187      <trkpt lat="47.19203531742096" lon="8.795832395553589">
     1188        <ele>479.1015625</ele>
     1189      </trkpt>
     1190      <trkpt lat="47.19198167324066" lon="8.795896768569946">
     1191        <ele>480.359375</ele>
     1192      </trkpt>
     1193      <trkpt lat="47.19194948673248" lon="8.795934319496155">
     1194        <ele>481.19140625</ele>
     1195      </trkpt>
     1196      <trkpt lat="47.191906571388245" lon="8.795993328094482">
     1197        <ele>482.2890625</ele>
     1198      </trkpt>
     1199      <trkpt lat="47.191869020462036" lon="8.79606306552887">
     1200        <ele>483.28125</ele>
     1201      </trkpt>
     1202      <trkpt lat="47.19184756278992" lon="8.796138167381287">
     1203        <ele>483.69921875</ele>
     1204      </trkpt>
     1205      <trkpt lat="47.19184219837189" lon="8.796213269233704">
     1206        <ele>483.4609375</ele>
     1207      </trkpt>
     1208      <trkpt lat="47.19184219837189" lon="8.79628300666809">
     1209        <ele>483.0390625</ele>
     1210      </trkpt>
     1211      <trkpt lat="47.19185829162598" lon="8.796374201774597">
     1212        <ele>481.9140625</ele>
     1213      </trkpt>
     1214      <trkpt lat="47.191885113716125" lon="8.796454668045044">
     1215        <ele>480.515625</ele>
     1216      </trkpt>
     1217      <trkpt lat="47.191917300224304" lon="8.796513676643372">
     1218        <ele>479.16796875</ele>
     1219      </trkpt>
     1220      <trkpt lat="47.19198703765869" lon="8.796631693840027">
     1221        <ele>476.74609375</ele>
     1222      </trkpt>
     1223      <trkpt lat="47.19206750392914" lon="8.796760439872742">
     1224        <ele>474.3515625</ele>
     1225      </trkpt>
     1226      <trkpt lat="47.192131876945496" lon="8.796835541725159">
     1227        <ele>473.28515625</ele>
     1228      </trkpt>
     1229      <trkpt lat="47.19219088554382" lon="8.796894550323486">
     1230        <ele>472.39453125</ele>
     1231      </trkpt>
     1232      <trkpt lat="47.19225525856018" lon="8.796948194503784">
     1233        <ele>471.3671875</ele>
     1234      </trkpt>
     1235      <trkpt lat="47.19234645366669" lon="8.797001838684082">
     1236        <ele>470.0390625</ele>
     1237      </trkpt>
     1238      <trkpt lat="47.192426919937134" lon="8.79704475402832">
     1239        <ele>468.76171875</ele>
     1240      </trkpt>
     1241      <trkpt lat="47.19250738620758" lon="8.797082304954529">
     1242        <ele>467.3671875</ele>
     1243      </trkpt>
     1244      <trkpt lat="47.19255566596985" lon="8.797114491462708">
     1245        <ele>466.60546875</ele>
     1246      </trkpt>
     1247      <trkpt lat="47.1927273273468" lon="8.797253966331482">
     1248        <ele>464.65625</ele>
     1249      </trkpt>
     1250      <trkpt lat="47.19280779361725" lon="8.79730761051178">
     1251        <ele>463.62109375</ele>
     1252      </trkpt>
     1253      <trkpt lat="47.19286604784429" lon="8.79733837209642">
     1254        <ele>462.84375</ele>
     1255      </trkpt>
     1256      <trkpt lat="47.19286596402526" lon="8.797338204458356">
     1257        <ele>462.84765625</ele>
     1258      </trkpt>
     1259    </trkseg>
     1260  </trk>
     1261  <trk>
     1262    <extensions>
     1263      <gpxd:color>#00FF00</gpxd:color>
     1264    </extensions>
     1265    <trkseg>
     1266      <trkpt lat="47.18561486341059" lon="8.796475538983941">
     1267        <ele>647.81640625</ele>
     1268      </trkpt>
     1269      <trkpt lat="47.185668759047985" lon="8.796514933928847">
     1270        <ele>646.87109375</ele>
     1271      </trkpt>
     1272      <trkpt lat="47.18571729026735" lon="8.796522812917829">
     1273        <ele>646.1953125</ele>
     1274      </trkpt>
     1275      <trkpt lat="47.185757691040635" lon="8.796562207862735">
     1276        <ele>645.5859375</ele>
     1277      </trkpt>
     1278      <trkpt lat="47.18580354005098" lon="8.796577882021666">
     1279        <ele>644.8203125</ele>
     1280      </trkpt>
     1281      <trkpt lat="47.185914013534784" lon="8.796581821516156">
     1282        <ele>642.9765625</ele>
     1283      </trkpt>
     1284      <trkpt lat="47.18596254475415" lon="8.796625155955553">
     1285        <ele>641.796875</ele>
     1286      </trkpt>
     1287      <trkpt lat="47.186081148684025" lon="8.796644853428006">
     1288        <ele>639.4296875</ele>
     1289      </trkpt>
     1290      <trkpt lat="47.186186257749796" lon="8.7967315223068">
     1291        <ele>636.4921875</ele>
     1292      </trkpt>
     1293      <trkpt lat="47.186264377087355" lon="8.796790530905128">
     1294        <ele>634.1328125</ele>
     1295      </trkpt>
     1296      <trkpt lat="47.18639915809035" lon="8.796892957761884">
     1297        <ele>629.55859375</ele>
     1298      </trkpt>
     1299      <trkpt lat="47.186458418145776" lon="8.7969284132123">
     1300        <ele>627.55078125</ele>
     1301      </trkpt>
     1302      <trkpt lat="47.18660661019385" lon="8.796955989673734">
     1303        <ele>624.0234375</ele>
     1304      </trkpt>
     1305    </trkseg>
     1306  </trk>
     1307  <trk>
     1308    <trkseg>
     1309      <trkpt lat="47.18490038998425" lon="8.796944171190262">
     1310        <ele>663.90234375</ele>
     1311      </trkpt>
     1312      <trkpt lat="47.18495160341263" lon="8.796790530905128">
     1313        <ele>662.74609375</ele>
     1314      </trkpt>
     1315      <trkpt lat="47.18495696783066" lon="8.796640913933516">
     1316        <ele>662.1328125</ele>
     1317      </trkpt>
     1318      <trkpt lat="47.184981275349855" lon="8.796550389379263">
     1319        <ele>661.39453125</ele>
     1320      </trkpt>
     1321      <trkpt lat="47.18511060811579" lon="8.796479478478432">
     1322        <ele>658.98828125</ele>
     1323      </trkpt>
     1324      <trkpt lat="47.18521839939058" lon="8.7964085675776">
     1325        <ele>657.47265625</ele>
     1326      </trkpt>
     1327      <trkpt lat="47.18536122702062" lon="8.796420386061072">
     1328        <ele>654.22265625</ele>
     1329      </trkpt>
     1330      <trkpt lat="47.18553640879691" lon="8.796440083533525">
     1331        <ele>650.0703125</ele>
     1332      </trkpt>
     1333      <trkpt lat="47.185609163716435" lon="8.796475538983941">
     1334        <ele>647.890625</ele>
     1335      </trkpt>
     1336    </trkseg>
     1337  </trk>
     1338  <trk>
     1339    <trkseg>
     1340      <trkpt lat="47.20138901844621" lon="8.774476982653141">
     1341        <ele>421.52999999999997</ele>
     1342        <time>2016-01-03T12:08:53Z</time>
     1343      </trkpt>
     1344      <trkpt lat="47.201492534950376" lon="8.773281471803784">
     1345        <ele>423.57999999999998</ele>
     1346        <time>2016-01-03T12:09:10Z</time>
     1347      </trkpt>
     1348      <trkpt lat="47.2014878410846" lon="8.772208672016859">
     1349        <ele>423</ele>
     1350        <time>2016-01-03T12:09:26Z</time>
     1351      </trkpt>
     1352      <trkpt lat="47.2014997433871" lon="8.772082105278969">
     1353        <ele>423.07999999999998</ele>
     1354        <time>2016-01-03T12:09:28Z</time>
     1355      </trkpt>
     1356      <trkpt lat="47.2014896851033" lon="8.772034915164113">
     1357        <ele>423.02999999999997</ele>
     1358        <time>2016-01-03T12:09:29Z</time>
     1359      </trkpt>
     1360      <trkpt lat="47.20144324935973" lon="8.77200884744525">
     1361        <ele>422.26999999999998</ele>
     1362        <time>2016-01-03T12:09:31Z</time>
     1363      </trkpt>
     1364      <trkpt lat="47.201405949890614" lon="8.7720338255167">
     1365        <ele>421.35000000000002</ele>
     1366        <time>2016-01-03T12:09:42Z</time>
     1367      </trkpt>
     1368      <trkpt lat="47.20132657326758" lon="8.771982276812196">
     1369        <ele>421.75</ele>
     1370        <time>2016-01-03T12:10:04Z</time>
     1371      </trkpt>
     1372      <trkpt lat="47.20126815140247" lon="8.77192972227931">
     1373        <ele>421.48000000000002</ele>
     1374        <time>2016-01-03T12:10:23Z</time>
     1375      </trkpt>
     1376    </trkseg>
     1377  </trk>
     1378</gpx>
  • test/data/tracks/tracks-extensions.osm

     
     1<?xml version='1.0' encoding='UTF-8'?>
     2<osm version='0.6' upload='false' generator='JOSM'>
     3  <node id='-102235' timestamp='2016-01-03T11:59:58Z' lat='47.1928684786' lon='8.79732714035'>
     4    <tag k='gpx:ele' v='471.86000000000001' />
     5    <tag k='gpx:time' v='2016-01-03T11:59:58Z' />
     6  </node>
     7  <node id='-102236' timestamp='2016-01-03T11:59:59Z' lat='47.19292195514' lon='8.79734247923'>
     8    <tag k='gpx:ele' v='471.43000000000001' />
     9    <tag k='gpx:time' v='2016-01-03T11:59:59Z' />
     10  </node>
     11  <node id='-102237' timestamp='2016-01-03T12:00:00Z' lat='47.19297501259' lon='8.79734423943'>
     12    <tag k='gpx:ele' v='470.69' />
     13    <tag k='gpx:time' v='2016-01-03T12:00:00Z' />
     14  </node>
     15  <node id='-102238' timestamp='2016-01-03T12:00:01Z' lat='47.19302739948' lon='8.79733258858'>
     16    <tag k='gpx:ele' v='470.94' />
     17    <tag k='gpx:time' v='2016-01-03T12:00:01Z' />
     18  </node>
     19  <node id='-102239' timestamp='2016-01-03T12:00:02Z' lat='47.19307341613' lon='8.79730744287'>
     20    <tag k='gpx:ele' v='471.5' />
     21    <tag k='gpx:time' v='2016-01-03T12:00:02Z' />
     22  </node>
     23  <node id='-102240' timestamp='2016-01-03T12:00:03Z' lat='47.19311348163' lon='8.79727299325'>
     24    <tag k='gpx:ele' v='471.67000000000002' />
     25    <tag k='gpx:time' v='2016-01-03T12:00:03Z' />
     26  </node>
     27  <node id='-102241' timestamp='2016-01-03T12:00:04Z' lat='47.19313560985' lon='8.79723242484'>
     28    <tag k='gpx:ele' v='471.58999999999997' />
     29    <tag k='gpx:time' v='2016-01-03T12:00:04Z' />
     30  </node>
     31  <node id='-102242' timestamp='2016-01-03T12:00:15Z' lat='47.19333660789' lon='8.79673864692'>
     32    <tag k='gpx:ele' v='472.44999999999999' />
     33    <tag k='gpx:time' v='2016-01-03T12:00:15Z' />
     34  </node>
     35  <node id='-102243' timestamp='2016-01-03T12:00:22Z' lat='47.19350483268' lon='8.7964047119'>
     36    <tag k='gpx:ele' v='472.54000000000002' />
     37    <tag k='gpx:time' v='2016-01-03T12:00:22Z' />
     38  </node>
     39  <node id='-102244' timestamp='2016-01-03T12:00:23Z' lat='47.19353936613' lon='8.79635970108'>
     40    <tag k='gpx:ele' v='472.45999999999998' />
     41    <tag k='gpx:time' v='2016-01-03T12:00:23Z' />
     42  </node>
     43  <node id='-102245' timestamp='2016-01-03T12:00:30Z' lat='47.19381898642' lon='8.79601738416'>
     44    <tag k='gpx:ele' v='471.38' />
     45    <tag k='gpx:time' v='2016-01-03T12:00:30Z' />
     46  </node>
     47  <node id='-102246' timestamp='2016-01-03T12:00:31Z' lat='47.19386676326' lon='8.79598528147'>
     48    <tag k='gpx:ele' v='471.26999999999998' />
     49    <tag k='gpx:time' v='2016-01-03T12:00:31Z' />
     50  </node>
     51  <node id='-102247' timestamp='2016-01-03T12:00:32Z' lat='47.19391797669' lon='8.79596809857'>
     52    <tag k='gpx:ele' v='471.36000000000001' />
     53    <tag k='gpx:time' v='2016-01-03T12:00:32Z' />
     54  </node>
     55  <node id='-102248' timestamp='2016-01-03T12:00:50Z' lat='47.19474418089' lon='8.79583658651'>
     56    <tag k='gpx:ele' v='473.19999999999999' />
     57    <tag k='gpx:time' v='2016-01-03T12:00:50Z' />
     58  </node>
     59  <node id='-102249' timestamp='2016-01-03T12:01:09Z' lat='47.19558698125' lon='8.79575042054'>
     60    <tag k='gpx:ele' v='475.06' />
     61    <tag k='gpx:time' v='2016-01-03T12:01:09Z' />
     62  </node>
     63  <node id='-102250' timestamp='2016-01-03T12:01:10Z' lat='47.19563358463' lon='8.79576902837'>
     64    <tag k='gpx:ele' v='474.75' />
     65    <tag k='gpx:time' v='2016-01-03T12:01:10Z' />
     66  </node>
     67  <node id='-102251' timestamp='2016-01-03T12:01:11Z' lat='47.19567717053' lon='8.79579568282'>
     68    <tag k='gpx:ele' v='474.62' />
     69    <tag k='gpx:time' v='2016-01-03T12:01:11Z' />
     70  </node>
     71  <node id='-102252' timestamp='2016-01-03T12:01:12Z' lat='47.19571597874' lon='8.79583046772'>
     72    <tag k='gpx:ele' v='474.74000000000001' />
     73    <tag k='gpx:time' v='2016-01-03T12:01:12Z' />
     74  </node>
     75  <node id='-102253' timestamp='2016-01-03T12:01:13Z' lat='47.19575093128' lon='8.79587598145'>
     76    <tag k='gpx:ele' v='474.06' />
     77    <tag k='gpx:time' v='2016-01-03T12:01:13Z' />
     78  </node>
     79  <node id='-102254' timestamp='2016-01-03T12:01:14Z' lat='47.19578169286' lon='8.79592711106'>
     80    <tag k='gpx:ele' v='474' />
     81    <tag k='gpx:time' v='2016-01-03T12:01:14Z' />
     82  </node>
     83  <node id='-102255' timestamp='2016-01-03T12:01:15Z' lat='47.19580834731' lon='8.79598083906'>
     84    <tag k='gpx:ele' v='474.58999999999997' />
     85    <tag k='gpx:time' v='2016-01-03T12:01:15Z' />
     86  </node>
     87  <node id='-102256' timestamp='2016-01-03T12:01:16Z' lat='47.19583324157' lon='8.7960360758'>
     88    <tag k='gpx:ele' v='474.81999999999999' />
     89    <tag k='gpx:time' v='2016-01-03T12:01:16Z' />
     90  </node>
     91  <node id='-102257' timestamp='2016-01-03T12:01:35Z' lat='47.19628603198' lon='8.79711063579'>
     92    <tag k='gpx:ele' v='476.29000000000002' />
     93    <tag k='gpx:time' v='2016-01-03T12:01:35Z' />
     94  </node>
     95  <node id='-102258' timestamp='2016-01-03T12:01:50Z' lat='47.19658995979' lon='8.79782611504'>
     96    <tag k='gpx:ele' v='478.48000000000002' />
     97    <tag k='gpx:time' v='2016-01-03T12:01:50Z' />
     98  </node>
     99  <node id='-102259' timestamp='2016-01-03T12:02:06Z' lat='47.19703386538' lon='8.79835601896'>
     100    <tag k='gpx:ele' v='481.02999999999997' />
     101    <tag k='gpx:time' v='2016-01-03T12:02:06Z' />
     102  </node>
     103  <node id='-102260' timestamp='2016-01-03T12:02:12Z' lat='47.19723687507' lon='8.79841879942'>
     104    <tag k='gpx:ele' v='480' />
     105    <tag k='gpx:time' v='2016-01-03T12:02:12Z' />
     106  </node>
     107  <node id='-102261' timestamp='2016-01-03T12:02:13Z' lat='47.19727886841' lon='8.79838845693'>
     108    <tag k='gpx:ele' v='479.58999999999997' />
     109    <tag k='gpx:time' v='2016-01-03T12:02:13Z' />
     110  </node>
     111  <node id='-102262' timestamp='2016-01-03T12:02:14Z' lat='47.1973154135' lon='8.79833975807'>
     112    <tag k='gpx:ele' v='478.99000000000001' />
     113    <tag k='gpx:time' v='2016-01-03T12:02:14Z' />
     114  </node>
     115  <node id='-102263' timestamp='2016-01-03T12:02:15Z' lat='47.19733335078' lon='8.79828276113'>
     116    <tag k='gpx:ele' v='479.58999999999997' />
     117    <tag k='gpx:time' v='2016-01-03T12:02:15Z' />
     118  </node>
     119  <node id='-102264' timestamp='2016-01-03T12:02:18Z' lat='47.19734919257' lon='8.79816164263'>
     120    <tag k='gpx:ele' v='481.18000000000001' />
     121    <tag k='gpx:time' v='2016-01-03T12:02:18Z' />
     122  </node>
     123  <node id='-102265' timestamp='2016-01-03T12:02:20Z' lat='47.19734827057' lon='8.79811889492'>
     124    <tag k='gpx:ele' v='481.88' />
     125    <tag k='gpx:time' v='2016-01-03T12:02:20Z' />
     126  </node>
     127  <node id='-102266' timestamp='2016-01-03T12:02:24Z' lat='47.19730585814' lon='8.79802887328'>
     128    <tag k='gpx:ele' v='483.11000000000001' />
     129    <tag k='gpx:time' v='2016-01-03T12:02:24Z' />
     130  </node>
     131  <node id='-102267' timestamp='2016-01-03T12:02:27Z' lat='47.19727643766' lon='8.79797455855'>
     132    <tag k='gpx:ele' v='484.49000000000001' />
     133    <tag k='gpx:time' v='2016-01-03T12:02:27Z' />
     134  </node>
     135  <node id='-102268' timestamp='2016-01-03T12:02:35Z' lat='47.19720158726' lon='8.79776844755'>
     136    <tag k='gpx:ele' v='486.88' />
     137    <tag k='gpx:time' v='2016-01-03T12:02:35Z' />
     138  </node>
     139  <node id='-102269' timestamp='2016-01-03T12:02:48Z' lat='47.19708633609' lon='8.79746401682'>
     140    <tag k='gpx:ele' v='488.05000000000001' />
     141    <tag k='gpx:time' v='2016-01-03T12:02:48Z' />
     142  </node>
     143  <node id='-102270' timestamp='2016-01-03T12:02:51Z' lat='47.1970700752' lon='8.79741758108'>
     144    <tag k='gpx:ele' v='487.76999999999998' />
     145    <tag k='gpx:time' v='2016-01-03T12:02:51Z' />
     146  </node>
     147  <node id='-102271' timestamp='2016-01-03T12:02:57Z' lat='47.19702917151' lon='8.79725052975'>
     148    <tag k='gpx:ele' v='488.95999999999998' />
     149    <tag k='gpx:time' v='2016-01-03T12:02:57Z' />
     150  </node>
     151  <node id='-102272' timestamp='2016-01-03T12:03:01Z' lat='47.19699899666' lon='8.79717215896'>
     152    <tag k='gpx:ele' v='488.36000000000001' />
     153    <tag k='gpx:time' v='2016-01-03T12:03:01Z' />
     154  </node>
     155  <node id='-102273' timestamp='2016-01-03T12:03:09Z' lat='47.19691224396' lon='8.79697803408'>
     156    <tag k='gpx:ele' v='488.35000000000002' />
     157    <tag k='gpx:time' v='2016-01-03T12:03:09Z' />
     158  </node>
     159  <node id='-102274' timestamp='2016-01-03T12:03:10Z' lat='47.19690545462' lon='8.79693126306'>
     160    <tag k='gpx:ele' v='487.19' />
     161    <tag k='gpx:time' v='2016-01-03T12:03:10Z' />
     162  </node>
     163  <node id='-102275' timestamp='2016-01-03T12:03:11Z' lat='47.19690595753' lon='8.79688667133'>
     164    <tag k='gpx:ele' v='487.63999999999999' />
     165    <tag k='gpx:time' v='2016-01-03T12:03:11Z' />
     166  </node>
     167  <node id='-102276' timestamp='2016-01-03T12:03:12Z' lat='47.19690511934' lon='8.79683738574'>
     168    <tag k='gpx:ele' v='487.55000000000001' />
     169    <tag k='gpx:time' v='2016-01-03T12:03:12Z' />
     170  </node>
     171  <node id='-102277' timestamp='2016-01-03T12:03:13Z' lat='47.19690562226' lon='8.79679371603'>
     172    <tag k='gpx:ele' v='488.39999999999998' />
     173    <tag k='gpx:time' v='2016-01-03T12:03:13Z' />
     174  </node>
     175  <node id='-102278' timestamp='2016-01-03T12:03:15Z' lat='47.19688760117' lon='8.79675113596'>
     176    <tag k='gpx:ele' v='489.18000000000001' />
     177    <tag k='gpx:time' v='2016-01-03T12:03:15Z' />
     178  </node>
     179  <node id='-102279' timestamp='2016-01-03T12:03:18Z' lat='47.19688927755' lon='8.79668936133'>
     180    <tag k='gpx:ele' v='490.73000000000002' />
     181    <tag k='gpx:time' v='2016-01-03T12:03:18Z' />
     182  </node>
     183  <node id='-102280' timestamp='2016-01-03T12:03:33Z' lat='47.19686815515' lon='8.79647453316'>
     184    <tag k='gpx:ele' v='491.24000000000001' />
     185    <tag k='gpx:time' v='2016-01-03T12:03:33Z' />
     186  </node>
     187  <node id='-102281' timestamp='2016-01-03T12:03:34Z' lat='47.19686932862' lon='8.79644243047'>
     188    <tag k='gpx:ele' v='490.25' />
     189    <tag k='gpx:time' v='2016-01-03T12:03:34Z' />
     190  </node>
     191  <node id='-102282' timestamp='2016-01-03T12:03:35Z' lat='47.19686849043' lon='8.79640663974'>
     192    <tag k='gpx:ele' v='490.00999999999999' />
     193    <tag k='gpx:time' v='2016-01-03T12:03:35Z' />
     194  </node>
     195  <node id='-102283' timestamp='2016-01-03T12:03:36Z' lat='47.19687301666' lon='8.7963681668'>
     196    <tag k='gpx:ele' v='489.54000000000002' />
     197    <tag k='gpx:time' v='2016-01-03T12:03:36Z' />
     198  </node>
     199  <node id='-102284' timestamp='2016-01-03T12:03:37Z' lat='47.19687905163' lon='8.79632961005'>
     200    <tag k='gpx:ele' v='489.66000000000003' />
     201    <tag k='gpx:time' v='2016-01-03T12:03:37Z' />
     202  </node>
     203  <node id='-102285' timestamp='2016-01-03T12:03:38Z' lat='47.19688676298' lon='8.79628074355'>
     204    <tag k='gpx:ele' v='489.06' />
     205    <tag k='gpx:time' v='2016-01-03T12:03:38Z' />
     206  </node>
     207  <node id='-102286' timestamp='2016-01-03T12:03:48Z' lat='47.1969577577' lon='8.79582912661'>
     208    <tag k='gpx:ele' v='490.08999999999997' />
     209    <tag k='gpx:time' v='2016-01-03T12:03:48Z' />
     210  </node>
     211  <node id='-102287' timestamp='2016-01-03T12:03:52Z' lat='47.19699287787' lon='8.79570951685'>
     212    <tag k='gpx:ele' v='489.41000000000003' />
     213    <tag k='gpx:time' v='2016-01-03T12:03:52Z' />
     214  </node>
     215  <node id='-102288' timestamp='2016-01-03T12:03:53Z' lat='47.1969855018' lon='8.79566760734'>
     216    <tag k='gpx:ele' v='489.17000000000002' />
     217    <tag k='gpx:time' v='2016-01-03T12:03:53Z' />
     218  </node>
     219  <node id='-102289' timestamp='2016-01-03T12:03:54Z' lat='47.19697644934' lon='8.79562460817'>
     220    <tag k='gpx:ele' v='488.87' />
     221    <tag k='gpx:time' v='2016-01-03T12:03:54Z' />
     222  </node>
     223  <node id='-102290' timestamp='2016-01-03T12:03:55Z' lat='47.19696890563' lon='8.79559032619'>
     224    <tag k='gpx:ele' v='489.37' />
     225    <tag k='gpx:time' v='2016-01-03T12:03:55Z' />
     226  </node>
     227  <node id='-102291' timestamp='2016-01-03T12:03:56Z' lat='47.19696664251' lon='8.79555839114'>
     228    <tag k='gpx:ele' v='488.94999999999999' />
     229    <tag k='gpx:time' v='2016-01-03T12:03:56Z' />
     230  </node>
     231  <node id='-102292' timestamp='2016-01-03T12:03:57Z' lat='47.19695256092' lon='8.79553676583'>
     232    <tag k='gpx:ele' v='488.93000000000001' />
     233    <tag k='gpx:time' v='2016-01-03T12:03:57Z' />
     234  </node>
     235  <node id='-102293' timestamp='2016-01-03T12:03:58Z' lat='47.19695650041' lon='8.79550768062'>
     236    <tag k='gpx:ele' v='488.88999999999999' />
     237    <tag k='gpx:time' v='2016-01-03T12:03:58Z' />
     238  </node>
     239  <node id='-102294' timestamp='2016-01-03T12:03:59Z' lat='47.19695859589' lon='8.79548110999'>
     240    <tag k='gpx:ele' v='488.86000000000001' />
     241    <tag k='gpx:time' v='2016-01-03T12:03:59Z' />
     242  </node>
     243  <node id='-102295' timestamp='2016-01-03T12:04:00Z' lat='47.19696907327' lon='8.7954525277'>
     244    <tag k='gpx:ele' v='489.19999999999999' />
     245    <tag k='gpx:time' v='2016-01-03T12:04:00Z' />
     246  </node>
     247  <node id='-102296' timestamp='2016-01-03T12:04:01Z' lat='47.19697988592' lon='8.79541271366'>
     248    <tag k='gpx:ele' v='489.29000000000002' />
     249    <tag k='gpx:time' v='2016-01-03T12:04:01Z' />
     250  </node>
     251  <node id='-102297' timestamp='2016-01-03T12:04:02Z' lat='47.196987262' lon='8.79535529763'>
     252    <tag k='gpx:ele' v='488.95999999999998' />
     253    <tag k='gpx:time' v='2016-01-03T12:04:02Z' />
     254  </node>
     255  <node id='-102298' timestamp='2016-01-03T12:04:03Z' lat='47.19698960893' lon='8.79530257545'>
     256    <tag k='gpx:ele' v='489.10000000000002' />
     257    <tag k='gpx:time' v='2016-01-03T12:04:03Z' />
     258  </node>
     259  <node id='-102299' timestamp='2016-01-03T12:04:14Z' lat='47.19690981321' lon='8.79494265653'>
     260    <tag k='gpx:ele' v='492.19' />
     261    <tag k='gpx:time' v='2016-01-03T12:04:14Z' />
     262  </node>
     263  <node id='-102300' timestamp='2016-01-03T12:04:25Z' lat='47.19696622342' lon='8.7945320271'>
     264    <tag k='gpx:ele' v='492.12' />
     265    <tag k='gpx:time' v='2016-01-03T12:04:25Z' />
     266  </node>
     267  <node id='-102301' timestamp='2016-01-03T12:04:26Z' lat='47.19697737135' lon='8.79450285807'>
     268    <tag k='gpx:ele' v='493.06999999999999' />
     269    <tag k='gpx:time' v='2016-01-03T12:04:26Z' />
     270  </node>
     271  <node id='-102302' timestamp='2016-01-03T12:04:27Z' lat='47.1969921235' lon='8.79445097409'>
     272    <tag k='gpx:ele' v='492.58999999999997' />
     273    <tag k='gpx:time' v='2016-01-03T12:04:27Z' />
     274  </node>
     275  <node id='-102303' timestamp='2016-01-03T12:04:28Z' lat='47.19699405134' lon='8.79439900629'>
     276    <tag k='gpx:ele' v='492.38999999999999' />
     277    <tag k='gpx:time' v='2016-01-03T12:04:28Z' />
     278  </node>
     279  <node id='-102304' timestamp='2016-01-03T12:04:29Z' lat='47.1969886031' lon='8.7943501398'>
     280    <tag k='gpx:ele' v='492.94' />
     281    <tag k='gpx:time' v='2016-01-03T12:04:29Z' />
     282  </node>
     283  <node id='-102305' timestamp='2016-01-03T12:04:30Z' lat='47.19698214903' lon='8.79429892637'>
     284    <tag k='gpx:ele' v='492.47000000000003' />
     285    <tag k='gpx:time' v='2016-01-03T12:04:30Z' />
     286  </node>
     287  <node id='-102306' timestamp='2016-01-03T12:04:31Z' lat='47.19697737135' lon='8.79424679093'>
     288    <tag k='gpx:ele' v='492.50999999999999' />
     289    <tag k='gpx:time' v='2016-01-03T12:04:31Z' />
     290  </node>
     291  <node id='-102307' timestamp='2016-01-03T12:04:32Z' lat='47.19698206522' lon='8.79418929107'>
     292    <tag k='gpx:ele' v='492.73000000000002' />
     293    <tag k='gpx:time' v='2016-01-03T12:04:32Z' />
     294  </node>
     295  <node id='-102308' timestamp='2016-01-03T12:04:33Z' lat='47.19698541798' lon='8.79413371906'>
     296    <tag k='gpx:ele' v='492.63' />
     297    <tag k='gpx:time' v='2016-01-03T12:04:33Z' />
     298  </node>
     299  <node id='-102309' timestamp='2016-01-03T12:04:34Z' lat='47.19698768109' lon='8.79407764412'>
     300    <tag k='gpx:ele' v='493' />
     301    <tag k='gpx:time' v='2016-01-03T12:04:34Z' />
     302  </node>
     303  <node id='-102310' timestamp='2016-01-03T12:04:35Z' lat='47.19699136913' lon='8.79402232356'>
     304    <tag k='gpx:ele' v='493.00999999999999' />
     305    <tag k='gpx:time' v='2016-01-03T12:04:35Z' />
     306  </node>
     307  <node id='-102311' timestamp='2016-01-03T12:04:36Z' lat='47.19698784873' lon='8.79397655837'>
     308    <tag k='gpx:ele' v='493.19999999999999' />
     309    <tag k='gpx:time' v='2016-01-03T12:04:36Z' />
     310  </node>
     311  <node id='-102312' timestamp='2016-01-03T12:04:37Z' lat='47.1969855018' lon='8.79392693751'>
     312    <tag k='gpx:ele' v='493.18000000000001' />
     313    <tag k='gpx:time' v='2016-01-03T12:04:37Z' />
     314  </node>
     315  <node id='-102313' timestamp='2016-01-03T12:04:38Z' lat='47.19698055647' lon='8.79388569854'>
     316    <tag k='gpx:ele' v='493' />
     317    <tag k='gpx:time' v='2016-01-03T12:04:38Z' />
     318  </node>
     319  <node id='-102314' timestamp='2016-01-03T12:04:39Z' lat='47.19698307104' lon='8.79383733496'>
     320    <tag k='gpx:ele' v='493.20999999999998' />
     321    <tag k='gpx:time' v='2016-01-03T12:04:39Z' />
     322  </node>
     323  <node id='-102315' timestamp='2016-01-03T12:04:40Z' lat='47.19699229114' lon='8.79378695972'>
     324    <tag k='gpx:ele' v='493.11000000000001' />
     325    <tag k='gpx:time' v='2016-01-03T12:04:40Z' />
     326  </node>
     327  <node id='-102316' timestamp='2016-01-03T12:04:41Z' lat='47.19699547626' lon='8.79374362528'>
     328    <tag k='gpx:ele' v='493.30000000000001' />
     329    <tag k='gpx:time' v='2016-01-03T12:04:41Z' />
     330  </node>
     331  <node id='-102317' timestamp='2016-01-03T12:04:42Z' lat='47.1970026847' lon='8.79369450733'>
     332    <tag k='gpx:ele' v='493.50999999999999' />
     333    <tag k='gpx:time' v='2016-01-03T12:04:42Z' />
     334  </node>
     335  <node id='-102318' timestamp='2016-01-03T12:04:50Z' lat='47.19700511545' lon='8.79335780628'>
     336    <tag k='gpx:ele' v='493' />
     337    <tag k='gpx:time' v='2016-01-03T12:04:50Z' />
     338  </node>
     339  <node id='-102319' timestamp='2016-01-03T12:04:51Z' lat='47.1970216278' lon='8.79331614822'>
     340    <tag k='gpx:ele' v='492.37' />
     341    <tag k='gpx:time' v='2016-01-03T12:04:51Z' />
     342  </node>
     343  <node id='-102320' timestamp='2016-01-03T12:04:52Z' lat='47.19704459421' lon='8.7932623364'>
     344    <tag k='gpx:ele' v='492.30000000000001' />
     345    <tag k='gpx:time' v='2016-01-03T12:04:52Z' />
     346  </node>
     347  <node id='-102321' timestamp='2016-01-03T12:04:53Z' lat='47.19706906937' lon='8.79320106469'>
     348    <tag k='gpx:ele' v='492.04000000000002' />
     349    <tag k='gpx:time' v='2016-01-03T12:04:53Z' />
     350  </node>
     351  <node id='-102322' timestamp='2016-01-03T12:04:54Z' lat='47.19708985649' lon='8.79312378354'>
     352    <tag k='gpx:ele' v='491.37' />
     353    <tag k='gpx:time' v='2016-01-03T12:04:54Z' />
     354  </node>
     355  <node id='-102323' timestamp='2016-01-03T12:04:55Z' lat='47.19710913487' lon='8.79304625094'>
     356    <tag k='gpx:ele' v='491.06999999999999' />
     357    <tag k='gpx:time' v='2016-01-03T12:04:55Z' />
     358  </node>
     359  <node id='-102324' timestamp='2016-01-03T12:04:56Z' lat='47.19713117927' lon='8.79297458567'>
     360    <tag k='gpx:ele' v='490.39999999999998' />
     361    <tag k='gpx:time' v='2016-01-03T12:04:56Z' />
     362  </node>
     363  <node id='-102325' timestamp='2016-01-03T12:04:58Z' lat='47.19718624838' lon='8.79280988127'>
     364    <tag k='gpx:ele' v='489.75' />
     365    <tag k='gpx:time' v='2016-01-03T12:04:58Z' />
     366  </node>
     367  <node id='-102326' timestamp='2016-01-03T12:04:59Z' lat='47.19720753841' lon='8.79271550104'>
     368    <tag k='gpx:ele' v='489.87' />
     369    <tag k='gpx:time' v='2016-01-03T12:04:59Z' />
     370  </node>
     371  <node id='-102327' timestamp='2016-01-03T12:05:00Z' lat='47.19722296111' lon='8.79262028262'>
     372    <tag k='gpx:ele' v='488.95999999999998' />
     373    <tag k='gpx:time' v='2016-01-03T12:05:00Z' />
     374  </node>
     375  <node id='-102328' timestamp='2016-01-03T12:05:06Z' lat='47.19733930193' lon='8.79204344004'>
     376    <tag k='gpx:ele' v='485.85000000000002' />
     377    <tag k='gpx:time' v='2016-01-03T12:05:06Z' />
     378  </node>
     379  <node id='-102329' timestamp='2016-01-03T12:05:11Z' lat='47.19745220616' lon='8.79151621833'>
     380    <tag k='gpx:ele' v='482.81' />
     381    <tag k='gpx:time' v='2016-01-03T12:05:11Z' />
     382  </node>
     383  <node id='-102330' timestamp='2016-01-03T12:05:12Z' lat='47.19745547511' lon='8.79138101824'>
     384    <tag k='gpx:ele' v='481.99000000000001' />
     385    <tag k='gpx:time' v='2016-01-03T12:05:12Z' />
     386  </node>
     387  <node id='-102331' lat='47.19749009237' lon='8.79095789976'>
     388    <tag k='gpx:ele' v='478.92' />
     389  </node>
     390  <node id='-102332' lat='47.19753225334' lon='8.7905339431'>
     391    <tag k='gpx:ele' v='476' />
     392  </node>
     393  <node id='-102333' timestamp='2016-01-03T12:05:23Z' lat='47.19762864523' lon='8.78988945857'>
     394    <tag k='gpx:ele' v='474.38' />
     395    <tag k='gpx:time' v='2016-01-03T12:05:23Z' />
     396  </node>
     397  <node id='-102334' timestamp='2016-01-03T12:05:24Z' lat='47.19763744622' lon='8.78979289904'>
     398    <tag k='gpx:ele' v='474.35000000000002' />
     399    <tag k='gpx:time' v='2016-01-03T12:05:24Z' />
     400  </node>
     401  <node id='-102335' timestamp='2016-01-03T12:05:25Z' lat='47.19764381647' lon='8.78972098231'>
     402    <tag k='gpx:ele' v='474.12' />
     403    <tag k='gpx:time' v='2016-01-03T12:05:25Z' />
     404  </node>
     405  <node id='-102336' timestamp='2016-01-03T12:05:33Z' lat='47.19764859416' lon='8.78936776891'>
     406    <tag k='gpx:ele' v='472.52999999999997' />
     407    <tag k='gpx:time' v='2016-01-03T12:05:33Z' />
     408  </node>
     409  <node id='-102337' timestamp='2016-01-03T12:05:34Z' lat='47.19765613787' lon='8.78931068815'>
     410    <tag k='gpx:ele' v='472.75' />
     411    <tag k='gpx:time' v='2016-01-03T12:05:34Z' />
     412  </node>
     413  <node id='-102338' timestamp='2016-01-03T12:05:37Z' lat='47.19769033603' lon='8.78910541534'>
     414    <tag k='gpx:ele' v='472.19' />
     415    <tag k='gpx:time' v='2016-01-03T12:05:37Z' />
     416  </node>
     417  <node id='-102339' timestamp='2016-01-03T12:05:38Z' lat='47.19770072959' lon='8.78903266042'>
     418    <tag k='gpx:ele' v='471.92000000000002' />
     419    <tag k='gpx:time' v='2016-01-03T12:05:38Z' />
     420  </node>
     421  <node id='-102340' timestamp='2016-01-03T12:05:41Z' lat='47.19771590084' lon='8.78876687028'>
     422    <tag k='gpx:ele' v='471.98000000000002' />
     423    <tag k='gpx:time' v='2016-01-03T12:05:41Z' />
     424  </node>
     425  <node id='-102341' timestamp='2016-01-03T12:05:47Z' lat='47.19775462523' lon='8.78843285143'>
     426    <tag k='gpx:ele' v='473.49000000000001' />
     427    <tag k='gpx:time' v='2016-01-03T12:05:47Z' />
     428  </node>
     429  <node id='-102342' timestamp='2016-01-03T12:05:48Z' lat='47.19777750783' lon='8.78841600381'>
     430    <tag k='gpx:ele' v='473.44999999999999' />
     431    <tag k='gpx:time' v='2016-01-03T12:05:48Z' />
     432  </node>
     433  <node id='-102343' timestamp='2016-01-03T12:05:49Z' lat='47.19780064188' lon='8.78840535879'>
     434    <tag k='gpx:ele' v='473.45999999999998' />
     435    <tag k='gpx:time' v='2016-01-03T12:05:49Z' />
     436  </node>
     437  <node id='-102344' timestamp='2016-01-03T12:05:53Z' lat='47.1979636699' lon='8.78838649951'>
     438    <tag k='gpx:ele' v='470.27999999999997' />
     439    <tag k='gpx:time' v='2016-01-03T12:05:53Z' />
     440  </node>
     441  <node id='-102345' timestamp='2016-01-03T12:05:57Z' lat='47.19813088886' lon='8.78829589114'>
     442    <tag k='gpx:ele' v='467.20999999999998' />
     443    <tag k='gpx:time' v='2016-01-03T12:05:57Z' />
     444  </node>
     445  <node id='-102346' timestamp='2016-01-03T12:05:58Z' lat='47.1981363371' lon='8.78826286644'>
     446    <tag k='gpx:ele' v='466.89999999999998' />
     447    <tag k='gpx:time' v='2016-01-03T12:05:58Z' />
     448  </node>
     449  <node id='-102347' timestamp='2016-01-03T12:05:59Z' lat='47.19812661409' lon='8.78822925501'>
     450    <tag k='gpx:ele' v='466.76999999999998' />
     451    <tag k='gpx:time' v='2016-01-03T12:05:59Z' />
     452  </node>
     453  <node id='-102348' timestamp='2016-01-03T12:06:00Z' lat='47.19810306095' lon='8.78818617202'>
     454    <tag k='gpx:ele' v='466.25' />
     455    <tag k='gpx:time' v='2016-01-03T12:06:00Z' />
     456  </node>
     457  <node id='-102349' timestamp='2016-01-03T12:06:01Z' lat='47.19808369875' lon='8.78812741488'>
     458    <tag k='gpx:ele' v='465.23000000000002' />
     459    <tag k='gpx:time' v='2016-01-03T12:06:01Z' />
     460  </node>
     461  <node id='-102350' timestamp='2016-01-03T12:06:02Z' lat='47.19807816669' lon='8.78805994056'>
     462    <tag k='gpx:ele' v='464.12' />
     463    <tag k='gpx:time' v='2016-01-03T12:06:02Z' />
     464  </node>
     465  <node id='-102351' timestamp='2016-01-03T12:06:03Z' lat='47.19808881171' lon='8.78799137659'>
     466    <tag k='gpx:ele' v='463.68000000000001' />
     467    <tag k='gpx:time' v='2016-01-03T12:06:03Z' />
     468  </node>
     469  <node id='-102352' timestamp='2016-01-03T12:06:04Z' lat='47.19811093993' lon='8.7879143469'>
     470    <tag k='gpx:ele' v='462.5' />
     471    <tag k='gpx:time' v='2016-01-03T12:06:04Z' />
     472  </node>
     473  <node id='-102353' timestamp='2016-01-03T12:06:05Z' lat='47.19815100543' lon='8.78783798777'>
     474    <tag k='gpx:ele' v='462.45999999999998' />
     475    <tag k='gpx:time' v='2016-01-03T12:06:05Z' />
     476  </node>
     477  <node id='-102354' timestamp='2016-01-03T12:06:06Z' lat='47.1981949266' lon='8.78775693476'>
     478    <tag k='gpx:ele' v='461.56999999999999' />
     479    <tag k='gpx:time' v='2016-01-03T12:06:06Z' />
     480  </node>
     481  <node id='-102355' timestamp='2016-01-03T12:06:08Z' lat='47.19828603789' lon='8.78758946434'>
     482    <tag k='gpx:ele' v='461.14999999999998' />
     483    <tag k='gpx:time' v='2016-01-03T12:06:08Z' />
     484  </node>
     485  <node id='-102356' timestamp='2016-01-03T12:06:09Z' lat='47.19831814058' lon='8.78749449737'>
     486    <tag k='gpx:ele' v='460.19' />
     487    <tag k='gpx:time' v='2016-01-03T12:06:09Z' />
     488  </node>
     489  <node id='-102357' timestamp='2016-01-03T12:06:10Z' lat='47.19833096489' lon='8.78738905303'>
     490    <tag k='gpx:ele' v='459.83999999999997' />
     491    <tag k='gpx:time' v='2016-01-03T12:06:10Z' />
     492  </node>
     493  <node id='-102358' timestamp='2016-01-03T12:06:11Z' lat='47.19833155163' lon='8.7872845307'>
     494    <tag k='gpx:ele' v='460.49000000000001' />
     495    <tag k='gpx:time' v='2016-01-03T12:06:11Z' />
     496  </node>
     497  <node id='-102359' timestamp='2016-01-03T12:06:12Z' lat='47.1983252652' lon='8.78718562424'>
     498    <tag k='gpx:ele' v='460.23000000000002' />
     499    <tag k='gpx:time' v='2016-01-03T12:06:12Z' />
     500  </node>
     501  <node id='-102360' timestamp='2016-01-03T12:06:22Z' lat='47.198242452' lon='8.78635254689'>
     502    <tag k='gpx:ele' v='460.56999999999999' />
     503    <tag k='gpx:time' v='2016-01-03T12:06:22Z' />
     504  </node>
     505  <node id='-102361' timestamp='2016-01-03T12:06:27Z' lat='47.19819274731' lon='8.78586145118'>
     506    <tag k='gpx:ele' v='457.79000000000002' />
     507    <tag k='gpx:time' v='2016-01-03T12:06:27Z' />
     508  </node>
     509  <node id='-102362' timestamp='2016-01-03T12:06:31Z' lat='47.19819249585' lon='8.78536943346'>
     510    <tag k='gpx:ele' v='454.66000000000003' />
     511    <tag k='gpx:time' v='2016-01-03T12:06:31Z' />
     512  </node>
     513  <node id='-102364' lat='47.18660652637' lon='8.79692137241'>
     514    <tag k='gpx:ele' v='624.7421875' />
     515  </node>
     516  <node id='-102365' lat='47.1866440773' lon='8.79682481289'>
     517    <tag k='gpx:ele' v='626.015625' />
     518  </node>
     519  <node id='-102366' lat='47.18669772148' lon='8.79671216011'>
     520    <tag k='gpx:ele' v='627.0625' />
     521  </node>
     522  <node id='-102367' lat='47.1867620945' lon='8.79661023617'>
     523    <tag k='gpx:ele' v='627.3671875' />
     524  </node>
     525  <node id='-102368' lat='47.18683719635' lon='8.79651904106'>
     526    <tag k='gpx:ele' v='626.90234375' />
     527  </node>
     528  <node id='-102369' lat='47.1869122982' lon='8.79640102386'>
     529    <tag k='gpx:ele' v='626.11328125' />
     530  </node>
     531  <node id='-102370' lat='47.18698740005' lon='8.79627764225'>
     532    <tag k='gpx:ele' v='625.33203125' />
     533  </node>
     534  <node id='-102371' lat='47.18707859516' lon='8.79614889622'>
     535    <tag k='gpx:ele' v='623.90625' />
     536  </node>
     537  <node id='-102372' lat='47.18710541725' lon='8.79609525204'>
     538    <tag k='gpx:ele' v='623.66796875' />
     539  </node>
     540  <node id='-102373' lat='47.18712687492' lon='8.79603087902'>
     541    <tag k='gpx:ele' v='623.71484375' />
     542  </node>
     543  <node id='-102374' lat='47.18713223934' lon='8.79597723484'>
     544    <tag k='gpx:ele' v='623.75' />
     545  </node>
     546  <node id='-102375' lat='47.18713223934' lon='8.79592359066'>
     547    <tag k='gpx:ele' v='623.9375' />
     548  </node>
     549  <node id='-102376' lat='47.18713223934' lon='8.79585921764'>
     550    <tag k='gpx:ele' v='624.1640625' />
     551  </node>
     552  <node id='-102377' lat='47.18711078167' lon='8.79573583603'>
     553    <tag k='gpx:ele' v='625.234375' />
     554  </node>
     555  <node id='-102378' lat='47.18710005283' lon='8.79560709'>
     556    <tag k='gpx:ele' v='626' />
     557  </node>
     558  <node id='-102379' lat='47.18709468842' lon='8.79552125931'>
     559    <tag k='gpx:ele' v='626.3515625' />
     560  </node>
     561  <node id='-102380' lat='47.18710541725' lon='8.79544079304'>
     562    <tag k='gpx:ele' v='626.14453125' />
     563  </node>
     564  <node id='-102381' lat='47.18712687492' lon='8.79536032677'>
     565    <tag k='gpx:ele' v='625.6640625' />
     566  </node>
     567  <node id='-102382' lat='47.18716442585' lon='8.79528522491'>
     568    <tag k='gpx:ele' v='624.7890625' />
     569  </node>
     570  <node id='-102383' lat='47.18723416328' lon='8.79518330097'>
     571    <tag k='gpx:ele' v='623.23046875' />
     572  </node>
     573  <node id='-102384' lat='47.18729317188' lon='8.79508674145'>
     574    <tag k='gpx:ele' v='622.09375' />
     575  </node>
     576  <node id='-102385' lat='47.18736290932' lon='8.7949848175'>
     577    <tag k='gpx:ele' v='620.9765625' />
     578  </node>
     579  <node id='-102386' lat='47.1874165535' lon='8.79490435123'>
     580    <tag k='gpx:ele' v='620.23828125' />
     581  </node>
     582  <node id='-102387' lat='47.18746483326' lon='8.79484534264'>
     583    <tag k='gpx:ele' v='619.42578125' />
     584  </node>
     585  <node id='-102388' lat='47.18752384186' lon='8.79479706287'>
     586    <tag k='gpx:ele' v='617.7734375' />
     587  </node>
     588  <node id='-102389' lat='47.18756139278' lon='8.79474341869'>
     589    <tag k='gpx:ele' v='616.8671875' />
     590  </node>
     591  <node id='-102390' lat='47.18759357929' lon='8.79469513893'>
     592    <tag k='gpx:ele' v='616.09765625' />
     593  </node>
     594  <node id='-102391' lat='47.18760967255' lon='8.79463076591'>
     595    <tag k='gpx:ele' v='615.87890625' />
     596  </node>
     597  <node id='-102392' lat='47.18762040138' lon='8.79455566406'>
     598    <tag k='gpx:ele' v='615.3984375' />
     599  </node>
     600  <node id='-102393' lat='47.18763649464' lon='8.79448592663'>
     601    <tag k='gpx:ele' v='614.76953125' />
     602  </node>
     603  <node id='-102394' lat='47.18767404556' lon='8.79440009594'>
     604    <tag k='gpx:ele' v='613.47265625' />
     605  </node>
     606  <node id='-102395' lat='47.18769550323' lon='8.79430890083'>
     607    <tag k='gpx:ele' v='612.72265625' />
     608  </node>
     609  <node id='-102396' lat='47.18770086765' lon='8.79418551922'>
     610    <tag k='gpx:ele' v='612.4609375' />
     611  </node>
     612  <node id='-102397' lat='47.18770086765' lon='8.79407823086'>
     613    <tag k='gpx:ele' v='611.85546875' />
     614  </node>
     615  <node id='-102398' lat='47.1876847744' lon='8.79399776459'>
     616    <tag k='gpx:ele' v='611.90625' />
     617  </node>
     618  <node id='-102399' lat='47.18766331673' lon='8.79393339157'>
     619    <tag k='gpx:ele' v='612.27734375' />
     620  </node>
     621  <node id='-102400' lat='47.18761503696' lon='8.79386901855'>
     622    <tag k='gpx:ele' v='613.66796875' />
     623  </node>
     624  <node id='-102401' lat='47.18758285046' lon='8.79381537437'>
     625    <tag k='gpx:ele' v='614.62890625' />
     626  </node>
     627  <node id='-102402' lat='47.1875667572' lon='8.79376709461'>
     628    <tag k='gpx:ele' v='615.0703125' />
     629  </node>
     630  <node id='-102403' lat='47.18757212162' lon='8.79371345043'>
     631    <tag k='gpx:ele' v='614.72265625' />
     632  </node>
     633  <node id='-102404' lat='47.18758821487' lon='8.79368126392'>
     634    <tag k='gpx:ele' v='614.26171875' />
     635  </node>
     636  <node id='-102405' lat='47.18764185905' lon='8.79368126392'>
     637    <tag k='gpx:ele' v='612.06640625' />
     638  </node>
     639  <node id='-102406' lat='47.18770086765' lon='8.79370808601'>
     640    <tag k='gpx:ele' v='609.6875' />
     641  </node>
     642  <node id='-102407' lat='47.18778133392' lon='8.79375100136'>
     643    <tag k='gpx:ele' v='606.83984375' />
     644  </node>
     645  <node id='-102408' lat='47.18785107136' lon='8.79380464554'>
     646    <tag k='gpx:ele' v='604.671875' />
     647  </node>
     648  <node id='-102409' lat='47.18792080879' lon='8.79384219646'>
     649    <tag k='gpx:ele' v='602.52734375' />
     650  </node>
     651  <node id='-102410' lat='47.18800127506' lon='8.7939119339'>
     652    <tag k='gpx:ele' v='600.78515625' />
     653  </node>
     654  <node id='-102411' lat='47.18811392784' lon='8.79401385784'>
     655    <tag k='gpx:ele' v='598.078125' />
     656  </node>
     657  <node id='-102412' lat='47.18815147877' lon='8.7940621376'>
     658    <tag k='gpx:ele' v='597.1953125' />
     659  </node>
     660  <node id='-102413' lat='47.18818902969' lon='8.79415333271'>
     661    <tag k='gpx:ele' v='596.5234375' />
     662  </node>
     663  <node id='-102414' lat='47.18821585178' lon='8.7942391634'>
     664    <tag k='gpx:ele' v='595.74609375' />
     665  </node>
     666  <node id='-102415' lat='47.18824267387' lon='8.79435181618'>
     667    <tag k='gpx:ele' v='594.82421875' />
     668  </node>
     669  <node id='-102416' lat='47.18826413155' lon='8.79442691803'>
     670    <tag k='gpx:ele' v='594.10546875' />
     671  </node>
     672  <node id='-102417' lat='47.18829095364' lon='8.79447519779'>
     673    <tag k='gpx:ele' v='593.2109375' />
     674  </node>
     675  <node id='-102418' lat='47.18830704689' lon='8.79452347755'>
     676    <tag k='gpx:ele' v='592.6875' />
     677  </node>
     678  <node id='-102419' lat='47.18831241131' lon='8.79461467266'>
     679    <tag k='gpx:ele' v='592.5390625' />
     680  </node>
     681  <node id='-102420' lat='47.18830704689' lon='8.79479706287'>
     682    <tag k='gpx:ele' v='592.7578125' />
     683  </node>
     684  <node id='-102421' lat='47.18830704689' lon='8.79488825798'>
     685    <tag k='gpx:ele' v='592.77734375' />
     686  </node>
     687  <node id='-102422' lat='47.18831241131' lon='8.79494190216'>
     688    <tag k='gpx:ele' v='592.63671875' />
     689  </node>
     690  <node id='-102423' lat='47.18833386898' lon='8.79499554634'>
     691    <tag k='gpx:ele' v='592.06640625' />
     692  </node>
     693  <node id='-102424' lat='47.18836605549' lon='8.79505991936'>
     694    <tag k='gpx:ele' v='591.2578125' />
     695  </node>
     696  <node id='-102425' lat='47.18840360641' lon='8.79511356354'>
     697    <tag k='gpx:ele' v='590.46875' />
     698  </node>
     699  <node id='-102426' lat='47.18843579292' lon='8.79516720772'>
     700    <tag k='gpx:ele' v='589.7578125' />
     701  </node>
     702  <node id='-102427' lat='47.18845188618' lon='8.79522621632'>
     703    <tag k='gpx:ele' v='589.359375' />
     704  </node>
     705  <node id='-102428' lat='47.18846261501' lon='8.79529058933'>
     706    <tag k='gpx:ele' v='589.05859375' />
     707  </node>
     708  <node id='-102429' lat='47.18845188618' lon='8.79535496235'>
     709    <tag k='gpx:ele' v='589.21875' />
     710  </node>
     711  <node id='-102430' lat='47.1884304285' lon='8.79545152187'>
     712    <tag k='gpx:ele' v='589.59375' />
     713  </node>
     714  <node id='-102431' lat='47.18840897083' lon='8.79556417465'>
     715    <tag k='gpx:ele' v='589.98046875' />
     716  </node>
     717  <node id='-102432' lat='47.18840360641' lon='8.79565536976'>
     718    <tag k='gpx:ele' v='589.51953125' />
     719  </node>
     720  <node id='-102433' lat='47.18840897083' lon='8.79574656487'>
     721    <tag k='gpx:ele' v='588.796875' />
     722  </node>
     723  <node id='-102434' lat='47.18843579292' lon='8.79585921764'>
     724    <tag k='gpx:ele' v='587.4375' />
     725  </node>
     726  <node id='-102435' lat='47.18847870827' lon='8.79597187042'>
     727    <tag k='gpx:ele' v='585.70703125' />
     728  </node>
     729  <node id='-102436' lat='47.18850553036' lon='8.79605233669'>
     730    <tag k='gpx:ele' v='584.48046875' />
     731  </node>
     732  <node id='-102437' lat='47.18852162361' lon='8.79613280296'>
     733    <tag k='gpx:ele' v='583.328125' />
     734  </node>
     735  <node id='-102438' lat='47.18852162361' lon='8.79619717598'>
     736    <tag k='gpx:ele' v='582.69921875' />
     737  </node>
     738  <node id='-102439' lat='47.18851625919' lon='8.79628300667'>
     739    <tag k='gpx:ele' v='581.96875' />
     740  </node>
     741  <node id='-102440' lat='47.18850016594' lon='8.79637956619'>
     742    <tag k='gpx:ele' v='581.3359375' />
     743  </node>
     744  <node id='-102441' lat='47.1884894371' lon='8.79646539688'>
     745    <tag k='gpx:ele' v='580.6796875' />
     746  </node>
     747  <node id='-102442' lat='47.1884894371' lon='8.79652440548'>
     748    <tag k='gpx:ele' v='580.33984375' />
     749  </node>
     750  <node id='-102443' lat='47.18850016594' lon='8.79657804966'>
     751    <tag k='gpx:ele' v='580.0234375' />
     752  </node>
     753  <node id='-102444' lat='47.18852162361' lon='8.79663169384'>
     754    <tag k='gpx:ele' v='579.5078125' />
     755  </node>
     756  <node id='-102445' lat='47.1885484457' lon='8.79668533802'>
     757    <tag k='gpx:ele' v='578.890625' />
     758  </node>
     759  <node id='-102446' lat='47.18856990337' lon='8.79674971104'>
     760    <tag k='gpx:ele' v='578.34375' />
     761  </node>
     762  <node id='-102447' lat='47.18858063221' lon='8.79681944847'>
     763    <tag k='gpx:ele' v='577.984375' />
     764  </node>
     765  <node id='-102448' lat='47.18857526779' lon='8.79689991474'>
     766    <tag k='gpx:ele' v='577.90234375' />
     767  </node>
     768  <node id='-102449' lat='47.18858599663' lon='8.79699110985'>
     769    <tag k='gpx:ele' v='577.50390625' />
     770  </node>
     771  <node id='-102450' lat='47.18859672546' lon='8.79704475403'>
     772    <tag k='gpx:ele' v='577.20703125' />
     773  </node>
     774  <node id='-102451' lat='47.18862354755' lon='8.79711985588'>
     775    <tag k='gpx:ele' v='576.6171875' />
     776  </node>
     777  <node id='-102452' lat='47.1886664629' lon='8.79718959332'>
     778    <tag k='gpx:ele' v='575.82421875' />
     779  </node>
     780  <node id='-102453' lat='47.18870937824' lon='8.79721105099'>
     781    <tag k='gpx:ele' v='575.125' />
     782  </node>
     783  <node id='-102454' lat='47.18871474266' lon='8.79715740681'>
     784    <tag k='gpx:ele' v='575.0546875' />
     785  </node>
     786  <node id='-102455' lat='47.1887254715' lon='8.79705548286'>
     787    <tag k='gpx:ele' v='574.89453125' />
     788  </node>
     789  <node id='-102456' lat='47.18873620033' lon='8.79693746567'>
     790    <tag k='gpx:ele' v='574.75' />
     791  </node>
     792  <node id='-102457' lat='47.188757658' lon='8.79681408405'>
     793    <tag k='gpx:ele' v='574.6484375' />
     794  </node>
     795  <node id='-102458' lat='47.18879520893' lon='8.79668533802'>
     796    <tag k='gpx:ele' v='574.26953125' />
     797  </node>
     798  <node id='-102459' lat='47.18883812428' lon='8.7965887785'>
     799    <tag k='gpx:ele' v='573.72265625' />
     800  </node>
     801  <node id='-102460' lat='47.18890786171' lon='8.79643857479'>
     802    <tag k='gpx:ele' v='572.4296875' />
     803  </node>
     804  <node id='-102461' lat='47.18901515007' lon='8.79624009132'>
     805    <tag k='gpx:ele' v='570.828125' />
     806  </node>
     807  <node id='-102462' lat='47.18906342983' lon='8.79615426064'>
     808    <tag k='gpx:ele' v='570.1015625' />
     809  </node>
     810  <node id='-102463' lat='47.18915462494' lon='8.79598796368'>
     811    <tag k='gpx:ele' v='568.7734375' />
     812  </node>
     813  <node id='-102464' lat='47.18921899796' lon='8.79586994648'>
     814    <tag k='gpx:ele' v='567.8125' />
     815  </node>
     816  <node id='-102465' lat='47.1892619133' lon='8.79577875137'>
     817    <tag k='gpx:ele' v='567.25390625' />
     818  </node>
     819  <node id='-102466' lat='47.18928337097' lon='8.79571437836'>
     820    <tag k='gpx:ele' v='567.09765625' />
     821  </node>
     822  <node id='-102467' lat='47.18929409981' lon='8.79566073418'>
     823    <tag k='gpx:ele' v='567.1640625' />
     824  </node>
     825  <node id='-102468' lat='47.18929946423' lon='8.79558026791'>
     826    <tag k='gpx:ele' v='567.546875' />
     827  </node>
     828  <node id='-102469' lat='47.18929946423' lon='8.79545152187'>
     829    <tag k='gpx:ele' v='567.86328125' />
     830  </node>
     831  <node id='-102470' lat='47.18929409981' lon='8.795312047'>
     832    <tag k='gpx:ele' v='568.2890625' />
     833  </node>
     834  <node id='-102471' lat='47.18929409981' lon='8.79521012306'>
     835    <tag k='gpx:ele' v='568.49609375' />
     836  </node>
     837  <node id='-102472' lat='47.18930482864' lon='8.79513502121'>
     838    <tag k='gpx:ele' v='568.33203125' />
     839  </node>
     840  <node id='-102473' lat='47.18934237957' lon='8.79490435123'>
     841    <tag k='gpx:ele' v='566.84765625' />
     842  </node>
     843  <node id='-102474' lat='47.18934774399' lon='8.79484534264'>
     844    <tag k='gpx:ele' v='566.609375' />
     845  </node>
     846  <node id='-102475' lat='47.18933701515' lon='8.79473805428'>
     847    <tag k='gpx:ele' v='566.94140625' />
     848  </node>
     849  <node id='-102476' lat='47.18933701515' lon='8.79465758801'>
     850    <tag k='gpx:ele' v='566.88671875' />
     851  </node>
     852  <node id='-102477' lat='47.18935310841' lon='8.79457712173'>
     853    <tag k='gpx:ele' v='566.27734375' />
     854  </node>
     855  <node id='-102478' lat='47.18937456608' lon='8.79450201988'>
     856    <tag k='gpx:ele' v='565.48046875' />
     857  </node>
     858  <node id='-102479' lat='47.18942821026' lon='8.79438400269'>
     859    <tag k='gpx:ele' v='563.5' />
     860  </node>
     861  <node id='-102480' lat='47.18945503235' lon='8.79430353642'>
     862    <tag k='gpx:ele' v='562.51953125' />
     863  </node>
     864  <node id='-102481' lat='47.1894711256' lon='8.79420697689'>
     865    <tag k='gpx:ele' v='561.9453125' />
     866  </node>
     867  <node id='-102482' lat='47.18948185444' lon='8.79407286644'>
     868    <tag k='gpx:ele' v='561.85546875' />
     869  </node>
     870  <node id='-102483' lat='47.18949794769' lon='8.7939119339'>
     871    <tag k='gpx:ele' v='561.7578125' />
     872  </node>
     873  <node id='-102484' lat='47.18950867653' lon='8.79378855228'>
     874    <tag k='gpx:ele' v='561.765625' />
     875  </node>
     876  <node id='-102485' lat='47.18952476978' lon='8.79368662834'>
     877    <tag k='gpx:ele' v='561.453125' />
     878  </node>
     879  <node id='-102486' lat='47.18955159187' lon='8.79356861115'>
     880    <tag k='gpx:ele' v='560.6015625' />
     881  </node>
     882  <node id='-102487' lat='47.18958377838' lon='8.7934666872'>
     883    <tag k='gpx:ele' v='559.65234375' />
     884  </node>
     885  <node id='-102488' lat='47.18961596489' lon='8.79338622093'>
     886    <tag k='gpx:ele' v='558.7578125' />
     887  </node>
     888  <node id='-102489' lat='47.18965888023' lon='8.79327893257'>
     889    <tag k='gpx:ele' v='557.66015625' />
     890  </node>
     891  <node id='-102490' lat='47.18971252441' lon='8.79318773746'>
     892    <tag k='gpx:ele' v='556.35546875' />
     893  </node>
     894  <node id='-102491' lat='47.18976080418' lon='8.79311800003'>
     895    <tag k='gpx:ele' v='555.2265625' />
     896  </node>
     897  <node id='-102492' lat='47.18983054161' lon='8.7930482626'>
     898    <tag k='gpx:ele' v='553.53125' />
     899  </node>
     900  <node id='-102493' lat='47.18992173672' lon='8.79296243191'>
     901    <tag k='gpx:ele' v='551.265625' />
     902  </node>
     903  <node id='-102494' lat='47.1899753809' lon='8.79290342331'>
     904    <tag k='gpx:ele' v='549.92578125' />
     905  </node>
     906  <node id='-102495' lat='47.19000756741' lon='8.79285514355'>
     907    <tag k='gpx:ele' v='549.140625' />
     908  </node>
     909  <node id='-102496' lat='47.1900343895' lon='8.79278004169'>
     910    <tag k='gpx:ele' v='548.48828125' />
     911  </node>
     912  <node id='-102497' lat='47.19004511833' lon='8.79272639751'>
     913    <tag k='gpx:ele' v='548.0078125' />
     914  </node>
     915  <node id='-102498' lat='47.19005584717' lon='8.79263520241'>
     916    <tag k='gpx:ele' v='547.39453125' />
     917  </node>
     918  <node id='-102499' lat='47.19005584717' lon='8.79256546497'>
     919    <tag k='gpx:ele' v='547.15234375' />
     920  </node>
     921  <node id='-102500' lat='47.19005048275' lon='8.79250109196'>
     922    <tag k='gpx:ele' v='547.08203125' />
     923  </node>
     924  <node id='-102501' lat='47.19001829624' lon='8.79236698151'>
     925    <tag k='gpx:ele' v='547.55078125' />
     926  </node>
     927  <node id='-102502' lat='47.18994319439' lon='8.79210412502'>
     928    <tag k='gpx:ele' v='548.69921875' />
     929  </node>
     930  <node id='-102503' lat='47.18993246555' lon='8.79203438759'>
     931    <tag k='gpx:ele' v='548.76953125' />
     932  </node>
     933  <node id='-102504' lat='47.18993246555' lon='8.79197001457'>
     934    <tag k='gpx:ele' v='548.51953125' />
     935  </node>
     936  <node id='-102505' lat='47.18994319439' lon='8.79190027714'>
     937    <tag k='gpx:ele' v='547.88671875' />
     938  </node>
     939  <node id='-102506' lat='47.18997001648' lon='8.7918305397'>
     940    <tag k='gpx:ele' v='546.7265625' />
     941  </node>
     942  <node id='-102507' lat='47.19000220299' lon='8.79175543785'>
     943    <tag k='gpx:ele' v='545.3984375' />
     944  </node>
     945  <node id='-102508' lat='47.19003975391' lon='8.791680336'>
     946    <tag k='gpx:ele' v='543.87890625' />
     947  </node>
     948  <node id='-102509' lat='47.19012022018' lon='8.79153549671'>
     949    <tag k='gpx:ele' v='540.6484375' />
     950  </node>
     951  <node id='-102510' lat='47.19023287296' lon='8.7912940979'>
     952    <tag k='gpx:ele' v='536.4296875' />
     953  </node>
     954  <node id='-102511' lat='47.19025433064' lon='8.79122436047'>
     955    <tag k='gpx:ele' v='536.046875' />
     956  </node>
     957  <node id='-102512' lat='47.19027042389' lon='8.79114925861'>
     958    <tag k='gpx:ele' v='535.765625' />
     959  </node>
     960  <node id='-102513' lat='47.19027578831' lon='8.79101514816'>
     961    <tag k='gpx:ele' v='535.71875' />
     962  </node>
     963  <node id='-102514' lat='47.19027578831' lon='8.79076838493'>
     964    <tag k='gpx:ele' v='535.55859375' />
     965  </node>
     966  <node id='-102515' lat='47.19026505947' lon='8.79043579102'>
     967    <tag k='gpx:ele' v='535.359375' />
     968  </node>
     969  <node id='-102516' lat='47.19025969505' lon='8.7901622057'>
     970    <tag k='gpx:ele' v='534.5234375' />
     971  </node>
     972  <node id='-102517' lat='47.19025969505' lon='8.78992080688'>
     973    <tag k='gpx:ele' v='534.2578125' />
     974  </node>
     975  <node id='-102518' lat='47.19025433064' lon='8.78967404366'>
     976    <tag k='gpx:ele' v='535.1328125' />
     977  </node>
     978  <node id='-102519' lat='47.19025433064' lon='8.78955602646'>
     979    <tag k='gpx:ele' v='535.46875' />
     980  </node>
     981  <node id='-102520' lat='47.19025433064' lon='8.78949165344'>
     982    <tag k='gpx:ele' v='535.04296875' />
     983  </node>
     984  <node id='-102521' lat='47.19027042389' lon='8.78943800926'>
     985    <tag k='gpx:ele' v='534.04296875' />
     986  </node>
     987  <node id='-102522' lat='47.19030797482' lon='8.78938436508'>
     988    <tag k='gpx:ele' v='532.234375' />
     989  </node>
     990  <node id='-102523' lat='47.19035625458' lon='8.78935754299'>
     991    <tag k='gpx:ele' v='530.2109375' />
     992  </node>
     993  <node id='-102524' lat='47.1903938055' lon='8.78935754299'>
     994    <tag k='gpx:ele' v='528.7890625' />
     995  </node>
     996  <node id='-102525' lat='47.19042599201' lon='8.78937363625'>
     997    <tag k='gpx:ele' v='527.69140625' />
     998  </node>
     999  <node id='-102526' lat='47.19045817852' lon='8.78941118717'>
     1000    <tag k='gpx:ele' v='526.76171875' />
     1001  </node>
     1002  <node id='-102527' lat='47.19048500061' lon='8.78945946693'>
     1003    <tag k='gpx:ele' v='526.1171875' />
     1004  </node>
     1005  <node id='-102528' lat='47.19050645828' lon='8.78952920437'>
     1006    <tag k='gpx:ele' v='525.83984375' />
     1007  </node>
     1008  <node id='-102529' lat='47.19052255154' lon='8.78960967064'>
     1009    <tag k='gpx:ele' v='525.51171875' />
     1010  </node>
     1011  <node id='-102530' lat='47.19053328037' lon='8.78972768784'>
     1012    <tag k='gpx:ele' v='525.34375' />
     1013  </node>
     1014  <node id='-102531' lat='47.19054400921' lon='8.78982961178'>
     1015    <tag k='gpx:ele' v='525.18359375' />
     1016  </node>
     1017  <node id='-102532' lat='47.19058692455' lon='8.79002273083'>
     1018    <tag k='gpx:ele' v='524.3515625' />
     1019  </node>
     1020  <node id='-102533' lat='47.19063520432' lon='8.79024267197'>
     1021    <tag k='gpx:ele' v='524.41015625' />
     1022  </node>
     1023  <node id='-102534' lat='47.19067275524' lon='8.79039824009'>
     1024    <tag k='gpx:ele' v='524.47265625' />
     1025  </node>
     1026  <node id='-102535' lat='47.19072639942' lon='8.79054844379'>
     1027    <tag k='gpx:ele' v='524.02734375' />
     1028  </node>
     1029  <node id='-102536' lat='47.1907800436' lon='8.79067718983'>
     1030    <tag k='gpx:ele' v='523.09375' />
     1031  </node>
     1032  <node id='-102537' lat='47.19083368778' lon='8.79082202911'>
     1033    <tag k='gpx:ele' v='521.92578125' />
     1034  </node>
     1035  <node id='-102538' lat='47.19087660313' lon='8.79096150398'>
     1036    <tag k='gpx:ele' v='520.87109375' />
     1037  </node>
     1038  <node id='-102539' lat='47.19090878963' lon='8.79109561443'>
     1039    <tag k='gpx:ele' v='520.51953125' />
     1040  </node>
     1041  <node id='-102540' lat='47.19093561172' lon='8.79119217396'>
     1042    <tag k='gpx:ele' v='520.2578125' />
     1043  </node>
     1044  <node id='-102541' lat='47.19097316265' lon='8.79126727581'>
     1045    <tag k='gpx:ele' v='519.6640625' />
     1046  </node>
     1047  <node id='-102542' lat='47.19103217125' lon='8.79133701324'>
     1048    <tag k='gpx:ele' v='518.5703125' />
     1049  </node>
     1050  <node id='-102543' lat='47.19111263752' lon='8.79140138626'>
     1051    <tag k='gpx:ele' v='517.04296875' />
     1052  </node>
     1053  <node id='-102544' lat='47.1912842989' lon='8.79152476788'>
     1054    <tag k='gpx:ele' v='513.03125' />
     1055  </node>
     1056  <node id='-102545' lat='47.19140768051' lon='8.79161059856'>
     1057    <tag k='gpx:ele' v='510.19921875' />
     1058  </node>
     1059  <node id='-102546' lat='47.19148814678' lon='8.79166960716'>
     1060    <tag k='gpx:ele' v='508.5' />
     1061  </node>
     1062  <node id='-102547' lat='47.19152033329' lon='8.79169642925'>
     1063    <tag k='gpx:ele' v='507.8828125' />
     1064  </node>
     1065  <node id='-102548' lat='47.19157934189' lon='8.7917393446'>
     1066    <tag k='gpx:ele' v='506.71875' />
     1067  </node>
     1068  <node id='-102549' lat='47.19162762165' lon='8.7917983532'>
     1069    <tag k='gpx:ele' v='506.03125' />
     1070  </node>
     1071  <node id='-102550' lat='47.19165980816' lon='8.79185199738'>
     1072    <tag k='gpx:ele' v='505.5390625' />
     1073  </node>
     1074  <node id='-102551' lat='47.19168663025' lon='8.79191637039'>
     1075    <tag k='gpx:ele' v='504.96875' />
     1076  </node>
     1077  <node id='-102552' lat='47.19171881676' lon='8.79201829433'>
     1078    <tag k='gpx:ele' v='504.28125' />
     1079  </node>
     1080  <node id='-102553' lat='47.19175100327' lon='8.79214167595'>
     1081    <tag k='gpx:ele' v='503.6640625' />
     1082  </node>
     1083  <node id='-102554' lat='47.19179928303' lon='8.79229724407'>
     1084    <tag k='gpx:ele' v='502.578125' />
     1085  </node>
     1086  <node id='-102555' lat='47.19184219837' lon='8.79245817661'>
     1087    <tag k='gpx:ele' v='500.921875' />
     1088  </node>
     1089  <node id='-102556' lat='47.19186902046' lon='8.79257082939'>
     1090    <tag k='gpx:ele' v='499.85546875' />
     1091  </node>
     1092  <node id='-102557' lat='47.1918797493' lon='8.7926620245'>
     1093    <tag k='gpx:ele' v='499.40234375' />
     1094  </node>
     1095  <node id='-102558' lat='47.19189047813' lon='8.79276394844'>
     1096    <tag k='gpx:ele' v='498.96875' />
     1097  </node>
     1098  <node id='-102559' lat='47.19189584255' lon='8.79288733006'>
     1099    <tag k='gpx:ele' v='497.8828125' />
     1100  </node>
     1101  <node id='-102560' lat='47.19189584255' lon='8.79306435585'>
     1102    <tag k='gpx:ele' v='496.2265625' />
     1103  </node>
     1104  <node id='-102561' lat='47.19190120697' lon='8.79318237305'>
     1105    <tag k='gpx:ele' v='494.9609375' />
     1106  </node>
     1107  <node id='-102562' lat='47.19190657139' lon='8.7932574749'>
     1108    <tag k='gpx:ele' v='494.13671875' />
     1109  </node>
     1110  <node id='-102563' lat='47.19192802906' lon='8.79334330559'>
     1111    <tag k='gpx:ele' v='493.6015625' />
     1112  </node>
     1113  <node id='-102564' lat='47.19196021557' lon='8.79347205162'>
     1114    <tag k='gpx:ele' v='492.828125' />
     1115  </node>
     1116  <node id='-102565' lat='47.19199282117' lon='8.79361672327'>
     1117    <tag k='gpx:ele' v='492.1015625' />
     1118  </node>
     1119  <node id='-102566' lat='47.19200849533' lon='8.79368662834'>
     1120    <tag k='gpx:ele' v='491.76953125' />
     1121  </node>
     1122  <node id='-102567' lat='47.19204068184' lon='8.7938529253'>
     1123    <tag k='gpx:ele' v='490.83984375' />
     1124  </node>
     1125  <node id='-102568' lat='47.19206213951' lon='8.79399240017'>
     1126    <tag k='gpx:ele' v='490.23828125' />
     1127  </node>
     1128  <node id='-102569' lat='47.19207286835' lon='8.79405140877'>
     1129    <tag k='gpx:ele' v='489.96484375' />
     1130  </node>
     1131  <node id='-102570' lat='47.19208359718' lon='8.79414796829'>
     1132    <tag k='gpx:ele' v='489.703125' />
     1133  </node>
     1134  <node id='-102571' lat='47.19212114811' lon='8.79500091076'>
     1135    <tag k='gpx:ele' v='484.7109375' />
     1136  </node>
     1137  <node id='-102572' lat='47.19213724136' lon='8.79530131817'>
     1138    <tag k='gpx:ele' v='482.328125' />
     1139  </node>
     1140  <node id='-102573' lat='47.19213724136' lon='8.79545152187'>
     1141    <tag k='gpx:ele' v='481.04296875' />
     1142  </node>
     1143  <node id='-102574' lat='47.19213724136' lon='8.79553735256'>
     1144    <tag k='gpx:ele' v='480.3046875' />
     1145  </node>
     1146  <node id='-102575' lat='47.19212651253' lon='8.79560709'>
     1147    <tag k='gpx:ele' v='479.70703125' />
     1148  </node>
     1149  <node id='-102576' lat='47.19211041927' lon='8.79568219185'>
     1150    <tag k='gpx:ele' v='478.984375' />
     1151  </node>
     1152  <node id='-102577' lat='47.1920889616' lon='8.79574656487'>
     1153    <tag k='gpx:ele' v='478.4609375' />
     1154  </node>
     1155  <node id='-102578' lat='47.19203531742' lon='8.79583239555'>
     1156    <tag k='gpx:ele' v='479.1015625' />
     1157  </node>
     1158  <node id='-102579' lat='47.19198167324' lon='8.79589676857'>
     1159    <tag k='gpx:ele' v='480.359375' />
     1160  </node>
     1161  <node id='-102580' lat='47.19194948673' lon='8.7959343195'>
     1162    <tag k='gpx:ele' v='481.19140625' />
     1163  </node>
     1164  <node id='-102581' lat='47.19190657139' lon='8.79599332809'>
     1165    <tag k='gpx:ele' v='482.2890625' />
     1166  </node>
     1167  <node id='-102582' lat='47.19186902046' lon='8.79606306553'>
     1168    <tag k='gpx:ele' v='483.28125' />
     1169  </node>
     1170  <node id='-102583' lat='47.19184756279' lon='8.79613816738'>
     1171    <tag k='gpx:ele' v='483.69921875' />
     1172  </node>
     1173  <node id='-102584' lat='47.19184219837' lon='8.79621326923'>
     1174    <tag k='gpx:ele' v='483.4609375' />
     1175  </node>
     1176  <node id='-102585' lat='47.19184219837' lon='8.79628300667'>
     1177    <tag k='gpx:ele' v='483.0390625' />
     1178  </node>
     1179  <node id='-102586' lat='47.19185829163' lon='8.79637420177'>
     1180    <tag k='gpx:ele' v='481.9140625' />
     1181  </node>
     1182  <node id='-102587' lat='47.19188511372' lon='8.79645466805'>
     1183    <tag k='gpx:ele' v='480.515625' />
     1184  </node>
     1185  <node id='-102588' lat='47.19191730022' lon='8.79651367664'>
     1186    <tag k='gpx:ele' v='479.16796875' />
     1187  </node>
     1188  <node id='-102589' lat='47.19198703766' lon='8.79663169384'>
     1189    <tag k='gpx:ele' v='476.74609375' />
     1190  </node>
     1191  <node id='-102590' lat='47.19206750393' lon='8.79676043987'>
     1192    <tag k='gpx:ele' v='474.3515625' />
     1193  </node>
     1194  <node id='-102591' lat='47.19213187695' lon='8.79683554173'>
     1195    <tag k='gpx:ele' v='473.28515625' />
     1196  </node>
     1197  <node id='-102592' lat='47.19219088554' lon='8.79689455032'>
     1198    <tag k='gpx:ele' v='472.39453125' />
     1199  </node>
     1200  <node id='-102593' lat='47.19225525856' lon='8.7969481945'>
     1201    <tag k='gpx:ele' v='471.3671875' />
     1202  </node>
     1203  <node id='-102594' lat='47.19234645367' lon='8.79700183868'>
     1204    <tag k='gpx:ele' v='470.0390625' />
     1205  </node>
     1206  <node id='-102595' lat='47.19242691994' lon='8.79704475403'>
     1207    <tag k='gpx:ele' v='468.76171875' />
     1208  </node>
     1209  <node id='-102596' lat='47.19250738621' lon='8.79708230495'>
     1210    <tag k='gpx:ele' v='467.3671875' />
     1211  </node>
     1212  <node id='-102597' lat='47.19255566597' lon='8.79711449146'>
     1213    <tag k='gpx:ele' v='466.60546875' />
     1214  </node>
     1215  <node id='-102598' lat='47.19272732735' lon='8.79725396633'>
     1216    <tag k='gpx:ele' v='464.65625' />
     1217  </node>
     1218  <node id='-102599' lat='47.19280779362' lon='8.79730761051'>
     1219    <tag k='gpx:ele' v='463.62109375' />
     1220  </node>
     1221  <node id='-102600' lat='47.19286604784' lon='8.7973383721'>
     1222    <tag k='gpx:ele' v='462.84375' />
     1223  </node>
     1224  <node id='-102601' lat='47.19286596403' lon='8.79733820446'>
     1225    <tag k='gpx:ele' v='462.84765625' />
     1226  </node>
     1227  <node id='-102603' lat='47.18561486341' lon='8.79647553898'>
     1228    <tag k='gpx:ele' v='647.81640625' />
     1229  </node>
     1230  <node id='-102604' lat='47.18566875905' lon='8.79651493393'>
     1231    <tag k='gpx:ele' v='646.87109375' />
     1232  </node>
     1233  <node id='-102605' lat='47.18571729027' lon='8.79652281292'>
     1234    <tag k='gpx:ele' v='646.1953125' />
     1235  </node>
     1236  <node id='-102606' lat='47.18575769104' lon='8.79656220786'>
     1237    <tag k='gpx:ele' v='645.5859375' />
     1238  </node>
     1239  <node id='-102607' lat='47.18580354005' lon='8.79657788202'>
     1240    <tag k='gpx:ele' v='644.8203125' />
     1241  </node>
     1242  <node id='-102608' lat='47.18591401353' lon='8.79658182152'>
     1243    <tag k='gpx:ele' v='642.9765625' />
     1244  </node>
     1245  <node id='-102609' lat='47.18596254475' lon='8.79662515596'>
     1246    <tag k='gpx:ele' v='641.796875' />
     1247  </node>
     1248  <node id='-102610' lat='47.18608114868' lon='8.79664485343'>
     1249    <tag k='gpx:ele' v='639.4296875' />
     1250  </node>
     1251  <node id='-102611' lat='47.18618625775' lon='8.79673152231'>
     1252    <tag k='gpx:ele' v='636.4921875' />
     1253  </node>
     1254  <node id='-102612' lat='47.18626437709' lon='8.79679053091'>
     1255    <tag k='gpx:ele' v='634.1328125' />
     1256  </node>
     1257  <node id='-102613' lat='47.18639915809' lon='8.79689295776'>
     1258    <tag k='gpx:ele' v='629.55859375' />
     1259  </node>
     1260  <node id='-102614' lat='47.18645841815' lon='8.79692841321'>
     1261    <tag k='gpx:ele' v='627.55078125' />
     1262  </node>
     1263  <node id='-102615' lat='47.18660661019' lon='8.79695598967'>
     1264    <tag k='gpx:ele' v='624.0234375' />
     1265  </node>
     1266  <node id='-102617' lat='47.18490038998' lon='8.79694417119'>
     1267    <tag k='gpx:ele' v='663.90234375' />
     1268  </node>
     1269  <node id='-102618' lat='47.18495160341' lon='8.79679053091'>
     1270    <tag k='gpx:ele' v='662.74609375' />
     1271  </node>
     1272  <node id='-102619' lat='47.18495696783' lon='8.79664091393'>
     1273    <tag k='gpx:ele' v='662.1328125' />
     1274  </node>
     1275  <node id='-102620' lat='47.18498127535' lon='8.79655038938'>
     1276    <tag k='gpx:ele' v='661.39453125' />
     1277  </node>
     1278  <node id='-102621' lat='47.18511060812' lon='8.79647947848'>
     1279    <tag k='gpx:ele' v='658.98828125' />
     1280  </node>
     1281  <node id='-102622' lat='47.18521839939' lon='8.79640856758'>
     1282    <tag k='gpx:ele' v='657.47265625' />
     1283  </node>
     1284  <node id='-102623' lat='47.18536122702' lon='8.79642038606'>
     1285    <tag k='gpx:ele' v='654.22265625' />
     1286  </node>
     1287  <node id='-102624' lat='47.1855364088' lon='8.79644008353'>
     1288    <tag k='gpx:ele' v='650.0703125' />
     1289  </node>
     1290  <node id='-102625' lat='47.18560916372' lon='8.79647553898'>
     1291    <tag k='gpx:ele' v='647.890625' />
     1292  </node>
     1293  <node id='-102627' timestamp='2016-01-03T12:08:53Z' lat='47.20138901845' lon='8.77447698265'>
     1294    <tag k='gpx:ele' v='421.52999999999997' />
     1295    <tag k='gpx:time' v='2016-01-03T12:08:53Z' />
     1296  </node>
     1297  <node id='-102628' timestamp='2016-01-03T12:09:10Z' lat='47.20149253495' lon='8.7732814718'>
     1298    <tag k='gpx:ele' v='423.57999999999998' />
     1299    <tag k='gpx:time' v='2016-01-03T12:09:10Z' />
     1300  </node>
     1301  <node id='-102629' timestamp='2016-01-03T12:09:26Z' lat='47.20148784108' lon='8.77220867202'>
     1302    <tag k='gpx:ele' v='423' />
     1303    <tag k='gpx:time' v='2016-01-03T12:09:26Z' />
     1304  </node>
     1305  <node id='-102630' timestamp='2016-01-03T12:09:28Z' lat='47.20149974339' lon='8.77208210528'>
     1306    <tag k='gpx:ele' v='423.07999999999998' />
     1307    <tag k='gpx:time' v='2016-01-03T12:09:28Z' />
     1308  </node>
     1309  <node id='-102631' timestamp='2016-01-03T12:09:29Z' lat='47.2014896851' lon='8.77203491516'>
     1310    <tag k='gpx:ele' v='423.02999999999997' />
     1311    <tag k='gpx:time' v='2016-01-03T12:09:29Z' />
     1312  </node>
     1313  <node id='-102632' timestamp='2016-01-03T12:09:31Z' lat='47.20144324936' lon='8.77200884745'>
     1314    <tag k='gpx:ele' v='422.26999999999998' />
     1315    <tag k='gpx:time' v='2016-01-03T12:09:31Z' />
     1316  </node>
     1317  <node id='-102633' timestamp='2016-01-03T12:09:42Z' lat='47.20140594989' lon='8.77203382552'>
     1318    <tag k='gpx:ele' v='421.35000000000002' />
     1319    <tag k='gpx:time' v='2016-01-03T12:09:42Z' />
     1320  </node>
     1321  <node id='-102634' timestamp='2016-01-03T12:10:04Z' lat='47.20132657327' lon='8.77198227681'>
     1322    <tag k='gpx:ele' v='421.75' />
     1323    <tag k='gpx:time' v='2016-01-03T12:10:04Z' />
     1324  </node>
     1325  <node id='-102635' timestamp='2016-01-03T12:10:23Z' lat='47.2012681514' lon='8.77192972228'>
     1326    <tag k='gpx:ele' v='421.48000000000002' />
     1327    <tag k='gpx:time' v='2016-01-03T12:10:23Z' />
     1328  </node>
     1329  <way id='-102363'>
     1330    <nd ref='-102235' />
     1331    <nd ref='-102236' />
     1332    <nd ref='-102237' />
     1333    <nd ref='-102238' />
     1334    <nd ref='-102239' />
     1335    <nd ref='-102240' />
     1336    <nd ref='-102241' />
     1337    <nd ref='-102242' />
     1338    <nd ref='-102243' />
     1339    <nd ref='-102244' />
     1340    <nd ref='-102245' />
     1341    <nd ref='-102246' />
     1342    <nd ref='-102247' />
     1343    <nd ref='-102248' />
     1344    <nd ref='-102249' />
     1345    <nd ref='-102250' />
     1346    <nd ref='-102251' />
     1347    <nd ref='-102252' />
     1348    <nd ref='-102253' />
     1349    <nd ref='-102254' />
     1350    <nd ref='-102255' />
     1351    <nd ref='-102256' />
     1352    <nd ref='-102257' />
     1353    <nd ref='-102258' />
     1354    <nd ref='-102259' />
     1355    <nd ref='-102260' />
     1356    <nd ref='-102261' />
     1357    <nd ref='-102262' />
     1358    <nd ref='-102263' />
     1359    <nd ref='-102264' />
     1360    <nd ref='-102265' />
     1361    <nd ref='-102266' />
     1362    <nd ref='-102267' />
     1363    <nd ref='-102268' />
     1364    <nd ref='-102269' />
     1365    <nd ref='-102270' />
     1366    <nd ref='-102271' />
     1367    <nd ref='-102272' />
     1368    <nd ref='-102273' />
     1369    <nd ref='-102274' />
     1370    <nd ref='-102275' />
     1371    <nd ref='-102276' />
     1372    <nd ref='-102277' />
     1373    <nd ref='-102278' />
     1374    <nd ref='-102279' />
     1375    <nd ref='-102280' />
     1376    <nd ref='-102281' />
     1377    <nd ref='-102282' />
     1378    <nd ref='-102283' />
     1379    <nd ref='-102284' />
     1380    <nd ref='-102285' />
     1381    <nd ref='-102286' />
     1382    <nd ref='-102287' />
     1383    <nd ref='-102288' />
     1384    <nd ref='-102289' />
     1385    <nd ref='-102290' />
     1386    <nd ref='-102291' />
     1387    <nd ref='-102292' />
     1388    <nd ref='-102293' />
     1389    <nd ref='-102294' />
     1390    <nd ref='-102295' />
     1391    <nd ref='-102296' />
     1392    <nd ref='-102297' />
     1393    <nd ref='-102298' />
     1394    <nd ref='-102299' />
     1395    <nd ref='-102300' />
     1396    <nd ref='-102301' />
     1397    <nd ref='-102302' />
     1398    <nd ref='-102303' />
     1399    <nd ref='-102304' />
     1400    <nd ref='-102305' />
     1401    <nd ref='-102306' />
     1402    <nd ref='-102307' />
     1403    <nd ref='-102308' />
     1404    <nd ref='-102309' />
     1405    <nd ref='-102310' />
     1406    <nd ref='-102311' />
     1407    <nd ref='-102312' />
     1408    <nd ref='-102313' />
     1409    <nd ref='-102314' />
     1410    <nd ref='-102315' />
     1411    <nd ref='-102316' />
     1412    <nd ref='-102317' />
     1413    <nd ref='-102318' />
     1414    <nd ref='-102319' />
     1415    <nd ref='-102320' />
     1416    <nd ref='-102321' />
     1417    <nd ref='-102322' />
     1418    <nd ref='-102323' />
     1419    <nd ref='-102324' />
     1420    <nd ref='-102325' />
     1421    <nd ref='-102326' />
     1422    <nd ref='-102327' />
     1423    <nd ref='-102328' />
     1424    <nd ref='-102329' />
     1425    <nd ref='-102330' />
     1426    <nd ref='-102331' />
     1427    <nd ref='-102332' />
     1428    <nd ref='-102333' />
     1429    <nd ref='-102334' />
     1430    <nd ref='-102335' />
     1431    <nd ref='-102336' />
     1432    <nd ref='-102337' />
     1433    <nd ref='-102338' />
     1434    <nd ref='-102339' />
     1435    <nd ref='-102340' />
     1436    <nd ref='-102341' />
     1437    <nd ref='-102342' />
     1438    <nd ref='-102343' />
     1439    <nd ref='-102344' />
     1440    <nd ref='-102345' />
     1441    <nd ref='-102346' />
     1442    <nd ref='-102347' />
     1443    <nd ref='-102348' />
     1444    <nd ref='-102349' />
     1445    <nd ref='-102350' />
     1446    <nd ref='-102351' />
     1447    <nd ref='-102352' />
     1448    <nd ref='-102353' />
     1449    <nd ref='-102354' />
     1450    <nd ref='-102355' />
     1451    <nd ref='-102356' />
     1452    <nd ref='-102357' />
     1453    <nd ref='-102358' />
     1454    <nd ref='-102359' />
     1455    <nd ref='-102360' />
     1456    <nd ref='-102361' />
     1457    <nd ref='-102362' />
     1458    <tag k='gpx:extension:test:segment:tag' v='Segment extension' />
     1459    <tag k='gpx:extension:test:tag' v='Track extension' />
     1460    <tag k='gpxx:DisplayColor' v='Red' />
     1461  </way>
     1462  <way id='-102602'>
     1463    <nd ref='-102364' />
     1464    <nd ref='-102365' />
     1465    <nd ref='-102366' />
     1466    <nd ref='-102367' />
     1467    <nd ref='-102368' />
     1468    <nd ref='-102369' />
     1469    <nd ref='-102370' />
     1470    <nd ref='-102371' />
     1471    <nd ref='-102372' />
     1472    <nd ref='-102373' />
     1473    <nd ref='-102374' />
     1474    <nd ref='-102375' />
     1475    <nd ref='-102376' />
     1476    <nd ref='-102377' />
     1477    <nd ref='-102378' />
     1478    <nd ref='-102379' />
     1479    <nd ref='-102380' />
     1480    <nd ref='-102381' />
     1481    <nd ref='-102382' />
     1482    <nd ref='-102383' />
     1483    <nd ref='-102384' />
     1484    <nd ref='-102385' />
     1485    <nd ref='-102386' />
     1486    <nd ref='-102387' />
     1487    <nd ref='-102388' />
     1488    <nd ref='-102389' />
     1489    <nd ref='-102390' />
     1490    <nd ref='-102391' />
     1491    <nd ref='-102392' />
     1492    <nd ref='-102393' />
     1493    <nd ref='-102394' />
     1494    <nd ref='-102395' />
     1495    <nd ref='-102396' />
     1496    <nd ref='-102397' />
     1497    <nd ref='-102398' />
     1498    <nd ref='-102399' />
     1499    <nd ref='-102400' />
     1500    <nd ref='-102401' />
     1501    <nd ref='-102402' />
     1502    <nd ref='-102403' />
     1503    <nd ref='-102404' />
     1504    <nd ref='-102405' />
     1505    <nd ref='-102406' />
     1506    <nd ref='-102407' />
     1507    <nd ref='-102408' />
     1508    <nd ref='-102409' />
     1509    <nd ref='-102410' />
     1510    <nd ref='-102411' />
     1511    <nd ref='-102412' />
     1512    <nd ref='-102413' />
     1513    <nd ref='-102414' />
     1514    <nd ref='-102415' />
     1515    <nd ref='-102416' />
     1516    <nd ref='-102417' />
     1517    <nd ref='-102418' />
     1518    <nd ref='-102419' />
     1519    <nd ref='-102420' />
     1520    <nd ref='-102421' />
     1521    <nd ref='-102422' />
     1522    <nd ref='-102423' />
     1523    <nd ref='-102424' />
     1524    <nd ref='-102425' />
     1525    <nd ref='-102426' />
     1526    <nd ref='-102427' />
     1527    <nd ref='-102428' />
     1528    <nd ref='-102429' />
     1529    <nd ref='-102430' />
     1530    <nd ref='-102431' />
     1531    <nd ref='-102432' />
     1532    <nd ref='-102433' />
     1533    <nd ref='-102434' />
     1534    <nd ref='-102435' />
     1535    <nd ref='-102436' />
     1536    <nd ref='-102437' />
     1537    <nd ref='-102438' />
     1538    <nd ref='-102439' />
     1539    <nd ref='-102440' />
     1540    <nd ref='-102441' />
     1541    <nd ref='-102442' />
     1542    <nd ref='-102443' />
     1543    <nd ref='-102444' />
     1544    <nd ref='-102445' />
     1545    <nd ref='-102446' />
     1546    <nd ref='-102447' />
     1547    <nd ref='-102448' />
     1548    <nd ref='-102449' />
     1549    <nd ref='-102450' />
     1550    <nd ref='-102451' />
     1551    <nd ref='-102452' />
     1552    <nd ref='-102453' />
     1553    <nd ref='-102454' />
     1554    <nd ref='-102455' />
     1555    <nd ref='-102456' />
     1556    <nd ref='-102457' />
     1557    <nd ref='-102458' />
     1558    <nd ref='-102459' />
     1559    <nd ref='-102460' />
     1560    <nd ref='-102461' />
     1561    <nd ref='-102462' />
     1562    <nd ref='-102463' />
     1563    <nd ref='-102464' />
     1564    <nd ref='-102465' />
     1565    <nd ref='-102466' />
     1566    <nd ref='-102467' />
     1567    <nd ref='-102468' />
     1568    <nd ref='-102469' />
     1569    <nd ref='-102470' />
     1570    <nd ref='-102471' />
     1571    <nd ref='-102472' />
     1572    <nd ref='-102473' />
     1573    <nd ref='-102474' />
     1574    <nd ref='-102475' />
     1575    <nd ref='-102476' />
     1576    <nd ref='-102477' />
     1577    <nd ref='-102478' />
     1578    <nd ref='-102479' />
     1579    <nd ref='-102480' />
     1580    <nd ref='-102481' />
     1581    <nd ref='-102482' />
     1582    <nd ref='-102483' />
     1583    <nd ref='-102484' />
     1584    <nd ref='-102485' />
     1585    <nd ref='-102486' />
     1586    <nd ref='-102487' />
     1587    <nd ref='-102488' />
     1588    <nd ref='-102489' />
     1589    <nd ref='-102490' />
     1590    <nd ref='-102491' />
     1591    <nd ref='-102492' />
     1592    <nd ref='-102493' />
     1593    <nd ref='-102494' />
     1594    <nd ref='-102495' />
     1595    <nd ref='-102496' />
     1596    <nd ref='-102497' />
     1597    <nd ref='-102498' />
     1598    <nd ref='-102499' />
     1599    <nd ref='-102500' />
     1600    <nd ref='-102501' />
     1601    <nd ref='-102502' />
     1602    <nd ref='-102503' />
     1603    <nd ref='-102504' />
     1604    <nd ref='-102505' />
     1605    <nd ref='-102506' />
     1606    <nd ref='-102507' />
     1607    <nd ref='-102508' />
     1608    <nd ref='-102509' />
     1609    <nd ref='-102510' />
     1610    <nd ref='-102511' />
     1611    <nd ref='-102512' />
     1612    <nd ref='-102513' />
     1613    <nd ref='-102514' />
     1614    <nd ref='-102515' />
     1615    <nd ref='-102516' />
     1616    <nd ref='-102517' />
     1617    <nd ref='-102518' />
     1618    <nd ref='-102519' />
     1619    <nd ref='-102520' />
     1620    <nd ref='-102521' />
     1621    <nd ref='-102522' />
     1622    <nd ref='-102523' />
     1623    <nd ref='-102524' />
     1624    <nd ref='-102525' />
     1625    <nd ref='-102526' />
     1626    <nd ref='-102527' />
     1627    <nd ref='-102528' />
     1628    <nd ref='-102529' />
     1629    <nd ref='-102530' />
     1630    <nd ref='-102531' />
     1631    <nd ref='-102532' />
     1632    <nd ref='-102533' />
     1633    <nd ref='-102534' />
     1634    <nd ref='-102535' />
     1635    <nd ref='-102536' />
     1636    <nd ref='-102537' />
     1637    <nd ref='-102538' />
     1638    <nd ref='-102539' />
     1639    <nd ref='-102540' />
     1640    <nd ref='-102541' />
     1641    <nd ref='-102542' />
     1642    <nd ref='-102543' />
     1643    <nd ref='-102544' />
     1644    <nd ref='-102545' />
     1645    <nd ref='-102546' />
     1646    <nd ref='-102547' />
     1647    <nd ref='-102548' />
     1648    <nd ref='-102549' />
     1649    <nd ref='-102550' />
     1650    <nd ref='-102551' />
     1651    <nd ref='-102552' />
     1652    <nd ref='-102553' />
     1653    <nd ref='-102554' />
     1654    <nd ref='-102555' />
     1655    <nd ref='-102556' />
     1656    <nd ref='-102557' />
     1657    <nd ref='-102558' />
     1658    <nd ref='-102559' />
     1659    <nd ref='-102560' />
     1660    <nd ref='-102561' />
     1661    <nd ref='-102562' />
     1662    <nd ref='-102563' />
     1663    <nd ref='-102564' />
     1664    <nd ref='-102565' />
     1665    <nd ref='-102566' />
     1666    <nd ref='-102567' />
     1667    <nd ref='-102568' />
     1668    <nd ref='-102569' />
     1669    <nd ref='-102570' />
     1670    <nd ref='-102571' />
     1671    <nd ref='-102572' />
     1672    <nd ref='-102573' />
     1673    <nd ref='-102574' />
     1674    <nd ref='-102575' />
     1675    <nd ref='-102576' />
     1676    <nd ref='-102577' />
     1677    <nd ref='-102578' />
     1678    <nd ref='-102579' />
     1679    <nd ref='-102580' />
     1680    <nd ref='-102581' />
     1681    <nd ref='-102582' />
     1682    <nd ref='-102583' />
     1683    <nd ref='-102584' />
     1684    <nd ref='-102585' />
     1685    <nd ref='-102586' />
     1686    <nd ref='-102587' />
     1687    <nd ref='-102588' />
     1688    <nd ref='-102589' />
     1689    <nd ref='-102590' />
     1690    <nd ref='-102591' />
     1691    <nd ref='-102592' />
     1692    <nd ref='-102593' />
     1693    <nd ref='-102594' />
     1694    <nd ref='-102595' />
     1695    <nd ref='-102596' />
     1696    <nd ref='-102597' />
     1697    <nd ref='-102598' />
     1698    <nd ref='-102599' />
     1699    <nd ref='-102600' />
     1700    <nd ref='-102601' />
     1701    <tag k='gpxd:color' v='#0000FF' />
     1702  </way>
     1703  <way id='-102616'>
     1704    <nd ref='-102603' />
     1705    <nd ref='-102604' />
     1706    <nd ref='-102605' />
     1707    <nd ref='-102606' />
     1708    <nd ref='-102607' />
     1709    <nd ref='-102608' />
     1710    <nd ref='-102609' />
     1711    <nd ref='-102610' />
     1712    <nd ref='-102611' />
     1713    <nd ref='-102612' />
     1714    <nd ref='-102613' />
     1715    <nd ref='-102614' />
     1716    <nd ref='-102615' />
     1717    <tag k='gpxd:color' v='#00FF00' />
     1718  </way>
     1719  <way id='-102626'>
     1720    <nd ref='-102617' />
     1721    <nd ref='-102618' />
     1722    <nd ref='-102619' />
     1723    <nd ref='-102620' />
     1724    <nd ref='-102621' />
     1725    <nd ref='-102622' />
     1726    <nd ref='-102623' />
     1727    <nd ref='-102624' />
     1728    <nd ref='-102625' />
     1729  </way>
     1730  <way id='-102636'>
     1731    <nd ref='-102627' />
     1732    <nd ref='-102628' />
     1733    <nd ref='-102629' />
     1734    <nd ref='-102630' />
     1735    <nd ref='-102631' />
     1736    <nd ref='-102632' />
     1737    <nd ref='-102633' />
     1738    <nd ref='-102634' />
     1739    <nd ref='-102635' />
     1740  </way>
     1741</osm>
  • test/data/tracks/tracks-layerprefs.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:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
     5    xmlns:gpxd="http://josm.openstreetmap.de/gpx-drawing-extensions-1.0"
     6    xmlns:test="TestExtensionNamespace"
     7    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     8    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 http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://josm.openstreetmap.de/gpx-drawing-extensions-1.0 http://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd TestExtensionNamespace http://example.com">
     9  <metadata>
     10    <bounds minlat="47.1849004" minlon="8.7719297" maxlat="47.2014997" maxlon="8.7984188"/>
     11    <extensions>
     12      <josm:layerPreferences>
     13        <josm:entry key="colormode" value="1"/>
     14        <josm:entry key="colormode.dynamic-range" value="true"/>
     15        <josm:entry key="colormode.velocity.tune" value="10"/>
     16        <josm:entry key="colors" value="0"/>
     17        <josm:entry key="lines" value="1"/>
     18        <josm:entry key="lines.arrows" value="true"/>
     19        <josm:entry key="lines.arrows.min-distance" value="20"/>
     20      </josm:layerPreferences>
     21    </extensions>
     22  </metadata>
     23  <trk>
     24    <extensions>
     25      <gpxd:color>#FF0000</gpxd:color>
     26    </extensions>
     27    <trkseg>
     28      <extensions>
     29        <test:tag>Segment extension</test:tag>
     30      </extensions>
     31      <trkpt lat="47.19286847859621" lon="8.79732714034617">
     32        <ele>471.86000000000001</ele>
     33        <time>2016-01-03T11:59:58Z</time>
     34      </trkpt>
     35      <trkpt lat="47.192921955138445" lon="8.797342479228973">
     36        <ele>471.43000000000001</ele>
     37        <time>2016-01-03T11:59:59Z</time>
     38      </trkpt>
     39      <trkpt lat="47.19297501258552" lon="8.79734423942864">
     40        <ele>470.69</ele>
     41        <time>2016-01-03T12:00:00Z</time>
     42      </trkpt>
     43      <trkpt lat="47.19302739948034" lon="8.797332588583231">
     44        <ele>470.94</ele>
     45        <time>2016-01-03T12:00:01Z</time>
     46      </trkpt>
     47      <trkpt lat="47.193073416128755" lon="8.797307442873716">
     48        <ele>471.5</ele>
     49        <time>2016-01-03T12:00:02Z</time>
     50      </trkpt>
     51      <trkpt lat="47.193113481625915" lon="8.797272993251681">
     52        <ele>471.67000000000002</ele>
     53        <time>2016-01-03T12:00:03Z</time>
     54      </trkpt>
     55      <trkpt lat="47.19313560985029" lon="8.797232424840331">
     56        <ele>471.58999999999997</ele>
     57        <time>2016-01-03T12:00:04Z</time>
     58      </trkpt>
     59      <trkpt lat="47.19333660788834" lon="8.796738646924496">
     60        <ele>472.44999999999999</ele>
     61        <time>2016-01-03T12:00:15Z</time>
     62      </trkpt>
     63      <trkpt lat="47.193504832684994" lon="8.796404711902142">
     64        <ele>472.54000000000002</ele>
     65        <time>2016-01-03T12:00:22Z</time>
     66      </trkpt>
     67      <trkpt lat="47.19353936612606" lon="8.79635970108211">
     68        <ele>472.45999999999998</ele>
     69        <time>2016-01-03T12:00:23Z</time>
     70      </trkpt>
     71      <trkpt lat="47.19381898641586" lon="8.796017384156585">
     72        <ele>471.38</ele>
     73        <time>2016-01-03T12:00:30Z</time>
     74      </trkpt>
     75      <trkpt lat="47.19386676326394" lon="8.795985281467438">
     76        <ele>471.26999999999998</ele>
     77        <time>2016-01-03T12:00:31Z</time>
     78      </trkpt>
     79      <trkpt lat="47.19391797669232" lon="8.795968098565936">
     80        <ele>471.36000000000001</ele>
     81        <time>2016-01-03T12:00:32Z</time>
     82      </trkpt>
     83      <trkpt lat="47.19474418088794" lon="8.795836586505175">
     84        <ele>473.19999999999999</ele>
     85        <time>2016-01-03T12:00:50Z</time>
     86      </trkpt>
     87      <trkpt lat="47.195586981251836" lon="8.795750420540571">
     88        <ele>475.06</ele>
     89        <time>2016-01-03T12:01:09Z</time>
     90      </trkpt>
     91      <trkpt lat="47.19563358463347" lon="8.795769028365612">
     92        <ele>474.75</ele>
     93        <time>2016-01-03T12:01:10Z</time>
     94      </trkpt>
     95      <trkpt lat="47.19567717052996" lon="8.795795682817698">
     96        <ele>474.62</ele>
     97        <time>2016-01-03T12:01:11Z</time>
     98      </trkpt>
     99      <trkpt lat="47.195715978741646" lon="8.79583046771586">
     100        <ele>474.74000000000001</ele>
     101        <time>2016-01-03T12:01:12Z</time>
     102      </trkpt>
     103      <trkpt lat="47.19575093127787" lon="8.79587598145008">
     104        <ele>474.06</ele>
     105        <time>2016-01-03T12:01:13Z</time>
     106      </trkpt>
     107      <trkpt lat="47.19578169286251" lon="8.795927111059427">
     108        <ele>474</ele>
     109        <time>2016-01-03T12:01:14Z</time>
     110      </trkpt>
     111      <trkpt lat="47.195808347314596" lon="8.795980839058757">
     112        <ele>474.58999999999997</ele>
     113        <time>2016-01-03T12:01:15Z</time>
     114      </trkpt>
     115      <trkpt lat="47.195833241567016" lon="8.796036075800657">
     116        <ele>474.81999999999999</ele>
     117        <time>2016-01-03T12:01:16Z</time>
     118      </trkpt>
     119      <trkpt lat="47.19628603197634" lon="8.797110635787249">
     120        <ele>476.29000000000002</ele>
     121        <time>2016-01-03T12:01:35Z</time>
     122      </trkpt>
     123      <trkpt lat="47.19658995978534" lon="8.797826115041971">
     124        <ele>478.48000000000002</ele>
     125        <time>2016-01-03T12:01:50Z</time>
     126      </trkpt>
     127      <trkpt lat="47.19703386537731" lon="8.798356018960476">
     128        <ele>481.02999999999997</ele>
     129        <time>2016-01-03T12:02:06Z</time>
     130      </trkpt>
     131      <trkpt lat="47.19723687507212" lon="8.79841879941523">
     132        <ele>480</ele>
     133        <time>2016-01-03T12:02:12Z</time>
     134      </trkpt>
     135      <trkpt lat="47.19727886840701" lon="8.79838845692575">
     136        <ele>479.58999999999997</ele>
     137        <time>2016-01-03T12:02:13Z</time>
     138      </trkpt>
     139      <trkpt lat="47.19731541350484" lon="8.798339758068323">
     140        <ele>478.99000000000001</ele>
     141        <time>2016-01-03T12:02:14Z</time>
     142      </trkpt>
     143      <trkpt lat="47.197333350777626" lon="8.798282761126757">
     144        <ele>479.58999999999997</ele>
     145        <time>2016-01-03T12:02:15Z</time>
     146      </trkpt>
     147      <trkpt lat="47.19734919257462" lon="8.798161642625928">
     148        <ele>481.18000000000001</ele>
     149        <time>2016-01-03T12:02:18Z</time>
     150      </trkpt>
     151      <trkpt lat="47.19734827056527" lon="8.798118894919753">
     152        <ele>481.88</ele>
     153        <time>2016-01-03T12:02:20Z</time>
     154      </trkpt>
     155      <trkpt lat="47.19730585813522" lon="8.79802887327969">
     156        <ele>483.11000000000001</ele>
     157        <time>2016-01-03T12:02:24Z</time>
     158      </trkpt>
     159      <trkpt lat="47.19727643765509" lon="8.79797455854714">
     160        <ele>484.49000000000001</ele>
     161        <time>2016-01-03T12:02:27Z</time>
     162      </trkpt>
     163      <trkpt lat="47.19720158725977" lon="8.797768447548151">
     164        <ele>486.88</ele>
     165        <time>2016-01-03T12:02:35Z</time>
     166      </trkpt>
     167      <trkpt lat="47.19708633609116" lon="8.79746401682496">
     168        <ele>488.05000000000001</ele>
     169        <time>2016-01-03T12:02:48Z</time>
     170      </trkpt>
     171      <trkpt lat="47.19707007519901" lon="8.79741758108139">
     172        <ele>487.76999999999998</ele>
     173        <time>2016-01-03T12:02:51Z</time>
     174      </trkpt>
     175      <trkpt lat="47.19702917151153" lon="8.797250529751182">
     176        <ele>488.95999999999998</ele>
     177        <time>2016-01-03T12:02:57Z</time>
     178      </trkpt>
     179      <trkpt lat="47.19699899666011" lon="8.797172158956528">
     180        <ele>488.36000000000001</ele>
     181        <time>2016-01-03T12:03:01Z</time>
     182      </trkpt>
     183      <trkpt lat="47.19691224396229" lon="8.796978034079075">
     184        <ele>488.35000000000002</ele>
     185        <time>2016-01-03T12:03:09Z</time>
     186      </trkpt>
     187      <trkpt lat="47.19690545462072" lon="8.796931263059378">
     188        <ele>487.19</ele>
     189        <time>2016-01-03T12:03:10Z</time>
     190      </trkpt>
     191      <trkpt lat="47.19690595753491" lon="8.796886671334505">
     192        <ele>487.63999999999999</ele>
     193        <time>2016-01-03T12:03:11Z</time>
     194      </trkpt>
     195      <trkpt lat="47.19690511934459" lon="8.796837385743856">
     196        <ele>487.55000000000001</ele>
     197        <time>2016-01-03T12:03:12Z</time>
     198      </trkpt>
     199      <trkpt lat="47.19690562225878" lon="8.796793716028333">
     200        <ele>488.39999999999998</ele>
     201        <time>2016-01-03T12:03:13Z</time>
     202      </trkpt>
     203      <trkpt lat="47.196887601166964" lon="8.796751135960221">
     204        <ele>489.18000000000001</ele>
     205        <time>2016-01-03T12:03:15Z</time>
     206      </trkpt>
     207      <trkpt lat="47.1968892775476" lon="8.796689361333847">
     208        <ele>490.73000000000002</ele>
     209        <time>2016-01-03T12:03:18Z</time>
     210      </trkpt>
     211      <trkpt lat="47.196868155151606" lon="8.79647453315556">
     212        <ele>491.24000000000001</ele>
     213        <time>2016-01-03T12:03:33Z</time>
     214      </trkpt>
     215      <trkpt lat="47.19686932861805" lon="8.796442430466413">
     216        <ele>490.25</ele>
     217        <time>2016-01-03T12:03:34Z</time>
     218      </trkpt>
     219      <trkpt lat="47.19686849042773" lon="8.796406639739871">
     220        <ele>490.00999999999999</ele>
     221        <time>2016-01-03T12:03:35Z</time>
     222      </trkpt>
     223      <trkpt lat="47.196873016655445" lon="8.796368166804314">
     224        <ele>489.54000000000002</ele>
     225        <time>2016-01-03T12:03:36Z</time>
     226      </trkpt>
     227      <trkpt lat="47.19687905162573" lon="8.796329610049725">
     228        <ele>489.66000000000003</ele>
     229        <time>2016-01-03T12:03:37Z</time>
     230      </trkpt>
     231      <trkpt lat="47.196886762976646" lon="8.796280743554235">
     232        <ele>489.06</ele>
     233        <time>2016-01-03T12:03:38Z</time>
     234      </trkpt>
     235      <trkpt lat="47.19695775769651" lon="8.795829126611352">
     236        <ele>490.08999999999997</ele>
     237        <time>2016-01-03T12:03:48Z</time>
     238      </trkpt>
     239      <trkpt lat="47.1969928778708" lon="8.795709516853094">
     240        <ele>489.41000000000003</ele>
     241        <time>2016-01-03T12:03:52Z</time>
     242      </trkpt>
     243      <trkpt lat="47.19698550179601" lon="8.795667607337236">
     244        <ele>489.17000000000002</ele>
     245        <time>2016-01-03T12:03:53Z</time>
     246      </trkpt>
     247      <trkpt lat="47.19697644934058" lon="8.795624608173966">
     248        <ele>488.87</ele>
     249        <time>2016-01-03T12:03:54Z</time>
     250      </trkpt>
     251      <trkpt lat="47.19696890562773" lon="8.795590326189995">
     252        <ele>489.37</ele>
     253        <time>2016-01-03T12:03:55Z</time>
     254      </trkpt>
     255      <trkpt lat="47.19696664251387" lon="8.795558391138911">
     256        <ele>488.94999999999999</ele>
     257        <time>2016-01-03T12:03:56Z</time>
     258      </trkpt>
     259      <trkpt lat="47.19695256091654" lon="8.795536765828729">
     260        <ele>488.93000000000001</ele>
     261        <time>2016-01-03T12:03:57Z</time>
     262      </trkpt>
     263      <trkpt lat="47.196956500411034" lon="8.795507680624723">
     264        <ele>488.88999999999999</ele>
     265        <time>2016-01-03T12:03:58Z</time>
     266      </trkpt>
     267      <trkpt lat="47.19695859588683" lon="8.79548110999167">
     268        <ele>488.86000000000001</ele>
     269        <time>2016-01-03T12:03:59Z</time>
     270      </trkpt>
     271      <trkpt lat="47.19696907326579" lon="8.795452527701855">
     272        <ele>489.19999999999999</ele>
     273        <time>2016-01-03T12:04:00Z</time>
     274      </trkpt>
     275      <trkpt lat="47.19697988592088" lon="8.79541271366179">
     276        <ele>489.29000000000002</ele>
     277        <time>2016-01-03T12:04:01Z</time>
     278      </trkpt>
     279      <trkpt lat="47.19698726199567" lon="8.795355297625065">
     280        <ele>488.95999999999998</ele>
     281        <time>2016-01-03T12:04:02Z</time>
     282      </trkpt>
     283      <trkpt lat="47.19698960892856" lon="8.795302575454116">
     284        <ele>489.10000000000002</ele>
     285        <time>2016-01-03T12:04:03Z</time>
     286      </trkpt>
     287      <trkpt lat="47.19690981321037" lon="8.79494265653193">
     288        <ele>492.19</ele>
     289        <time>2016-01-03T12:04:14Z</time>
     290      </trkpt>
     291      <trkpt lat="47.19696622341871" lon="8.794532027095556">
     292        <ele>492.12</ele>
     293        <time>2016-01-03T12:04:25Z</time>
     294      </trkpt>
     295      <trkpt lat="47.19697737134993" lon="8.79450285807252">
     296        <ele>493.06999999999999</ele>
     297        <time>2016-01-03T12:04:26Z</time>
     298      </trkpt>
     299      <trkpt lat="47.19699212349951" lon="8.794450974091887">
     300        <ele>492.58999999999997</ele>
     301        <time>2016-01-03T12:04:27Z</time>
     302      </trkpt>
     303      <trkpt lat="47.19699405133724" lon="8.794399006292224">
     304        <ele>492.38999999999999</ele>
     305        <time>2016-01-03T12:04:28Z</time>
     306      </trkpt>
     307      <trkpt lat="47.19698860310018" lon="8.794350139796734">
     308        <ele>492.94</ele>
     309        <time>2016-01-03T12:04:29Z</time>
     310      </trkpt>
     311      <trkpt lat="47.19698214903474" lon="8.794298926368356">
     312        <ele>492.47000000000003</ele>
     313        <time>2016-01-03T12:04:30Z</time>
     314      </trkpt>
     315      <trkpt lat="47.19697737134993" lon="8.794246790930629">
     316        <ele>492.50999999999999</ele>
     317        <time>2016-01-03T12:04:31Z</time>
     318      </trkpt>
     319      <trkpt lat="47.19698206521571" lon="8.794189291074872">
     320        <ele>492.73000000000002</ele>
     321        <time>2016-01-03T12:04:32Z</time>
     322      </trkpt>
     323      <trkpt lat="47.196985417976975" lon="8.794133719056845">
     324        <ele>492.63</ele>
     325        <time>2016-01-03T12:04:33Z</time>
     326      </trkpt>
     327      <trkpt lat="47.19698768109083" lon="8.794077644124627">
     328        <ele>493</ele>
     329        <time>2016-01-03T12:04:34Z</time>
     330      </trkpt>
     331      <trkpt lat="47.19699136912823" lon="8.794022323563695">
     332        <ele>493.00999999999999</ele>
     333        <time>2016-01-03T12:04:35Z</time>
     334      </trkpt>
     335      <trkpt lat="47.196987848728895" lon="8.793976558372378">
     336        <ele>493.19999999999999</ele>
     337        <time>2016-01-03T12:04:36Z</time>
     338      </trkpt>
     339      <trkpt lat="47.19698550179601" lon="8.793926937505603">
     340        <ele>493.18000000000001</ele>
     341        <time>2016-01-03T12:04:37Z</time>
     342      </trkpt>
     343      <trkpt lat="47.196980556473136" lon="8.793885698541999">
     344        <ele>493</ele>
     345        <time>2016-01-03T12:04:38Z</time>
     346      </trkpt>
     347      <trkpt lat="47.19698307104409" lon="8.793837334960699">
     348        <ele>493.20999999999998</ele>
     349        <time>2016-01-03T12:04:39Z</time>
     350      </trkpt>
     351      <trkpt lat="47.196992291137576" lon="8.793786959722638">
     352        <ele>493.11000000000001</ele>
     353        <time>2016-01-03T12:04:40Z</time>
     354      </trkpt>
     355      <trkpt lat="47.19699547626078" lon="8.793743625283241">
     356        <ele>493.30000000000001</ele>
     357        <time>2016-01-03T12:04:41Z</time>
     358      </trkpt>
     359      <trkpt lat="47.19700268469751" lon="8.793694507330656">
     360        <ele>493.50999999999999</ele>
     361        <time>2016-01-03T12:04:42Z</time>
     362      </trkpt>
     363      <trkpt lat="47.19700511544943" lon="8.793357806280255">
     364        <ele>493</ele>
     365        <time>2016-01-03T12:04:50Z</time>
     366      </trkpt>
     367      <trkpt lat="47.19702162779868" lon="8.793316148221493">
     368        <ele>492.37</ele>
     369        <time>2016-01-03T12:04:51Z</time>
     370      </trkpt>
     371      <trkpt lat="47.19704459421337" lon="8.793262336403131">
     372        <ele>492.30000000000001</ele>
     373        <time>2016-01-03T12:04:52Z</time>
     374      </trkpt>
     375      <trkpt lat="47.19706906937063" lon="8.793201064690948">
     376        <ele>492.04000000000002</ele>
     377        <time>2016-01-03T12:04:53Z</time>
     378      </trkpt>
     379      <trkpt lat="47.19708985649049" lon="8.793123783543706">
     380        <ele>491.37</ele>
     381        <time>2016-01-03T12:04:54Z</time>
     382      </trkpt>
     383      <trkpt lat="47.19710913486779" lon="8.79304625093937">
     384        <ele>491.06999999999999</ele>
     385        <time>2016-01-03T12:04:55Z</time>
     386      </trkpt>
     387      <trkpt lat="47.19713117927313" lon="8.792974585667253">
     388        <ele>490.39999999999998</ele>
     389        <time>2016-01-03T12:04:56Z</time>
     390      </trkpt>
     391      <trkpt lat="47.197186248376966" lon="8.792809881269932">
     392        <ele>489.75</ele>
     393        <time>2016-01-03T12:04:58Z</time>
     394      </trkpt>
     395      <trkpt lat="47.19720753841102" lon="8.79271550104022">
     396        <ele>489.87</ele>
     397        <time>2016-01-03T12:04:59Z</time>
     398      </trkpt>
     399      <trkpt lat="47.19722296111286" lon="8.792620282620192">
     400        <ele>488.95999999999998</ele>
     401        <time>2016-01-03T12:05:00Z</time>
     402      </trkpt>
     403      <trkpt lat="47.19733930192888" lon="8.792043440043926">
     404        <ele>485.85000000000002</ele>
     405        <time>2016-01-03T12:05:06Z</time>
     406      </trkpt>
     407      <trkpt lat="47.1974522061646" lon="8.791516218334436">
     408        <ele>482.81</ele>
     409        <time>2016-01-03T12:05:11Z</time>
     410      </trkpt>
     411      <trkpt lat="47.197455475106835" lon="8.79138101823628">
     412        <ele>481.99000000000001</ele>
     413        <time>2016-01-03T12:05:12Z</time>
     414      </trkpt>
     415      <trkpt lat="47.197490092366934" lon="8.79095789976418">
     416        <ele>478.92</ele>
     417      </trkpt>
     418      <trkpt lat="47.19753225333989" lon="8.790533943101764">
     419        <ele>476</ele>
     420      </trkpt>
     421      <trkpt lat="47.19762864522636" lon="8.789889458566904">
     422        <ele>474.38</ele>
     423        <time>2016-01-03T12:05:23Z</time>
     424      </trkpt>
     425      <trkpt lat="47.19763744622469" lon="8.789792899042368">
     426        <ele>474.35000000000002</ele>
     427        <time>2016-01-03T12:05:24Z</time>
     428      </trkpt>
     429      <trkpt lat="47.1976438164711" lon="8.789720982313156">
     430        <ele>474.12</ele>
     431        <time>2016-01-03T12:05:25Z</time>
     432      </trkpt>
     433      <trkpt lat="47.19764859415591" lon="8.789367768913507">
     434        <ele>472.52999999999997</ele>
     435        <time>2016-01-03T12:05:33Z</time>
     436      </trkpt>
     437      <trkpt lat="47.19765613786876" lon="8.78931068815291">
     438        <ele>472.75</ele>
     439        <time>2016-01-03T12:05:34Z</time>
     440      </trkpt>
     441      <trkpt lat="47.1976903360337" lon="8.789105415344238">
     442        <ele>472.19</ele>
     443        <time>2016-01-03T12:05:37Z</time>
     444      </trkpt>
     445      <trkpt lat="47.197700729593635" lon="8.78903266042471">
     446        <ele>471.92000000000002</ele>
     447        <time>2016-01-03T12:05:38Z</time>
     448      </trkpt>
     449      <trkpt lat="47.197715900838375" lon="8.78876687027514">
     450        <ele>471.98000000000002</ele>
     451        <time>2016-01-03T12:05:41Z</time>
     452      </trkpt>
     453      <trkpt lat="47.19775462523103" lon="8.788432851433754">
     454        <ele>473.49000000000001</ele>
     455        <time>2016-01-03T12:05:47Z</time>
     456      </trkpt>
     457      <trkpt lat="47.197777507826686" lon="8.78841600380838">
     458        <ele>473.44999999999999</ele>
     459        <time>2016-01-03T12:05:48Z</time>
     460      </trkpt>
     461      <trkpt lat="47.19780064187944" lon="8.788405358791351">
     462        <ele>473.45999999999998</ele>
     463        <time>2016-01-03T12:05:49Z</time>
     464      </trkpt>
     465      <trkpt lat="47.197963669896126" lon="8.788386499509215">
     466        <ele>470.27999999999997</ele>
     467        <time>2016-01-03T12:05:53Z</time>
     468      </trkpt>
     469      <trkpt lat="47.1981308888644" lon="8.788295891135931">
     470        <ele>467.20999999999998</ele>
     471        <time>2016-01-03T12:05:57Z</time>
     472      </trkpt>
     473      <trkpt lat="47.19813633710146" lon="8.788262866437435">
     474        <ele>466.89999999999998</ele>
     475        <time>2016-01-03T12:05:58Z</time>
     476      </trkpt>
     477      <trkpt lat="47.19812661409378" lon="8.788229255005717">
     478        <ele>466.76999999999998</ele>
     479        <time>2016-01-03T12:05:59Z</time>
     480      </trkpt>
     481      <trkpt lat="47.19810306094587" lon="8.788186172023416">
     482        <ele>466.25</ele>
     483        <time>2016-01-03T12:06:00Z</time>
     484      </trkpt>
     485      <trkpt lat="47.19808369874954" lon="8.788127414882183">
     486        <ele>465.23000000000002</ele>
     487        <time>2016-01-03T12:06:01Z</time>
     488      </trkpt>
     489      <trkpt lat="47.19807816669345" lon="8.788059940561652">
     490        <ele>464.12</ele>
     491        <time>2016-01-03T12:06:02Z</time>
     492      </trkpt>
     493      <trkpt lat="47.19808881171048" lon="8.787991376593709">
     494        <ele>463.68000000000001</ele>
     495        <time>2016-01-03T12:06:03Z</time>
     496      </trkpt>
     497      <trkpt lat="47.19811093993485" lon="8.787914346903563">
     498        <ele>462.5</ele>
     499        <time>2016-01-03T12:06:04Z</time>
     500      </trkpt>
     501      <trkpt lat="47.19815100543201" lon="8.78783798776567">
     502        <ele>462.45999999999998</ele>
     503        <time>2016-01-03T12:06:05Z</time>
     504      </trkpt>
     505      <trkpt lat="47.19819492660463" lon="8.787756934762001">
     506        <ele>461.56999999999999</ele>
     507        <time>2016-01-03T12:06:06Z</time>
     508      </trkpt>
     509      <trkpt lat="47.1982860378921" lon="8.787589464336634">
     510        <ele>461.14999999999998</ele>
     511        <time>2016-01-03T12:06:08Z</time>
     512      </trkpt>
     513      <trkpt lat="47.19831814058125" lon="8.7874944973737">
     514        <ele>460.19</ele>
     515        <time>2016-01-03T12:06:09Z</time>
     516      </trkpt>
     517      <trkpt lat="47.1983309648931" lon="8.787389053031802">
     518        <ele>459.83999999999997</ele>
     519        <time>2016-01-03T12:06:10Z</time>
     520      </trkpt>
     521      <trkpt lat="47.198331551626325" lon="8.787284530699253">
     522        <ele>460.49000000000001</ele>
     523        <time>2016-01-03T12:06:11Z</time>
     524      </trkpt>
     525      <trkpt lat="47.198325265198946" lon="8.787185624241829">
     526        <ele>460.23000000000002</ele>
     527        <time>2016-01-03T12:06:12Z</time>
     528      </trkpt>
     529      <trkpt lat="47.19824245199561" lon="8.78635254688561">
     530        <ele>460.56999999999999</ele>
     531        <time>2016-01-03T12:06:22Z</time>
     532      </trkpt>
     533      <trkpt lat="47.198192747309804" lon="8.78586145117879">
     534        <ele>457.79000000000002</ele>
     535        <time>2016-01-03T12:06:27Z</time>
     536      </trkpt>
     537      <trkpt lat="47.19819249585271" lon="8.78536943346262">
     538        <ele>454.66000000000003</ele>
     539        <time>2016-01-03T12:06:31Z</time>
     540      </trkpt>
     541    </trkseg>
     542  </trk>
     543  <trk>
     544    <extensions>
     545      <gpxd:color>#0000FF</gpxd:color>
     546    </extensions>
     547    <trkseg>
     548      <trkpt lat="47.18660652637482" lon="8.796921372413635">
     549        <ele>624.7421875</ele>
     550      </trkpt>
     551      <trkpt lat="47.186644077301025" lon="8.7968248128891">
     552        <ele>626.015625</ele>
     553      </trkpt>
     554      <trkpt lat="47.18669772148132" lon="8.796712160110474">
     555        <ele>627.0625</ele>
     556      </trkpt>
     557      <trkpt lat="47.18676209449768" lon="8.796610236167908">
     558        <ele>627.3671875</ele>
     559      </trkpt>
     560      <trkpt lat="47.1868371963501" lon="8.796519041061401">
     561        <ele>626.90234375</ele>
     562      </trkpt>
     563      <trkpt lat="47.186912298202515" lon="8.796401023864746">
     564        <ele>626.11328125</ele>
     565      </trkpt>
     566      <trkpt lat="47.18698740005493" lon="8.796277642250061">
     567        <ele>625.33203125</ele>
     568      </trkpt>
     569      <trkpt lat="47.18707859516144" lon="8.796148896217346">
     570        <ele>623.90625</ele>
     571      </trkpt>
     572      <trkpt lat="47.18710541725159" lon="8.796095252037048">
     573        <ele>623.66796875</ele>
     574      </trkpt>
     575      <trkpt lat="47.187126874923706" lon="8.796030879020691">
     576        <ele>623.71484375</ele>
     577      </trkpt>
     578      <trkpt lat="47.187132239341736" lon="8.795977234840393">
     579        <ele>623.75</ele>
     580      </trkpt>
     581      <trkpt lat="47.187132239341736" lon="8.795923590660095">
     582        <ele>623.9375</ele>
     583      </trkpt>
     584      <trkpt lat="47.187132239341736" lon="8.795859217643738">
     585        <ele>624.1640625</ele>
     586      </trkpt>
     587      <trkpt lat="47.18711078166962" lon="8.795735836029053">
     588        <ele>625.234375</ele>
     589      </trkpt>
     590      <trkpt lat="47.18710005283356" lon="8.795607089996338">
     591        <ele>626</ele>
     592      </trkpt>
     593      <trkpt lat="47.18709468841553" lon="8.795521259307861">
     594        <ele>626.3515625</ele>
     595      </trkpt>
     596      <trkpt lat="47.18710541725159" lon="8.795440793037415">
     597        <ele>626.14453125</ele>
     598      </trkpt>
     599      <trkpt lat="47.187126874923706" lon="8.795360326766968">
     600        <ele>625.6640625</ele>
     601      </trkpt>
     602      <trkpt lat="47.187164425849915" lon="8.79528522491455">
     603        <ele>624.7890625</ele>
     604      </trkpt>
     605      <trkpt lat="47.1872341632843" lon="8.795183300971985">
     606        <ele>623.23046875</ele>
     607      </trkpt>
     608      <trkpt lat="47.18729317188263" lon="8.795086741447449">
     609        <ele>622.09375</ele>
     610      </trkpt>
     611      <trkpt lat="47.18736290931702" lon="8.794984817504883">
     612        <ele>620.9765625</ele>
     613      </trkpt>
     614      <trkpt lat="47.187416553497314" lon="8.794904351234436">
     615        <ele>620.23828125</ele>
     616      </trkpt>
     617      <trkpt lat="47.18746483325958" lon="8.794845342636108">
     618        <ele>619.42578125</ele>
     619      </trkpt>
     620      <trkpt lat="47.18752384185791" lon="8.79479706287384">
     621        <ele>617.7734375</ele>
     622      </trkpt>
     623      <trkpt lat="47.18756139278412" lon="8.794743418693542">
     624        <ele>616.8671875</ele>
     625      </trkpt>
     626      <trkpt lat="47.1875935792923" lon="8.794695138931274">
     627        <ele>616.09765625</ele>
     628      </trkpt>
     629      <trkpt lat="47.18760967254639" lon="8.794630765914917">
     630        <ele>615.87890625</ele>
     631      </trkpt>
     632      <trkpt lat="47.187620401382446" lon="8.7945556640625">
     633        <ele>615.3984375</ele>
     634      </trkpt>
     635      <trkpt lat="47.187636494636536" lon="8.794485926628113">
     636        <ele>614.76953125</ele>
     637      </trkpt>
     638      <trkpt lat="47.187674045562744" lon="8.794400095939636">
     639        <ele>613.47265625</ele>
     640      </trkpt>
     641      <trkpt lat="47.18769550323486" lon="8.79430890083313">
     642        <ele>612.72265625</ele>
     643      </trkpt>
     644      <trkpt lat="47.18770086765289" lon="8.794185519218445">
     645        <ele>612.4609375</ele>
     646      </trkpt>
     647      <trkpt lat="47.18770086765289" lon="8.79407823085785">
     648        <ele>611.85546875</ele>
     649      </trkpt>
     650      <trkpt lat="47.187684774398804" lon="8.793997764587402">
     651        <ele>611.90625</ele>
     652      </trkpt>
     653      <trkpt lat="47.187663316726685" lon="8.793933391571045">
     654        <ele>612.27734375</ele>
     655      </trkpt>
     656      <trkpt lat="47.18761503696442" lon="8.793869018554688">
     657        <ele>613.66796875</ele>
     658      </trkpt>
     659      <trkpt lat="47.18758285045624" lon="8.79381537437439">
     660        <ele>614.62890625</ele>
     661      </trkpt>
     662      <trkpt lat="47.18756675720215" lon="8.793767094612122">
     663        <ele>615.0703125</ele>
     664      </trkpt>
     665      <trkpt lat="47.18757212162018" lon="8.793713450431824">
     666        <ele>614.72265625</ele>
     667      </trkpt>
     668      <trkpt lat="47.18758821487427" lon="8.793681263923645">
     669        <ele>614.26171875</ele>
     670      </trkpt>
     671      <trkpt lat="47.187641859054565" lon="8.793681263923645">
     672        <ele>612.06640625</ele>
     673      </trkpt>
     674      <trkpt lat="47.18770086765289" lon="8.793708086013794">
     675        <ele>609.6875</ele>
     676      </trkpt>
     677      <trkpt lat="47.18778133392334" lon="8.793751001358032">
     678        <ele>606.83984375</ele>
     679      </trkpt>
     680      <trkpt lat="47.18785107135773" lon="8.79380464553833">
     681        <ele>604.671875</ele>
     682      </trkpt>
     683      <trkpt lat="47.187920808792114" lon="8.793842196464539">
     684        <ele>602.52734375</ele>
     685      </trkpt>
     686      <trkpt lat="47.18800127506256" lon="8.793911933898926">
     687        <ele>600.78515625</ele>
     688      </trkpt>
     689      <trkpt lat="47.18811392784119" lon="8.794013857841492">
     690        <ele>598.078125</ele>
     691      </trkpt>
     692      <trkpt lat="47.188151478767395" lon="8.79406213760376">
     693        <ele>597.1953125</ele>
     694      </trkpt>
     695      <trkpt lat="47.1881890296936" lon="8.794153332710266">
     696        <ele>596.5234375</ele>
     697      </trkpt>
     698      <trkpt lat="47.18821585178375" lon="8.794239163398743">
     699        <ele>595.74609375</ele>
     700      </trkpt>
     701      <trkpt lat="47.1882426738739" lon="8.794351816177368">
     702        <ele>594.82421875</ele>
     703      </trkpt>
     704      <trkpt lat="47.18826413154602" lon="8.794426918029785">
     705        <ele>594.10546875</ele>
     706      </trkpt>
     707      <trkpt lat="47.18829095363617" lon="8.794475197792053">
     708        <ele>593.2109375</ele>
     709      </trkpt>
     710      <trkpt lat="47.18830704689026" lon="8.794523477554321">
     711        <ele>592.6875</ele>
     712      </trkpt>
     713      <trkpt lat="47.18831241130829" lon="8.794614672660828">
     714        <ele>592.5390625</ele>
     715      </trkpt>
     716      <trkpt lat="47.18830704689026" lon="8.79479706287384">
     717        <ele>592.7578125</ele>
     718      </trkpt>
     719      <trkpt lat="47.18830704689026" lon="8.794888257980347">
     720        <ele>592.77734375</ele>
     721      </trkpt>
     722      <trkpt lat="47.18831241130829" lon="8.794941902160645">
     723        <ele>592.63671875</ele>
     724      </trkpt>
     725      <trkpt lat="47.18833386898041" lon="8.794995546340942">
     726        <ele>592.06640625</ele>
     727      </trkpt>
     728      <trkpt lat="47.188366055488586" lon="8.7950599193573">
     729        <ele>591.2578125</ele>
     730      </trkpt>
     731      <trkpt lat="47.188403606414795" lon="8.795113563537598">
     732        <ele>590.46875</ele>
     733      </trkpt>
     734      <trkpt lat="47.188435792922974" lon="8.795167207717896">
     735        <ele>589.7578125</ele>
     736      </trkpt>
     737      <trkpt lat="47.18845188617706" lon="8.795226216316223">
     738        <ele>589.359375</ele>
     739      </trkpt>
     740      <trkpt lat="47.18846261501312" lon="8.79529058933258">
     741        <ele>589.05859375</ele>
     742      </trkpt>
     743      <trkpt lat="47.18845188617706" lon="8.795354962348938">
     744        <ele>589.21875</ele>
     745      </trkpt>
     746      <trkpt lat="47.188430428504944" lon="8.795451521873474">
     747        <ele>589.59375</ele>
     748      </trkpt>
     749      <trkpt lat="47.188408970832825" lon="8.7955641746521">
     750        <ele>589.98046875</ele>
     751      </trkpt>
     752      <trkpt lat="47.188403606414795" lon="8.795655369758606">
     753        <ele>589.51953125</ele>
     754      </trkpt>
     755      <trkpt lat="47.188408970832825" lon="8.795746564865112">
     756        <ele>588.796875</ele>
     757      </trkpt>
     758      <trkpt lat="47.188435792922974" lon="8.795859217643738">
     759        <ele>587.4375</ele>
     760      </trkpt>
     761      <trkpt lat="47.18847870826721" lon="8.795971870422363">
     762        <ele>585.70703125</ele>
     763      </trkpt>
     764      <trkpt lat="47.18850553035736" lon="8.79605233669281">
     765        <ele>584.48046875</ele>
     766      </trkpt>
     767      <trkpt lat="47.18852162361145" lon="8.796132802963257">
     768        <ele>583.328125</ele>
     769      </trkpt>
     770      <trkpt lat="47.18852162361145" lon="8.796197175979614">
     771        <ele>582.69921875</ele>
     772      </trkpt>
     773      <trkpt lat="47.18851625919342" lon="8.79628300666809">
     774        <ele>581.96875</ele>
     775      </trkpt>
     776      <trkpt lat="47.18850016593933" lon="8.796379566192627">
     777        <ele>581.3359375</ele>
     778      </trkpt>
     779      <trkpt lat="47.18848943710327" lon="8.796465396881104">
     780        <ele>580.6796875</ele>
     781      </trkpt>
     782      <trkpt lat="47.18848943710327" lon="8.796524405479431">
     783        <ele>580.33984375</ele>
     784      </trkpt>
     785      <trkpt lat="47.18850016593933" lon="8.796578049659729">
     786        <ele>580.0234375</ele>
     787      </trkpt>
     788      <trkpt lat="47.18852162361145" lon="8.796631693840027">
     789        <ele>579.5078125</ele>
     790      </trkpt>
     791      <trkpt lat="47.1885484457016" lon="8.796685338020325">
     792        <ele>578.890625</ele>
     793      </trkpt>
     794      <trkpt lat="47.18856990337372" lon="8.796749711036682">
     795        <ele>578.34375</ele>
     796      </trkpt>
     797      <trkpt lat="47.18858063220978" lon="8.79681944847107">
     798        <ele>577.984375</ele>
     799      </trkpt>
     800      <trkpt lat="47.18857526779175" lon="8.796899914741516">
     801        <ele>577.90234375</ele>
     802      </trkpt>
     803      <trkpt lat="47.18858599662781" lon="8.796991109848022">
     804        <ele>577.50390625</ele>
     805      </trkpt>
     806      <trkpt lat="47.18859672546387" lon="8.79704475402832">
     807        <ele>577.20703125</ele>
     808      </trkpt>
     809      <trkpt lat="47.188623547554016" lon="8.797119855880737">
     810        <ele>576.6171875</ele>
     811      </trkpt>
     812      <trkpt lat="47.188666462898254" lon="8.797189593315125">
     813        <ele>575.82421875</ele>
     814      </trkpt>
     815      <trkpt lat="47.18870937824249" lon="8.797211050987244">
     816        <ele>575.125</ele>
     817      </trkpt>
     818      <trkpt lat="47.18871474266052" lon="8.797157406806946">
     819        <ele>575.0546875</ele>
     820      </trkpt>
     821      <trkpt lat="47.18872547149658" lon="8.79705548286438">
     822        <ele>574.89453125</ele>
     823      </trkpt>
     824      <trkpt lat="47.18873620033264" lon="8.796937465667725">
     825        <ele>574.75</ele>
     826      </trkpt>
     827      <trkpt lat="47.18875765800476" lon="8.79681408405304">
     828        <ele>574.6484375</ele>
     829      </trkpt>
     830      <trkpt lat="47.18879520893097" lon="8.796685338020325">
     831        <ele>574.26953125</ele>
     832      </trkpt>
     833      <trkpt lat="47.18883812427521" lon="8.796588778495789">
     834        <ele>573.72265625</ele>
     835      </trkpt>
     836      <trkpt lat="47.188907861709595" lon="8.796438574790955">
     837        <ele>572.4296875</ele>
     838      </trkpt>
     839      <trkpt lat="47.18901515007019" lon="8.796240091323853">
     840        <ele>570.828125</ele>
     841      </trkpt>
     842      <trkpt lat="47.18906342983246" lon="8.796154260635376">
     843        <ele>570.1015625</ele>
     844      </trkpt>
     845      <trkpt lat="47.189154624938965" lon="8.795987963676453">
     846        <ele>568.7734375</ele>
     847      </trkpt>
     848      <trkpt lat="47.18921899795532" lon="8.795869946479797">
     849        <ele>567.8125</ele>
     850      </trkpt>
     851      <trkpt lat="47.18926191329956" lon="8.795778751373291">
     852        <ele>567.25390625</ele>
     853      </trkpt>
     854      <trkpt lat="47.18928337097168" lon="8.795714378356934">
     855        <ele>567.09765625</ele>
     856      </trkpt>
     857      <trkpt lat="47.18929409980774" lon="8.795660734176636">
     858        <ele>567.1640625</ele>
     859      </trkpt>
     860      <trkpt lat="47.18929946422577" lon="8.795580267906189">
     861        <ele>567.546875</ele>
     862      </trkpt>
     863      <trkpt lat="47.18929946422577" lon="8.795451521873474">
     864        <ele>567.86328125</ele>
     865      </trkpt>
     866      <trkpt lat="47.18929409980774" lon="8.7953120470047">
     867        <ele>568.2890625</ele>
     868      </trkpt>
     869      <trkpt lat="47.18929409980774" lon="8.795210123062134">
     870        <ele>568.49609375</ele>
     871      </trkpt>
     872      <trkpt lat="47.1893048286438" lon="8.795135021209717">
     873        <ele>568.33203125</ele>
     874      </trkpt>
     875      <trkpt lat="47.18934237957001" lon="8.794904351234436">
     876        <ele>566.84765625</ele>
     877      </trkpt>
     878      <trkpt lat="47.18934774398804" lon="8.794845342636108">
     879        <ele>566.609375</ele>
     880      </trkpt>
     881      <trkpt lat="47.18933701515198" lon="8.794738054275513">
     882        <ele>566.94140625</ele>
     883      </trkpt>
     884      <trkpt lat="47.18933701515198" lon="8.794657588005066">
     885        <ele>566.88671875</ele>
     886      </trkpt>
     887      <trkpt lat="47.18935310840607" lon="8.79457712173462">
     888        <ele>566.27734375</ele>
     889      </trkpt>
     890      <trkpt lat="47.189374566078186" lon="8.794502019882202">
     891        <ele>565.48046875</ele>
     892      </trkpt>
     893      <trkpt lat="47.189428210258484" lon="8.794384002685547">
     894        <ele>563.5</ele>
     895      </trkpt>
     896      <trkpt lat="47.18945503234863" lon="8.7943035364151">
     897        <ele>562.51953125</ele>
     898      </trkpt>
     899      <trkpt lat="47.18947112560272" lon="8.794206976890564">
     900        <ele>561.9453125</ele>
     901      </trkpt>
     902      <trkpt lat="47.18948185443878" lon="8.79407286643982">
     903        <ele>561.85546875</ele>
     904      </trkpt>
     905      <trkpt lat="47.18949794769287" lon="8.793911933898926">
     906        <ele>561.7578125</ele>
     907      </trkpt>
     908      <trkpt lat="47.18950867652893" lon="8.79378855228424">
     909        <ele>561.765625</ele>
     910      </trkpt>
     911      <trkpt lat="47.18952476978302" lon="8.793686628341675">
     912        <ele>561.453125</ele>
     913      </trkpt>
     914      <trkpt lat="47.18955159187317" lon="8.79356861114502">
     915        <ele>560.6015625</ele>
     916      </trkpt>
     917      <trkpt lat="47.18958377838135" lon="8.793466687202454">
     918        <ele>559.65234375</ele>
     919      </trkpt>
     920      <trkpt lat="47.189615964889526" lon="8.793386220932007">
     921        <ele>558.7578125</ele>
     922      </trkpt>
     923      <trkpt lat="47.189658880233765" lon="8.793278932571411">
     924        <ele>557.66015625</ele>
     925      </trkpt>
     926      <trkpt lat="47.18971252441406" lon="8.793187737464905">
     927        <ele>556.35546875</ele>
     928      </trkpt>
     929      <trkpt lat="47.18976080417633" lon="8.793118000030518">
     930        <ele>555.2265625</ele>
     931      </trkpt>
     932      <trkpt lat="47.18983054161072" lon="8.79304826259613">
     933        <ele>553.53125</ele>
     934      </trkpt>
     935      <trkpt lat="47.189921736717224" lon="8.792962431907654">
     936        <ele>551.265625</ele>
     937      </trkpt>
     938      <trkpt lat="47.18997538089752" lon="8.792903423309326">
     939        <ele>549.92578125</ele>
     940      </trkpt>
     941      <trkpt lat="47.1900075674057" lon="8.792855143547058">
     942        <ele>549.140625</ele>
     943      </trkpt>
     944      <trkpt lat="47.19003438949585" lon="8.792780041694641">
     945        <ele>548.48828125</ele>
     946      </trkpt>
     947      <trkpt lat="47.19004511833191" lon="8.792726397514343">
     948        <ele>548.0078125</ele>
     949      </trkpt>
     950      <trkpt lat="47.19005584716797" lon="8.792635202407837">
     951        <ele>547.39453125</ele>
     952      </trkpt>
     953      <trkpt lat="47.19005584716797" lon="8.79256546497345">
     954        <ele>547.15234375</ele>
     955      </trkpt>
     956      <trkpt lat="47.19005048274994" lon="8.792501091957092">
     957        <ele>547.08203125</ele>
     958      </trkpt>
     959      <trkpt lat="47.19001829624176" lon="8.792366981506348">
     960        <ele>547.55078125</ele>
     961      </trkpt>
     962      <trkpt lat="47.18994319438934" lon="8.792104125022888">
     963        <ele>548.69921875</ele>
     964      </trkpt>
     965      <trkpt lat="47.189932465553284" lon="8.792034387588501">
     966        <ele>548.76953125</ele>
     967      </trkpt>
     968      <trkpt lat="47.189932465553284" lon="8.791970014572144">
     969        <ele>548.51953125</ele>
     970      </trkpt>
     971      <trkpt lat="47.18994319438934" lon="8.791900277137756">
     972        <ele>547.88671875</ele>
     973      </trkpt>
     974      <trkpt lat="47.18997001647949" lon="8.79183053970337">
     975        <ele>546.7265625</ele>
     976      </trkpt>
     977      <trkpt lat="47.19000220298767" lon="8.791755437850952">
     978        <ele>545.3984375</ele>
     979      </trkpt>
     980      <trkpt lat="47.19003975391388" lon="8.791680335998535">
     981        <ele>543.87890625</ele>
     982      </trkpt>
     983      <trkpt lat="47.190120220184326" lon="8.791535496711731">
     984        <ele>540.6484375</ele>
     985      </trkpt>
     986      <trkpt lat="47.19023287296295" lon="8.79129409790039">
     987        <ele>536.4296875</ele>
     988      </trkpt>
     989      <trkpt lat="47.19025433063507" lon="8.791224360466003">
     990        <ele>536.046875</ele>
     991      </trkpt>
     992      <trkpt lat="47.19027042388916" lon="8.791149258613586">
     993        <ele>535.765625</ele>
     994      </trkpt>
     995      <trkpt lat="47.19027578830719" lon="8.791015148162842">
     996        <ele>535.71875</ele>
     997      </trkpt>
     998      <trkpt lat="47.19027578830719" lon="8.790768384933472">
     999        <ele>535.55859375</ele>
     1000      </trkpt>
     1001      <trkpt lat="47.19026505947113" lon="8.790435791015625">
     1002        <ele>535.359375</ele>
     1003      </trkpt>
     1004      <trkpt lat="47.1902596950531" lon="8.790162205696106">
     1005        <ele>534.5234375</ele>
     1006      </trkpt>
     1007      <trkpt lat="47.1902596950531" lon="8.789920806884766">
     1008        <ele>534.2578125</ele>
     1009      </trkpt>
     1010      <trkpt lat="47.19025433063507" lon="8.789674043655396">
     1011        <ele>535.1328125</ele>
     1012      </trkpt>
     1013      <trkpt lat="47.19025433063507" lon="8.78955602645874">
     1014        <ele>535.46875</ele>
     1015      </trkpt>
     1016      <trkpt lat="47.19025433063507" lon="8.789491653442383">
     1017        <ele>535.04296875</ele>
     1018      </trkpt>
     1019      <trkpt lat="47.19027042388916" lon="8.789438009262085">
     1020        <ele>534.04296875</ele>
     1021      </trkpt>
     1022      <trkpt lat="47.19030797481537" lon="8.789384365081787">
     1023        <ele>532.234375</ele>
     1024      </trkpt>
     1025      <trkpt lat="47.19035625457764" lon="8.789357542991638">
     1026        <ele>530.2109375</ele>
     1027      </trkpt>
     1028      <trkpt lat="47.190393805503845" lon="8.789357542991638">
     1029        <ele>528.7890625</ele>
     1030      </trkpt>
     1031      <trkpt lat="47.190425992012024" lon="8.789373636245728">
     1032        <ele>527.69140625</ele>
     1033      </trkpt>
     1034      <trkpt lat="47.1904581785202" lon="8.789411187171936">
     1035        <ele>526.76171875</ele>
     1036      </trkpt>
     1037      <trkpt lat="47.19048500061035" lon="8.789459466934204">
     1038        <ele>526.1171875</ele>
     1039      </trkpt>
     1040      <trkpt lat="47.19050645828247" lon="8.789529204368591">
     1041        <ele>525.83984375</ele>
     1042      </trkpt>
     1043      <trkpt lat="47.19052255153656" lon="8.789609670639038">
     1044        <ele>525.51171875</ele>
     1045      </trkpt>
     1046      <trkpt lat="47.19053328037262" lon="8.789727687835693">
     1047        <ele>525.34375</ele>
     1048      </trkpt>
     1049      <trkpt lat="47.19054400920868" lon="8.78982961177826">
     1050        <ele>525.18359375</ele>
     1051      </trkpt>
     1052      <trkpt lat="47.19058692455292" lon="8.790022730827332">
     1053        <ele>524.3515625</ele>
     1054      </trkpt>
     1055      <trkpt lat="47.190635204315186" lon="8.790242671966553">
     1056        <ele>524.41015625</ele>
     1057      </trkpt>
     1058      <trkpt lat="47.190672755241394" lon="8.790398240089417">
     1059        <ele>524.47265625</ele>
     1060      </trkpt>
     1061      <trkpt lat="47.19072639942169" lon="8.79054844379425">
     1062        <ele>524.02734375</ele>
     1063      </trkpt>
     1064      <trkpt lat="47.19078004360199" lon="8.790677189826965">
     1065        <ele>523.09375</ele>
     1066      </trkpt>
     1067      <trkpt lat="47.19083368778229" lon="8.79082202911377">
     1068        <ele>521.92578125</ele>
     1069      </trkpt>
     1070      <trkpt lat="47.190876603126526" lon="8.790961503982544">
     1071        <ele>520.87109375</ele>
     1072      </trkpt>
     1073      <trkpt lat="47.190908789634705" lon="8.791095614433289">
     1074        <ele>520.51953125</ele>
     1075      </trkpt>
     1076      <trkpt lat="47.19093561172485" lon="8.791192173957825">
     1077        <ele>520.2578125</ele>
     1078      </trkpt>
     1079      <trkpt lat="47.19097316265106" lon="8.791267275810242">
     1080        <ele>519.6640625</ele>
     1081      </trkpt>
     1082      <trkpt lat="47.19103217124939" lon="8.791337013244629">
     1083        <ele>518.5703125</ele>
     1084      </trkpt>
     1085      <trkpt lat="47.191112637519836" lon="8.791401386260986">
     1086        <ele>517.04296875</ele>
     1087      </trkpt>
     1088      <trkpt lat="47.19128429889679" lon="8.791524767875671">
     1089        <ele>513.03125</ele>
     1090      </trkpt>
     1091      <trkpt lat="47.191407680511475" lon="8.791610598564148">
     1092        <ele>510.19921875</ele>
     1093      </trkpt>
     1094      <trkpt lat="47.19148814678192" lon="8.791669607162476">
     1095        <ele>508.5</ele>
     1096      </trkpt>
     1097      <trkpt lat="47.1915203332901" lon="8.791696429252625">
     1098        <ele>507.8828125</ele>
     1099      </trkpt>
     1100      <trkpt lat="47.19157934188843" lon="8.791739344596863">
     1101        <ele>506.71875</ele>
     1102      </trkpt>
     1103      <trkpt lat="47.191627621650696" lon="8.79179835319519">
     1104        <ele>506.03125</ele>
     1105      </trkpt>
     1106      <trkpt lat="47.191659808158875" lon="8.791851997375488">
     1107        <ele>505.5390625</ele>
     1108      </trkpt>
     1109      <trkpt lat="47.19168663024902" lon="8.791916370391846">
     1110        <ele>504.96875</ele>
     1111      </trkpt>
     1112      <trkpt lat="47.1917188167572" lon="8.792018294334412">
     1113        <ele>504.28125</ele>
     1114      </trkpt>
     1115      <trkpt lat="47.19175100326538" lon="8.792141675949097">
     1116        <ele>503.6640625</ele>
     1117      </trkpt>
     1118      <trkpt lat="47.19179928302765" lon="8.79229724407196">
     1119        <ele>502.578125</ele>
     1120      </trkpt>
     1121      <trkpt lat="47.19184219837189" lon="8.792458176612854">
     1122        <ele>500.921875</ele>
     1123      </trkpt>
     1124      <trkpt lat="47.191869020462036" lon="8.79257082939148">
     1125        <ele>499.85546875</ele>
     1126      </trkpt>
     1127      <trkpt lat="47.191879749298096" lon="8.792662024497986">
     1128        <ele>499.40234375</ele>
     1129      </trkpt>
     1130      <trkpt lat="47.191890478134155" lon="8.792763948440552">
     1131        <ele>498.96875</ele>
     1132      </trkpt>
     1133      <trkpt lat="47.191895842552185" lon="8.792887330055237">
     1134        <ele>497.8828125</ele>
     1135      </trkpt>
     1136      <trkpt lat="47.191895842552185" lon="8.79306435585022">
     1137        <ele>496.2265625</ele>
     1138      </trkpt>
     1139      <trkpt lat="47.191901206970215" lon="8.793182373046875">
     1140        <ele>494.9609375</ele>
     1141      </trkpt>
     1142      <trkpt lat="47.191906571388245" lon="8.793257474899292">
     1143        <ele>494.13671875</ele>
     1144      </trkpt>
     1145      <trkpt lat="47.191928029060364" lon="8.793343305587769">
     1146        <ele>493.6015625</ele>
     1147      </trkpt>
     1148      <trkpt lat="47.19196021556854" lon="8.793472051620483">
     1149        <ele>492.828125</ele>
     1150      </trkpt>
     1151      <trkpt lat="47.19199282117188" lon="8.793616723269224">
     1152        <ele>492.1015625</ele>
     1153      </trkpt>
     1154      <trkpt lat="47.19200849533081" lon="8.793686628341675">
     1155        <ele>491.76953125</ele>
     1156      </trkpt>
     1157      <trkpt lat="47.19204068183899" lon="8.793852925300598">
     1158        <ele>490.83984375</ele>
     1159      </trkpt>
     1160      <trkpt lat="47.19206213951111" lon="8.793992400169373">
     1161        <ele>490.23828125</ele>
     1162      </trkpt>
     1163      <trkpt lat="47.19207286834717" lon="8.7940514087677">
     1164        <ele>489.96484375</ele>
     1165      </trkpt>
     1166      <trkpt lat="47.19208359718323" lon="8.794147968292236">
     1167        <ele>489.703125</ele>
     1168      </trkpt>
     1169      <trkpt lat="47.192121148109436" lon="8.795000910758972">
     1170        <ele>484.7109375</ele>
     1171      </trkpt>
     1172      <trkpt lat="47.192137241363525" lon="8.79530131816864">
     1173        <ele>482.328125</ele>
     1174      </trkpt>
     1175      <trkpt lat="47.192137241363525" lon="8.795451521873474">
     1176        <ele>481.04296875</ele>
     1177      </trkpt>
     1178      <trkpt lat="47.192137241363525" lon="8.79553735256195">
     1179        <ele>480.3046875</ele>
     1180      </trkpt>
     1181      <trkpt lat="47.192126512527466" lon="8.795607089996338">
     1182        <ele>479.70703125</ele>
     1183      </trkpt>
     1184      <trkpt lat="47.19211041927338" lon="8.795682191848755">
     1185        <ele>478.984375</ele>
     1186      </trkpt>
     1187      <trkpt lat="47.19208896160126" lon="8.795746564865112">
     1188        <ele>478.4609375</ele>
     1189      </trkpt>
     1190      <trkpt lat="47.19203531742096" lon="8.795832395553589">
     1191        <ele>479.1015625</ele>
     1192      </trkpt>
     1193      <trkpt lat="47.19198167324066" lon="8.795896768569946">
     1194        <ele>480.359375</ele>
     1195      </trkpt>
     1196      <trkpt lat="47.19194948673248" lon="8.795934319496155">
     1197        <ele>481.19140625</ele>
     1198      </trkpt>
     1199      <trkpt lat="47.191906571388245" lon="8.795993328094482">
     1200        <ele>482.2890625</ele>
     1201      </trkpt>
     1202      <trkpt lat="47.191869020462036" lon="8.79606306552887">
     1203        <ele>483.28125</ele>
     1204      </trkpt>
     1205      <trkpt lat="47.19184756278992" lon="8.796138167381287">
     1206        <ele>483.69921875</ele>
     1207      </trkpt>
     1208      <trkpt lat="47.19184219837189" lon="8.796213269233704">
     1209        <ele>483.4609375</ele>
     1210      </trkpt>
     1211      <trkpt lat="47.19184219837189" lon="8.79628300666809">
     1212        <ele>483.0390625</ele>
     1213      </trkpt>
     1214      <trkpt lat="47.19185829162598" lon="8.796374201774597">
     1215        <ele>481.9140625</ele>
     1216      </trkpt>
     1217      <trkpt lat="47.191885113716125" lon="8.796454668045044">
     1218        <ele>480.515625</ele>
     1219      </trkpt>
     1220      <trkpt lat="47.191917300224304" lon="8.796513676643372">
     1221        <ele>479.16796875</ele>
     1222      </trkpt>
     1223      <trkpt lat="47.19198703765869" lon="8.796631693840027">
     1224        <ele>476.74609375</ele>
     1225      </trkpt>
     1226      <trkpt lat="47.19206750392914" lon="8.796760439872742">
     1227        <ele>474.3515625</ele>
     1228      </trkpt>
     1229      <trkpt lat="47.192131876945496" lon="8.796835541725159">
     1230        <ele>473.28515625</ele>
     1231      </trkpt>
     1232      <trkpt lat="47.19219088554382" lon="8.796894550323486">
     1233        <ele>472.39453125</ele>
     1234      </trkpt>
     1235      <trkpt lat="47.19225525856018" lon="8.796948194503784">
     1236        <ele>471.3671875</ele>
     1237      </trkpt>
     1238      <trkpt lat="47.19234645366669" lon="8.797001838684082">
     1239        <ele>470.0390625</ele>
     1240      </trkpt>
     1241      <trkpt lat="47.192426919937134" lon="8.79704475402832">
     1242        <ele>468.76171875</ele>
     1243      </trkpt>
     1244      <trkpt lat="47.19250738620758" lon="8.797082304954529">
     1245        <ele>467.3671875</ele>
     1246      </trkpt>
     1247      <trkpt lat="47.19255566596985" lon="8.797114491462708">
     1248        <ele>466.60546875</ele>
     1249      </trkpt>
     1250      <trkpt lat="47.1927273273468" lon="8.797253966331482">
     1251        <ele>464.65625</ele>
     1252      </trkpt>
     1253      <trkpt lat="47.19280779361725" lon="8.79730761051178">
     1254        <ele>463.62109375</ele>
     1255      </trkpt>
     1256      <trkpt lat="47.19286604784429" lon="8.79733837209642">
     1257        <ele>462.84375</ele>
     1258      </trkpt>
     1259      <trkpt lat="47.19286596402526" lon="8.797338204458356">
     1260        <ele>462.84765625</ele>
     1261      </trkpt>
     1262    </trkseg>
     1263  </trk>
     1264  <trk>
     1265    <extensions>
     1266      <gpxd:color>#00FF00</gpxd:color>
     1267    </extensions>
     1268    <trkseg>
     1269      <trkpt lat="47.18561486341059" lon="8.796475538983941">
     1270        <ele>647.81640625</ele>
     1271      </trkpt>
     1272      <trkpt lat="47.185668759047985" lon="8.796514933928847">
     1273        <ele>646.87109375</ele>
     1274      </trkpt>
     1275      <trkpt lat="47.18571729026735" lon="8.796522812917829">
     1276        <ele>646.1953125</ele>
     1277      </trkpt>
     1278      <trkpt lat="47.185757691040635" lon="8.796562207862735">
     1279        <ele>645.5859375</ele>
     1280      </trkpt>
     1281      <trkpt lat="47.18580354005098" lon="8.796577882021666">
     1282        <ele>644.8203125</ele>
     1283      </trkpt>
     1284      <trkpt lat="47.185914013534784" lon="8.796581821516156">
     1285        <ele>642.9765625</ele>
     1286      </trkpt>
     1287      <trkpt lat="47.18596254475415" lon="8.796625155955553">
     1288        <ele>641.796875</ele>
     1289      </trkpt>
     1290      <trkpt lat="47.186081148684025" lon="8.796644853428006">
     1291        <ele>639.4296875</ele>
     1292      </trkpt>
     1293      <trkpt lat="47.186186257749796" lon="8.7967315223068">
     1294        <ele>636.4921875</ele>
     1295      </trkpt>
     1296      <trkpt lat="47.186264377087355" lon="8.796790530905128">
     1297        <ele>634.1328125</ele>
     1298      </trkpt>
     1299      <trkpt lat="47.18639915809035" lon="8.796892957761884">
     1300        <ele>629.55859375</ele>
     1301      </trkpt>
     1302      <trkpt lat="47.186458418145776" lon="8.7969284132123">
     1303        <ele>627.55078125</ele>
     1304      </trkpt>
     1305      <trkpt lat="47.18660661019385" lon="8.796955989673734">
     1306        <ele>624.0234375</ele>
     1307      </trkpt>
     1308    </trkseg>
     1309  </trk>
     1310  <trk>
     1311    <extensions>
     1312      <gpxd:color>#FFCC00</gpxd:color>
     1313    </extensions>
     1314    <trkseg>
     1315      <trkpt lat="47.18490038998425" lon="8.796944171190262">
     1316        <ele>663.90234375</ele>
     1317      </trkpt>
     1318      <trkpt lat="47.18495160341263" lon="8.796790530905128">
     1319        <ele>662.74609375</ele>
     1320      </trkpt>
     1321      <trkpt lat="47.18495696783066" lon="8.796640913933516">
     1322        <ele>662.1328125</ele>
     1323      </trkpt>
     1324      <trkpt lat="47.184981275349855" lon="8.796550389379263">
     1325        <ele>661.39453125</ele>
     1326      </trkpt>
     1327      <trkpt lat="47.18511060811579" lon="8.796479478478432">
     1328        <ele>658.98828125</ele>
     1329      </trkpt>
     1330      <trkpt lat="47.18521839939058" lon="8.7964085675776">
     1331        <ele>657.47265625</ele>
     1332      </trkpt>
     1333      <trkpt lat="47.18536122702062" lon="8.796420386061072">
     1334        <ele>654.22265625</ele>
     1335      </trkpt>
     1336      <trkpt lat="47.18553640879691" lon="8.796440083533525">
     1337        <ele>650.0703125</ele>
     1338      </trkpt>
     1339      <trkpt lat="47.185609163716435" lon="8.796475538983941">
     1340        <ele>647.890625</ele>
     1341      </trkpt>
     1342    </trkseg>
     1343  </trk>
     1344  <trk>
     1345    <trkseg>
     1346      <trkpt lat="47.20138901844621" lon="8.774476982653141">
     1347        <ele>421.52999999999997</ele>
     1348        <time>2016-01-03T12:08:53Z</time>
     1349      </trkpt>
     1350      <trkpt lat="47.201492534950376" lon="8.773281471803784">
     1351        <ele>423.57999999999998</ele>
     1352        <time>2016-01-03T12:09:10Z</time>
     1353      </trkpt>
     1354      <trkpt lat="47.2014878410846" lon="8.772208672016859">
     1355        <ele>423</ele>
     1356        <time>2016-01-03T12:09:26Z</time>
     1357      </trkpt>
     1358      <trkpt lat="47.2014997433871" lon="8.772082105278969">
     1359        <ele>423.07999999999998</ele>
     1360        <time>2016-01-03T12:09:28Z</time>
     1361      </trkpt>
     1362      <trkpt lat="47.2014896851033" lon="8.772034915164113">
     1363        <ele>423.02999999999997</ele>
     1364        <time>2016-01-03T12:09:29Z</time>
     1365      </trkpt>
     1366      <trkpt lat="47.20144324935973" lon="8.77200884744525">
     1367        <ele>422.26999999999998</ele>
     1368        <time>2016-01-03T12:09:31Z</time>
     1369      </trkpt>
     1370      <trkpt lat="47.201405949890614" lon="8.7720338255167">
     1371        <ele>421.35000000000002</ele>
     1372        <time>2016-01-03T12:09:42Z</time>
     1373      </trkpt>
     1374      <trkpt lat="47.20132657326758" lon="8.771982276812196">
     1375        <ele>421.75</ele>
     1376        <time>2016-01-03T12:10:04Z</time>
     1377      </trkpt>
     1378      <trkpt lat="47.20126815140247" lon="8.77192972227931">
     1379        <ele>421.48000000000002</ele>
     1380        <time>2016-01-03T12:10:23Z</time>
     1381      </trkpt>
     1382    </trkseg>
     1383  </trk>
     1384</gpx>
  • test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java

     
    3535
    3636import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    3737import nl.jqno.equalsverifier.EqualsVerifier;
     38import nl.jqno.equalsverifier.Warning;
    3839
    3940/**
    4041 * Unit tests for class {@link GpxData}.
     
    6364     */
    6465    @Test
    6566    public void testMergeFrom() {
    66         ImmutableGpxTrack track = singleWaypointGpxTrack();
     67        GpxTrack track = singleWaypointGpxTrack();
    6768        GpxRoute route = singleWaypointRoute();
    6869        WayPoint newWP = new WayPoint(LatLon.NORTH_POLE);
    6970        WayPoint existingWP = new WayPoint(LatLon.SOUTH_POLE);
     
    102103        final GpxData other = getGpx("Layer2");
    103104        final GpxData expected = getGpx(exp);
    104105        own.mergeFrom(other, cut, connect);
    105         assertEquals(expected, own);
     106        own.put(GpxConstants.META_BOUNDS, null);
     107        expected.put(GpxConstants.META_BOUNDS, null); //they are only updated by GpxWriter
     108        assertEquals(exp + " didn't match!", expected, own);
    106109    }
    107110
    108111    private static GpxData getGpx(String file) throws IOException, SAXException {
     
    116119    public void testTracks() {
    117120        assertEquals(0, data.getTracks().size());
    118121
    119         ImmutableGpxTrack track1 = emptyGpxTrack();
    120         ImmutableGpxTrack track2 = singleWaypointGpxTrack();
     122        GpxTrack track1 = emptyGpxTrack();
     123        GpxTrack track2 = singleWaypointGpxTrack();
    121124        data.addTrack(track1);
    122125        assertEquals(1, data.getTracks().size());
    123126        data.addTrack(track2);
     
    136139     */
    137140    @Test(expected = IllegalArgumentException.class)
    138141    public void testAddTrackFails() {
    139         ImmutableGpxTrack track1 = emptyGpxTrack();
     142        GpxTrack track1 = emptyGpxTrack();
    140143        data.addTrack(track1);
    141144        data.addTrack(track1);
    142145    }
     
    146149     */
    147150    @Test(expected = IllegalArgumentException.class)
    148151    public void testRemoveTrackFails() {
    149         ImmutableGpxTrack track1 = emptyGpxTrack();
     152        GpxTrack track1 = emptyGpxTrack();
    150153        data.addTrack(track1);
    151154        data.removeTrack(track1);
    152155        data.removeTrack(track1);
     
    245248    @Test
    246249    public void testHasTrackPoints() {
    247250        assertFalse(data.hasTrackPoints());
    248         ImmutableGpxTrack track1 = emptyGpxTrack();
     251        GpxTrack track1 = emptyGpxTrack();
    249252        data.addTrack(track1);
    250253        assertFalse(data.hasTrackPoints());
    251         ImmutableGpxTrack track2 = singleWaypointGpxTrack();
     254        GpxTrack track2 = singleWaypointGpxTrack();
    252255        data.addTrack(track2);
    253256        assertTrue(data.hasTrackPoints());
    254257    }
     
    259262    @Test
    260263    public void testGetTrackPoints() {
    261264        assertEquals(0, data.getTrackPoints().count());
    262         ImmutableGpxTrack track1 = singleWaypointGpxTrack();
     265        GpxTrack track1 = singleWaypointGpxTrack();
    263266        data.addTrack(track1);
    264267        assertEquals(1, data.getTrackPoints().count());
    265         ImmutableGpxTrack track2 = singleWaypointGpxTrack();
     268        GpxTrack track2 = singleWaypointGpxTrack();
    266269        data.addTrack(track2);
    267270        assertEquals(2, data.getTrackPoints().count());
    268271    }
     
    280283     */
    281284    @Test
    282285    public void testIsEmpty() {
    283         ImmutableGpxTrack track1 = singleWaypointGpxTrack();
     286        GpxTrack track1 = singleWaypointGpxTrack();
    284287        WayPoint waypoint = new WayPoint(LatLon.ZERO);
    285288        GpxRoute route = singleWaypointRoute();
    286289
     
    307310     */
    308311    @Test
    309312    public void testLength() {
    310         ImmutableGpxTrack track1 = waypointGpxTrack(
     313        GpxTrack track1 = waypointGpxTrack(
    311314                new WayPoint(new LatLon(0, 0)),
    312315                new WayPoint(new LatLon(1, 1)),
    313316                new WayPoint(new LatLon(0, 2)));
    314         ImmutableGpxTrack track2 = waypointGpxTrack(
     317        GpxTrack track2 = waypointGpxTrack(
    315318                new WayPoint(new LatLon(0, 0)),
    316319                new WayPoint(new LatLon(-1, 1)));
    317320        data.addTrack(track1);
     
    335338        p1.setTime(new Date(200020));
    336339        p2.setTime(new Date(100020));
    337340        p4.setTime(new Date(500020));
    338         data.addTrack(new ImmutableGpxTrack(Arrays.asList(Arrays.asList(p1, p2)), Collections.emptyMap()));
    339         data.addTrack(new ImmutableGpxTrack(Arrays.asList(Arrays.asList(p3, p4, p5)), Collections.emptyMap()));
     341        data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p1, p2)), Collections.emptyMap()));
     342        data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p3, p4, p5)), Collections.emptyMap()));
    340343
    341344        Date[] times = data.getMinMaxTimeForAllTracks();
    342345        assertEquals(times.length, 2);
     
    354357                .map(ProjectionRegistry.getProjection()::eastNorth2latlon)
    355358                .map(WayPoint::new)
    356359                .collect(Collectors.toList());
    357         data.addTrack(new ImmutableGpxTrack(Arrays.asList(points), Collections.emptyMap()));
     360        data.addTrack(new GpxTrack(Arrays.asList(points), Collections.emptyMap()));
    358361
    359362        WayPoint closeToMiddle = data.nearestPointOnTrack(new EastNorth(10, 0), 10);
    360363        assertEquals(points.get(1), closeToMiddle);
     
    448451
    449452    }
    450453
    451     private static ImmutableGpxTrack emptyGpxTrack() {
    452         return new ImmutableGpxTrack(Collections.<Collection<WayPoint>>emptyList(), Collections.emptyMap());
     454    private static GpxTrack emptyGpxTrack() {
     455        return new GpxTrack(Collections.<Collection<WayPoint>>emptyList(), Collections.emptyMap());
    453456    }
    454457
    455     private static ImmutableGpxTrack singleWaypointGpxTrack() {
    456         return new ImmutableGpxTrack(Collections.singleton(Collections.singleton(new WayPoint(LatLon.ZERO))), Collections.emptyMap());
     458    private static GpxTrack singleWaypointGpxTrack() {
     459        return new GpxTrack(Collections.singleton(Collections.singleton(new WayPoint(LatLon.ZERO))), Collections.emptyMap());
    457460    }
    458461
    459     private static ImmutableGpxTrack waypointGpxTrack(WayPoint... wps) {
    460         return new ImmutableGpxTrack(Collections.singleton(Arrays.asList(wps)), Collections.emptyMap());
     462    private static GpxTrack waypointGpxTrack(WayPoint... wps) {
     463        return new GpxTrack(Collections.singleton(Arrays.asList(wps)), Collections.emptyMap());
    461464    }
    462465
    463466    private static GpxRoute singleWaypointRoute() {
     
    472475    @Test
    473476    public void testEqualsContract() {
    474477        TestUtils.assumeWorkingEqualsVerifier();
     478        GpxExtensionCollection col = new GpxExtensionCollection();
     479        col.add("josm", "from-server", "true");
    475480        EqualsVerifier.forClass(GpxData.class).usingGetClass()
    476             .withIgnoredFields("attr", "creator", "fromServer", "storageFile", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans")
     481            .suppress(Warning.NONFINAL_FIELDS)
     482            .withIgnoredFields("creator", "fromServer", "storageFile", "initializing", "updating", "suppressedInvalidate", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans", "modified")
    477483            .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
    478484            .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create())
     485            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
    479486            .verify();
    480487    }
    481488}
  • test/unit/org/openstreetmap/josm/data/gpx/GpxExtensionTest.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import org.junit.Rule;
     5import org.junit.Test;
     6import org.openstreetmap.josm.TestUtils;
     7import org.openstreetmap.josm.gui.layer.gpx.ConvertToDataLayerActionTest;
     8import org.openstreetmap.josm.io.GpxReaderTest;
     9import org.openstreetmap.josm.io.GpxWriterTest;
     10import org.openstreetmap.josm.testutils.JOSMTestRules;
     11
     12import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     13import nl.jqno.equalsverifier.EqualsVerifier;
     14import nl.jqno.equalsverifier.Warning;
     15
     16/**
     17 * Unit tests for class {@link GpxExtension}
     18 */
     19public class GpxExtensionTest {
     20
     21    /**
     22     * Setup test.
     23     */
     24    @Rule
     25    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     26    public JOSMTestRules test = new JOSMTestRules();
     27
     28    /**
     29     * Unit test of methods {@link GpxExtension#equals} and {@link GpxExtension#hashCode}.
     30     * @see GpxWriterTest#testExtensions()
     31     * @see GpxReaderTest#testLayerPrefs()
     32     * @see ConvertToDataLayerActionTest#testFromTrack()
     33     */
     34    @Test
     35    public void testEqualsContract() {
     36        TestUtils.assumeWorkingEqualsVerifier();
     37        GpxExtensionCollection col = new GpxExtensionCollection();
     38        col.add("josm", "from-server", "true");
     39        EqualsVerifier.forClass(GpxExtension.class).usingGetClass()
     40        .suppress(Warning.NONFINAL_FIELDS)
     41        .withIgnoredFields("qualifiedName")
     42        .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
     43        .verify();
     44    }
     45
     46}
  • test/unit/org/openstreetmap/josm/data/gpx/GpxRouteTest.java

     
    2929    @Test
    3030    public void testEqualsContract() {
    3131        TestUtils.assumeWorkingEqualsVerifier();
     32        GpxExtensionCollection col = new GpxExtensionCollection();
     33        col.add("josm", "from-server", "true");
    3234        EqualsVerifier.forClass(GpxRoute.class).usingGetClass()
    3335            .suppress(Warning.NONFINAL_FIELDS)
    3436            .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
     37            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
    3538            .verify();
    3639    }
    3740}
  • test/unit/org/openstreetmap/josm/data/gpx/GpxTrackSegmentTest.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import org.junit.Rule;
     5import org.junit.Test;
     6import org.openstreetmap.josm.TestUtils;
     7import org.openstreetmap.josm.data.coor.LatLon;
     8import org.openstreetmap.josm.testutils.JOSMTestRules;
     9
     10import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     11import nl.jqno.equalsverifier.EqualsVerifier;
     12import nl.jqno.equalsverifier.Warning;
     13
     14/**
     15 * Unit tests for class {@link GpxTrackSegment}.
     16 */
     17public class GpxTrackSegmentTest {
     18
     19    /**
     20     * Setup test.
     21     */
     22    @Rule
     23    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     24    public JOSMTestRules test = new JOSMTestRules();
     25
     26    /**
     27     * Unit test of methods {@link GpxTrackSegment#equals} and {@link GpxTrackSegment#hashCode}.
     28     */
     29    @Test
     30    public void testEqualsContract() {
     31        TestUtils.assumeWorkingEqualsVerifier();
     32        GpxExtensionCollection col = new GpxExtensionCollection();
     33        col.add("josm", "from-server", "true");
     34        EqualsVerifier.forClass(GpxTrackSegment.class).usingGetClass()
     35            .suppress(Warning.NONFINAL_FIELDS)
     36            .withIgnoredFields("bounds", "length")
     37            .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
     38            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
     39            .verify();
     40    }
     41}
  • test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.gpx;
     3
     4import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertNull;
     6
     7import java.awt.Color;
     8import java.util.ArrayList;
     9import java.util.HashMap;
     10
     11import org.junit.Rule;
     12import org.junit.Test;
     13import org.openstreetmap.josm.TestUtils;
     14import org.openstreetmap.josm.testutils.JOSMTestRules;
     15import org.openstreetmap.josm.tools.ListenerList;
     16
     17import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     18import nl.jqno.equalsverifier.EqualsVerifier;
     19import nl.jqno.equalsverifier.Warning;
     20
     21/**
     22 * Unit tests for class {@link GpxTrack}.
     23 */
     24public class GpxTrackTest {
     25
     26    /**
     27     * Setup test.
     28     */
     29    @Rule
     30    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     31    public JOSMTestRules test = new JOSMTestRules();
     32
     33    /**
     34     * Tests weather the track can read and write colors.
     35     */
     36    @Test
     37    public void testColors() {
     38        GpxTrack trk = new GpxTrack(new ArrayList<IGpxTrackSegment>(), new HashMap<>());
     39        GpxExtensionCollection ext = trk.getExtensions();
     40        ext.add("gpxd", "color", "#FF0000");
     41        trk.invalidate();
     42        assertEquals(trk.getColor(), Color.RED);
     43        trk.setColor(Color.GREEN);
     44        assertEquals(trk.getColor(), Color.GREEN);
     45        trk.invalidate();
     46        assertEquals(trk.getColor(), Color.GREEN);
     47        ext.remove("gpxd", "color");
     48        trk.invalidate();
     49        assertNull(trk.getColor());
     50        ext.add("gpxx", "TrackExtensions").getExtensions().add("gpxx", "DisplayColor", "Blue");
     51        trk.invalidate();
     52        assertEquals(trk.getColor(), Color.BLUE);
     53        trk.setColor(null);
     54        assertNull(trk.getColor());
     55        trk.invalidate();
     56        assertNull(trk.getColor());
     57    }
     58
     59    /**
     60     * Unit test of methods {@link GpxTrack#equals} and {@link GpxTrack#hashCode}.
     61     */
     62    @Test
     63    public void testEqualsContract() {
     64        TestUtils.assumeWorkingEqualsVerifier();
     65        GpxExtensionCollection col = new GpxExtensionCollection();
     66        col.add("josm", "from-server", "true");
     67        EqualsVerifier.forClass(GpxTrack.class).usingGetClass()
     68            .suppress(Warning.NONFINAL_FIELDS)
     69            .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create())
     70            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
     71            .withIgnoredFields("bounds", "length", "colorCache", "colorFormat", "listeners")
     72            .verify();
     73    }
     74}
  • test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegmentTest.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.data.gpx;
    3 
    4 import org.junit.Rule;
    5 import org.junit.Test;
    6 import org.openstreetmap.josm.TestUtils;
    7 import org.openstreetmap.josm.data.coor.LatLon;
    8 import org.openstreetmap.josm.testutils.JOSMTestRules;
    9 
    10 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    11 import nl.jqno.equalsverifier.EqualsVerifier;
    12 
    13 /**
    14  * Unit tests for class {@link ImmutableGpxTrackSegment}.
    15  */
    16 public class ImmutableGpxTrackSegmentTest {
    17 
    18     /**
    19      * Setup test.
    20      */
    21     @Rule
    22     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    23     public JOSMTestRules test = new JOSMTestRules();
    24 
    25     /**
    26      * Unit test of methods {@link ImmutableGpxTrackSegment#equals} and {@link ImmutableGpxTrackSegment#hashCode}.
    27      */
    28     @Test
    29     public void testEqualsContract() {
    30         TestUtils.assumeWorkingEqualsVerifier();
    31         EqualsVerifier.forClass(ImmutableGpxTrackSegment.class).usingGetClass()
    32             .withIgnoredFields("bounds", "length")
    33             .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
    34             .verify();
    35     }
    36 }
  • test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackTest.java

    Property changes on: test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegmentTest.java
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.data.gpx;
    3 
    4 import org.junit.Rule;
    5 import org.junit.Test;
    6 import org.openstreetmap.josm.TestUtils;
    7 import org.openstreetmap.josm.testutils.JOSMTestRules;
    8 
    9 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    10 import nl.jqno.equalsverifier.EqualsVerifier;
    11 import nl.jqno.equalsverifier.Warning;
    12 
    13 /**
    14  * Unit tests for class {@link ImmutableGpxTrack}.
    15  */
    16 public class ImmutableGpxTrackTest {
    17 
    18     /**
    19      * Setup test.
    20      */
    21     @Rule
    22     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    23     public JOSMTestRules test = new JOSMTestRules();
    24 
    25     /**
    26      * Unit test of methods {@link ImmutableGpxTrack#equals} and {@link ImmutableGpxTrack#hashCode}.
    27      */
    28     @Test
    29     public void testEqualsContract() {
    30         TestUtils.assumeWorkingEqualsVerifier();
    31         EqualsVerifier.forClass(ImmutableGpxTrack.class).usingGetClass()
    32             .suppress(Warning.NONFINAL_FIELDS)
    33             .withIgnoredFields("bounds", "length")
    34             .verify();
    35     }
    36 }
  • test/unit/org/openstreetmap/josm/data/gpx/WayPointTest.java

    Property changes on: test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackTest.java
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    2828    @Test
    2929    public void testEqualsContract() {
    3030        TestUtils.assumeWorkingEqualsVerifier();
     31        GpxExtensionCollection col = new GpxExtensionCollection();
     32        col.add("josm", "from-server", "true");
    3133        EqualsVerifier.forClass(WayPoint.class).usingGetClass()
    3234            .suppress(Warning.NONFINAL_FIELDS)
    3335            .withIgnoredFields("customColoring", "dir", "drawLine", "east", "north", "eastNorthCacheKey")
     36            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
    3437            .verify();
    3538    }
    3639}
  • test/unit/org/openstreetmap/josm/data/gpx/WithAttributesTest.java

     
    2828    @Test
    2929    public void testEqualsContract() {
    3030        TestUtils.assumeWorkingEqualsVerifier();
     31        GpxExtensionCollection col = new GpxExtensionCollection();
     32        col.add("josm", "from-server", "true");
    3133        EqualsVerifier.forClass(WithAttributes.class).usingGetClass()
    3234            .suppress(Warning.NONFINAL_FIELDS)
     35            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
    3336            .verify();
    3437    }
    3538}
  • test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java

     
    33
    44import static org.junit.Assert.assertEquals;
    55import static org.junit.Assert.assertFalse;
     6import static org.junit.Assert.assertNull;
    67import static org.junit.Assert.assertTrue;
    78
    89import java.awt.Color;
     
    1819import org.junit.Test;
    1920import org.openstreetmap.josm.TestUtils;
    2021import org.openstreetmap.josm.data.gpx.GpxData;
    21 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
     22import org.openstreetmap.josm.data.gpx.GpxTrack;
     23import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2224import org.openstreetmap.josm.data.gpx.WayPoint;
    2325import org.openstreetmap.josm.data.osm.DataSet;
    2426import org.openstreetmap.josm.data.projection.Projections;
     
    7375    @Test
    7476    public void testGpxLayer() throws Exception {
    7577        GpxLayer layer = new GpxLayer(new GpxData(), "foo", false);
     78        GpxTrack trk = new GpxTrack(new ArrayList<IGpxTrackSegment>(), new HashMap<>());
     79        trk.getExtensions().add("gpxd", "color", "#FF0000");
     80        layer.data.addTrack(trk);
     81
    7682        assertEquals("foo", layer.getName());
    7783        assertFalse(layer.isLocalFile());
    78         assertEquals(Color.MAGENTA, layer.getColorProperty().get());
    79         assertEquals("<html>0 tracks (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText());
     84        assertEquals(layer.getColor(), Color.RED);
     85        assertEquals("<html>1 track (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText());
    8086
    8187        GpxLayer layer2 = new GpxLayer(new GpxData(), "bar", true);
    8288        assertEquals("bar", layer2.getName());
    8389        assertTrue(layer2.isLocalFile());
    84         assertEquals(Color.MAGENTA, layer2.getColorProperty().get());
     90        assertNull(layer2.getColor());
    8591        assertEquals("<html>0 tracks (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer2.getToolTipText());
    8692
    8793        assertTrue(layer.checkSaveConditions());
     
    187193    @Test
    188194    public void testGetTimespanForTrack() throws Exception {
    189195        assertEquals("", GpxLayer.getTimespanForTrack(
    190                 new ImmutableGpxTrack(new ArrayList<Collection<WayPoint>>(), new HashMap<String, Object>())));
     196                new GpxTrack(new ArrayList<Collection<WayPoint>>(), new HashMap<String, Object>())));
    191197
    192198        assertEquals("1/3/16 11:59 AM - 12:00 PM (0:00)", GpxLayer.getTimespanForTrack(getMinimalGpxData().tracks.iterator().next()));
    193199
  • test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java

     
    77import static org.junit.Assert.assertNull;
    88import static org.junit.Assert.assertTrue;
    99
    10 import java.awt.Color;
    1110import java.io.File;
    1211
    1312import org.junit.Before;
    1413import org.junit.Rule;
    1514import org.junit.Test;
    16 import org.openstreetmap.josm.data.preferences.AbstractProperty;
    17 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
    1815import org.openstreetmap.josm.data.projection.ProjectionRegistry;
    1916import org.openstreetmap.josm.testutils.JOSMTestRules;
    2017
     
    4340    }
    4441
    4542    /**
    46      * Test {@link Layer#getColorProperty()}
    47      */
    48     @Test
    49     public void testGetColorProperty() {
    50         assertEquals(null, testLayer.getColorProperty());
    51 
    52         AbstractProperty<Color> color = new LayerManagerTest.TestLayer() {
    53             @Override
    54             protected NamedColorProperty getBaseColorProperty() {
    55                 return new NamedColorProperty("x", Color.BLACK);
    56             }
    57         }.getColorProperty();
    58 
    59         assertEquals(Color.BLACK, color.get());
    60         assertEquals(Color.BLACK, color.getDefaultValue());
    61         assertEquals("clr.layer.Test Layer.x", color.getKey());
    62     }
    63 
    64     /**
    6543     * Test of {@link Layer#isInfoResizable}
    6644     */
    6745    @Test
     
    9775        testLayer.setName("Test Layer2");
    9876        assertEquals("Test Layer2", testLayer.getName());
    9977
    100         testLayer = new LayerManagerTest.TestLayer() {
    101             @Override
    102             public AbstractProperty<Color> getColorProperty() {
    103                 return new NamedColorProperty("test", Color.RED);
    104             }
    105         };
     78        testLayer = new LayerManagerTest.TestLayer();
    10679
    10780        testLayer.setName("Test Layer2");
    10881        testLayer.setName(null);
  • test/unit/org/openstreetmap/josm/gui/layer/MarkerLayerTest.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.gui.layer;
    3 
    4 import static org.junit.Assert.assertNotNull;
    5 import static org.junit.Assert.assertNull;
    6 
    7 import org.junit.Rule;
    8 import org.junit.Test;
    9 import org.openstreetmap.josm.data.gpx.GpxData;
    10 import org.openstreetmap.josm.data.osm.DataSet;
    11 import org.openstreetmap.josm.gui.MainApplication;
    12 import org.openstreetmap.josm.gui.MapFrame;
    13 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    14 import org.openstreetmap.josm.testutils.JOSMTestRules;
    15 
    16 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    17 
    18 /**
    19  * Unit tests of {@link MarkerLayer} class.
    20  */
    21 public class MarkerLayerTest {
    22 
    23     /**
    24      * For creating layers
    25      */
    26     @Rule
    27     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    28     public JOSMTestRules test = new JOSMTestRules().main().projection();
    29 
    30     /**
    31      * Unit test of {@code Main.map.mapView.playHeadMarker}.
    32      */
    33     @Test
    34     public void testPlayHeadMarker() {
    35         try {
    36             MainApplication.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "", null));
    37             MapFrame map = MainApplication.getMap();
    38             MarkerLayer layer = new MarkerLayer(new GpxData(), null, null, null);
    39             assertNull(map.mapView.playHeadMarker);
    40             MainApplication.getLayerManager().addLayer(layer);
    41             assertNotNull(map.mapView.playHeadMarker);
    42             MainApplication.getLayerManager().removeLayer(layer);
    43         } finally {
    44             if (MainApplication.isDisplayingMapView()) {
    45                 MainApplication.getMap().mapView.playHeadMarker = null;
    46             }
    47         }
    48     }
    49 }
  • test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java

    Property changes on: test/unit/org/openstreetmap/josm/gui/layer/MarkerLayerTest.java
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    2323import org.openstreetmap.josm.data.gpx.GpxConstants;
    2424import org.openstreetmap.josm.data.gpx.GpxData;
    2525import org.openstreetmap.josm.data.gpx.GpxTrack;
    26 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     26import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2727import org.openstreetmap.josm.data.gpx.WayPoint;
    2828import org.openstreetmap.josm.data.osm.DataSet;
    2929import org.openstreetmap.josm.data.osm.Node;
     
    240240        // Check that track is correct
    241241        assertEquals(1, gpx.getTrackCount());
    242242        GpxTrack track = gpx.getTracks().iterator().next();
    243         Collection<GpxTrackSegment> segments = track.getSegments();
     243        Collection<IGpxTrackSegment> segments = track.getSegments();
    244244        assertEquals(1, segments.size());
    245245        Collection<WayPoint> trackpoints = segments.iterator().next().getWayPoints();
    246246        assertEquals(3, trackpoints.size());
  • test/unit/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityActionTest.java

     
    3838        final ExtendedDialogMocker edMocker = new ExtendedDialogMocker() {
    3939            @Override
    4040            protected String getString(final ExtendedDialog instance) {
    41                 return ((JLabel) ((JPanel) this.getContent(instance)).getComponent(2)).getText();
     41                return ((JLabel) ((JPanel) instance.getContentPane().getComponent(0)).getComponent(2)).getText();
    4242            }
    4343        };
    4444        edMocker.getMockResultMap().put(
    45             "<html>Select all tracks that you want to be displayed. You can drag select a range of " +
    46             "tracks or use CTRL+Click to select specific ones. The map is updated live in the " +
    47             "background. Open the URLs by double clicking them.</html>",
    48             "Show all"
    49         );
     45                "<html>Select all tracks that you want to be displayed. " +
     46                        "You can drag select a range of tracks or use CTRL+Click to select specific ones. " +
     47                        "The map is updated live in the background. Open the URLs by double clicking them, edit name and description by double clicking the cell.</html>",
     48                        "Show all"
     49                );
    5050
    5151        new ChooseTrackVisibilityAction(GpxLayerTest.getMinimalGpxLayer()).actionPerformed(null);
    5252
    5353        assertEquals(1, edMocker.getInvocationLog().size());
    5454        Object[] invocationLogEntry = edMocker.getInvocationLog().get(0);
    55         assertEquals(1, (int) invocationLogEntry[0]);
     55        assertEquals(2, (int) invocationLogEntry[0]);
    5656        assertEquals("Set track visibility for Bananas", invocationLogEntry[2]);
    5757    }
    5858}
  • test/unit/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerActionTest.java

     
    6666    @Test
    6767    public void testFromTrack() throws Exception {
    6868        Config.getPref().put("gpx.convert-tags", "no");
    69         testFromTrack("tracks");
     69        testFromTrack("tracks.gpx", "tracks.osm");
    7070
    7171        Config.getPref().put("gpx.convert-tags", "yes");
    72         testFromTrack("tracks-ele-time");
     72        testFromTrack("tracks.gpx", "tracks-ele-time.osm");
    7373
    7474        Config.getPref().put("gpx.convert-tags", "list");
    7575        Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("ele"));
    7676        Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList("time"));
    77         testFromTrack("tracks-ele");
     77        testFromTrack("tracks.gpx", "tracks-ele.osm");
    7878
    79 
    8079        Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("time"));
    8180        Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList("ele"));
    82         testFromTrack("tracks-time");
     81        testFromTrack("tracks.gpx", "tracks-time.osm");
     82
     83        //Extension tests:
     84        Config.getPref().put("gpx.convert-tags", "yes");
     85        testFromTrack("tracks-extensions.gpx", "tracks-extensions.osm");
     86
     87        Config.getPref().put("gpx.convert-tags", "list");
     88        Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("time", "ele"));
     89        Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList(
     90                "gpxx:DisplayColor",
     91                "gpxd:color",
     92                "gpx:extension:test:tag",
     93                "gpx:extension:test:segment:tag"));
     94        testFromTrack("tracks-extensions.gpx", "tracks-ele-time.osm");
     95
    8396    }
    8497
    8598    private static class GenericNode {
     
    106119        }
    107120    }
    108121
    109     private void testFromTrack(String expected) throws IOException, SAXException, IllegalDataException {
    110         final GpxData data = GpxReaderTest.parseGpxData(TestUtils.getTestDataRoot() + "tracks/tracks.gpx");
     122    private void testFromTrack(String originalGpx, String expectedOsm) throws IOException, SAXException, IllegalDataException {
     123        final GpxData data = GpxReaderTest.parseGpxData(TestUtils.getTestDataRoot() + "tracks/" + originalGpx);
    111124        final DataSet osmExpected = OsmReader.parseDataSet(Files.newInputStream(
    112                 Paths.get(TestUtils.getTestDataRoot(), "tracks/" + expected + ".osm")), null);
     125                Paths.get(TestUtils.getTestDataRoot(), "tracks/" + expectedOsm)), null);
    113126        final GpxLayer layer = new GpxLayer(data);
    114127        final DataSet osm = new ConvertFromGpxLayerAction(layer).convert();
    115         //compare sorted coordinates/tags and total amount of primitives, because IDs and order will vary after reload
     128        //compare sorted nodes/ways, tags and total amount of primitives, because IDs and order will vary after reload
    116129
    117130        List<GenericNode> nodes = osm.getNodes().stream()
    118131                .map(GenericNode::new)
     
    124137                .sorted(Comparator.comparing(g -> g.coor.hashCode()))
    125138                .collect(Collectors.toList());
    126139
    127         assertEquals(nodesExpected, nodes);
    128         assertEquals(osmExpected.allPrimitives().size(), osm.allPrimitives().size());
     140        assertEquals("Conversion " + originalGpx + " -> " + expectedOsm + " didn't match!", nodesExpected, nodes);
     141
     142        List<String> ways = osm.getWays().stream()
     143                .map(w -> Integer.toString(w.getNodes().size()) + ":" + w.getKeys().entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey())).collect(Collectors.toList()).toString())
     144                .sorted()
     145                .collect(Collectors.toList());
     146
     147        List<String> waysExpected = osmExpected.getWays().stream()
     148                .map(w -> Integer.toString(w.getNodes().size()) + ":" + w.getKeys().entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey())).collect(Collectors.toList()).toString())
     149                .sorted()
     150                .collect(Collectors.toList());
     151
     152        assertEquals("Conversion " + originalGpx + " -> " + expectedOsm + " didn't match!", waysExpected, ways);
     153
     154        assertEquals("Conversion " + originalGpx + " -> " + expectedOsm + " didn't match!", osmExpected.allPrimitives().size(), osm.allPrimitives().size());
    129155    }
    130156
    131157    /**
  • test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java

     
    66import java.io.FileNotFoundException;
    77import java.io.IOException;
    88import java.util.List;
     9import java.util.Map;
    910import java.util.stream.Collectors;
    1011
    1112import org.junit.Rule;
     
    1314import org.openstreetmap.josm.TestUtils;
    1415import org.openstreetmap.josm.data.gpx.GpxData;
    1516import org.openstreetmap.josm.gui.layer.GpxLayer;
     17import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper.ColorMode;
    1618import org.openstreetmap.josm.io.GpxReaderTest;
    17 import org.openstreetmap.josm.spi.preferences.Config;
    1819import org.openstreetmap.josm.testutils.JOSMTestRules;
    1920import org.openstreetmap.josm.tools.ColorHelper;
    2021import org.xml.sax.SAXException;
    2122
     23import com.google.common.collect.ImmutableMap;
     24
    2225import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    2326
    2427/**
     
    4144     */
    4245    @Test
    4346    public void testTicket12312() throws FileNotFoundException, IOException, SAXException {
    44         Config.getPref().putBoolean("draw.rawgps.colors.dynamic.layer 12312", true);
    45         Config.getPref().putInt("draw.rawgps.colors.layer 12312", GpxDrawHelper.ColorMode.VELOCITY.toIndex());
    46         final List<String> colors = calculateColors(TestUtils.getRegressionDataFile(12312, "single_trackpoint.gpx"), "12312", 1);
    47         assertEquals("[#FF00FF]", colors.toString());
     47        final List<String> colors = calculateColors(TestUtils.getRegressionDataFile(12312, "single_trackpoint.gpx"),
     48                ImmutableMap.of("colormode.dynamic-range", "true",
     49                        "colormode", Integer.toString(ColorMode.VELOCITY.toIndex())),
     50                1);
     51        assertEquals("[null]", colors.toString());
    4852    }
    4953
    5054    /**
     
    5559     */
    5660    @Test
    5761    public void testNone() throws IOException, SAXException {
    58         final List<String> colors = calculateColors("data_nodist/2094047.gpx", "000", 10);
    59         assertEquals("[#FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF]", colors.toString());
     62        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of(), 10);
     63        assertEquals("[#000000, #000000, #000000, #000000, #000000, #000000, #000000, #000000, #000000, #000000]", colors.toString());
    6064    }
    6165
    6266    /**
     
    6771     */
    6872    @Test
    6973    public void testVelocity() throws IOException, SAXException {
    70         Config.getPref().putInt("draw.rawgps.colors.layer 001", GpxDrawHelper.ColorMode.VELOCITY.toIndex());
    71         final List<String> colors = calculateColors("data_nodist/2094047.gpx", "001", 10);
    72         assertEquals("[#FF00FF, #FFAD00, #FFA800, #FFA800, #FF9E00, #FF9400, #FF7000, #FF7000, #FF8000, #FF9400]", colors.toString());
     74        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of("colormode", Integer.toString(ColorMode.VELOCITY.toIndex())), 10);
     75        assertEquals("[#000000, #FFAD00, #FFA800, #FFA800, #FF9E00, #FF9400, #FF7000, #FF7000, #FF8000, #FF9400]", colors.toString());
    7376    }
    7477
    7578    /**
     
    8083     */
    8184    @Test
    8285    public void testVelocityDynamic() throws IOException, SAXException {
    83         Config.getPref().putInt("draw.rawgps.colors.layer 002", GpxDrawHelper.ColorMode.VELOCITY.toIndex());
    84         Config.getPref().putBoolean("draw.rawgps.colors.dynamic.layer 002", true);
    85         final List<String> colors = calculateColors("data_nodist/2094047.gpx", "002", 10);
    86         assertEquals("[#FF00FF, #00FFE0, #00FFC2, #00FFC2, #00FF75, #00FF3D, #99FF00, #94FF00, #38FF00, #00FF38]", colors.toString());
     86        final List<String> colors = calculateColors("data_nodist/2094047.gpx",
     87                ImmutableMap.of("colormode.dynamic-range", "true",
     88                        "colormode", Integer.toString(ColorMode.VELOCITY.toIndex())),
     89                10);
     90        assertEquals("[#000000, #00FFE0, #00FFC2, #00FFC2, #00FF75, #00FF3D, #99FF00, #94FF00, #38FF00, #00FF38]", colors.toString());
    8791    }
    8892
    8993    /**
     
    9498     */
    9599    @Test
    96100    public void testDirection() throws IOException, SAXException {
    97         Config.getPref().putInt("draw.rawgps.colors.layer 003", GpxDrawHelper.ColorMode.DIRECTION.toIndex());
    98         final List<String> colors = calculateColors("data_nodist/2094047.gpx", "003", 10);
    99         assertEquals("[#FF00FF, #EAEC25, #EDEA26, #EDE525, #ECD322, #EBB81D, #E85A0D, #E73708, #E84D0B, #EA8A15]", colors.toString());
     101        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of("colormode", Integer.toString(ColorMode.DIRECTION.toIndex())), 10);
     102        assertEquals("[#000000, #EAEC25, #EDEA26, #EDE525, #ECD322, #EBB81D, #E85A0D, #E73708, #E84D0B, #EA8A15]", colors.toString());
    100103    }
    101104
    102105    /**
     
    107110     */
    108111    @Test
    109112    public void testTime() throws IOException, SAXException {
    110         Config.getPref().putInt("draw.rawgps.colors.layer 003", GpxDrawHelper.ColorMode.TIME.toIndex());
    111         final List<String> colors = calculateColors("data_nodist/2094047.gpx", "003", 10);
    112         assertEquals("[#FF00FF, #FF0000, #FF0000, #FF0500, #FF0500, #FF0A00, #FF0A00, #FF1F00, #FF2E00, #FF3300]", colors.toString());
     113        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of("colormode", Integer.toString(ColorMode.TIME.toIndex())), 10);
     114        assertEquals("[#000000, #FF0000, #FF0000, #FF0500, #FF0500, #FF0A00, #FF0A00, #FF1F00, #FF2E00, #FF3300]", colors.toString());
    113115    }
    114116
    115117    /**
    116118     *
    117119     * @param fileName the GPX filename to parse
    118      * @param layerName the layer name used to fetch the color settings, see {@link GpxDrawHelper#readPreferences(java.lang.String)}
     120     * @param layerPrefs a HashMap representing the layer specific preferences
    119121     * @param n the number of waypoints of the first track/segment to analyze
    120122     * @return the HTML color codes for the first {@code n} points
    121123     * @throws IOException if any I/O error occurs
     
    122124     * @throws FileNotFoundException if the data file is not found
    123125     * @throws SAXException if any SAX error occurs
    124126     */
    125     static List<String> calculateColors(String fileName, String layerName, int n) throws IOException, SAXException {
     127    static List<String> calculateColors(String fileName, Map<String, String> layerPrefs, int n) throws IOException, SAXException {
    126128        final GpxData data = GpxReaderTest.parseGpxData(fileName);
     129        data.getLayerPrefs().putAll(layerPrefs);
    127130        final GpxLayer layer = new GpxLayer(data);
    128131        final GpxDrawHelper gdh = new GpxDrawHelper(layer);
    129         gdh.readPreferences(layerName);
     132        gdh.readPreferences();
    130133        gdh.calculateColors();
    131134        return data.getTrackPoints().limit(n).map(p -> ColorHelper.color2html(p.customColoring)).collect(Collectors.toList());
    132135    }
  • test/unit/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarkerTest.java

     
    1010import org.junit.Test;
    1111import org.openstreetmap.josm.JOSMFixture;
    1212import org.openstreetmap.josm.data.coor.LatLon;
    13 import org.openstreetmap.josm.data.gpx.Extensions;
    14 import org.openstreetmap.josm.data.gpx.GpxConstants;
    1513import org.openstreetmap.josm.data.gpx.GpxData;
    1614import org.openstreetmap.josm.data.gpx.WayPoint;
    1715
     
    4543        assertEquals("2", marker.getText());
    4644        WayPoint wpt = marker.convertToWayPoint();
    4745        assertEquals(LatLon.ZERO, wpt.getCoor());
    48         Extensions ext = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);
    49         assertEquals("2.0", ext.get("offset"));
     46        assertEquals("2.0", wpt.getExtensions().get("josm", "offset").getValue());
    5047    }
    5148}
  • test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java

     
    33
    44import static org.junit.Assert.assertEquals;
    55import static org.junit.Assert.assertNotNull;
     6import static org.junit.Assert.assertNull;
    67import static org.junit.Assert.assertTrue;
    78
    8 import java.awt.Color;
    99import java.util.Arrays;
    1010
    1111import org.junit.Before;
     
    1616import org.openstreetmap.josm.data.gpx.GpxData;
    1717import org.openstreetmap.josm.data.gpx.GpxLink;
    1818import org.openstreetmap.josm.data.gpx.WayPoint;
     19import org.openstreetmap.josm.data.osm.DataSet;
    1920import org.openstreetmap.josm.gui.MainApplication;
     21import org.openstreetmap.josm.gui.MapFrame;
     22import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2023import org.openstreetmap.josm.spi.preferences.Config;
    2124import org.openstreetmap.josm.testutils.JOSMTestRules;
    2225
     
    2831public class MarkerLayerTest {
    2932
    3033    /**
    31      * Setup tests
     34     * For creating layers
    3235     */
    3336    @Rule
    3437    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     
    4750     */
    4851    @Test
    4952    public void testMarkerLayer() {
    50         assertEquals(Color.magenta, MarkerLayer.getGenericColor());
    5153        MarkerLayer layer = new MarkerLayer(new GpxData(), "foo", null, null);
    5254        MainApplication.getLayerManager().addLayer(layer);
    5355
    5456        assertEquals("foo", layer.getName());
    55         assertEquals(Color.magenta, layer.getColorProperty().get());
     57        assertNull(layer.getColor());
    5658        assertNotNull(layer.getIcon());
    5759        assertEquals("0 markers", layer.getToolTipText());
    5860        assertEquals("<html>foo consists of 0 markers</html>", layer.getInfoComponent());
     
    6163        GpxData gpx = new GpxData();
    6264        WayPoint wpt = new WayPoint(LatLon.ZERO);
    6365        wpt.attr.put(GpxConstants.META_LINKS, Arrays.asList(new GpxLink("https://josm.openstreetmap.de")));
    64         wpt.addExtension("offset", "1.0");
     66        wpt.getExtensions().add("josm", "offset", "1.0");
    6567        gpx.waypoints.add(wpt);
    6668        wpt = new WayPoint(LatLon.ZERO);
    67         wpt.addExtension("offset", "NaN");
     69        wpt.getExtensions().add("josm", "offset", "NaN");
    6870        gpx.waypoints.add(wpt);
    6971        layer = new MarkerLayer(gpx, "bar", null, null);
    7072
    7173        assertEquals("bar", layer.getName());
    72         assertEquals(Color.magenta, layer.getColorProperty().get());
     74        assertNull(layer.getColor());
    7375        assertNotNull(layer.getIcon());
    7476        assertEquals("3 markers", layer.getToolTipText());
    7577        assertEquals("<html>bar consists of 3 markers</html>", layer.getInfoComponent());
    7678        assertTrue(layer.getMenuEntries().length > 10);
    7779    }
     80
     81    /**
     82     * Unit test of {@code Main.map.mapView.playHeadMarker}.
     83     */
     84    @Test
     85    public void testPlayHeadMarker() {
     86        try {
     87            MainApplication.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "", null));
     88            MapFrame map = MainApplication.getMap();
     89            MarkerLayer layer = new MarkerLayer(new GpxData(), null, null, null);
     90            assertNull(map.mapView.playHeadMarker);
     91            MainApplication.getLayerManager().addLayer(layer);
     92            assertNotNull(map.mapView.playHeadMarker);
     93            MainApplication.getLayerManager().removeLayer(layer);
     94        } finally {
     95            if (MainApplication.isDisplayingMapView()) {
     96                MainApplication.getMap().mapView.playHeadMarker = null;
     97            }
     98        }
     99    }
    78100}
  • test/unit/org/openstreetmap/josm/io/GpxReaderTest.java

     
    1010import java.io.IOException;
    1111import java.nio.charset.StandardCharsets;
    1212import java.util.ArrayList;
     13import java.util.HashMap;
     14import java.util.Map;
    1315
    1416import org.junit.Rule;
    1517import org.junit.Test;
     
    6971    }
    7072
    7173    /**
     74     * Tests if layer preferences can be read
     75     * @throws Exception if track can't be parsed
     76     */
     77    @Test
     78    public void testLayerPrefs() throws Exception {
     79        final GpxData data = parseGpxData(TestUtils.getTestDataRoot() + "tracks/tracks-layerprefs.gpx");
     80        Map<String, String> e = new HashMap<>();
     81        e.put("colormode.velocity.tune", "10");
     82        e.put("lines.arrows.min-distance", "20");
     83        e.put("colormode", "1");
     84        e.put("lines", "1");
     85        e.put("lines.arrows", "true");
     86        e.put("colormode.dynamic-range", "true");
     87        e.put("colors", "0");
     88        assertEquals(data.getLayerPrefs(), e);
     89    }
     90
     91    /**
    7292     * Tests invalid data.
    7393     * @throws Exception always SAXException
    7494     */
  • test/unit/org/openstreetmap/josm/io/GpxWriterTest.java

     
    33
    44import static org.junit.Assert.assertEquals;
    55
     6import java.awt.Color;
    67import java.io.ByteArrayOutputStream;
    78import java.io.IOException;
    89import java.nio.charset.StandardCharsets;
     
    910import java.time.LocalDate;
    1011import java.time.Month;
    1112import java.time.ZoneOffset;
     13import java.util.Arrays;
    1214import java.util.Date;
     15import java.util.HashMap;
    1316import java.util.function.Consumer;
    1417
    1518import org.junit.Rule;
     
    1720import org.openstreetmap.josm.data.coor.LatLon;
    1821import org.openstreetmap.josm.data.gpx.GpxConstants;
    1922import org.openstreetmap.josm.data.gpx.GpxData;
     23import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace;
     24import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
     25import org.openstreetmap.josm.data.gpx.GpxTrack;
     26import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
    2027import org.openstreetmap.josm.data.gpx.WayPoint;
    2128import org.openstreetmap.josm.testutils.JOSMTestRules;
    2229
     
    8794                "    <vdop>0.9</vdop>%n" +
    8895                "    <pdop>1.2</pdop>%n");
    8996    }
     97
     98    /**
     99     * Tests if extensions are written correctly.
     100     * @throws IOException
     101     */
     102    @Test
     103    public void testExtensions() throws IOException {
     104        GpxData data = new GpxData();
     105        data.getNamespaces().add(new XMLNamespace("test", "http://example.com/testURI")); //only namespace, no location printed
     106        data.getNamespaces().add(new XMLNamespace("knownprefix", "http://example.com/URI", "http://example.com/location.xsd")); //printed
     107        data.getNamespaces().add(new XMLNamespace("notpresent", "http://example.com/notpresent", "http://example.com/notpresent.xsd")); //NOT printed
     108
     109        GpxExtensionCollection exts = data.getExtensions();
     110        data.fromServer = true; //printed
     111        data.getLayerPrefs().put("foo", "bar"); //printed depending on writer config
     112        exts.add("knownprefix", "foo", "bar"); //printed
     113        exts.add("unknownprefix", "foo", "bar"); //NOT printed
     114
     115        WayPoint wpt = new WayPoint(LatLon.ZERO);
     116        wpt.getExtensions().add("test", "foo", "extension of a waypoint"); //printed
     117
     118        GpxTrackSegment seg = new GpxTrackSegment(Arrays.asList(wpt));
     119        seg.getExtensions().add("test", "foo", "extension of a segment"); //printed
     120
     121        GpxTrack trk = new GpxTrack(Arrays.asList(seg), new HashMap<>());
     122
     123        trk.setColor(Color.RED); //printed depending on writer configuration
     124
     125        data.addTrack(trk);
     126
     127        ByteArrayOutputStream baos = new ByteArrayOutputStream();
     128        GpxWriter writer = new GpxWriter(baos);
     129
     130        writer.write(data);
     131        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
     132                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
     133                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
     134                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
     135                "    xmlns:gpxd=\"http://josm.openstreetmap.de/gpx-drawing-extensions-1.0\"\n" +
     136                "    xmlns:test=\"http://example.com/testURI\"\n" +
     137                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
     138                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd http://josm.openstreetmap.de/gpx-drawing-extensions-1.0 http://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd\">\n" +
     139                "  <metadata>\n" +
     140                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
     141                "    <extensions>\n" +
     142                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
     143                "      <josm:from-server>true</josm:from-server>\n" +
     144                "      <josm:layerPreferences>\n" +
     145                "        <josm:entry key=\"foo\" value=\"bar\"/>\n" +
     146                "      </josm:layerPreferences>\n" +
     147                "    </extensions>\n" +
     148                "  </metadata>\n" +
     149                "  <trk>\n" +
     150                "    <extensions>\n" +
     151                "      <gpxd:color>#FF0000</gpxd:color>\n" +
     152                "    </extensions>\n" +
     153                "    <trkseg>\n" +
     154                "      <extensions>\n" +
     155                "        <test:foo>extension of a segment</test:foo>\n" +
     156                "      </extensions>\n" +
     157                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
     158                "        <extensions>\n" +
     159                "          <test:foo>extension of a waypoint</test:foo>\n" +
     160                "        </extensions>\n" +
     161                "      </trkpt>\n" +
     162                "    </trkseg>\n" +
     163                "  </trk>\n" +
     164                "</gpx>", baos.toString());
     165
     166        baos.reset();
     167        writer.write(data, GpxConstants.ColorFormat.GPXX, true);
     168        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
     169                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
     170                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
     171                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
     172                "    xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\"\n" +
     173                "    xmlns:test=\"http://example.com/testURI\"\n" +
     174                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
     175                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd\">\n" +
     176                "  <metadata>\n" +
     177                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
     178                "    <extensions>\n" +
     179                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
     180                "      <josm:from-server>true</josm:from-server>\n" +
     181                "      <josm:layerPreferences>\n" +
     182                "        <josm:entry key=\"foo\" value=\"bar\"/>\n" +
     183                "      </josm:layerPreferences>\n" +
     184                "    </extensions>\n" +
     185                "  </metadata>\n" +
     186                "  <trk>\n" +
     187                "    <extensions>\n" +
     188                "      <gpxx:TrackExtensions>\n" +
     189                "        <gpxx:DisplayColor>Red</gpxx:DisplayColor>\n" +
     190                "      </gpxx:TrackExtensions>\n" +
     191                "    </extensions>\n" +
     192                "    <trkseg>\n" +
     193                "      <extensions>\n" +
     194                "        <test:foo>extension of a segment</test:foo>\n" +
     195                "      </extensions>\n" +
     196                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
     197                "        <extensions>\n" +
     198                "          <test:foo>extension of a waypoint</test:foo>\n" +
     199                "        </extensions>\n" +
     200                "      </trkpt>\n" +
     201                "    </trkseg>\n" +
     202                "  </trk>\n" +
     203                "</gpx>", baos.toString());
     204
     205        baos.reset();
     206        writer.write(data, null, false);
     207        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
     208                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
     209                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
     210                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
     211                "    xmlns:test=\"http://example.com/testURI\"\n" +
     212                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
     213                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd\">\n" +
     214                "  <metadata>\n" +
     215                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
     216                "    <extensions>\n" +
     217                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
     218                "      <josm:from-server>true</josm:from-server>\n" +
     219                "    </extensions>\n" +
     220                "  </metadata>\n" +
     221                "  <trk>\n" +
     222                "    <trkseg>\n" +
     223                "      <extensions>\n" +
     224                "        <test:foo>extension of a segment</test:foo>\n" +
     225                "      </extensions>\n" +
     226                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
     227                "        <extensions>\n" +
     228                "          <test:foo>extension of a waypoint</test:foo>\n" +
     229                "        </extensions>\n" +
     230                "      </trkpt>\n" +
     231                "    </trkseg>\n" +
     232                "  </trk>\n" +
     233                "</gpx>", baos.toString());
     234
     235        baos.reset();
     236        writer.write(data, GpxConstants.ColorFormat.GPXX, true);
     237        // checked again to make sure that extensions are shown again after
     238        // being hidden, even if they don't actually have to be converted
     239        // (GPXD -> convertColor() -> GPXX -> hide() -> null -> show() -> GPXX)
     240        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
     241                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
     242                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
     243                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
     244                "    xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\"\n" +
     245                "    xmlns:test=\"http://example.com/testURI\"\n" +
     246                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
     247                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd\">\n" +
     248                "  <metadata>\n" +
     249                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
     250                "    <extensions>\n" +
     251                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
     252                "      <josm:from-server>true</josm:from-server>\n" +
     253                "      <josm:layerPreferences>\n" +
     254                "        <josm:entry key=\"foo\" value=\"bar\"/>\n" +
     255                "      </josm:layerPreferences>\n" +
     256                "    </extensions>\n" +
     257                "  </metadata>\n" +
     258                "  <trk>\n" +
     259                "    <extensions>\n" +
     260                "      <gpxx:TrackExtensions>\n" +
     261                "        <gpxx:DisplayColor>Red</gpxx:DisplayColor>\n" +
     262                "      </gpxx:TrackExtensions>\n" +
     263                "    </extensions>\n" +
     264                "    <trkseg>\n" +
     265                "      <extensions>\n" +
     266                "        <test:foo>extension of a segment</test:foo>\n" +
     267                "      </extensions>\n" +
     268                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
     269                "        <extensions>\n" +
     270                "          <test:foo>extension of a waypoint</test:foo>\n" +
     271                "        </extensions>\n" +
     272                "      </trkpt>\n" +
     273                "    </trkseg>\n" +
     274                "  </trk>\n" +
     275                "</gpx>", baos.toString());
     276
     277        writer.close();
     278    }
    90279}
  • test/unit/org/openstreetmap/josm/io/nmea/NmeaReaderTest.java

     
    2424import org.openstreetmap.josm.data.gpx.GpxConstants;
    2525import org.openstreetmap.josm.data.gpx.GpxData;
    2626import org.openstreetmap.josm.data.gpx.GpxTrack;
    27 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
     27import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
    2828import org.openstreetmap.josm.data.gpx.WayPoint;
    2929import org.openstreetmap.josm.io.GpxReaderTest;
    3030import org.openstreetmap.josm.testutils.JOSMTestRules;
     
    100100        int nTracks = gpxTrack.getSegments().size();
    101101        assertEquals(nTracks, nmeaTrack.getSegments().size());
    102102        if (nTracks > 0) {
    103             GpxTrackSegment gpxSeg = gpxTrack.getSegments().iterator().next();
    104             GpxTrackSegment nmeaSeg = nmeaTrack.getSegments().iterator().next();
     103            IGpxTrackSegment gpxSeg = gpxTrack.getSegments().iterator().next();
     104            IGpxTrackSegment nmeaSeg = nmeaTrack.getSegments().iterator().next();
    105105            assertEquals(gpxSeg.getBounds(), nmeaSeg.getBounds());
    106106            assertEquals(numCoor, gpxSeg.getWayPoints().size());
    107107            assertEquals(numCoor, nmeaSeg.getWayPoints().size());