Ticket #16796: GpxColors-v1.01.patch
File GpxColors-v1.01.patch, 499.2 KB (added by , 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
661 661 assertNoMatch: "way roof:color=red roof:colour=red"; 662 662 assertMatch: "way roof:color=red roof:colour=green"; 663 663 } 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] { 666 667 throwWarning: tr("{0} is deprecated", "{0.key}"); 667 668 suggestAlternative: ":colour"; 668 669 group: tr("deprecated tagging"); -
data/validator/unnecessary.mapcss
167 167 } 168 168 169 169 /* #2760 */ 170 *[/^ gpx:/] {170 *[/^(gpx|gpxx|gpxd):/] { 171 171 throwWarning: tr("{0} should not be uploaded", "{0.key}"); 172 172 group: tr("unnecessary tag"); 173 173 fixRemove: "{0.key}"; 174 174 assertMatch: "node gpx:time=2018-01-01T12:00:00Z"; 175 assertMatch: "node gpxd:color=#FF0000"; 175 176 assertNoMatch: "node source=gpx:foo"; 176 177 } -
src/org/openstreetmap/josm/actions/SaveAction.java
4 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 6 7 import java.awt.GridBagLayout; 7 8 import java.awt.event.KeyEvent; 8 9 import java.beans.PropertyChangeListener; 9 10 import java.io.File; 10 11 12 import javax.swing.JCheckBox; 13 import javax.swing.JLabel; 14 import javax.swing.JPanel; 15 import javax.swing.SwingConstants; 16 17 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener; 11 18 import org.openstreetmap.josm.gui.ExtendedDialog; 12 19 import org.openstreetmap.josm.gui.MainApplication; 13 20 import org.openstreetmap.josm.gui.layer.GpxLayer; … … 16 23 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 17 24 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 18 25 import org.openstreetmap.josm.gui.layer.SaveToFile; 26 import org.openstreetmap.josm.gui.util.GuiHelper; 27 import org.openstreetmap.josm.spi.preferences.Config; 28 import org.openstreetmap.josm.tools.GBC; 19 29 import org.openstreetmap.josm.tools.Shortcut; 20 30 21 31 /** … … 32 42 } 33 43 }; 34 44 45 private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState(); 46 35 47 /** 36 48 * Construct the action with "Save" as label. 37 49 */ 38 50 private SaveAction() { 39 51 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); 41 54 setHelpId(ht("/Action/Save")); 42 55 } 43 56 … … 54 67 return new LayerChangeAdapter() { 55 68 @Override 56 69 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); 59 73 } 74 if (l instanceof GpxLayer) { 75 ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx); 76 } 60 77 super.layerAdded(e); 61 78 } 62 79 63 80 @Override 64 81 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); 67 85 } 86 if (l instanceof GpxLayer) { 87 ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx); 88 } 68 89 super.layerRemoving(e); 69 90 } 70 91 }; … … 73 94 @Override 74 95 protected void updateEnabledState() { 75 96 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)); 79 101 } 80 102 81 103 @Override … … 85 107 f = null; 86 108 } 87 109 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)); 91 119 ExtendedDialog dialog = new ExtendedDialog( 92 120 MainApplication.getMainFrame(), 93 121 tr("Overwrite"), 94 122 tr("Overwrite"), tr("Cancel")) 95 123 .setButtonIcons("save_as", "cancel") 96 .setContent( tr("File {0} exists. Overwrite?", f.getName()));124 .setContent(p); 97 125 if (dialog.showDialog().getValue() != 1) { 98 126 f = null; 127 } else if (remember.isSelected()) { 128 Config.getPref().putBoolean("gpx.export.overwrite", true); 99 129 } 100 130 } 101 131 return f == null ? layer.createAndOpenSaveFileChooser() : f; -
src/org/openstreetmap/josm/actions/SaveActionBase.java
19 19 import org.openstreetmap.josm.gui.ExtendedDialog; 20 20 import org.openstreetmap.josm.gui.MainApplication; 21 21 import org.openstreetmap.josm.gui.io.importexport.FileExporter; 22 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; 22 23 import org.openstreetmap.josm.gui.layer.Layer; 23 import org.openstreetmap.josm.gui.layer.OsmDataLayer;24 import org.openstreetmap.josm.gui.layer.SaveToFile;25 24 import org.openstreetmap.josm.gui.util.GuiHelper; 26 25 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser; 27 26 import org.openstreetmap.josm.spi.preferences.Config; … … 35 34 */ 36 35 public abstract class SaveActionBase extends DiskAccessAction { 37 36 37 private boolean quiet; 38 38 39 /** 39 40 * Constructs a new {@code SaveActionBase}. 40 41 * @param name The action's text as displayed on the menu (if it is added to a menu) … … 46 47 super(name, iconName, tooltip, shortcut); 47 48 } 48 49 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 49 64 @Override 50 65 public void actionPerformed(ActionEvent e) { 51 66 if (!isEnabled()) 52 67 return; 53 doSave( );68 doSave(quiet); 54 69 } 55 70 56 71 /** … … 58 73 * @return {@code true} if the save operation succeeds 59 74 */ 60 75 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) { 61 86 Layer layer = getLayerManager().getActiveLayer(); 62 87 if (layer != null && layer.isSavable()) { 63 return doSave(layer );88 return doSave(layer, quiet); 64 89 } 65 90 return false; 66 91 } … … 71 96 * @return {@code true} if the save operation succeeds 72 97 */ 73 98 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) { 74 110 if (!layer.checkSaveConditions()) 75 111 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(); 81 114 return result; 82 115 } 83 116 … … 86 119 * @param layer The layer to save 87 120 * @param file The destination file 88 121 * @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. 91 123 * @return {@code true} if the layer has been successfully saved, {@code false} otherwise 92 124 * @since 7204 93 125 */ … … 94 126 public static boolean doSave(Layer layer, File file, boolean checkSaveConditions) { 95 127 if (checkSaveConditions && !layer.checkSaveConditions()) 96 128 return false; 97 return doInternalSave(layer, file );129 return doInternalSave(layer, file, !checkSaveConditions); 98 130 } 99 131 100 private static boolean doInternalSave(Layer layer, File file ) {132 private static boolean doInternalSave(Layer layer, File file, boolean quiet) { 101 133 if (file == null) 102 134 return false; 103 135 … … 106 138 boolean canceled = false; 107 139 for (FileExporter exporter : ExtensionFileFilter.getExporters()) { 108 140 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 } 110 146 exported = true; 111 147 canceled = exporter.isCanceled(); 112 148 break; … … 124 160 layer.setName(file.getName()); 125 161 } 126 162 layer.setAssociatedFile(file); 127 if (layer instanceof OsmDataLayer) {128 (( OsmDataLayer) layer).onPostSaveToFile();163 if (layer instanceof AbstractModifiableLayer) { 164 ((AbstractModifiableLayer) layer).onPostSaveToFile(); 129 165 } 130 166 } catch (IOException | InvalidPathException e) { 131 167 showAndLogException(e); -
src/org/openstreetmap/josm/actions/SaveAsAction.java
24 24 public SaveAsAction() { 25 25 super(tr("Save As..."), "save_as", tr("Save the current data to a new file."), 26 26 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); 28 28 setHelpId(ht("/Action/SaveAs")); 29 29 } 30 30 -
src/org/openstreetmap/josm/actions/ShowStatusReportAction.java
293 293 text.append(reportHeader); 294 294 295 295 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) 298 297 || "download.overpass.query".equals(key) 299 298 || "download.overpass.queries".equals(key) 300 299 || key.contains("username") -
src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java
25 25 import org.openstreetmap.josm.actions.GpxExportAction; 26 26 import org.openstreetmap.josm.actions.IPrimitiveAction; 27 27 import org.openstreetmap.josm.data.gpx.GpxData; 28 import org.openstreetmap.josm.data.gpx. ImmutableGpxTrack;28 import org.openstreetmap.josm.data.gpx.GpxTrack; 29 29 import org.openstreetmap.josm.data.gpx.WayPoint; 30 30 import org.openstreetmap.josm.data.osm.IPrimitive; 31 31 import org.openstreetmap.josm.data.osm.Node; … … 154 154 WayConnectionType wayConnectionType = wct.get(i); 155 155 if (!wayConnectionType.isOnewayLoopBackwardPart && !wayConnectionType.direction.isRoundabout()) { 156 156 if (!wayConnectionType.linkPrev && !trkseg.isEmpty()) { 157 gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));157 gpxData.addTrack(new GpxTrack(trk, trkAttr)); 158 158 trkAttr.clear(); 159 159 trk.clear(); 160 160 trkseg.clear(); … … 179 179 } 180 180 } 181 181 } 182 gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));182 gpxData.addTrack(new GpxTrack(trk, trkAttr)); 183 183 184 184 String lprefix = relations.iterator().next().getName(); 185 185 if (lprefix == null || relations.size() > 1) -
src/org/openstreetmap/josm/data/Preferences.java
21 21 import java.util.HashMap; 22 22 import java.util.HashSet; 23 23 import java.util.Iterator; 24 import java.util.List; 24 25 import java.util.Map; 25 26 import java.util.Map.Entry; 26 27 import java.util.Optional; … … 29 30 import java.util.TreeMap; 30 31 import java.util.concurrent.TimeUnit; 31 32 import java.util.function.Predicate; 33 import java.util.stream.Collectors; 32 34 import java.util.stream.Stream; 33 35 34 36 import javax.swing.JOptionPane; … … 81 83 */ 82 84 public class Preferences extends AbstractPreferences { 83 85 86 /** remove if key equals */ 84 87 private static final String[] OBSOLETE_PREF_KEYS = { 85 88 "remotecontrol.https.enabled", /* remove entry after Dec. 2019 */ 86 89 "remotecontrol.https.port", /* remove entry after Dec. 2019 */ 87 90 }; 88 91 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 89 162 private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50); 90 163 91 164 private final IBaseDirectories dirs; 165 boolean modifiedDefault; 92 166 93 167 /** 94 168 * Determines if preferences file is saved each time a property is changed. … … 415 489 reader.parse(); 416 490 settingsMap.clear(); 417 491 settingsMap.putAll(reader.getSettings()); 418 remove Obsolete(reader.getVersion());492 removeAndUpdateObsolete(reader.getVersion()); 419 493 } 420 494 421 495 /** … … 520 594 } 521 595 return; 522 596 } 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 } 523 610 try { 524 611 load(); 525 612 initSuccessful = true; … … 545 632 Logging.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile())); 546 633 } 547 634 } 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 }561 635 } 562 636 563 637 /** … … 755 829 } 756 830 757 831 /** 758 * Removes obsolete preference settings. If you throw out a once-used preference832 * Removes and updates obsolete preference settings. If you throw out a once-used preference 759 833 * setting, add it to the list here with an expiry date (written as comment). If you 760 834 * see something with an expiry date in the past, remove it from the list. 761 835 * @param loadedVersion JOSM version when the preferences file was written 762 836 */ 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 764 849 Logging.trace("Remove obsolete preferences for version {0}", Integer.toString(loadedVersion)); 765 850 for (String key : OBSOLETE_PREF_KEYS) { 766 851 if (settingsMap.containsKey(key)) { 767 852 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)); 769 854 } 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 } 770 860 } 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 } 771 893 } 772 894 773 895 /** -
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
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.awt.Color; 4 5 import java.util.Arrays; 5 6 import java.util.Collection; 6 7 import java.util.Collections; 7 8 import java.util.List; 9 import java.util.Map; 10 import java.util.TreeMap; 8 11 9 12 import org.openstreetmap.josm.data.Bounds; 10 13 import org.openstreetmap.josm.spi.preferences.Config; … … 95 98 * @see GpxData#getMetaBounds() 96 99 */ 97 100 String META_BOUNDS = META_PREFIX + "bounds"; 101 98 102 /** 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 102 104 */ 103 String META_EXTENSIONS = META_PREFIX + "extensions";105 String XML_URI_XSD = "http://www.w3.org/2001/XMLSchema-instance"; 104 106 105 107 /** 106 * A namespace for josmGPX extensions108 * Namespace for JOSM GPX extensions 107 109 */ 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"; 109 115 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 110 134 /** Elevation (in meters) of the point. */ 111 135 String PT_ELE = "ele"; 112 136 … … 154 178 */ 155 179 List<String> WPT_KEYS = Collections.unmodifiableList(Arrays.asList(PT_ELE, PT_TIME, PT_MAGVAR, PT_GEOIDHEIGHT, 156 180 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)); 158 182 159 183 /** 160 184 * Ordered list of all possible route and track keys. 161 185 */ 162 186 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)); 164 188 165 189 /** 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 /** 166 247 * Possible fix values. NMEA 0183 Version 4.00 167 248 */ 168 249 Collection<String> FIX_VALUES = Collections.unmodifiableList( -
src/org/openstreetmap/josm/data/gpx/GpxData.java
15 15 import java.util.LongSummaryStatistics; 16 16 import java.util.Map; 17 17 import java.util.NoSuchElementException; 18 import java.util.Objects; 19 import java.util.Optional; 18 20 import java.util.Set; 19 21 import java.util.stream.Collectors; 20 22 import java.util.stream.Stream; … … 23 25 import org.openstreetmap.josm.data.Data; 24 26 import org.openstreetmap.josm.data.DataSource; 25 27 import org.openstreetmap.josm.data.coor.EastNorth; 26 import org.openstreetmap.josm.data.gpx. GpxTrack.GpxTrackChangeListener;28 import org.openstreetmap.josm.data.gpx.IGpxTrack.GpxTrackChangeListener; 27 29 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 28 30 import org.openstreetmap.josm.gui.MainApplication; 29 31 import org.openstreetmap.josm.gui.layer.GpxLayer; … … 40 42 public class GpxData extends WithAttributes implements Data { 41 43 42 44 /** 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 /** 43 59 * The disk file this layer is stored in, if it is a local layer. May be <code>null</code>. 44 60 */ 45 61 public File storageFile; … … 65 81 * Addidionaly waypoints for this file. 66 82 */ 67 83 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<>(); 69 93 94 private final GpxTrackChangeListener proxy = e -> invalidate(); 95 private boolean modified, updating, initializing; 96 private boolean suppressedInvalidate; 97 70 98 /** 71 99 * Tracks. Access is discouraged, use {@link #getTracks()} to read. 72 100 * @see #getTracks() 73 101 */ 74 public final Collection<GpxTrack> tracks = new ListeningCollection<GpxTrack>(privateTracks, this:: fireInvalidate) {102 public final Collection<GpxTrack> tracks = new ListeningCollection<GpxTrack>(privateTracks, this::invalidate) { 75 103 76 104 @Override 77 105 protected void removed(GpxTrack cursor) { … … 90 118 * Routes. Access is discouraged, use {@link #getTracks()} to read. 91 119 * @see #getRoutes() 92 120 */ 93 public final Collection<GpxRoute> routes = new ListeningCollection<>(privateRoutes, this:: fireInvalidate);121 public final Collection<GpxRoute> routes = new ListeningCollection<>(privateRoutes, this::invalidate); 94 122 95 123 /** 96 124 * Waypoints. Access is discouraged, use {@link #getTracks()} to read. 97 125 * @see #getWaypoints() 98 126 */ 99 public final Collection<WayPoint> waypoints = new ListeningCollection<>(privateWaypoints, this:: fireInvalidate);127 public final Collection<WayPoint> waypoints = new ListeningCollection<>(privateWaypoints, this::invalidate); 100 128 101 129 /** 102 130 * All data sources (bounds of downloaded bounds) of this GpxData.<br> … … 108 136 109 137 private final ListenerList<GpxDataChangeListener> listeners = ListenerList.create(); 110 138 111 static class TimestampConfictException extends Exception {}112 113 139 private List<GpxTrackSegmentSpan> segSpans; 114 140 115 141 /** … … 156 182 other.privateRoutes.forEach(this::addRoute); 157 183 other.privateWaypoints.forEach(this::addWaypoint); 158 184 dataSources.addAll(other.dataSources); 159 fireInvalidate();185 invalidate(); 160 186 } 161 187 162 188 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) { 166 192 GpxTrackSegmentSpan s = GpxTrackSegmentSpan.tryGetFromSegment(seg); 167 193 if (s != null && anySegmentOverlapsWith(s)) { 168 194 List<WayPoint> wpsNew = new ArrayList<>(); … … 203 229 // splitting needs to be handled here, 204 230 // because other high priority tracks between the same waypoints could follow 205 231 if (!wpsNew.isEmpty()) { 206 segsNew.add(new ImmutableGpxTrackSegment(wpsNew));232 segsNew.add(new GpxTrackSegment(wpsNew)); 207 233 } 208 234 if (!segsNew.isEmpty()) { 209 privateTracks.add(new ImmutableGpxTrack(segsNew, trk.getAttributes()));235 privateTracks.add(new GpxTrack(segsNew, trk.getAttributes())); 210 236 } 211 237 segsNew = new ArrayList<>(); 212 238 wpsNew = new ArrayList<>(); … … 221 247 if (split) { 222 248 //track has to be split, because we have an overlapping short track in the middle 223 249 if (!wpsNew.isEmpty()) { 224 segsNew.add(new ImmutableGpxTrackSegment(wpsNew));250 segsNew.add(new GpxTrackSegment(wpsNew)); 225 251 } 226 252 if (!segsNew.isEmpty()) { 227 privateTracks.add(new ImmutableGpxTrack(segsNew, trk.getAttributes()));253 privateTracks.add(new GpxTrack(segsNew, trk.getAttributes())); 228 254 } 229 255 segsNew = new ArrayList<>(); 230 256 wpsNew = new ArrayList<>(); … … 238 264 } 239 265 } 240 266 if (!wpsNew.isEmpty()) { 241 segsNew.add(new ImmutableGpxTrackSegment(wpsNew));267 segsNew.add(new GpxTrackSegment(wpsNew)); 242 268 } 243 269 } else { 244 270 segsNew.add(seg); … … 247 273 if (segsNew.equals(segsOld)) { 248 274 privateTracks.add(trk); 249 275 } else if (!segsNew.isEmpty()) { 250 privateTracks.add(new ImmutableGpxTrack(segsNew, trk.getAttributes()));276 privateTracks.add(new GpxTrack(segsNew, trk.getAttributes())); 251 277 } 252 278 } 253 279 254 280 private void connectTracks(WayPoint prevWp, GpxTrackSegmentSpan span, Map<String, Object> attr) { 255 281 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)); 257 283 } 258 284 } 259 285 … … 309 335 || (other.firstTime.before(lastTime) && firstTime.before(other.lastTime)); 310 336 } 311 337 312 static GpxTrackSegmentSpan tryGetFromSegment( GpxTrackSegment seg) {338 static GpxTrackSegmentSpan tryGetFromSegment(IGpxTrackSegment seg) { 313 339 WayPoint b = getNextWpWithTime(seg, true); 314 340 if (b != null) { 315 341 WayPoint e = getNextWpWithTime(seg, false); … … 320 346 return null; 321 347 } 322 348 323 private static WayPoint getNextWpWithTime( GpxTrackSegment seg, boolean forward) {349 private static WayPoint getNextWpWithTime(IGpxTrackSegment seg, boolean forward) { 324 350 List<WayPoint> wps = new ArrayList<>(seg.getWayPoints()); 325 351 for (int i = forward ? 0 : wps.size() - 1; i >= 0 && i < wps.size(); i += forward ? 1 : -1) { 326 352 if (wps.get(i).hasDate()) { … … 340 366 if (segSpans == null) { 341 367 segSpans = new ArrayList<>(); 342 368 for (GpxTrack trk : privateTracks) { 343 for ( GpxTrackSegment seg : trk.getSegments()) {369 for (IGpxTrackSegment seg : trk.getSegments()) { 344 370 GpxTrackSegmentSpan s = GpxTrackSegmentSpan.tryGetFromSegment(seg); 345 371 if (s != null) { 346 372 segSpans.add(s); … … 373 399 * Get stream of track segments. 374 400 * @return {@code Stream<GPXTrack>} 375 401 */ 376 p rivate synchronized Stream<GpxTrackSegment> getTrackSegmentsStream() {402 public synchronized Stream<IGpxTrackSegment> getTrackSegmentsStream() { 377 403 return getTracks().stream().flatMap(trk -> trk.getSegments().stream()); 378 404 } 379 405 … … 397 423 } 398 424 privateTracks.add(track); 399 425 track.addListener(proxy); 400 fireInvalidate();426 invalidate(); 401 427 } 402 428 403 429 /** … … 410 436 throw new IllegalArgumentException(MessageFormat.format("The track was not in this data: {0}", track)); 411 437 } 412 438 track.removeListener(proxy); 413 fireInvalidate();439 invalidate(); 414 440 } 415 441 416 442 /** … … 420 446 * @since 13210 421 447 */ 422 448 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)); 425 451 Map<String, Object> attrs = new HashMap<>(privateTracks.get(0).getAttributes()); 426 452 427 453 // do not let the name grow if split / combine operations are called iteratively … … 431 457 } 432 458 433 459 clearTracks(); 434 addTrack(new ImmutableGpxTrack(segs, attrs));460 addTrack(new GpxTrack(segs, attrs)); 435 461 } 436 462 437 463 /** … … 467 493 .flatMap(trk -> trk.getSegments().stream().map(seg -> { 468 494 HashMap<String, Object> attrs = new HashMap<>(trk.getAttributes()); 469 495 ensureUniqueName(attrs, counts, srcLayerName); 470 return new ImmutableGpxTrack(Arrays.asList(seg), attrs);496 return new GpxTrack(Arrays.asList(seg), attrs); 471 497 })) 472 498 .collect(Collectors.toCollection(ArrayList<GpxTrack>::new)); 473 499 … … 538 564 throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", route)); 539 565 } 540 566 privateRoutes.add(route); 541 fireInvalidate();567 invalidate(); 542 568 } 543 569 544 570 /** … … 550 576 if (!privateRoutes.removeIf(r -> r == route)) { 551 577 throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", route)); 552 578 } 553 fireInvalidate();579 invalidate(); 554 580 } 555 581 556 582 /** … … 572 598 throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", waypoint)); 573 599 } 574 600 privateWaypoints.add(waypoint); 575 fireInvalidate();601 invalidate(); 576 602 } 577 603 578 604 /** … … 584 610 if (!privateWaypoints.removeIf(w -> w == waypoint)) { 585 611 throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", waypoint)); 586 612 } 587 fireInvalidate();613 invalidate(); 588 614 } 589 615 590 616 /** … … 600 626 * @return The stream 601 627 * @see #getTracks() 602 628 * @see GpxTrack#getSegments() 603 * @see GpxTrackSegment#getWayPoints()629 * @see IGpxTrackSegment#getWayPoints() 604 630 * @since 12156 605 631 */ 606 632 public synchronized Stream<WayPoint> getTrackPoints() { … … 767 793 double py = p.north(); 768 794 double rx = 0.0, ry = 0.0, sx, sy, x, y; 769 795 for (GpxTrack track : privateTracks) { 770 for ( GpxTrackSegment seg : track.getSegments()) {796 for (IGpxTrackSegment seg : track.getSegments()) { 771 797 WayPoint r = null; 772 798 for (WayPoint wpSeg : seg.getWayPoints()) { 773 799 EastNorth en = wpSeg.getEastNorth(ProjectionRegistry.getProjection()); … … 882 908 883 909 private Iterator<GpxTrack> itTracks; 884 910 private int idxTracks; 885 private Iterator< GpxTrackSegment> itTrackSegments;911 private Iterator<IGpxTrackSegment> itTrackSegments; 886 912 private final Iterator<GpxRoute> itRoutes; 887 913 888 914 private Line next; 889 915 private final boolean[] trackVisibility; 890 916 private Map<String, Object> trackAttributes; 917 private GpxTrack curTrack; 891 918 892 919 /** 893 920 * Constructs a new {@code LinesIterator}. … … 921 948 private Line getNext() { 922 949 if (itTracks != null) { 923 950 if (itTrackSegments != null && itTrackSegments.hasNext()) { 924 return new Line(itTrackSegments.next(), trackAttributes );951 return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor()); 925 952 } else { 926 953 while (itTracks.hasNext()) { 927 GpxTrack nxtTrack = itTracks.next();928 trackAttributes = nxtTrack.getAttributes();954 curTrack = itTracks.next(); 955 trackAttributes = curTrack.getAttributes(); 929 956 idxTracks++; 930 957 if (trackVisibility != null && !trackVisibility[idxTracks]) 931 958 continue; 932 itTrackSegments = nxtTrack.getSegments().iterator();959 itTrackSegments = curTrack.getSegments().iterator(); 933 960 if (itTrackSegments.hasNext()) { 934 return new Line(itTrackSegments.next(), trackAttributes );961 return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor()); 935 962 } 936 963 } 937 964 // if we get here, all the Tracks are finished; Continue with Routes … … 956 983 return Collections.unmodifiableCollection(dataSources); 957 984 } 958 985 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 959 1005 @Override 960 1006 public synchronized int hashCode() { 961 1007 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()); 963 1011 result = prime * result + ((dataSources == null) ? 0 : dataSources.hashCode()); 964 1012 result = prime * result + ((privateRoutes == null) ? 0 : privateRoutes.hashCode()); 965 1013 result = prime * result + ((privateTracks == null) ? 0 : privateTracks.hashCode()); … … 973 1021 return true; 974 1022 if (obj == null) 975 1023 return false; 1024 if (!super.equals(obj)) 1025 return false; 976 1026 if (getClass() != obj.getClass()) 977 1027 return false; 978 1028 GpxData other = (GpxData) obj; … … 981 1031 return false; 982 1032 } else if (!dataSources.equals(other.dataSources)) 983 1033 return false; 1034 if (layerPrefs == null) { 1035 if (other.layerPrefs != null) 1036 return false; 1037 } else if (!layerPrefs.equals(other.layerPrefs)) 1038 return false; 984 1039 if (privateRoutes == null) { 985 1040 if (other.privateRoutes != null) 986 1041 return false; … … 996 1051 return false; 997 1052 } else if (!privateWaypoints.equals(other.privateWaypoints)) 998 1053 return false; 1054 if (namespaces == null) { 1055 if (other.namespaces != null) 1056 return false; 1057 } else if (!namespaces.equals(other.namespaces)) 1058 return false; 999 1059 return true; 1000 1060 } 1001 1061 1062 @Override 1063 public void put(String key, Object value) { 1064 super.put(key, value); 1065 invalidate(); 1066 } 1067 1002 1068 /** 1003 1069 * Adds a listener that gets called whenever the data changed. 1004 1070 * @param listener The listener … … 1025 1091 listeners.removeListener(listener); 1026 1092 } 1027 1093 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 } 1032 1112 } 1033 1113 } 1034 1114 1035 1115 /** 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 /** 1036 1137 * A listener that listens to GPX data changes. 1037 1138 * @author Michael Zangl 1038 1139 * @since 12156 … … 1067 1168 return source; 1068 1169 } 1069 1170 } 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 } 1070 1281 } -
src/org/openstreetmap/josm/data/gpx/GpxExtension.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.util.Objects; 5 import java.util.Optional; 6 7 import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace; 8 import org.xml.sax.Attributes; 9 10 /** 11 * A GpxExtension that has attributes and child extensions (implements {@link IWithAttributes}). 12 * @since xxx 13 */ 14 public 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. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.util.ArrayList; 5 import java.util.Collection; 6 import java.util.Objects; 7 import java.util.Optional; 8 import java.util.Stack; 9 import java.util.stream.Collectors; 10 import java.util.stream.Stream; 11 12 import org.apache.commons.jcs.access.exception.InvalidArgumentException; 13 import org.openstreetmap.josm.io.GpxReader; 14 import org.xml.sax.Attributes; 15 16 /** 17 * Class extending <code>ArrayList<GpxExtension></code>. 18 * Can be used to collect {@link GpxExtension}s while reading GPX files, see {@link GpxReader} 19 * @since xxx 20 */ 21 public 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<{@link GpxExtension}></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
39 39 40 40 for (GpxTrack trk : selectedGpx.tracks) { 41 41 List<List<WayPoint>> segs = new ArrayList<>(); 42 for ( GpxTrackSegment seg : trk.getSegments()) {42 for (IGpxTrackSegment seg : trk.getSegments()) { 43 43 List<WayPoint> wps = new ArrayList<>(seg.getWayPoints()); 44 44 if (!wps.isEmpty()) { 45 45 //remove waypoints at the beginning of the track/segment without timestamps -
src/org/openstreetmap/josm/data/gpx/GpxTrack.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.awt.Color; 5 import java.util.ArrayList; 4 6 import java.util.Collection; 7 import java.util.Collections; 8 import java.util.HashMap; 9 import java.util.List; 5 10 import java.util.Map; 11 import java.util.Map.Entry; 12 import java.util.Optional; 6 13 7 14 import org.openstreetmap.josm.data.Bounds; 15 import org.openstreetmap.josm.tools.ListenerList; 16 import org.openstreetmap.josm.tools.Logging; 8 17 9 18 /** 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 12 22 */ 13 public interface GpxTrack extends IWithAttributes{23 public class GpxTrack extends WithAttributes implements IGpxTrack { 14 24 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; 20 32 21 33 /** 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 24 37 */ 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 } 26 50 27 51 /** 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. 30 58 */ 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 } 32 65 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 33 141 /** 34 * Returns the track length.35 * @ return the track length142 * Converts the color to the given format, if present. 143 * @param cFormat can be a {@link GpxConstants.ColorFormat} 36 144 */ 37 double length(); 145 public void convertColor(ColorFormat cFormat) { 146 Color c = getColor(); 147 if (c == null) return; 38 148 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 } 45 178 } 46 179 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 47 246 /** 48 * Remove a listener that listens to changes in the GPX track. 49 * @param l The listener 247 * Resets the color cache 50 248 */ 51 default void removeListener(GpxTrackChangeListener l) {52 // nop249 public void invalidate() { 250 colorCache = null; 53 251 } 54 252 55 253 /** 56 254 * A listener that listens to GPX track changes. 57 * @author Michael Zangl 58 * @since 12156 255 * @deprecated use {@link IGpxTrack.GpxTrackChangeListener} instead 59 256 */ 257 @Deprecated 60 258 @FunctionalInterface 61 259 interface GpxTrackChangeListener { 62 /**63 * Called when the gpx data changed.64 * @param e The event65 */66 260 void gpxDataChanged(GpxTrackChangeEvent e); 67 261 } 68 262 69 263 /** 70 264 * A track change event for the current track. 71 * @author Michael Zangl 72 * @since 12156 265 * @deprecated use {@link IGpxTrack.GpxTrackChangeEvent} instead 73 266 */ 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); 80 271 } 272 } 81 273 82 /**83 * Get the track that was changed.84 * @return The track.85 */86 public GpxTrack getSource() {87 return source;88 }89 }90 274 } -
src/org/openstreetmap/josm/data/gpx/GpxTrackSegment.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.util.ArrayList; 4 5 import java.util.Collection; 6 import java.util.Collections; 7 import java.util.List; 5 8 6 9 import org.openstreetmap.josm.data.Bounds; 7 10 8 11 /** 9 * Read-only gpx track segments. Implementations doesn't have to be immutable, but should always be thread safe.10 * @since 290712 * A gpx track segment consisting of multiple waypoints. 13 * @since xxx 11 14 */ 12 public interfaceGpxTrackSegment {15 public class GpxTrackSegment extends WithAttributes implements IGpxTrackSegment { 13 16 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; 19 20 20 21 /** 21 * Returns the segment waypoints.22 * @ return the segmentwaypoints22 * Constructs a new {@code GpxTrackSegment}. 23 * @param wayPoints list of waypoints 23 24 */ 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 } 25 30 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 } 31 42 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 } 37 104 } -
src/org/openstreetmap/josm/data/gpx/IGpxTrack.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.awt.Color; 5 import java.util.Collection; 6 7 import 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 */ 13 public 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. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.util.Collection; 5 6 import 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 */ 12 public 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
2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 4 import java.util.Collection; 5 import java.util.Map; 5 6 6 7 /** 7 8 * Object with attributes (in the context of GPX data). … … 50 51 void put(String key, Object value); 51 52 52 53 /** 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 57 56 */ 58 void addExtension(String key, String value);57 Map<String, Object> getAttributes(); 59 58 59 /** 60 * Returns the extensions 61 * @return the extensions 62 */ 63 GpxExtensionCollection getExtensions(); 64 60 65 } -
src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.util.ArrayList;5 4 import java.util.Collection; 6 import java.util.Collections;7 import java.util.HashMap;8 5 import java.util.List; 9 6 import java.util.Map; 10 7 11 import org.openstreetmap.josm.data.Bounds;12 13 8 /** 14 * Immutable GPX track.9 * GPX track, NOT immutable 15 10 * @since 2907 11 * @deprecated Use {@link GpxTrack} instead! 16 12 */ 17 public class ImmutableGpxTrack extends WithAttributes implements GpxTrack { 13 @Deprecated 14 public class ImmutableGpxTrack extends GpxTrack { 18 15 19 private final List<GpxTrackSegment> segments;20 private final double length;21 private final Bounds bounds;22 23 16 /** 24 17 * Constructs a new {@code ImmutableGpxTrack}. 25 18 * @param trackSegs track segments … … 26 19 * @param attributes track attributes 27 20 */ 28 21 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); 39 23 } 40 24 41 25 /** … … 47 31 * @param attributes Attributes for the GpxTrack, the input map is copied. 48 32 * @since 13210 49 33 */ 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); 55 36 } 56 57 private double calculateLength() {58 double result = 0.0; // in meters59 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 @Override82 public Map<String, Object> getAttributes() {83 return attr;84 }85 86 @Override87 public Bounds getBounds() {88 return bounds == null ? null : new Bounds(bounds);89 }90 91 @Override92 public double length() {93 return length;94 }95 96 @Override97 public Collection<GpxTrackSegment> getSegments() {98 return segments;99 }100 101 @Override102 public int hashCode() {103 return 31 * super.hashCode() + ((segments == null) ? 0 : segments.hashCode());104 }105 106 @Override107 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 }122 37 } -
src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.util.ArrayList;5 4 import java.util.Collection; 6 import java.util.Collections;7 import java.util.List;8 5 9 import org.openstreetmap.josm.data.Bounds;10 11 6 /** 12 7 * A gpx track segment consisting of multiple waypoints, that cannot be changed. 8 * @deprecated Use {@link GpxTrackSegment} instead! 13 9 */ 14 public class ImmutableGpxTrackSegment implements GpxTrackSegment { 10 @Deprecated 11 public class ImmutableGpxTrackSegment extends GpxTrackSegment { 15 12 16 private final List<WayPoint> wayPoints;17 private final Bounds bounds;18 private final double length;19 20 13 /** 21 14 * Constructs a new {@code ImmutableGpxTrackSegment}. 22 15 * @param wayPoints list of waypoints 23 16 */ 24 17 public ImmutableGpxTrackSegment(Collection<WayPoint> wayPoints) { 25 this.wayPoints = Collections.unmodifiableList(new ArrayList<>(wayPoints)); 26 this.bounds = calculateBounds(); 27 this.length = calculateLength(); 18 super(wayPoints); 28 19 } 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 meters44 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 @Override58 public Bounds getBounds() {59 return bounds == null ? null : new Bounds(bounds);60 }61 62 @Override63 public Collection<WayPoint> getWayPoints() {64 return Collections.unmodifiableList(wayPoints);65 }66 67 @Override68 public double length() {69 return length;70 }71 72 @Override73 public int getUpdateCount() {74 return 0;75 }76 77 @Override78 public int hashCode() {79 return 31 + ((wayPoints == null) ? 0 : wayPoints.hashCode());80 }81 82 @Override83 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 }98 20 } -
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. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.awt.Color; 5 import java.util.Collection; 6 import java.util.Iterator; 7 import java.util.Map; 7 8 import java.util.Objects; 8 9 9 10 /** … … 10 11 * Line represents a linear collection of GPX waypoints with the ordered/unordered distinction. 11 12 * @since 14451 12 13 */ 13 public class Line implements Collection<WayPoint> { 14 private final Collection<WayPoint> waypoints; 15 private final boolean unordered; 14 public 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 } 16 31 17 32 /** 18 33 * 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 /** 28 44 * 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 /** 45 52 * Determines if waypoints are ordered. 46 53 * @return {@code true} if waypoints are ordered 47 54 */ 48 55 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(); 50 71 } 51 72 52 73 @Override 53 public int size() {54 return waypoints.size();55 }56 57 @Override58 74 public boolean isEmpty() { 59 75 return waypoints.isEmpty(); 60 76 } -
src/org/openstreetmap/josm/data/gpx/WithAttributes.java
4 4 import java.util.Collection; 5 5 import java.util.HashMap; 6 6 import java.util.Map; 7 import java.util.Objects; 7 8 8 9 /** 9 10 * Default implementation for IWithAttributes. … … 21 22 public Map<String, Object> attr = new HashMap<>(0); 22 23 23 24 /** 25 * The "exts" collection contains all extensions. 26 */ 27 private final GpxExtensionCollection exts = new GpxExtensionCollection(this); 28 29 /** 24 30 * Returns the Object value to which the specified key is mapped, 25 31 * or {@code null} if this map contains no mapping for the key. 26 32 * … … 64 70 65 71 /** 66 72 * Put a key / value pair as a new attribute. 67 *68 73 * Overrides key / value pair with the same key (if present). 69 74 * 70 75 * @param key the key … … 75 80 attr.put(key, value); 76 81 } 77 82 78 /**79 * Add a key / value pair that is not part of the GPX schema as an extension.80 *81 * @param key the key82 * @param value the value83 */84 83 @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; 91 86 } 92 87 93 88 @Override 89 public GpxExtensionCollection getExtensions() { 90 return exts; 91 } 92 93 @Override 94 94 public int hashCode() { 95 return 31 + ((attr == null) ? 0 : attr.hashCode());95 return Objects.hash(attr, exts); 96 96 } 97 97 98 98 @Override … … 109 109 return false; 110 110 } else if (!attr.equals(other.attr)) 111 111 return false; 112 if (exts == null) { 113 if (other.exts != null) 114 return false; 115 } else if (!exts.equals(other.exts)) 116 return false; 112 117 return true; 113 118 } 114 119 } -
src/org/openstreetmap/josm/data/osm/DataSet.java
32 32 import org.openstreetmap.josm.data.conflict.ConflictCollection; 33 33 import org.openstreetmap.josm.data.coor.EastNorth; 34 34 import org.openstreetmap.josm.data.coor.LatLon; 35 import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace; 35 36 import org.openstreetmap.josm.data.osm.DataSelectionListener.SelectionAddEvent; 36 37 import org.openstreetmap.josm.data.osm.DataSelectionListener.SelectionChangeEvent; 37 38 import org.openstreetmap.josm.data.osm.DataSelectionListener.SelectionRemoveEvent; … … 170 171 private String remark; 171 172 172 173 /** 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 /** 173 180 * Constructs a new {@code DataSet}. 174 181 */ 175 182 public DataSet() { … … 1194 1201 } 1195 1202 1196 1203 /** 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 /** 1197 1220 * @return true if this Dataset contains no primitives 1198 1221 * @since 14835 1199 1222 */ -
src/org/openstreetmap/josm/data/preferences/NamedColorProperty.java
22 22 23 23 public static final String COLOR_CATEGORY_GENERAL = "general"; 24 24 public static final String COLOR_CATEGORY_MAPPAINT = "mappaint"; 25 public static final String COLOR_CATEGORY_LAYER = "layer";26 25 27 26 private final String category; 28 27 private final String source; … … 31 30 /** 32 31 * Construct a new {@code NamedColorProperty}. 33 32 * @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} 36 34 * @param source a filename or similar associated with the color, can be null if not applicable 37 35 * @param name a short description of the color 38 36 * @param defaultValue the default value, can be null -
src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.Color;7 6 import java.awt.Component; 8 7 import java.awt.Dimension; 9 8 import java.awt.Font; … … 17 16 import java.util.ArrayList; 18 17 import java.util.Arrays; 19 18 import java.util.List; 20 import java.util.O bjects;19 import java.util.Optional; 21 20 import java.util.concurrent.CopyOnWriteArrayList; 22 21 23 22 import javax.swing.AbstractAction; … … 42 41 import org.openstreetmap.josm.actions.MergeLayerAction; 43 42 import org.openstreetmap.josm.data.coor.EastNorth; 44 43 import org.openstreetmap.josm.data.imagery.OffsetBookmark; 45 import org.openstreetmap.josm.data.preferences.AbstractProperty;46 44 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener; 47 45 import org.openstreetmap.josm.data.preferences.BooleanProperty; 48 46 import org.openstreetmap.josm.gui.MainApplication; … … 611 609 label.setFont(label.getFont().deriveFont(Font.BOLD)); 612 610 } 613 611 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"))); 626 615 } 627 616 label.setIcon(layer.getIcon()); 628 617 label.setToolTipText(layer.getToolTipText()); -
src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java
593 593 List<Layer> layers = layerSupplier.get(); 594 594 for (Layer l : layers) { 595 595 if (l instanceof GpxLayer) { 596 l. getColorProperty().put(color);596 l.setColor(color); 597 597 } 598 598 } 599 599 highlightColor(color); … … 602 602 add(colorPanel, GBC.std().weight(1, 1).fill().insets(5)); 603 603 panels.put(color, colorPanel); 604 604 605 List<Color> colors = layerSupplier.get().stream().map(l -> l.getColor Property().get()).distinct().collect(Collectors.toList());605 List<Color> colors = layerSupplier.get().stream().map(l -> l.getColor()).distinct().collect(Collectors.toList()); 606 606 if (colors.size() == 1) { 607 607 highlightColor(colors.get(0)); 608 608 } … … 611 611 @Override 612 612 public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) { 613 613 List<Color> colors = layers.stream().filter(l -> l instanceof GpxLayer) 614 .map(l -> ((GpxLayer) l).getColor Property().get())614 .map(l -> ((GpxLayer) l).getColor()) 615 615 .distinct() 616 616 .collect(Collectors.toList()); 617 617 if (colors.size() == 1) { -
src/org/openstreetmap/josm/gui/io/importexport/FileExporter.java
55 55 } 56 56 57 57 /** 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 /** 58 70 * Returns the enabled state of this {@code FileExporter}. When enabled, it is listed and usable in "File->Save" dialogs. 59 71 * @return true if this {@code FileExporter} is enabled 60 72 * @since 5459 -
src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java
25 25 26 26 import org.openstreetmap.josm.data.gpx.GpxConstants; 27 27 import org.openstreetmap.josm.data.gpx.GpxData; 28 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 28 29 import org.openstreetmap.josm.gui.ExtendedDialog; 29 30 import org.openstreetmap.josm.gui.MainApplication; 30 31 import org.openstreetmap.josm.gui.layer.GpxLayer; … … 77 78 78 79 @Override 79 80 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 { 80 90 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 81 91 if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer)) 82 92 throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer … … 89 99 file = new File(fn); 90 100 } 91 101 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 92 114 // open the dialog asking for options 93 115 JPanel p = new JPanel(new GridBagLayout()); 94 116 95 GpxData gpxData;96 117 // At this moment, we only need to know the attributes of the GpxData, 97 118 // conversion of OsmDataLayer (if needed) will be done after the dialog is closed. 98 119 if (layer instanceof GpxLayer) { … … 146 167 p.add(new JLabel(tr("Keywords")), GBC.eol()); 147 168 JosmTextField keywords = new JosmTextField(); 148 169 keywords.setText(gpxData.getString(META_KEYWORDS)); 149 p.add(keywords, GBC.eo p().fill(GBC.HORIZONTAL));170 p.add(keywords, GBC.eol().fill(GBC.HORIZONTAL)); 150 171 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 151 186 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 152 187 tr("Export options"), 153 188 tr("Export and Save"), tr("Cancel")) … … 154 189 .setButtonIcons("exportgpx", "cancel") 155 190 .setContent(p); 156 191 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 157 210 if (ed.showDialog().getValue() != 1) { 158 211 setCanceled(true); 159 212 return; … … 167 220 if (!copyright.getText().isEmpty()) { 168 221 Config.getPref().put("lastCopyright", copyright.getText()); 169 222 } 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()); 177 227 } 228 ColorFormat cFormat = null; 229 if (colors.isSelected()) { 230 cFormat = garmin.isSelected() ? ColorFormat.GPXX : ColorFormat.GPXD; 231 } 178 232 233 gpxData = getGpxData(layer, file); 234 179 235 // add author and copyright details to the gpx data 180 236 if (author.isSelected()) { 181 237 if (!authorName.getText().isEmpty()) { … … 203 259 gpxData.put(META_KEYWORDS, keywords.getText()); 204 260 } 205 261 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(); 208 267 } 209 268 } 210 269 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 211 279 private static void enableCopyright(final GpxData data, final JosmTextField copyright, final JButton predefined, 212 280 final JosmTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel, 213 281 final JLabel warning, boolean enable) { -
src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java
149 149 markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer); 150 150 if (markerLayer.data.isEmpty()) { 151 151 markerLayer = null; 152 } else { 153 gpxLayer.setLinkedMarkerLayer(markerLayer); 152 154 } 153 155 } 154 156 Runnable postLayerTask = () -> { -
src/org/openstreetmap/josm/gui/layer/CustomizeColor.java
18 18 import javax.swing.JMenuItem; 19 19 import javax.swing.JOptionPane; 20 20 21 import org.openstreetmap.josm.data.preferences.AbstractProperty;22 21 import org.openstreetmap.josm.gui.MainApplication; 23 22 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 24 23 import org.openstreetmap.josm.gui.layer.Layer.LayerAction; 25 24 import org.openstreetmap.josm.gui.layer.Layer.MultiLayerAction; 26 import org.openstreetmap.josm.tools.CheckParameterUtil;27 25 import org.openstreetmap.josm.tools.ImageProvider; 28 26 29 27 /** … … 33 31 * of a certain {@link GpxLayer} or {@link org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer}. 34 32 */ 35 33 public class CustomizeColor extends AbstractAction implements LayerAction, MultiLayerAction { 36 private final transient List< AbstractProperty<Color>> colors;34 private final transient List<Layer> colorLayers; 37 35 38 36 /** 39 37 * Constructs a new {@code CustomizeColor} for a given list of layers. … … 42 40 public CustomizeColor(List<Layer> l) { 43 41 super(tr("Customize Color")); 44 42 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()); 47 44 putValue("help", ht("/Action/LayerCustomizeColor")); 48 45 } 49 46 … … 57 54 58 55 @Override 59 56 public boolean supportLayers(List<Layer> layers) { 60 return layers.stream().allMatch( l -> l.getColorProperty() != null);57 return layers.stream().allMatch(Layer::hasColor); 61 58 } 62 59 63 60 @Override … … 72 69 73 70 @Override 74 71 public void actionPerformed(ActionEvent e) { 75 Color cl = color s.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); 76 73 JColorChooser c = new JColorChooser(cl); 77 74 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 78 75 int answer = JOptionPane.showOptionDialog( … … 87 84 ); 88 85 switch (answer) { 89 86 case 0: 90 color s.stream().forEach(prop -> prop.put(c.getColor()));87 colorLayers.stream().forEach(l -> l.setColor(c.getColor())); 91 88 break; 92 89 case 1: 93 90 return; 94 91 case 2: 95 color s.stream().forEach(prop -> prop.put(null));92 colorLayers.stream().forEach(l -> l.setColor(null)); 96 93 break; 97 94 } 98 95 // 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
4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 import static org.openstreetmap.josm.tools.I18n.trn; 6 6 7 import java.awt.Color; 7 8 import java.awt.Dimension; 8 9 import java.awt.Graphics2D; 9 10 import java.awt.event.ActionEvent; … … 13 14 import java.util.Arrays; 14 15 import java.util.Date; 15 16 import java.util.List; 17 import java.util.stream.Collectors; 16 18 17 19 import javax.swing.AbstractAction; 18 20 import javax.swing.Action; … … 31 33 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener; 32 34 import org.openstreetmap.josm.data.gpx.GpxTrack; 33 35 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 34 import org.openstreetmap.josm.data.preferences.NamedColorProperty;35 36 import org.openstreetmap.josm.data.projection.Projection; 36 37 import org.openstreetmap.josm.gui.MapView; 37 38 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; … … 46 47 import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction; 47 48 import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction; 48 49 import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction; 50 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 51 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 49 52 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 50 53 import org.openstreetmap.josm.tools.ImageProvider; 54 import org.openstreetmap.josm.tools.Logging; 51 55 import org.openstreetmap.josm.tools.Utils; 52 56 import org.openstreetmap.josm.tools.date.DateUtils; 53 57 … … 54 58 /** 55 59 * A layer that displays data from a Gpx file / the OSM gpx downloads. 56 60 */ 57 public class GpxLayer extends Layer implements ExpertModeChangeListener {61 public class GpxLayer extends AbstractModifiableLayer implements ExpertModeChangeListener { 58 62 59 63 /** GPX data */ 60 64 public GpxData data; 61 private finalboolean isLocalFile;65 private boolean isLocalFile; 62 66 private boolean isExpertMode; 67 63 68 /** 64 69 * used by {@link ChooseTrackVisibilityAction} to determine which tracks to show/hide 65 70 * … … 72 77 * Added as field to be kept as reference. 73 78 */ 74 79 private final GpxDataChangeListener dataChangeListener = e -> this.invalidate(); 80 /** 81 * The MarkerLayer imported from the same file. 82 */ 83 private MarkerLayer linkedMarkerLayer; 75 84 76 85 /** 77 86 * Constructs a new {@code GpxLayer} without name. … … 108 117 } 109 118 110 119 @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 113 123 } 114 124 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 115 140 /** 116 141 * Returns a human readable string that shows the timespan of the given track 117 142 * @param trk The GPX track for which timespan is displayed … … 276 301 .append(", ") 277 302 .append(trn("{0} route, ", "{0} routes, ", data.getRoutes().size(), data.getRoutes().size())) 278 303 .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 281 313 return info.toString(); 282 314 } 283 315 … … 341 373 data.storageFile = file; 342 374 } 343 375 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 344 392 @Override 345 393 public void projectionChanged(Projection oldValue, Projection newValue) { 346 394 if (newValue == null) return; … … 480 528 } 481 529 482 530 @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 483 548 public String getChangesetSourceTag() { 484 549 // no i18n for international values 485 550 return "survey"; -
src/org/openstreetmap/josm/gui/layer/Layer.java
25 25 import org.openstreetmap.josm.actions.SaveAsAction; 26 26 import org.openstreetmap.josm.data.ProjectionBounds; 27 27 import 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;31 28 import org.openstreetmap.josm.data.projection.Projection; 32 29 import org.openstreetmap.josm.data.projection.ProjectionChangeListener; 33 30 import org.openstreetmap.josm.gui.MainApplication; … … 164 161 */ 165 162 private File associatedFile; 166 163 167 private final ValueChangeListener<Object> invalidateListener = change -> invalidate();168 164 private boolean isDestroyed; 169 165 170 166 /** … … 198 194 public abstract Icon getIcon(); 199 195 200 196 /** 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 204 199 */ 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; 212 202 } 213 203 214 204 /** 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 10824205 * Return the current color of the layer 206 * @return null when not present or not supported 207 * @since xxx 218 208 */ 219 p rotected NamedColorProperty getBaseColorProperty() {209 public Color getColor() { 220 210 return null; 221 211 } 222 212 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) { 228 219 } 229 220 230 private void removeColorPropertyListener() {231 AbstractProperty<Color> colorProperty = getColorProperty();232 if (colorProperty != null) {233 colorProperty.removeListener(invalidateListener);234 }235 }236 237 221 /** 238 222 * @return A small tooltip hint about some statistics for this layer. 239 223 */ … … 302 286 } 303 287 isDestroyed = true; 304 288 // Override in subclasses if needed 305 removeColorPropertyListener();306 289 } 307 290 308 291 /** … … 339 322 * @param name the name. If null, the name is set to the empty string. 340 323 */ 341 324 public void setName(String name) { 342 if (this.name != null) {343 removeColorPropertyListener();344 }345 325 String oldValue = this.name; 346 326 this.name = Optional.ofNullable(name).orElse(""); 347 327 if (!this.name.equals(oldValue)) { 348 328 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name); 349 329 } 350 351 // re-add listener352 addColorPropertyListener();353 330 invalidate(); 354 331 } 355 332 … … 539 516 540 517 @Override 541 518 public void actionPerformed(ActionEvent e) { 542 SaveAction.getInstance().doSave(layer );519 SaveAction.getInstance().doSave(layer, true); 543 520 } 544 521 } 545 522 -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
31 31 import java.util.LinkedHashMap; 32 32 import java.util.List; 33 33 import java.util.Map; 34 import java.util.Map.Entry; 34 35 import java.util.Optional; 35 36 import java.util.Set; 36 37 import java.util.concurrent.CopyOnWriteArrayList; … … 60 61 import org.openstreetmap.josm.data.coor.LatLon; 61 62 import org.openstreetmap.josm.data.gpx.GpxConstants; 62 63 import org.openstreetmap.josm.data.gpx.GpxData; 64 import org.openstreetmap.josm.data.gpx.GpxExtensionCollection; 63 65 import org.openstreetmap.josm.data.gpx.GpxLink; 64 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack; 66 import org.openstreetmap.josm.data.gpx.GpxTrack; 67 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 68 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 65 69 import org.openstreetmap.josm.data.gpx.WayPoint; 66 70 import org.openstreetmap.josm.data.osm.DataIntegrityProblemException; 67 71 import org.openstreetmap.josm.data.osm.DataSelectionListener; … … 757 761 */ 758 762 public static GpxData toGpxData(DataSet data, File file) { 759 763 GpxData gpxData = new GpxData(); 764 if (data.getGPXNamespaces() != null) { 765 gpxData.getNamespaces().addAll(data.getGPXNamespaces()); 766 } 760 767 gpxData.storageFile = file; 761 768 Set<Node> doneNodes = new HashSet<>(); 762 769 waysToGpxData(data.getWays(), gpxData, doneNodes); … … 777 784 if (!w.isUsable()) { 778 785 return; 779 786 } 780 Collection<Collection<WayPoint>> trk = new ArrayList<>();787 List<IGpxTrackSegment> trk = new ArrayList<>(); 781 788 Map<String, Object> trkAttr = new HashMap<>(); 782 789 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 } 786 814 } 787 788 List<WayPoint> trkseg = null; 815 List<WayPoint> trkseg = new ArrayList<>(); 789 816 for (Node n : w.getNodes()) { 790 817 if (!n.isUsable()) { 791 trkseg = null; 818 if (!trkseg.isEmpty()) { 819 trk.add(new GpxTrackSegment(trkseg)); 820 trkseg.clear(); 821 } 792 822 continue; 793 823 } 794 if (trkseg == null) {795 trkseg = new ArrayList<>();796 trk.add(trkseg);797 }798 824 if (!n.isTagged() || containsOnlyGpxTags(n)) { 799 825 doneNodes.add(n); 800 826 } 801 827 trkseg.add(nodeToWayPoint(n)); 802 828 } 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); 805 834 }); 806 835 } 807 836 -
src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
70 70 import org.openstreetmap.josm.data.gpx.GpxTimeOffset; 71 71 import org.openstreetmap.josm.data.gpx.GpxTimezone; 72 72 import org.openstreetmap.josm.data.gpx.GpxTrack; 73 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;73 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 74 74 import org.openstreetmap.josm.data.gpx.WayPoint; 75 75 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 76 76 import org.openstreetmap.josm.gui.ExtendedDialog; … … 1268 1268 long firstGPXDate = -1; 1269 1269 // Finds first GPX point 1270 1270 outer: for (GpxTrack trk : gpx.tracks) { 1271 for ( GpxTrackSegment segment : trk.getSegments()) {1271 for (IGpxTrackSegment segment : trk.getSegments()) { 1272 1272 for (WayPoint curWp : segment.getWayPoints()) { 1273 1273 if (curWp.hasDate()) { 1274 1274 firstGPXDate = curWp.getTimeInMillis(); -
src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java
4 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 6 7 import java.awt.Color; 7 8 import java.awt.Component; 8 9 import java.awt.Dimension; 9 10 import java.awt.GridBagLayout; … … 12 13 import java.awt.event.MouseEvent; 13 14 import java.awt.event.MouseListener; 14 15 import java.io.Serializable; 16 import java.util.ArrayList; 15 17 import java.util.Arrays; 16 18 import java.util.Comparator; 19 import java.util.List; 17 20 import java.util.Map; 21 import java.util.Objects; 18 22 import java.util.Optional; 19 23 20 24 import javax.swing.AbstractAction; 25 import javax.swing.JColorChooser; 21 26 import javax.swing.JComponent; 22 27 import javax.swing.JLabel; 28 import javax.swing.JOptionPane; 23 29 import javax.swing.JPanel; 24 30 import javax.swing.JScrollPane; 25 31 import javax.swing.JTable; 26 32 import javax.swing.JToggleButton; 27 33 import javax.swing.ListSelectionModel; 34 import javax.swing.event.TableModelEvent; 28 35 import javax.swing.table.DefaultTableModel; 29 36 import javax.swing.table.TableCellRenderer; 30 37 import javax.swing.table.TableRowSorter; 31 38 39 import org.apache.commons.jcs.access.exception.InvalidArgumentException; 32 40 import org.openstreetmap.josm.data.SystemOfMeasurement; 33 41 import org.openstreetmap.josm.data.gpx.GpxConstants; 34 42 import org.openstreetmap.josm.data.gpx.GpxTrack; … … 35 43 import org.openstreetmap.josm.gui.ExtendedDialog; 36 44 import org.openstreetmap.josm.gui.MainApplication; 37 45 import org.openstreetmap.josm.gui.layer.GpxLayer; 46 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 38 47 import org.openstreetmap.josm.gui.util.WindowGeometry; 39 48 import org.openstreetmap.josm.tools.GBC; 40 49 import org.openstreetmap.josm.tools.ImageProvider; … … 55 64 * @param layer The associated GPX layer 56 65 */ 57 66 public ChooseTrackVisibilityAction(final GpxLayer layer) { 58 super(tr("Choose visible tracks"));67 super(tr("Choose track visibility and colors")); 59 68 new ImageProvider("dialogs/filter").getResource().attachImageIcon(this, true); 60 69 this.layer = layer; 61 70 putValue("help", ht("/Action/ChooseTrackVisibility")); … … 116 125 String time = GpxLayer.getTimespanForTrack(trk); 117 126 TrackLength length = new TrackLength(trk.length()); 118 127 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}; 120 129 i++; 121 130 } 122 131 return tracks; 123 132 } 124 133 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 125 164 /** 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. 127 166 * The table will fill its parent. 128 167 * @param content table data 129 168 * @return non-editable table … … 138 177 if (c instanceof JComponent) { 139 178 JComponent jc = (JComponent) c; 140 179 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 } 141 214 } 142 215 return c; 143 216 } … … 144 217 145 218 @Override 146 219 public boolean isCellEditable(int rowIndex, int colIndex) { 147 return false;220 return colIndex <= 1; 148 221 } 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 } 149 243 }; 150 244 // define how to sort row 151 245 TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<>(); … … 180 274 }; 181 275 t.addMouseListener(urlOpener); 182 276 t.setFillsViewportHeight(true); 277 t.putClientProperty("terminateEditOnFocusLost", true); 183 278 return t; 184 279 } 185 280 … … 249 344 250 345 msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. " + 251 346 "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>")), 253 349 GBC.eop().fill(GBC.HORIZONTAL)); 254 350 // build table 255 351 final boolean[] trackVisibilityBackup = layer.trackVisibility.clone(); 256 table = buildTable(buildTableContents()); 352 Object[][] content = buildTableContents(); 353 table = buildTable(content); 257 354 selectVisibleTracksInTable(); 258 355 listenToSelectionChanges(); 259 356 // make the table scrollable … … 262 359 263 360 int v = 1; 264 361 // 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"); 268 382 ed.setContent(msg, false); 269 383 ed.setDefaultButton(2); 270 384 ed.setCancelButton(3); … … 275 389 dateFilter.saveInPrefs(); 276 390 v = ed.getValue(); 277 391 // cancel for unknown buttons and copy back original settings 278 if (v != 1 && v != 2) {392 if (v != 2 && v != 3) { 279 393 layer.trackVisibility = Arrays.copyOf(trackVisibilityBackup, layer.trackVisibility.length); 280 394 MainApplication.getMap().repaint(); 281 395 return; 282 396 } 283 // set visibility ( 1 = show all, 2= filter). If no tracks are selected397 // set visibility (2 = show all, 3 = filter). If no tracks are selected 284 398 // set all of them visible and... 285 399 ListSelectionModel s = table.getSelectionModel(); 286 final boolean all = v == 1|| s.isSelectionEmpty();400 final boolean all = v == 2 || s.isSelectionEmpty(); 287 401 for (int i = 0; i < layer.trackVisibility.length; i++) { 288 402 layer.trackVisibility[table.convertRowIndexToModel(i)] = all || s.isSelectedIndex(i); 289 403 } … … 290 404 // layer has been changed 291 405 layer.invalidate(); 292 406 // ...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()); 294 408 } 295 409 } -
src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java
7 7 import java.awt.event.ActionEvent; 8 8 import java.awt.event.ActionListener; 9 9 import java.util.ArrayList; 10 import java.util.Collection;11 10 import java.util.Date; 12 11 import java.util.List; 12 import java.util.Map; 13 13 import java.util.Map.Entry; 14 14 15 15 import javax.swing.BorderFactory; … … 20 20 import javax.swing.JRadioButton; 21 21 22 22 import org.openstreetmap.josm.data.gpx.GpxConstants; 23 import org.openstreetmap.josm.data.gpx.GpxTrack; 24 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 23 import org.openstreetmap.josm.data.gpx.GpxExtension; 24 import org.openstreetmap.josm.data.gpx.GpxExtensionCollection; 25 import org.openstreetmap.josm.data.gpx.IGpxTrack; 26 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 25 27 import org.openstreetmap.josm.data.gpx.WayPoint; 26 28 import org.openstreetmap.josm.data.osm.DataSet; 27 29 import org.openstreetmap.josm.data.osm.Node; 30 import org.openstreetmap.josm.data.osm.OsmPrimitive; 28 31 import org.openstreetmap.josm.data.osm.Way; 29 32 import org.openstreetmap.josm.gui.ExtendedDialog; 30 33 import org.openstreetmap.josm.gui.MainApplication; … … 53 56 @Override 54 57 public DataSet convert() { 55 58 final DataSet ds = new DataSet(); 59 ds.setGPXNamespaces(layer.data.getNamespaces()); 56 60 57 61 List<String> keys = new ArrayList<>(); // note that items in this list don't have the GPX_PREFIX 58 62 String convertTags = Config.getPref().get(GPX_SETTING, "ask"); … … 59 63 boolean check = "list".equals(convertTags) || "ask".equals(convertTags); 60 64 boolean none = "no".equals(convertTags); // no need to convert tags when no dialog will be shown anyways 61 65 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()) { 64 68 List<Node> nodes = new ArrayList<>(); 65 69 for (WayPoint p : segment.getWayPoints()) { 66 70 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); 84 74 } 85 75 ds.addPrimitive(n); 86 76 nodes.add(n); … … 87 77 } 88 78 Way w = new Way(); 89 79 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 } 90 86 ds.addPrimitive(w); 91 87 } 92 88 } … … 122 118 return ds; 123 119 } 124 120 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 125 166 /** 126 167 * Filters the tags of the given {@link DataSet} 127 168 * @param ds The {@link DataSet} … … 130 171 * @since 14103 131 172 */ 132 173 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; 138 181 } 182 if (listPos == null || !listPos.contains(listkey)) { 183 p.put(key, null); 184 } 139 185 } 140 186 } 141 187 return ds; -
src/org/openstreetmap/josm/gui/layer/gpx/CustomizeDrawingAction.java
9 9 import java.awt.event.ActionEvent; 10 10 import java.util.LinkedList; 11 11 import java.util.List; 12 import java.util.stream.Collectors; 12 13 13 14 import javax.swing.AbstractAction; 14 15 import javax.swing.Action; … … 59 60 60 61 @Override 61 62 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); 68 64 } 69 65 70 66 @Override … … 79 75 80 76 @Override 81 77 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())); 94 79 JScrollPane scrollpane = GuiHelper.embedInVerticalScrollPane(panel); 95 80 scrollpane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 96 81 int screenHeight = GuiHelper.getScreenSize().height; … … 103 88 if (answer == JOptionPane.CANCEL_OPTION || answer == JOptionPane.CLOSED_OPTION) { 104 89 return; 105 90 } 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); 115 94 } 116 95 117 96 } -
src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java
8 8 import org.openstreetmap.josm.actions.DownloadAlongAction; 9 9 import org.openstreetmap.josm.data.gpx.GpxData; 10 10 import org.openstreetmap.josm.data.gpx.GpxTrack; 11 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;11 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 12 12 import org.openstreetmap.josm.data.gpx.WayPoint; 13 13 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 14 14 import org.openstreetmap.josm.gui.help.HelpUtil; … … 60 60 Path2D gpxPath = new Path2D.Double(); 61 61 if (near == NEAR_TRACK || near == NEAR_BOTH) { 62 62 for (GpxTrack trk : data.tracks) { 63 for ( GpxTrackSegment segment : trk.getSegments()) {63 for (IGpxTrackSegment segment : trk.getSegments()) { 64 64 boolean first = true; 65 65 for (WayPoint p : segment.getWayPoints()) { 66 66 if (first) { -
src/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackAction.java
16 16 import org.openstreetmap.josm.data.coor.LatLon; 17 17 import org.openstreetmap.josm.data.gpx.GpxData; 18 18 import org.openstreetmap.josm.data.gpx.GpxTrack; 19 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;19 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 20 20 import org.openstreetmap.josm.data.gpx.WayPoint; 21 21 import org.openstreetmap.josm.gui.MainApplication; 22 22 import org.openstreetmap.josm.gui.PleaseWaitRunnable; … … 92 92 PrecacheWmsTask createTask() { 93 93 List<LatLon> points = new ArrayList<>(); 94 94 for (GpxTrack trk : data.tracks) { 95 for ( GpxTrackSegment segment : trk.getSegments()) {95 for (IGpxTrackSegment segment : trk.getSegments()) { 96 96 for (WayPoint p : segment.getWayPoints()) { 97 97 points.add(p.getCoor()); 98 98 } -
src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
27 27 import java.util.Date; 28 28 import java.util.LinkedList; 29 29 import java.util.List; 30 import java.util.Objects; 30 31 import java.util.Random; 31 32 32 33 import javax.swing.ImageIcon; 33 34 34 35 import org.openstreetmap.josm.data.Bounds; 35 import org.openstreetmap.josm.data.PreferencesUtils;36 36 import org.openstreetmap.josm.data.SystemOfMeasurement; 37 37 import org.openstreetmap.josm.data.SystemOfMeasurement.SoMChangeListener; 38 38 import org.openstreetmap.josm.data.coor.LatLon; … … 51 51 import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent; 52 52 import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent; 53 53 import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener; 54 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 54 55 import org.openstreetmap.josm.io.CachedFile; 55 56 import org.openstreetmap.josm.spi.preferences.Config; 56 57 import org.openstreetmap.josm.tools.ColorScale; … … 65 66 public class GpxDrawHelper implements SoMChangeListener, MapViewPaintable.LayerPainter, PaintableInvalidationListener, GpxDataChangeListener { 66 67 67 68 /** 68 * The colorthat is used for drawing GPX points.69 * @since 1082469 * The default color property that is used for drawing GPX points. 70 * @since xxx 70 71 */ 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); 72 73 73 74 private final GpxData data; 74 75 private final GpxLayer layer; … … 78 79 // use alpha blending for line draw 79 80 private boolean alphaLines; 80 81 // draw direction arrows on the lines 81 private boolean direction;82 private boolean arrows; 82 83 /** width of line for paint **/ 83 84 private int lineWidth; 84 85 /** don't draw lines if longer than x meters **/ … … 90 91 private int largesize; 91 92 private boolean hdopCircle; 92 93 /** paint direction arrow with alternate math. may be faster **/ 93 private boolean a lternateDirection;94 private boolean arrowsFast; 94 95 /** don't draw arrows nearer to each other than this **/ 95 private int delta;96 private int arrowsDelta; 96 97 private double minTrackDurationForTimeColoring; 97 98 98 99 /** maximum value of displayed HDOP, minimum is 0 */ … … 106 107 private Color computeCacheColorUsed; 107 108 private boolean computeCacheColorDynamic; 108 109 private ColorMode computeCacheColored; 109 private int computeCache ColorTracksTune;110 private int computeCacheVelocityTune; 110 111 private int computeCacheHeatMapDrawColorTableIdx; 111 112 private boolean computeCacheHeatMapDrawPointMode; 112 113 private int computeCacheHeatMapDrawGain; 113 114 private int computeCacheHeatMapDrawLowerLimit; 114 115 116 private Color colorCache; 117 private Color colorCacheTransparent; 118 115 119 //// Color-related fields 116 120 /** Mode of the line coloring **/ 117 121 private ColorMode colored; 118 122 /** max speed for coloring - allows to tweak line coloring for different speed levels. **/ 119 private int colorTracksTune;123 private int velocityTune; 120 124 private boolean colorModeDynamic; 121 125 private Color neutralColor; 122 126 private int largePointAlpha; … … 145 149 146 150 /** heat map parameters **/ 147 151 148 // enabled or not (override by settings)149 private boolean heatMapEnabled;150 152 // draw small extra line 151 153 private boolean heatMapDrawExtraLine; 152 154 // used index for color table (parameter) … … 268 270 setupColors(); 269 271 } 270 272 271 private static String specName(String layerName) {272 return "layer " + layerName;273 }274 275 273 /** 276 * Get the default color for gps tracks for specified layer277 * @param layerName name of the GpxLayer278 * @param ignoreCustom do not use preferences279 * @return the color or null if the color is not constant280 */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 /**293 274 * Read coloring mode for specified layer from preferences 294 * @param layerName name of the GpxLayer295 275 * @return coloring mode 296 276 */ 297 public ColorMode getColorMode( String layerName) {277 public ColorMode getColorMode() { 298 278 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 300 281 return ColorMode.fromIndex(i); 301 282 } catch (IndexOutOfBoundsException e) { 302 283 Logging.warn(e); … … 304 285 return ColorMode.NONE; 305 286 } 306 287 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); 312 290 } 313 291 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 314 300 /** 315 301 * Read all drawing-related settings from preferences 316 * @param layerName layer name used to access its specific preferences317 302 **/ 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"); 324 310 311 int l = optInt("lines"); 325 312 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) 328 315 } 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) 331 318 } 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"); 340 325 /* good HDOP's are between 1 and 3, very bad HDOP's go into 3 digit values */ 341 326 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; 344 329 345 330 // 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"); 352 336 353 337 // shrink to range 354 338 heatMapDrawGain = Utils.clamp(heatMapDrawGain, -10, 10); 355 356 neutralColor = getColor(layerName, true); 339 neutralColor = DEFAULT_COLOR_PROPERTY.get(); 357 340 velocityScale.setNoDataColor(neutralColor); 358 341 dateScale.setNoDataColor(neutralColor); 359 342 hdopScale.setNoDataColor(neutralColor); … … 368 351 Bounds clipBounds = graphics.getClipBounds().getLatLonBoundsBox(); 369 352 List<WayPoint> visibleSegments = listVisibleSegments(clipBounds); 370 353 if (!visibleSegments.isEmpty()) { 371 readPreferences( layer.getName());354 readPreferences(); 372 355 drawAll(graphics.getDefaultGraphics(), graphics.getMapView(), visibleSegments, clipBounds); 373 356 if (graphics.getMapView().getLayerManager().getActiveLayer() == layer) { 374 357 drawColorBar(graphics.getDefaultGraphics(), graphics.getMapView()); … … 430 413 * @param clipBounds the clipping rectangle for the current view 431 414 * @since 14748 : new parameter clipBounds 432 415 */ 433 434 416 public void drawAll(Graphics2D g, MapView mv, List<WayPoint> visibleSegments, Bounds clipBounds) { 435 417 436 418 final long timeStart = System.currentTimeMillis(); … … 462 444 } 463 445 464 446 // global enabled or select via color 465 boolean useHeatMap = heatMapEnabled ||ColorMode.HEATMAP == colored;447 boolean useHeatMap = ColorMode.HEATMAP == colored; 466 448 467 449 // default global alpha level 468 450 float layerAlpha = 1.00f; … … 570 552 } 571 553 oldWp = null; 572 554 } else { // color mode not dynamic 573 velocityScale.setRange(0, colorTracksTune);555 velocityScale.setRange(0, velocityTune); 574 556 hdopScale.setRange(0, hdoprange); 575 557 qualityScale.setRange(1, rtkLibQualityColors.length); 576 558 } … … 594 576 } 595 577 for (WayPoint trkPnt : segment) { 596 578 LatLon c = trkPnt.getCoor(); 597 trkPnt.customColoring = neutralColor;579 trkPnt.customColoring = segment.getColor(); 598 580 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) { 599 581 continue; 600 582 } … … 642 624 } 643 625 } else { // make sure we reset outdated data 644 626 trkPnt.drawLine = false; 645 color = neutralColor;627 color = segment.getColor(); 646 628 } 647 629 if (color != null) { 648 630 trkPnt.customColoring = color; … … 700 682 /**************************************************************** 701 683 ********** STEP 3b - DRAW NICE ARROWS ************************** 702 684 ****************************************************************/ 703 if (lines && direction && !alternateDirection) {685 if (lines && arrows && !arrowsFast) { 704 686 Point old = null; 705 687 Point oldA = null; // last arrow painted 706 688 for (WayPoint trkPnt : visibleSegments) { … … 712 694 Point screen = mv.getPoint(trkPnt); 713 695 // skip points that are on the same screenposition 714 696 if (old != null 715 && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta716 || 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)) { 717 699 g.setColor(trkPnt.customColoring); 718 700 double t = Math.atan2((double) screen.y - old.y, (double) screen.x - old.x) + Math.PI; 719 701 g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t - PHI)), … … 730 712 /**************************************************************** 731 713 ********** STEP 3c - DRAW FAST ARROWS ************************** 732 714 ****************************************************************/ 733 if (lines && direction && alternateDirection) {715 if (lines && arrows && arrowsFast) { 734 716 Point old = null; 735 717 Point oldA = null; // last arrow painted 736 718 for (WayPoint trkPnt : visibleSegments) { … … 742 724 Point screen = mv.getPoint(trkPnt); 743 725 // skip points that are on the same screenposition 744 726 if (old != null 745 && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta746 || 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)) { 747 729 g.setColor(trkPnt.customColoring); 748 730 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y 749 731 + dir[trkPnt.dir][1]); … … 794 776 if (large) { 795 777 // color the large GPS points like the gps lines 796 778 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); 799 784 800 g.setColor(customColoringTransparent); 785 g.setColor(customColoringTransparent); 786 colorCache = trkPnt.customColoring; 787 colorCacheTransparent = customColoringTransparent; 788 } 801 789 } 802 790 g.fillRect(screen.x-halfSize, screen.y-halfSize, largesize, largesize); 803 791 } … … 815 803 continue; 816 804 } 817 805 if (!trkPnt.drawLine) { 806 g.setColor(trkPnt.customColoring); 818 807 Point screen = mv.getPoint(trkPnt); 819 808 g.drawRect(screen.x, screen.y, 0, 0); 820 809 } … … 1480 1469 // CHECKSTYLE.OFF: BooleanExpressionComplexity 1481 1470 if ((computeCacheMaxLineLengthUsed != maxLineLength) 1482 1471 || (computeCacheColored != colored) 1483 || (computeCache ColorTracksTune != colorTracksTune)1472 || (computeCacheVelocityTune != velocityTune) 1484 1473 || (computeCacheColorDynamic != colorModeDynamic) 1485 1474 || (computeCacheHeatMapDrawColorTableIdx != heatMapDrawColorTableIdx) 1486 || (!neutralColor.equals(computeCacheColorUsed)1475 || !Objects.equals(neutralColor, computeCacheColorUsed) 1487 1476 || (computeCacheHeatMapDrawPointMode != heatMapDrawPointMode) 1488 || (computeCacheHeatMapDrawGain != heatMapDrawGain) )1477 || (computeCacheHeatMapDrawGain != heatMapDrawGain) 1489 1478 || (computeCacheHeatMapDrawLowerLimit != heatMapDrawLowerLimit) 1490 1479 ) { 1491 1480 // CHECKSTYLE.ON: BooleanExpressionComplexity … … 1493 1482 computeCacheInSync = false; 1494 1483 computeCacheColorUsed = neutralColor; 1495 1484 computeCacheColored = colored; 1496 computeCache ColorTracksTune = colorTracksTune;1485 computeCacheVelocityTune = velocityTune; 1497 1486 computeCacheColorDynamic = colorModeDynamic; 1498 1487 computeCacheHeatMapDrawColorTableIdx = heatMapDrawColorTableIdx; 1499 1488 computeCacheHeatMapDrawPointMode = heatMapDrawPointMode; -
src/org/openstreetmap/josm/gui/layer/gpx/ImportAudioAction.java
21 21 import org.openstreetmap.josm.data.gpx.GpxConstants; 22 22 import org.openstreetmap.josm.data.gpx.GpxData; 23 23 import org.openstreetmap.josm.data.gpx.GpxTrack; 24 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;24 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 25 25 import org.openstreetmap.josm.data.gpx.WayPoint; 26 26 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 27 27 import org.openstreetmap.josm.gui.HelpAwareOptionPane; … … 147 147 double firstTime = -1.0; 148 148 if (hasTracks) { 149 149 for (GpxTrack track : layer.data.tracks) { 150 for ( GpxTrackSegment seg : track.getSegments()) {150 for (IGpxTrackSegment seg : track.getSegments()) { 151 151 for (WayPoint w : seg.getWayPoints()) { 152 152 firstTime = w.getTime(); 153 153 break; … … 206 206 if (layer.data.tracks != null && Config.getPref().getBoolean("marker.audiofromnamedtrackpoints", false) 207 207 && !layer.data.tracks.isEmpty()) { 208 208 for (GpxTrack track : layer.data.tracks) { 209 for ( GpxTrackSegment seg : track.getSegments()) {209 for (IGpxTrackSegment seg : track.getSegments()) { 210 210 for (WayPoint w : seg.getWayPoints()) { 211 211 if (w.attr.containsKey(GpxConstants.GPX_NAME) || w.attr.containsKey(GpxConstants.GPX_DESC)) { 212 212 waypoints.add(w); … … 227 227 WayPoint w2 = null; 228 228 229 229 for (GpxTrack track : layer.data.tracks) { 230 for ( GpxTrackSegment seg : track.getSegments()) {230 for (IGpxTrackSegment seg : track.getSegments()) { 231 231 for (WayPoint w : seg.getWayPoints()) { 232 232 if (startTime < w.getTime()) { 233 233 w2 = w; … … 263 263 if ((Config.getPref().getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && hasTracks) { 264 264 boolean gotOne = false; 265 265 for (GpxTrack track : layer.data.tracks) { 266 for ( GpxTrackSegment seg : track.getSegments()) {266 for (IGpxTrackSegment seg : track.getSegments()) { 267 267 for (WayPoint w : seg.getWayPoints()) { 268 268 WayPoint wStart = new WayPoint(w.getCoor()); 269 269 wStart.put(GpxConstants.GPX_NAME, "start"); -
src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java
93 93 } 94 94 95 95 @Override 96 protected TemplateEntryProperty getTextTemplate() {97 return TemplateEntryProperty.forAudioMarker(parentLayer.getName());96 protected String getTextTemplateKey() { 97 return "markers.audio.pattern"; 98 98 } 99 99 100 100 @Override … … 103 103 GpxLink link = new GpxLink(audioUrl.toString()); 104 104 link.type = "audio"; 105 105 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)); 108 108 return wpt; 109 109 } 110 110 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/DefaultMarkerProducers.java
9 9 import java.util.Collections; 10 10 import java.util.Optional; 11 11 12 import org.openstreetmap.josm.data.gpx.Extensions;13 12 import org.openstreetmap.josm.data.gpx.GpxConstants; 13 import org.openstreetmap.josm.data.gpx.GpxExtension; 14 14 import org.openstreetmap.josm.data.gpx.GpxLink; 15 15 import org.openstreetmap.josm.data.gpx.WayPoint; 16 16 import org.openstreetmap.josm.tools.Logging; … … 45 45 return Collections.singleton(marker); 46 46 } else if (Utils.hasExtension(urlStr, "wav", "mp3", "aac", "aif", "aiff")) { 47 47 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) { 50 50 try { 51 audioMarker.syncOffset = Double.parseDouble( exts.get("sync-offset"));51 audioMarker.syncOffset = Double.parseDouble(offsetExt.getValue()); 52 52 } catch (NumberFormatException nfe) { 53 53 Logging.warn(nfe); 54 54 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
18 18 19 19 import javax.swing.ImageIcon; 20 20 21 import org.openstreetmap.josm.data.Preferences; 21 22 import org.openstreetmap.josm.data.coor.CachedLatLon; 22 23 import org.openstreetmap.josm.data.coor.EastNorth; 23 24 import org.openstreetmap.josm.data.coor.ILatLon; … … 25 26 import org.openstreetmap.josm.data.gpx.GpxConstants; 26 27 import org.openstreetmap.josm.data.gpx.WayPoint; 27 28 import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match; 28 import org.openstreetmap.josm.data.preferences.CachedProperty;29 29 import org.openstreetmap.josm.gui.MapView; 30 import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent; 30 import org.openstreetmap.josm.gui.layer.GpxLayer; 31 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 31 32 import org.openstreetmap.josm.tools.ImageProvider; 32 33 import org.openstreetmap.josm.tools.Logging; 33 34 import org.openstreetmap.josm.tools.template_engine.ParseError; … … 72 73 */ 73 74 public class Marker implements TemplateEngineDataProvider, ILatLon { 74 75 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 because77 // GPXSettingsPanel had possibility to set waypoint label but then I've realized that markers use different layer then gpx data78 // so per layer settings is useless. Anyway it's possible to specify marker layer pattern in Einstein preferences and maybe somebody79 // will make gui for it so I'm keeping it here80 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 constructor119 }120 121 @Override122 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 @Override134 public String getDefaultValueAsString() {135 if (parent == null)136 return super.getDefaultValueAsString();137 else138 return parent.getAsString();139 }140 141 @Override142 public void preferenceChanged(PreferenceChangeEvent e) {143 if (e.getKey().equals(key) || (parent != null && e.getKey().equals(parent.getKey()))) {144 updateValue();145 }146 }147 }148 149 76 /** 150 77 * Plugins can add their Marker creation stuff at the bottom or top of this list 151 78 * (depending on whether they want to override default behaviour or just add new stuff). … … 216 143 public double offset; 217 144 218 145 private String cachedText; 219 private int textVersion = -1; 146 private static Map<GpxLayer, String> cachedTemplates = new HashMap<>(); 147 private String cachedDefaultTemplate; 148 220 149 private CachedLatLon coor; 221 150 222 151 private boolean erroneous; … … 244 173 245 174 this.dataProvider = dataProvider; 246 175 this.text = text; 176 177 Preferences.main().addKeyPreferenceChangeListener("draw.rawgps." + getTextTemplateKey(), l -> updateText()); 247 178 } 248 179 249 180 /** … … 257 188 WayPoint wpt = new WayPoint(getCoor()); 258 189 wpt.setTimeInMillis((long) (time * 1000)); 259 190 if (text != null) { 260 wpt. addExtension("text", text);191 wpt.getExtensions().add("josm", "text", text); 261 192 } else if (dataProvider != null) { 262 193 for (String key : dataProvider.getTemplateKeys()) { 263 194 Object value = dataProvider.getTemplateValue(key, false); … … 372 303 } 373 304 } 374 305 375 protected TemplateEntryProperty getTextTemplate() {376 return TemplateEntryProperty.forMarker(parentLayer.getName());306 protected String getTextTemplateKey() { 307 return "markers.pattern"; 377 308 } 378 309 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 379 328 /** 380 329 * Returns the Text which should be displayed, depending on chosen preference 381 330 * @return Text of the label 382 331 */ 383 332 public String getText() { 384 if (text != null) 333 if (text != null) { 385 334 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(); 392 356 393 cachedText = sb.toString();394 textVersion = property.getUpdateCount();395 }396 return cachedText;397 357 } 358 return cachedText; 398 359 } 399 360 361 /** 362 * Called when the template changes 363 */ 364 public void updateText() { 365 cachedText = null; 366 cachedDefaultTemplate = null; 367 cachedTemplates = new HashMap<>(); 368 } 369 400 370 @Override 401 371 public Collection<String> getTemplateKeys() { 402 372 Collection<String> result; -
src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
20 20 import java.util.Collection; 21 21 import java.util.Comparator; 22 22 import java.util.List; 23 import java.util.Optional; 23 24 24 25 import javax.swing.AbstractAction; 25 26 import javax.swing.Action; … … 30 31 import org.openstreetmap.josm.actions.RenameLayerAction; 31 32 import org.openstreetmap.josm.data.Bounds; 32 33 import org.openstreetmap.josm.data.coor.LatLon; 33 import org.openstreetmap.josm.data.gpx.Extensions;34 34 import org.openstreetmap.josm.data.gpx.GpxConstants; 35 35 import org.openstreetmap.josm.data.gpx.GpxData; 36 import org.openstreetmap.josm.data.gpx.GpxExtension; 36 37 import org.openstreetmap.josm.data.gpx.GpxLink; 37 38 import org.openstreetmap.josm.data.gpx.WayPoint; 38 39 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; … … 48 49 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker; 49 50 import org.openstreetmap.josm.gui.layer.Layer; 50 51 import org.openstreetmap.josm.gui.layer.gpx.ConvertFromMarkerLayerAction; 52 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 51 53 import org.openstreetmap.josm.io.audio.AudioPlayer; 52 54 import org.openstreetmap.josm.spi.preferences.Config; 53 55 import org.openstreetmap.josm.tools.ImageProvider; … … 75 77 public GpxLayer fromLayer; 76 78 private Marker currentMarker; 77 79 public AudioMarker syncAudioMarker; 80 private Color color, realcolor; 78 81 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); 81 86 82 87 /** 83 88 * Constructs a new {@code MarkerLayer}. … … 94 99 double firstTime = -1.0; 95 100 String lastLinkedFile = ""; 96 101 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 97 113 for (WayPoint wpt : indata.waypoints) { 98 114 /* calculate time differences in waypoints */ 99 115 double time = wpt.getTime(); … … 123 139 // audio file) calculate the offset relative to the first marker of 124 140 // that group. This way the user can jump to the corresponding 125 141 // 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) { 128 144 try { 129 offset = Double.valueOf( exts.get("offset"));145 offset = Double.valueOf(offsetExt.getValue()); 130 146 } catch (NumberFormatException nfe) { 131 147 Logging.warn(nfe); 132 148 } … … 173 189 } 174 190 175 191 @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 @Override186 192 public void paint(Graphics2D g, MapView mv, Bounds box) { 187 193 boolean showTextOrIcon = isTextOrIconShown(); 188 g.setColor(getColorProperty().get()); 189 194 g.setColor(realcolor); 190 195 if (mousePressed) { 191 196 boolean mousePressedTmp = mousePressed; 192 197 Point mousePos = mv.getMousePosition(); // Get mouse position only when necessary (it's the slowest part of marker layer painting) … … 450 455 * @return <code>true</code> if text should be shown, <code>false</code> otherwise. 451 456 */ 452 457 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")); 455 459 } 456 460 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 457 489 private final class MarkerMouseAdapter extends MouseAdapter { 458 490 @Override 459 491 public void mousePressed(MouseEvent e) { … … 503 535 504 536 @Override 505 537 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())); 507 539 layer.invalidate(); 508 540 } 509 541 -
src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
18 18 import org.openstreetmap.josm.data.coor.EastNorth; 19 19 import org.openstreetmap.josm.data.coor.LatLon; 20 20 import org.openstreetmap.josm.data.gpx.GpxTrack; 21 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;21 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 22 22 import org.openstreetmap.josm.data.gpx.WayPoint; 23 23 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 24 24 import org.openstreetmap.josm.gui.MainApplication; … … 327 327 WayPoint w2 = null; 328 328 329 329 for (GpxTrack track : trackLayer.data.getTracks()) { 330 for ( GpxTrackSegment trackseg : track.getSegments()) {330 for (IGpxTrackSegment trackseg : track.getSegments()) { 331 331 for (WayPoint w: trackseg.getWayPoints()) { 332 332 if (audioTime < w.getTime()) { 333 333 w2 = w; -
src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java
100 100 */ 101 101 public String getDisplay() { 102 102 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);116 103 case NamedColorProperty.COLOR_CATEGORY_MAPPAINT: 117 104 if (info.getSource() != null) 118 105 return tr("Paint style {0}: {1}", tr(I18n.escape(info.getSource())), tr(info.getName())); … … 251 238 switch (category) { 252 239 case NamedColorProperty.COLOR_CATEGORY_GENERAL: return 1; 253 240 case NamedColorProperty.COLOR_CATEGORY_MAPPAINT: return 2; 254 case NamedColorProperty.COLOR_CATEGORY_LAYER: return 3; 255 default: return 4; 241 default: return 3; 256 242 } 257 243 } 258 244 … … 379 365 gui.getDisplayPreference().addSubTab(this, tr("Colors"), panel); 380 366 } 381 367 368 @SuppressWarnings("PMD.UnusedFormalParameter") 382 369 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()); 384 374 } 385 375 386 376 /** … … 390 380 PaintColors.values(); 391 381 ConflictColors.getColors(); 392 382 Severity.getColors(); 393 MarkerLayer. getGenericColor();394 GpxDrawHelper. getGenericColor();383 MarkerLayer.DEFAULT_COLOR_PROPERTY.get(); 384 GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get(); 395 385 OsmDataLayer.getOutsideColor(); 396 386 MapScaler.getColor(); 397 387 MapStatus.getColors(); -
src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java
4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 import static org.openstreetmap.josm.tools.I18n.trc; 6 6 7 import java.awt.Color;8 7 import java.awt.Component; 9 8 import java.awt.Dimension; 10 9 import java.awt.GridBagLayout; 11 10 import java.awt.event.ActionListener; 11 import java.util.Collections; 12 12 import java.util.Enumeration; 13 import java.util.HashMap; 14 import java.util.List; 15 import java.util.Map; 16 import java.util.Optional; 13 17 14 18 import javax.swing.AbstractButton; 15 19 import javax.swing.BorderFactory; … … 22 26 import javax.swing.JRadioButton; 23 27 import javax.swing.JSlider; 24 28 29 import org.apache.commons.jcs.access.exception.InvalidArgumentException; 25 30 import org.openstreetmap.josm.actions.ExpertToggleAction; 26 import org.openstreetmap.josm.data.PreferencesUtils; 27 import org.openstreetmap.josm.data.preferences.NamedColorProperty; 31 import org.openstreetmap.josm.data.gpx.GpxData; 28 32 import org.openstreetmap.josm.gui.MainApplication; 33 import org.openstreetmap.josm.gui.layer.GpxLayer; 29 34 import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper; 30 35 import org.openstreetmap.josm.gui.layer.markerlayer.Marker; 31 import org.openstreetmap.josm.gui.layer.markerlayer.Marker.TemplateEntryProperty;32 36 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener; 33 37 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 34 38 import org.openstreetmap.josm.gui.widgets.JosmTextField; … … 67 71 private final JRadioButton colorTypeQuality = new JRadioButton(tr("Quality (RTKLib only, if available)")); 68 72 private final JRadioButton colorTypeTime = new JRadioButton(tr("Track date")); 69 73 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)")); 71 75 private final JRadioButton colorTypeGlobal = new JRadioButton(tr("Use global settings")); 72 76 private final JosmComboBox<String> colorTypeVelocityTune = new JosmComboBox<>(new String[] {tr("Car"), tr("Bicycle"), tr("Foot")}); 73 77 private final JosmComboBox<String> colorTypeHeatMapTune = new JosmComboBox<>(new String[] { … … 93 97 private final JCheckBox useGpsAntialiasing = new JCheckBox(tr("Smooth GPX graphics (antialiasing)")); 94 98 private final JCheckBox drawLineWithAlpha = new JCheckBox(tr("Draw with Opacity (alpha blending) ")); 95 99 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 99 105 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 100 139 /** 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 105 142 */ 106 public GPXSettingsPanel( String layerName, boolean local, boolean nonlocal) {143 public GPXSettingsPanel(List<GpxLayer> layers) { 107 144 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()); 111 153 initComponents(); 112 154 loadPreferences(); 113 155 } … … 117 159 */ 118 160 public GPXSettingsPanel() { 119 161 super(new GridBagLayout()); 162 layers = null; 163 firstLayer = null; 164 global = hasLocalFile = hasNonLocalFile = true; 120 165 initComponents(); 121 local = false;122 nonlocal = false;123 166 loadPreferences(); // preferences -> controls 124 167 } 125 168 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 126 297 // CHECKSTYLE.OFF: ExecutableStatementCountCheck 127 298 private void initComponents() { 128 299 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 129 300 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 } 134 307 135 308 // drawRawGpsLines 136 309 ButtonGroup gpsLinesGroup = new ButtonGroup(); 137 if ( layerName != null) {310 if (!global) { 138 311 gpsLinesGroup.add(drawRawGpsLinesGlobal); 139 312 } 140 313 gpsLinesGroup.add(drawRawGpsLinesNone); … … 145 318 146 319 JLabel label = new JLabel(tr("Draw lines between raw GPS points")); 147 320 add(label, GBC.eol().insets(20, 0, 0, 0)); 148 if ( layerName != null) {321 if (!global) { 149 322 add(drawRawGpsLinesGlobal, GBC.eol().insets(40, 0, 0, 0)); 150 323 } 151 324 add(drawRawGpsLinesNone, GBC.eol().insets(40, 0, 0, 0)); 152 if ( layerName == null || local) {325 if (hasLocalFile) { 153 326 add(drawRawGpsLinesLocal, GBC.eol().insets(40, 0, 0, 0)); 154 327 } 155 if ( layerName == null || nonlocal) {328 if (hasNonLocalFile) { 156 329 add(drawRawGpsLinesAll, GBC.eol().insets(40, 0, 0, 0)); 157 330 } 158 331 ExpertToggleAction.addVisibilitySwitcher(label); … … 242 415 243 416 // colorTracks 244 417 ButtonGroup colorGroup = new ButtonGroup(); 245 if ( layerName != null) {418 if (!global) { 246 419 colorGroup.add(colorTypeGlobal); 247 420 } 248 421 colorGroup.add(colorTypeNone); … … 253 426 colorGroup.add(colorTypeTime); 254 427 colorGroup.add(colorTypeHeatMap); 255 428 256 colorTypeNone.setToolTipText(tr("All points and track segments will have the samecolor. 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.")); 257 430 colorTypeVelocity.setToolTipText(tr("Colors points and track segments by velocity.")); 258 431 colorTypeDirection.setToolTipText(tr("Colors points and track segments by direction.")); 259 432 colorTypeDilution.setToolTipText( … … 272 445 add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0)); 273 446 274 447 add(new JLabel(tr("Track and Point Coloring")), GBC.eol().insets(20, 0, 0, 0)); 275 if ( layerName != null) {448 if (!global) { 276 449 add(colorTypeGlobal, GBC.eol().insets(40, 0, 0, 0)); 277 450 } 278 451 add(colorTypeNone, GBC.eol().insets(40, 0, 0, 0)); … … 331 504 if (null != dim) { 332 505 // get image size of environment 333 506 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)); 343 511 } 344 512 }); 345 513 … … 353 521 add(colorDynamic, GBC.eop().insets(40, 0, 0, 0)); 354 522 ExpertToggleAction.addVisibilitySwitcher(colorDynamic); 355 523 356 if ( layerName == null) {524 if (global) { 357 525 // Setting waypoints for gpx layer doesn't make sense - waypoints are shown in marker layer that has different name - so show 358 526 // this only for global config 359 527 … … 363 531 label.setLabelFor(waypointLabel); 364 532 add(waypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 365 533 waypointLabel.addActionListener(e -> updateWaypointPattern(waypointLabel, waypointLabelPattern)); 366 updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, TemplateEntryProperty.forMarker(layerName));367 534 add(waypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5)); 368 535 ExpertToggleAction.addVisibilitySwitcher(label); 369 536 ExpertToggleAction.addVisibilitySwitcher(waypointLabel); … … 379 546 label.setLabelFor(audioWaypointLabel); 380 547 add(audioWaypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 381 548 audioWaypointLabel.addActionListener(e -> updateWaypointPattern(audioWaypointLabel, audioWaypointLabelPattern)); 382 updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, TemplateEntryProperty.forAudioMarker(layerName));383 549 add(audioWaypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5)); 384 550 ExpertToggleAction.addVisibilitySwitcher(label); 385 551 ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabel); … … 395 561 */ 396 562 public final void loadPreferences() { 397 563 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) { 401 572 drawRawGpsLinesGlobal.setSelected(true); 402 573 } 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); 411 575 } 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")); 433 586 useGpsAntialiasing.setSelected(Config.getPref().getBoolean("mappaint.gpx.use-antialiasing", false)); 434 587 435 588 drawRawGpsLinesActionListener.actionPerformed(null); 436 437 if (layerName != null && Config.getPref().get("draw.rawgps.colors."+layerName).isEmpty()) { 589 if (!global && prefIntLocal("colormode") == -1) { 438 590 colorTypeGlobal.setSelected(true); 439 591 colorDynamic.setSelected(false); 440 592 colorDynamic.setEnabled(false); … … 442 594 colorTypeHeatMapGain.setValue(0); 443 595 colorTypeHeatMapLowerLimit.setValue(0); 444 596 } else { 445 int colorType = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colors", layerName, 0);597 int colorType = prefInt("colormode"); 446 598 switch (colorType) { 447 case 0: colorTypeNone.setSelected(true); break;599 case -1: case 0: colorTypeNone.setSelected(true); break; 448 600 case 1: colorTypeVelocity.setSelected(true); break; 449 601 case 2: colorTypeDilution.setSelected(true); break; 450 602 case 3: colorTypeDirection.setSelected(true); break; … … 453 605 case 6: colorTypeQuality.setSelected(true); break; 454 606 default: Logging.warn("Unknown color type: " + colorType); 455 607 } 456 int ccts = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colorTracksTune", layerName, 45);608 int ccts = prefInt("colormode.velocity.tune"); 457 609 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")); 468 615 } 616 updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, pref("markers.pattern")); 617 updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, pref("markers.audio.pattern")); 618 469 619 } 470 620 471 621 /** 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. 475 623 * @return {@code true} when restart is required, {@code false} otherwise 476 624 */ 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()); 481 630 } 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); 492 644 } 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); 496 649 } 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()); 507 656 } 508 657 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()); 513 662 514 663 Config.getPref().putBoolean("mappaint.gpx.use-antialiasing", useGpsAntialiasing.isSelected()); 515 664 516 TemplateEntryProperty.forMarker(layerName).put(waypointLabelPattern.getText());517 TemplateEntryProperty.forAudioMarker(layerName).put(audioWaypointLabelPattern.getText());518 519 665 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); 523 669 return false; 524 670 } else if (colorTypeVelocity.isSelected()) { 525 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 1);671 putPref("colormode", 1); 526 672 } else if (colorTypeDilution.isSelected()) { 527 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 2);673 putPref("colormode", 2); 528 674 } else if (colorTypeDirection.isSelected()) { 529 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 3);675 putPref("colormode", 3); 530 676 } else if (colorTypeTime.isSelected()) { 531 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 4);677 putPref("colormode", 4); 532 678 } else if (colorTypeHeatMap.isSelected()) { 533 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 5);679 putPref("colormode", 5); 534 680 } else if (colorTypeQuality.isSelected()) { 535 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 6);681 putPref("colormode", 6); 536 682 } else { 537 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 0);683 putPref("colormode", 0); 538 684 } 539 Config.getPref().putBoolean("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected());685 putPref("colormode.dynamic-range", colorDynamic.isSelected()); 540 686 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()); 546 692 693 if (!global && layers != null && !layers.isEmpty()) { 694 layers.forEach(l -> l.data.invalidate()); 695 } 696 547 697 return false; 548 698 } 549 699 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) { 560 701 boolean found = false; 561 702 for (int i = 0; i < LABEL_PATTERN_TEMPLATE.length; i++) { 562 703 if (LABEL_PATTERN_TEMPLATE[i].equals(labelPattern)) { -
src/org/openstreetmap/josm/io/GpxReader.java
18 18 19 19 import org.openstreetmap.josm.data.Bounds; 20 20 import org.openstreetmap.josm.data.coor.LatLon; 21 import org.openstreetmap.josm.data.gpx.Extensions;22 21 import org.openstreetmap.josm.data.gpx.GpxConstants; 23 22 import org.openstreetmap.josm.data.gpx.GpxData; 23 import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace; 24 import org.openstreetmap.josm.data.gpx.GpxExtensionCollection; 24 25 import org.openstreetmap.josm.data.gpx.GpxLink; 25 26 import org.openstreetmap.josm.data.gpx.GpxRoute; 26 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack; 27 import org.openstreetmap.josm.data.gpx.GpxTrack; 28 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 29 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 27 30 import org.openstreetmap.josm.data.gpx.WayPoint; 28 31 import org.openstreetmap.josm.tools.Logging; 29 32 import org.openstreetmap.josm.tools.UncheckedParseException; … … 67 70 private class Parser extends DefaultHandler { 68 71 69 72 private GpxData data; 70 private Collection< Collection<WayPoint>> currentTrack;73 private Collection<IGpxTrackSegment> currentTrack; 71 74 private Map<String, Object> currentTrackAttr; 72 75 private Collection<WayPoint> currentTrackSeg; 73 76 private GpxRoute currentRoute; … … 76 79 private State currentState = State.INIT; 77 80 78 81 private GpxLink currentLink; 79 private Extensions currentExtensions; 82 private GpxExtensionCollection currentExtensionCollection; 83 private GpxExtensionCollection currentTrackExtensionCollection; 80 84 private Stack<State> states; 81 85 private final Stack<String> elements = new Stack<>(); 82 86 … … 88 92 public void startDocument() { 89 93 accumulator = new StringBuilder(); 90 94 states = new Stack<>(); 91 data = new GpxData(); 95 data = new GpxData(true); 96 currentExtensionCollection = new GpxExtensionCollection(); 97 currentTrackExtensionCollection = new GpxExtensionCollection(); 92 98 } 93 99 100 @Override 101 public void startPrefixMapping(String prefix, String uri) throws SAXException { 102 data.getNamespaces().add(new XMLNamespace(prefix, uri)); 103 } 104 94 105 private double parseCoord(Attributes atts, String key) { 95 106 String val = atts.getValue(key); 96 107 if (val != null) { … … 133 144 // unknown version, assume 1.1 134 145 version = "1.1"; 135 146 } 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 } 136 158 break; 137 159 case GPX: 138 160 switch (localName) { … … 159 181 case "extensions": 160 182 states.push(currentState); 161 183 currentState = State.EXT; 162 currentExtensions = new Extensions();163 184 break; 164 185 case "gpx": 165 186 if (atts.getValue("creator") != null && atts.getValue("creator").startsWith("Nokia Sports Tracker")) { … … 178 199 case "extensions": 179 200 states.push(currentState); 180 201 currentState = State.EXT; 181 currentExtensions = new Extensions();182 202 break; 183 203 case "copyright": 184 204 states.push(currentState); … … 228 248 case "extensions": 229 249 states.push(currentState); 230 250 currentState = State.EXT; 231 currentExtensions = new Extensions();232 251 break; 233 252 default: // Do nothing 234 253 } 235 254 break; 236 255 case TRKSEG: 237 if ("trkpt".equals(localName)) { 256 switch (localName) { 257 case "trkpt": 238 258 states.push(currentState); 239 259 currentState = State.WPT; 240 260 currentWayPoint = new WayPoint(parseLatLon(atts)); 261 break; 262 case "extensions": 263 states.push(currentState); 264 currentState = State.EXT; 265 break; 241 266 } 242 267 break; 243 268 case WPT: … … 250 275 case "extensions": 251 276 states.push(currentState); 252 277 currentState = State.EXT; 253 currentExtensions = new Extensions();254 278 break; 255 279 default: // Do nothing 256 280 } … … 270 294 case "extensions": 271 295 states.push(currentState); 272 296 currentState = State.EXT; 273 currentExtensions = new Extensions();274 297 break; 275 298 default: // Do nothing 276 299 } 277 300 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; 278 308 default: // Do nothing 279 309 } 280 310 accumulator.setLength(0); … … 349 379 if ((currentState == State.METADATA && "metadata".equals(localName)) || 350 380 (currentState == State.GPX && "gpx".equals(localName))) { 351 381 convertUrlToLink(data.attr); 352 if (currentExtensions != null && !currentExtensions.isEmpty()) { 353 data.put(META_EXTENSIONS, currentExtensions); 354 } 382 data.getExtensions().addAll(currentExtensionCollection); 383 currentExtensionCollection.clear(); 355 384 currentState = states.pop(); 356 385 } 357 386 break; … … 359 388 // do nothing, has been parsed on startElement 360 389 break; 361 390 default: 362 //TODO: parse extensions363 391 } 364 392 break; 365 393 case AUTHOR: … … 464 492 case "wpt": 465 493 currentState = states.pop(); 466 494 convertUrlToLink(currentWayPoint.attr); 467 if (currentExtensions != null && !currentExtensions.isEmpty()) { 468 currentWayPoint.put(META_EXTENSIONS, currentExtensions); 469 } 495 currentWayPoint.getExtensions().addAll(currentExtensionCollection); 470 496 data.waypoints.add(currentWayPoint); 497 currentExtensionCollection.clear(); 471 498 break; 472 499 default: // Do nothing 473 500 } … … 475 502 case TRKSEG: 476 503 if ("trkseg".equals(localName)) { 477 504 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(); 479 511 } 480 512 break; 481 513 case TRK: … … 483 515 case "trk": 484 516 currentState = states.pop(); 485 517 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(); 487 522 break; 488 523 case "name": 489 524 case "cmt": … … 501 536 case EXT: 502 537 if ("extensions".equals(localName)) { 503 538 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 } 507 546 } 508 547 break; 509 548 default: … … 519 558 default: // Do nothing 520 559 } 521 560 } 561 accumulator.setLength(0); 522 562 } 523 563 524 564 @Override … … 525 565 public void endDocument() throws SAXException { 526 566 if (!states.empty()) 527 567 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(); 532 583 gpxData = data; 533 584 } 534 585 -
src/org/openstreetmap/josm/io/GpxWriter.java
8 8 import java.io.OutputStreamWriter; 9 9 import java.io.PrintWriter; 10 10 import java.nio.charset.StandardCharsets; 11 import java.util.ArrayList; 11 12 import java.util.Collection; 12 13 import java.util.Date; 13 14 import java.util.List; 14 15 import java.util.Map; 15 import java.util.Map.Entry; 16 import java.util.Objects; 17 import java.util.stream.Collectors; 16 18 17 19 import javax.xml.XMLConstants; 18 20 19 21 import org.openstreetmap.josm.data.Bounds; 20 22 import org.openstreetmap.josm.data.coor.LatLon; 21 import org.openstreetmap.josm.data.gpx.Extensions;22 23 import org.openstreetmap.josm.data.gpx.GpxConstants; 23 24 import org.openstreetmap.josm.data.gpx.GpxData; 25 import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace; 26 import org.openstreetmap.josm.data.gpx.GpxExtension; 27 import org.openstreetmap.josm.data.gpx.GpxExtensionCollection; 24 28 import org.openstreetmap.josm.data.gpx.GpxLink; 25 29 import org.openstreetmap.josm.data.gpx.GpxRoute; 26 30 import org.openstreetmap.josm.data.gpx.GpxTrack; 27 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;31 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 28 32 import org.openstreetmap.josm.data.gpx.IWithAttributes; 29 33 import org.openstreetmap.josm.data.gpx.WayPoint; 30 34 import org.openstreetmap.josm.tools.JosmRuntimeException; … … 54 58 55 59 private GpxData data; 56 60 private String indent = ""; 61 private List<String> validprefixes; 57 62 58 63 private static final int WAY_POINT = 0; 59 64 private static final int ROUTE_POINT = 1; … … 64 69 * @param data The data to write 65 70 */ 66 71 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) { 67 83 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(); 81 137 } 82 138 } 83 139 } 84 140 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)); 90 143 indent = " "; 91 144 writeMetaData(); 92 145 writeWayPoints(); … … 105 158 gpxLink(link); 106 159 } 107 160 } 108 } else if (META_EXTENSIONS.equals(key)) {109 Extensions extensions = (Extensions) obj.get(key);110 if (extensions != null) {111 gpxExtensions(extensions);112 }113 161 } else { 114 162 String value = obj.getString(key); 115 163 if (value != null) { … … 147 195 if (attr.containsKey(META_AUTHOR_EMAIL)) { 148 196 String[] tmp = data.getString(META_AUTHOR_EMAIL).split("@"); 149 197 if (tmp.length == 2) { 150 inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+'\"');198 inline("email", "id=\"" + encode(tmp[0]) + "\" domain=\"" + encode(tmp[1]) +'\"'); 151 199 } 152 200 } 153 201 // write the author link … … 158 206 // write the copyright details 159 207 if (attr.containsKey(META_COPYRIGHT_LICENSE) 160 208 || attr.containsKey(META_COPYRIGHT_YEAR)) { 161 open Att("copyright", "author=\""+ data.get(META_COPYRIGHT_AUTHOR) +'\"');209 openln("copyright", "author=\""+ encode(data.get(META_COPYRIGHT_AUTHOR).toString()) +'\"'); 162 210 if (attr.containsKey(META_COPYRIGHT_YEAR)) { 163 211 simpleTag("year", (String) data.get(META_COPYRIGHT_YEAR)); 164 212 } … … 187 235 inline("bounds", b); 188 236 } 189 237 190 if (data.fromServer) { 191 openln("extensions"); 192 simpleTag("josm:from-server", "true"); 193 closeln("extensions"); 194 } 195 238 gpxExtensions(data.getExtensions()); 196 239 closeln("metadata"); 197 240 } 198 241 … … 206 249 for (GpxRoute rte : data.getRoutes()) { 207 250 openln("rte"); 208 251 writeAttr(rte, RTE_TRK_KEYS); 252 gpxExtensions(rte.getExtensions()); 209 253 for (WayPoint pnt : rte.routePoints) { 210 254 wayPoint(pnt, ROUTE_POINT); 211 255 } … … 217 261 for (GpxTrack trk : data.getTracks()) { 218 262 openln("trk"); 219 263 writeAttr(trk, RTE_TRK_KEYS); 220 for (GpxTrackSegment seg : trk.getSegments()) { 264 gpxExtensions(trk.getExtensions()); 265 for (IGpxTrackSegment seg : trk.getSegments()) { 221 266 openln("trkseg"); 267 gpxExtensions(seg.getExtensions()); 222 268 for (WayPoint pnt : seg.getWayPoints()) { 223 269 wayPoint(pnt, TRACK_POINT); 224 270 } … … 233 279 out.println(); 234 280 } 235 281 282 private void openln(String tag, String attributes) { 283 open(tag, attributes); 284 out.println(); 285 } 286 236 287 private void open(String tag) { 237 288 out.print(indent + '<' + tag + '>'); 238 289 indent += " "; 239 290 } 240 291 241 private void open Att(String tag, String attributes) {242 out.print ln(indent + '<' + tag + ' '+ attributes + '>');292 private void open(String tag, String attributes) { 293 out.print(indent + '<' + tag + (attributes.isEmpty() ? "" : ' ') + attributes + '>'); 243 294 indent += " "; 244 295 } 245 296 246 297 private void inline(String tag, String attributes) { 247 out.println(indent + '<' + tag + ' '+ attributes + "/>");298 out.println(indent + '<' + tag + (attributes.isEmpty() ? "" : ' ') + attributes + "/>"); 248 299 } 249 300 250 301 private void close(String tag) { … … 272 323 } 273 324 } 274 325 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 275 335 /** 276 336 * output link 277 337 * @param link link … … 278 338 */ 279 339 private void gpxLink(GpxLink link) { 280 340 if (link != null) { 281 open Att("link", "href=\"" + link.uri+ '\"');341 openln("link", "href=\"" + encode(link.uri) + '\"'); 282 342 simpleTag("text", link.text); 283 343 simpleTag("type", link.type); 284 344 closeln("link"); … … 308 368 if (pnt != null) { 309 369 LatLon c = pnt.getCoor(); 310 370 String coordAttr = "lat=\"" + c.lat() + "\" lon=\"" + c.lon() + '\"'; 311 if (pnt.attr.isEmpty() ) {371 if (pnt.attr.isEmpty() && pnt.getExtensions().isEmpty()) { 312 372 inline(type, coordAttr); 313 373 } else { 314 open Att(type, coordAttr);374 openln(type, coordAttr); 315 375 writeAttr(pnt, WPT_KEYS); 376 gpxExtensions(pnt.getExtensions()); 316 377 closeln(type); 317 378 } 318 379 } 319 380 } 320 381 321 private void gpxExtensions( Extensions extensions) {322 if ( extensions != null && !extensions.isEmpty()) {382 private void gpxExtensions(GpxExtensionCollection allExtensions) { 383 if (allExtensions.isVisible()) { 323 384 openln("extensions"); 324 for (Entry<String, String> e : extensions.entrySet()) { 325 simpleTag("josm:" + e.getKey(), e.getValue()); 326 } 385 writeExtension(allExtensions); 327 386 closeln("extensions"); 328 387 } 329 388 } 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 } 330 412 } -
src/org/openstreetmap/josm/io/nmea/NmeaReader.java
20 20 import org.openstreetmap.josm.data.coor.LatLon; 21 21 import org.openstreetmap.josm.data.gpx.GpxConstants; 22 22 import org.openstreetmap.josm.data.gpx.GpxData; 23 import org.openstreetmap.josm.data.gpx. ImmutableGpxTrack;23 import org.openstreetmap.josm.data.gpx.GpxTrack; 24 24 import org.openstreetmap.josm.data.gpx.WayPoint; 25 25 import org.openstreetmap.josm.io.IGpxReader; 26 26 import org.openstreetmap.josm.io.IllegalDataException; … … 264 264 } 265 265 } 266 266 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())); 268 268 269 269 } catch (IllegalDataException e) { 270 270 Logging.warn(e); -
src/org/openstreetmap/josm/io/rtklib/RtkLibPosReader.java
18 18 import org.openstreetmap.josm.data.coor.LatLon; 19 19 import org.openstreetmap.josm.data.gpx.GpxConstants; 20 20 import org.openstreetmap.josm.data.gpx.GpxData; 21 import org.openstreetmap.josm.data.gpx. ImmutableGpxTrack;21 import org.openstreetmap.josm.data.gpx.GpxTrack; 22 22 import org.openstreetmap.josm.data.gpx.WayPoint; 23 23 import org.openstreetmap.josm.io.IGpxReader; 24 24 import org.openstreetmap.josm.tools.Logging; … … 114 114 } while (line != null); 115 115 } 116 116 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())); 118 118 return true; 119 119 } 120 120 -
src/org/openstreetmap/josm/io/session/GenericSessionExporter.java
81 81 82 82 @Override 83 83 public void actionPerformed(ActionEvent e) { 84 SaveAction.getInstance().doSave(layer );84 SaveAction.getInstance().doSave(layer, true); 85 85 updateEnabledState(); 86 86 } 87 87 -
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
35 35 36 36 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 37 37 import nl.jqno.equalsverifier.EqualsVerifier; 38 import nl.jqno.equalsverifier.Warning; 38 39 39 40 /** 40 41 * Unit tests for class {@link GpxData}. … … 63 64 */ 64 65 @Test 65 66 public void testMergeFrom() { 66 ImmutableGpxTrack track = singleWaypointGpxTrack();67 GpxTrack track = singleWaypointGpxTrack(); 67 68 GpxRoute route = singleWaypointRoute(); 68 69 WayPoint newWP = new WayPoint(LatLon.NORTH_POLE); 69 70 WayPoint existingWP = new WayPoint(LatLon.SOUTH_POLE); … … 102 103 final GpxData other = getGpx("Layer2"); 103 104 final GpxData expected = getGpx(exp); 104 105 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); 106 109 } 107 110 108 111 private static GpxData getGpx(String file) throws IOException, SAXException { … … 116 119 public void testTracks() { 117 120 assertEquals(0, data.getTracks().size()); 118 121 119 ImmutableGpxTrack track1 = emptyGpxTrack();120 ImmutableGpxTrack track2 = singleWaypointGpxTrack();122 GpxTrack track1 = emptyGpxTrack(); 123 GpxTrack track2 = singleWaypointGpxTrack(); 121 124 data.addTrack(track1); 122 125 assertEquals(1, data.getTracks().size()); 123 126 data.addTrack(track2); … … 136 139 */ 137 140 @Test(expected = IllegalArgumentException.class) 138 141 public void testAddTrackFails() { 139 ImmutableGpxTrack track1 = emptyGpxTrack();142 GpxTrack track1 = emptyGpxTrack(); 140 143 data.addTrack(track1); 141 144 data.addTrack(track1); 142 145 } … … 146 149 */ 147 150 @Test(expected = IllegalArgumentException.class) 148 151 public void testRemoveTrackFails() { 149 ImmutableGpxTrack track1 = emptyGpxTrack();152 GpxTrack track1 = emptyGpxTrack(); 150 153 data.addTrack(track1); 151 154 data.removeTrack(track1); 152 155 data.removeTrack(track1); … … 245 248 @Test 246 249 public void testHasTrackPoints() { 247 250 assertFalse(data.hasTrackPoints()); 248 ImmutableGpxTrack track1 = emptyGpxTrack();251 GpxTrack track1 = emptyGpxTrack(); 249 252 data.addTrack(track1); 250 253 assertFalse(data.hasTrackPoints()); 251 ImmutableGpxTrack track2 = singleWaypointGpxTrack();254 GpxTrack track2 = singleWaypointGpxTrack(); 252 255 data.addTrack(track2); 253 256 assertTrue(data.hasTrackPoints()); 254 257 } … … 259 262 @Test 260 263 public void testGetTrackPoints() { 261 264 assertEquals(0, data.getTrackPoints().count()); 262 ImmutableGpxTrack track1 = singleWaypointGpxTrack();265 GpxTrack track1 = singleWaypointGpxTrack(); 263 266 data.addTrack(track1); 264 267 assertEquals(1, data.getTrackPoints().count()); 265 ImmutableGpxTrack track2 = singleWaypointGpxTrack();268 GpxTrack track2 = singleWaypointGpxTrack(); 266 269 data.addTrack(track2); 267 270 assertEquals(2, data.getTrackPoints().count()); 268 271 } … … 280 283 */ 281 284 @Test 282 285 public void testIsEmpty() { 283 ImmutableGpxTrack track1 = singleWaypointGpxTrack();286 GpxTrack track1 = singleWaypointGpxTrack(); 284 287 WayPoint waypoint = new WayPoint(LatLon.ZERO); 285 288 GpxRoute route = singleWaypointRoute(); 286 289 … … 307 310 */ 308 311 @Test 309 312 public void testLength() { 310 ImmutableGpxTrack track1 = waypointGpxTrack(313 GpxTrack track1 = waypointGpxTrack( 311 314 new WayPoint(new LatLon(0, 0)), 312 315 new WayPoint(new LatLon(1, 1)), 313 316 new WayPoint(new LatLon(0, 2))); 314 ImmutableGpxTrack track2 = waypointGpxTrack(317 GpxTrack track2 = waypointGpxTrack( 315 318 new WayPoint(new LatLon(0, 0)), 316 319 new WayPoint(new LatLon(-1, 1))); 317 320 data.addTrack(track1); … … 335 338 p1.setTime(new Date(200020)); 336 339 p2.setTime(new Date(100020)); 337 340 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())); 340 343 341 344 Date[] times = data.getMinMaxTimeForAllTracks(); 342 345 assertEquals(times.length, 2); … … 354 357 .map(ProjectionRegistry.getProjection()::eastNorth2latlon) 355 358 .map(WayPoint::new) 356 359 .collect(Collectors.toList()); 357 data.addTrack(new ImmutableGpxTrack(Arrays.asList(points), Collections.emptyMap()));360 data.addTrack(new GpxTrack(Arrays.asList(points), Collections.emptyMap())); 358 361 359 362 WayPoint closeToMiddle = data.nearestPointOnTrack(new EastNorth(10, 0), 10); 360 363 assertEquals(points.get(1), closeToMiddle); … … 448 451 449 452 } 450 453 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()); 453 456 } 454 457 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()); 457 460 } 458 461 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()); 461 464 } 462 465 463 466 private static GpxRoute singleWaypointRoute() { … … 472 475 @Test 473 476 public void testEqualsContract() { 474 477 TestUtils.assumeWorkingEqualsVerifier(); 478 GpxExtensionCollection col = new GpxExtensionCollection(); 479 col.add("josm", "from-server", "true"); 475 480 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") 477 483 .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE)) 478 484 .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create()) 485 .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col) 479 486 .verify(); 480 487 } 481 488 } -
test/unit/org/openstreetmap/josm/data/gpx/GpxExtensionTest.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.gui.layer.gpx.ConvertToDataLayerActionTest; 8 import org.openstreetmap.josm.io.GpxReaderTest; 9 import org.openstreetmap.josm.io.GpxWriterTest; 10 import org.openstreetmap.josm.testutils.JOSMTestRules; 11 12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 13 import nl.jqno.equalsverifier.EqualsVerifier; 14 import nl.jqno.equalsverifier.Warning; 15 16 /** 17 * Unit tests for class {@link GpxExtension} 18 */ 19 public 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
29 29 @Test 30 30 public void testEqualsContract() { 31 31 TestUtils.assumeWorkingEqualsVerifier(); 32 GpxExtensionCollection col = new GpxExtensionCollection(); 33 col.add("josm", "from-server", "true"); 32 34 EqualsVerifier.forClass(GpxRoute.class).usingGetClass() 33 35 .suppress(Warning.NONFINAL_FIELDS) 34 36 .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE)) 37 .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col) 35 38 .verify(); 36 39 } 37 40 } -
test/unit/org/openstreetmap/josm/data/gpx/GpxTrackSegmentTest.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 import nl.jqno.equalsverifier.Warning; 13 14 /** 15 * Unit tests for class {@link GpxTrackSegment}. 16 */ 17 public 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. 2 package org.openstreetmap.josm.data.gpx; 3 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertNull; 6 7 import java.awt.Color; 8 import java.util.ArrayList; 9 import java.util.HashMap; 10 11 import org.junit.Rule; 12 import org.junit.Test; 13 import org.openstreetmap.josm.TestUtils; 14 import org.openstreetmap.josm.testutils.JOSMTestRules; 15 import org.openstreetmap.josm.tools.ListenerList; 16 17 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 18 import nl.jqno.equalsverifier.EqualsVerifier; 19 import nl.jqno.equalsverifier.Warning; 20 21 /** 22 * Unit tests for class {@link GpxTrack}. 23 */ 24 public 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 @Rule22 @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 @Test29 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 @Rule22 @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 @Test29 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
28 28 @Test 29 29 public void testEqualsContract() { 30 30 TestUtils.assumeWorkingEqualsVerifier(); 31 GpxExtensionCollection col = new GpxExtensionCollection(); 32 col.add("josm", "from-server", "true"); 31 33 EqualsVerifier.forClass(WayPoint.class).usingGetClass() 32 34 .suppress(Warning.NONFINAL_FIELDS) 33 35 .withIgnoredFields("customColoring", "dir", "drawLine", "east", "north", "eastNorthCacheKey") 36 .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col) 34 37 .verify(); 35 38 } 36 39 } -
test/unit/org/openstreetmap/josm/data/gpx/WithAttributesTest.java
28 28 @Test 29 29 public void testEqualsContract() { 30 30 TestUtils.assumeWorkingEqualsVerifier(); 31 GpxExtensionCollection col = new GpxExtensionCollection(); 32 col.add("josm", "from-server", "true"); 31 33 EqualsVerifier.forClass(WithAttributes.class).usingGetClass() 32 34 .suppress(Warning.NONFINAL_FIELDS) 35 .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col) 33 36 .verify(); 34 37 } 35 38 } -
test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java
3 3 4 4 import static org.junit.Assert.assertEquals; 5 5 import static org.junit.Assert.assertFalse; 6 import static org.junit.Assert.assertNull; 6 7 import static org.junit.Assert.assertTrue; 7 8 8 9 import java.awt.Color; … … 18 19 import org.junit.Test; 19 20 import org.openstreetmap.josm.TestUtils; 20 21 import org.openstreetmap.josm.data.gpx.GpxData; 21 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack; 22 import org.openstreetmap.josm.data.gpx.GpxTrack; 23 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 22 24 import org.openstreetmap.josm.data.gpx.WayPoint; 23 25 import org.openstreetmap.josm.data.osm.DataSet; 24 26 import org.openstreetmap.josm.data.projection.Projections; … … 73 75 @Test 74 76 public void testGpxLayer() throws Exception { 75 77 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 76 82 assertEquals("foo", layer.getName()); 77 83 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()); 80 86 81 87 GpxLayer layer2 = new GpxLayer(new GpxData(), "bar", true); 82 88 assertEquals("bar", layer2.getName()); 83 89 assertTrue(layer2.isLocalFile()); 84 assert Equals(Color.MAGENTA, layer2.getColorProperty().get());90 assertNull(layer2.getColor()); 85 91 assertEquals("<html>0 tracks (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer2.getToolTipText()); 86 92 87 93 assertTrue(layer.checkSaveConditions()); … … 187 193 @Test 188 194 public void testGetTimespanForTrack() throws Exception { 189 195 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>()))); 191 197 192 198 assertEquals("1/3/16 11:59 AM - 12:00 PM (0:00)", GpxLayer.getTimespanForTrack(getMinimalGpxData().tracks.iterator().next())); 193 199 -
test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java
7 7 import static org.junit.Assert.assertNull; 8 8 import static org.junit.Assert.assertTrue; 9 9 10 import java.awt.Color;11 10 import java.io.File; 12 11 13 12 import org.junit.Before; 14 13 import org.junit.Rule; 15 14 import org.junit.Test; 16 import org.openstreetmap.josm.data.preferences.AbstractProperty;17 import org.openstreetmap.josm.data.preferences.NamedColorProperty;18 15 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 19 16 import org.openstreetmap.josm.testutils.JOSMTestRules; 20 17 … … 43 40 } 44 41 45 42 /** 46 * Test {@link Layer#getColorProperty()}47 */48 @Test49 public void testGetColorProperty() {50 assertEquals(null, testLayer.getColorProperty());51 52 AbstractProperty<Color> color = new LayerManagerTest.TestLayer() {53 @Override54 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 /**65 43 * Test of {@link Layer#isInfoResizable} 66 44 */ 67 45 @Test … … 97 75 testLayer.setName("Test Layer2"); 98 76 assertEquals("Test Layer2", testLayer.getName()); 99 77 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(); 106 79 107 80 testLayer.setName("Test Layer2"); 108 81 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 layers25 */26 @Rule27 @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 @Test34 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
23 23 import org.openstreetmap.josm.data.gpx.GpxConstants; 24 24 import org.openstreetmap.josm.data.gpx.GpxData; 25 25 import org.openstreetmap.josm.data.gpx.GpxTrack; 26 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;26 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 27 27 import org.openstreetmap.josm.data.gpx.WayPoint; 28 28 import org.openstreetmap.josm.data.osm.DataSet; 29 29 import org.openstreetmap.josm.data.osm.Node; … … 240 240 // Check that track is correct 241 241 assertEquals(1, gpx.getTrackCount()); 242 242 GpxTrack track = gpx.getTracks().iterator().next(); 243 Collection< GpxTrackSegment> segments = track.getSegments();243 Collection<IGpxTrackSegment> segments = track.getSegments(); 244 244 assertEquals(1, segments.size()); 245 245 Collection<WayPoint> trackpoints = segments.iterator().next().getWayPoints(); 246 246 assertEquals(3, trackpoints.size()); -
test/unit/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityActionTest.java
38 38 final ExtendedDialogMocker edMocker = new ExtendedDialogMocker() { 39 39 @Override 40 40 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(); 42 42 } 43 43 }; 44 44 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 ); 50 50 51 51 new ChooseTrackVisibilityAction(GpxLayerTest.getMinimalGpxLayer()).actionPerformed(null); 52 52 53 53 assertEquals(1, edMocker.getInvocationLog().size()); 54 54 Object[] invocationLogEntry = edMocker.getInvocationLog().get(0); 55 assertEquals( 1, (int) invocationLogEntry[0]);55 assertEquals(2, (int) invocationLogEntry[0]); 56 56 assertEquals("Set track visibility for Bananas", invocationLogEntry[2]); 57 57 } 58 58 } -
test/unit/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerActionTest.java
66 66 @Test 67 67 public void testFromTrack() throws Exception { 68 68 Config.getPref().put("gpx.convert-tags", "no"); 69 testFromTrack("tracks ");69 testFromTrack("tracks.gpx", "tracks.osm"); 70 70 71 71 Config.getPref().put("gpx.convert-tags", "yes"); 72 testFromTrack("tracks -ele-time");72 testFromTrack("tracks.gpx", "tracks-ele-time.osm"); 73 73 74 74 Config.getPref().put("gpx.convert-tags", "list"); 75 75 Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("ele")); 76 76 Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList("time")); 77 testFromTrack("tracks -ele");77 testFromTrack("tracks.gpx", "tracks-ele.osm"); 78 78 79 80 79 Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("time")); 81 80 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 83 96 } 84 97 85 98 private static class GenericNode { … … 106 119 } 107 120 } 108 121 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); 111 124 final DataSet osmExpected = OsmReader.parseDataSet(Files.newInputStream( 112 Paths.get(TestUtils.getTestDataRoot(), "tracks/" + expected + ".osm")), null);125 Paths.get(TestUtils.getTestDataRoot(), "tracks/" + expectedOsm)), null); 113 126 final GpxLayer layer = new GpxLayer(data); 114 127 final DataSet osm = new ConvertFromGpxLayerAction(layer).convert(); 115 //compare sorted coordinates/tags and total amount of primitives, because IDs and order will vary after reload128 //compare sorted nodes/ways, tags and total amount of primitives, because IDs and order will vary after reload 116 129 117 130 List<GenericNode> nodes = osm.getNodes().stream() 118 131 .map(GenericNode::new) … … 124 137 .sorted(Comparator.comparing(g -> g.coor.hashCode())) 125 138 .collect(Collectors.toList()); 126 139 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()); 129 155 } 130 156 131 157 /** -
test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java
6 6 import java.io.FileNotFoundException; 7 7 import java.io.IOException; 8 8 import java.util.List; 9 import java.util.Map; 9 10 import java.util.stream.Collectors; 10 11 11 12 import org.junit.Rule; … … 13 14 import org.openstreetmap.josm.TestUtils; 14 15 import org.openstreetmap.josm.data.gpx.GpxData; 15 16 import org.openstreetmap.josm.gui.layer.GpxLayer; 17 import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper.ColorMode; 16 18 import org.openstreetmap.josm.io.GpxReaderTest; 17 import org.openstreetmap.josm.spi.preferences.Config;18 19 import org.openstreetmap.josm.testutils.JOSMTestRules; 19 20 import org.openstreetmap.josm.tools.ColorHelper; 20 21 import org.xml.sax.SAXException; 21 22 23 import com.google.common.collect.ImmutableMap; 24 22 25 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 23 26 24 27 /** … … 41 44 */ 42 45 @Test 43 46 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()); 48 52 } 49 53 50 54 /** … … 55 59 */ 56 60 @Test 57 61 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()); 60 64 } 61 65 62 66 /** … … 67 71 */ 68 72 @Test 69 73 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()); 73 76 } 74 77 75 78 /** … … 80 83 */ 81 84 @Test 82 85 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()); 87 91 } 88 92 89 93 /** … … 94 98 */ 95 99 @Test 96 100 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()); 100 103 } 101 104 102 105 /** … … 107 110 */ 108 111 @Test 109 112 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()); 113 115 } 114 116 115 117 /** 116 118 * 117 119 * @param fileName the GPX filename to parse 118 * @param layer Name 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 119 121 * @param n the number of waypoints of the first track/segment to analyze 120 122 * @return the HTML color codes for the first {@code n} points 121 123 * @throws IOException if any I/O error occurs … … 122 124 * @throws FileNotFoundException if the data file is not found 123 125 * @throws SAXException if any SAX error occurs 124 126 */ 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 { 126 128 final GpxData data = GpxReaderTest.parseGpxData(fileName); 129 data.getLayerPrefs().putAll(layerPrefs); 127 130 final GpxLayer layer = new GpxLayer(data); 128 131 final GpxDrawHelper gdh = new GpxDrawHelper(layer); 129 gdh.readPreferences( layerName);132 gdh.readPreferences(); 130 133 gdh.calculateColors(); 131 134 return data.getTrackPoints().limit(n).map(p -> ColorHelper.color2html(p.customColoring)).collect(Collectors.toList()); 132 135 } -
test/unit/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarkerTest.java
10 10 import org.junit.Test; 11 11 import org.openstreetmap.josm.JOSMFixture; 12 12 import org.openstreetmap.josm.data.coor.LatLon; 13 import org.openstreetmap.josm.data.gpx.Extensions;14 import org.openstreetmap.josm.data.gpx.GpxConstants;15 13 import org.openstreetmap.josm.data.gpx.GpxData; 16 14 import org.openstreetmap.josm.data.gpx.WayPoint; 17 15 … … 45 43 assertEquals("2", marker.getText()); 46 44 WayPoint wpt = marker.convertToWayPoint(); 47 45 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()); 50 47 } 51 48 } -
test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java
3 3 4 4 import static org.junit.Assert.assertEquals; 5 5 import static org.junit.Assert.assertNotNull; 6 import static org.junit.Assert.assertNull; 6 7 import static org.junit.Assert.assertTrue; 7 8 8 import java.awt.Color;9 9 import java.util.Arrays; 10 10 11 11 import org.junit.Before; … … 16 16 import org.openstreetmap.josm.data.gpx.GpxData; 17 17 import org.openstreetmap.josm.data.gpx.GpxLink; 18 18 import org.openstreetmap.josm.data.gpx.WayPoint; 19 import org.openstreetmap.josm.data.osm.DataSet; 19 20 import org.openstreetmap.josm.gui.MainApplication; 21 import org.openstreetmap.josm.gui.MapFrame; 22 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 20 23 import org.openstreetmap.josm.spi.preferences.Config; 21 24 import org.openstreetmap.josm.testutils.JOSMTestRules; 22 25 … … 28 31 public class MarkerLayerTest { 29 32 30 33 /** 31 * Setup tests34 * For creating layers 32 35 */ 33 36 @Rule 34 37 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") … … 47 50 */ 48 51 @Test 49 52 public void testMarkerLayer() { 50 assertEquals(Color.magenta, MarkerLayer.getGenericColor());51 53 MarkerLayer layer = new MarkerLayer(new GpxData(), "foo", null, null); 52 54 MainApplication.getLayerManager().addLayer(layer); 53 55 54 56 assertEquals("foo", layer.getName()); 55 assert Equals(Color.magenta, layer.getColorProperty().get());57 assertNull(layer.getColor()); 56 58 assertNotNull(layer.getIcon()); 57 59 assertEquals("0 markers", layer.getToolTipText()); 58 60 assertEquals("<html>foo consists of 0 markers</html>", layer.getInfoComponent()); … … 61 63 GpxData gpx = new GpxData(); 62 64 WayPoint wpt = new WayPoint(LatLon.ZERO); 63 65 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"); 65 67 gpx.waypoints.add(wpt); 66 68 wpt = new WayPoint(LatLon.ZERO); 67 wpt. addExtension("offset", "NaN");69 wpt.getExtensions().add("josm", "offset", "NaN"); 68 70 gpx.waypoints.add(wpt); 69 71 layer = new MarkerLayer(gpx, "bar", null, null); 70 72 71 73 assertEquals("bar", layer.getName()); 72 assert Equals(Color.magenta, layer.getColorProperty().get());74 assertNull(layer.getColor()); 73 75 assertNotNull(layer.getIcon()); 74 76 assertEquals("3 markers", layer.getToolTipText()); 75 77 assertEquals("<html>bar consists of 3 markers</html>", layer.getInfoComponent()); 76 78 assertTrue(layer.getMenuEntries().length > 10); 77 79 } 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 } 78 100 } -
test/unit/org/openstreetmap/josm/io/GpxReaderTest.java
10 10 import java.io.IOException; 11 11 import java.nio.charset.StandardCharsets; 12 12 import java.util.ArrayList; 13 import java.util.HashMap; 14 import java.util.Map; 13 15 14 16 import org.junit.Rule; 15 17 import org.junit.Test; … … 69 71 } 70 72 71 73 /** 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 /** 72 92 * Tests invalid data. 73 93 * @throws Exception always SAXException 74 94 */ -
test/unit/org/openstreetmap/josm/io/GpxWriterTest.java
3 3 4 4 import static org.junit.Assert.assertEquals; 5 5 6 import java.awt.Color; 6 7 import java.io.ByteArrayOutputStream; 7 8 import java.io.IOException; 8 9 import java.nio.charset.StandardCharsets; … … 9 10 import java.time.LocalDate; 10 11 import java.time.Month; 11 12 import java.time.ZoneOffset; 13 import java.util.Arrays; 12 14 import java.util.Date; 15 import java.util.HashMap; 13 16 import java.util.function.Consumer; 14 17 15 18 import org.junit.Rule; … … 17 20 import org.openstreetmap.josm.data.coor.LatLon; 18 21 import org.openstreetmap.josm.data.gpx.GpxConstants; 19 22 import org.openstreetmap.josm.data.gpx.GpxData; 23 import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace; 24 import org.openstreetmap.josm.data.gpx.GpxExtensionCollection; 25 import org.openstreetmap.josm.data.gpx.GpxTrack; 26 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 20 27 import org.openstreetmap.josm.data.gpx.WayPoint; 21 28 import org.openstreetmap.josm.testutils.JOSMTestRules; 22 29 … … 87 94 " <vdop>0.9</vdop>%n" + 88 95 " <pdop>1.2</pdop>%n"); 89 96 } 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 } 90 279 } -
test/unit/org/openstreetmap/josm/io/nmea/NmeaReaderTest.java
24 24 import org.openstreetmap.josm.data.gpx.GpxConstants; 25 25 import org.openstreetmap.josm.data.gpx.GpxData; 26 26 import org.openstreetmap.josm.data.gpx.GpxTrack; 27 import org.openstreetmap.josm.data.gpx. GpxTrackSegment;27 import org.openstreetmap.josm.data.gpx.IGpxTrackSegment; 28 28 import org.openstreetmap.josm.data.gpx.WayPoint; 29 29 import org.openstreetmap.josm.io.GpxReaderTest; 30 30 import org.openstreetmap.josm.testutils.JOSMTestRules; … … 100 100 int nTracks = gpxTrack.getSegments().size(); 101 101 assertEquals(nTracks, nmeaTrack.getSegments().size()); 102 102 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(); 105 105 assertEquals(gpxSeg.getBounds(), nmeaSeg.getBounds()); 106 106 assertEquals(numCoor, gpxSeg.getWayPoints().size()); 107 107 assertEquals(numCoor, nmeaSeg.getWayPoints().size());