Changeset 15496 in josm for trunk/src/org
- Timestamp:
- 2019-11-02T15:11:34+01:00 (5 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 4 added
- 1 deleted
- 47 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/SaveAction.java
r15404 r15496 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; … … 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 … … 33 43 }; 34 44 45 private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState(); 46 35 47 /** 36 48 * Construct the action with "Save" as label. … … 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 } … … 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); 73 } 74 if (l instanceof GpxLayer) { 75 ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx); 59 76 } 60 77 super.layerAdded(e); … … 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); 85 } 86 if (l instanceof GpxLayer) { 87 ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx); 67 88 } 68 89 super.layerRemoving(e); … … 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 … … 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(), … … 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 } -
trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
r15404 r15496 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; … … 36 35 public abstract class SaveActionBase extends DiskAccessAction { 37 36 37 private boolean quiet; 38 38 39 /** 39 40 * Constructs a new {@code SaveActionBase}. … … 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 15496 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 … … 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 15496 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; … … 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 15496 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 } … … 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 … … 95 127 if (checkSaveConditions && !layer.checkSaveConditions()) 96 128 return false; 97 return doInternalSave(layer, file );98 } 99 100 private static boolean doInternalSave(Layer layer, File file ) {129 return doInternalSave(layer, file, !checkSaveConditions); 130 } 131 132 private static boolean doInternalSave(Layer layer, File file, boolean quiet) { 101 133 if (file == null) 102 134 return 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(); … … 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) { -
trunk/src/org/openstreetmap/josm/actions/SaveAsAction.java
r14397 r15496 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 } -
trunk/src/org/openstreetmap/josm/actions/ShowStatusReportAction.java
r15206 r15496 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) -
trunk/src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java
r15397 r15496 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; … … 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(); … … 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(); -
trunk/src/org/openstreetmap/josm/data/Preferences.java
r15469 r15496 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; … … 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 … … 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 */ … … 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 /** … … 416 490 settingsMap.clear(); 417 491 settingsMap.putAll(reader.getSettings()); 418 remove Obsolete(reader.getVersion());492 removeAndUpdateObsolete(reader.getVersion()); 419 493 } 420 494 … … 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(); … … 544 631 Logging.error(e1); 545 632 Logging.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile())); 546 }547 }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 633 } 560 634 } … … 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)); 769 } 853 Logging.info(tr("Removed preference setting {0} since it is no longer used", key)); 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 } 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; 770 892 } 771 893 } -
trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java
r15419 r15496 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; … … 96 99 */ 97 100 String META_BOUNDS = META_PREFIX + "bounds"; 98 /** 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) 102 */ 103 String META_EXTENSIONS = META_PREFIX + "extensions"; 104 105 /** 106 * A namespace for josm GPX extensions 107 */ 108 String JOSM_EXTENSIONS_NAMESPACE_URI = Config.getUrls().getXMLBase() + "/gpx-extensions-1.0"; 101 102 /** 103 * Namespace for the XSD 104 */ 105 String XML_URI_XSD = "http://www.w3.org/2001/XMLSchema-instance"; 106 107 /** 108 * Namespace for JOSM GPX extensions 109 */ 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"; 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"; 109 133 110 134 /** Elevation (in meters) of the point. */ … … 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 /** … … 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)); 188 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 } 164 245 165 246 /** -
trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
r15427 r15496 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; … … 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; … … 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 15496 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 */ … … 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<>(); 93 94 private final GpxTrackChangeListener proxy = e -> invalidate(); 95 private boolean modified, updating, initializing; 96 private boolean suppressedInvalidate; 69 97 70 98 /** … … 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 … … 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 /** … … 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 /** … … 108 136 109 137 private final ListenerList<GpxDataChangeListener> listeners = ListenerList.create(); 110 111 static class TimestampConfictException extends Exception {}112 138 113 139 private List<GpxTrackSegmentSpan> segSpans; … … 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)) { … … 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<>(); … … 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<>(); … … 239 265 } 240 266 if (!wpsNew.isEmpty()) { 241 segsNew.add(new ImmutableGpxTrackSegment(wpsNew));267 segsNew.add(new GpxTrackSegment(wpsNew)); 242 268 } 243 269 } else { … … 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 } … … 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 } … … 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) { … … 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) { … … 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) { … … 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 } … … 398 424 privateTracks.add(track); 399 425 track.addListener(proxy); 400 fireInvalidate();426 invalidate(); 401 427 } 402 428 … … 411 437 } 412 438 track.removeListener(proxy); 413 fireInvalidate();439 invalidate(); 414 440 } 415 441 … … 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 … … 432 458 433 459 clearTracks(); 434 addTrack(new ImmutableGpxTrack(segs, attrs));460 addTrack(new GpxTrack(segs, attrs)); 435 461 } 436 462 … … 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)); … … 539 565 } 540 566 privateRoutes.add(route); 541 fireInvalidate();567 invalidate(); 542 568 } 543 569 … … 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 … … 573 599 } 574 600 privateWaypoints.add(waypoint); 575 fireInvalidate();601 invalidate(); 576 602 } 577 603 … … 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 … … 601 627 * @see #getTracks() 602 628 * @see GpxTrack#getSegments() 603 * @see GpxTrackSegment#getWayPoints()629 * @see IGpxTrackSegment#getWayPoints() 604 630 * @since 12156 605 631 */ … … 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()) { … … 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 … … 889 915 private final boolean[] trackVisibility; 890 916 private Map<String, Object> trackAttributes; 917 private GpxTrack curTrack; 891 918 892 919 /** … … 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 } … … 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 15496 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 15496 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()); … … 973 1021 return true; 974 1022 if (obj == null) 1023 return false; 1024 if (!super.equals(obj)) 975 1025 return false; 976 1026 if (getClass() != obj.getClass()) … … 981 1031 return false; 982 1032 } else if (!dataSources.equals(other.dataSources)) 1033 return false; 1034 if (layerPrefs == null) { 1035 if (other.layerPrefs != null) 1036 return false; 1037 } else if (!layerPrefs.equals(other.layerPrefs)) 983 1038 return false; 984 1039 if (privateRoutes == null) { … … 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; 1060 } 1061 1062 @Override 1063 public void put(String key, Object value) { 1064 super.put(key, value); 1065 invalidate(); 1000 1066 } 1001 1067 … … 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 } 1112 } 1113 } 1114 1115 /** 1116 * Begins updating this GpxData and prevents listeners from being fired. 1117 * @since 15496 1118 */ 1119 public void beginUpdate() { 1120 updating = true; 1121 } 1122 1123 /** 1124 * Finishes updating this GpxData and fires listeners if required. 1125 * @since 15496 1126 */ 1127 public void endUpdate() { 1128 boolean setModified = updating; 1129 updating = initializing = false; 1130 if (suppressedInvalidate) { 1131 fireInvalidate(setModified); 1132 suppressedInvalidate = false; 1032 1133 } 1033 1134 } … … 1068 1169 } 1069 1170 } 1171 1172 /** 1173 * @return whether anything has been modified (e.g. colors) 1174 * @since 15496 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 15496 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 15496 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 } -
trunk/src/org/openstreetmap/josm/data/gpx/GpxImageCorrelation.java
r14797 r15496 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()) { -
trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
r12289 r15496 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 15496 12 22 */ 13 public interface GpxTrack extends IWithAttributes { 14 15 /** 16 * Returns the track segments. 17 * @return the track segments 18 */ 19 Collection<GpxTrackSegment> getSegments(); 20 21 /** 22 * Returns the track attributes. 23 * @return the track attributes 24 */ 25 Map<String, Object> getAttributes(); 26 27 /** 28 * Returns the track bounds. 29 * @return the track bounds 30 */ 31 Bounds getBounds(); 32 33 /** 34 * Returns the track length. 35 * @return the track length 36 */ 37 double length(); 38 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 45 } 46 47 /** 48 * Remove a listener that listens to changes in the GPX track. 49 * @param l The listener 50 */ 51 default void removeListener(GpxTrackChangeListener l) { 52 // nop 23 public class GpxTrack extends WithAttributes implements IGpxTrack { 24 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; 32 33 /** 34 * Constructs a new {@code GpxTrack}. 35 * @param trackSegs track segments 36 * @param attributes track attributes 37 */ 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 } 50 51 /** 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. 58 */ 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 } 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 141 /** 142 * Converts the color to the given format, if present. 143 * @param cFormat can be a {@link GpxConstants.ColorFormat} 144 */ 145 public void convertColor(ColorFormat cFormat) { 146 Color c = getColor(); 147 if (c == null) return; 148 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 } 178 } 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 246 /** 247 * Resets the color cache 248 */ 249 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 Zangl58 * @since 1215659 */255 * @deprecated use {@link IGpxTrack.GpxTrackChangeListener} instead 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 } … … 69 263 /** 70 264 * A track change event for the current track. 71 * @author Michael Zangl 72 * @since 12156 73 */ 74 class GpxTrackChangeEvent { 75 private final GpxTrack source; 76 77 GpxTrackChangeEvent(GpxTrack source) { 78 super(); 79 this.source = source; 80 } 81 82 /** 83 * Get the track that was changed. 84 * @return The track. 85 */ 86 public GpxTrack getSource() { 87 return source; 88 } 89 } 265 * @deprecated use {@link IGpxTrack.GpxTrackChangeEvent} instead 266 */ 267 @Deprecated 268 static class GpxTrackChangeEvent extends IGpxTrack.GpxTrackChangeEvent { 269 GpxTrackChangeEvent(IGpxTrack source) { 270 super(source); 271 } 272 } 273 90 274 } -
trunk/src/org/openstreetmap/josm/data/gpx/GpxTrackSegment.java
r9949 r15496 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 15496 11 14 */ 12 public interface GpxTrackSegment { 15 public class GpxTrackSegment extends WithAttributes implements IGpxTrackSegment { 16 17 private final List<WayPoint> wayPoints; 18 private final Bounds bounds; 19 private final double length; 13 20 14 21 /** 15 * Returns the segment bounds.16 * @ return the segment bounds22 * Constructs a new {@code GpxTrackSegment}. 23 * @param wayPoints list of waypoints 17 24 */ 18 Bounds getBounds(); 25 public GpxTrackSegment(Collection<WayPoint> wayPoints) { 26 this.wayPoints = Collections.unmodifiableList(new ArrayList<>(wayPoints)); 27 this.bounds = calculateBounds(); 28 this.length = calculateLength(); 29 } 19 30 20 /** 21 * Returns the segment waypoints. 22 * @return the segment waypoints 23 */ 24 Collection<WayPoint> getWayPoints(); 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 } 25 42 26 /** 27 * Returns the segment length. 28 * @return the segment length 29 */ 30 double length(); 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 } 31 57 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(); 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 } -
trunk/src/org/openstreetmap/josm/data/gpx/IWithAttributes.java
r9246 r15496 3 3 4 4 import java.util.Collection; 5 import java.util.Map; 5 6 6 7 /** … … 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(); 58 59 /** 60 * Returns the extensions 61 * @return the extensions 62 */ 63 GpxExtensionCollection getExtensions(); 59 64 60 65 } -
trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java
r13210 r15496 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 { 18 19 private final List<GpxTrackSegment> segments; 20 private final double length; 21 private final Bounds bounds; 13 @Deprecated 14 public class ImmutableGpxTrack extends GpxTrack { 22 15 23 16 /** … … 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 … … 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(); 55 } 56 57 private double calculateLength() { 58 double result = 0.0; // in meters 59 60 for (GpxTrackSegment trkseg : segments) { 61 result += trkseg.length(); 62 } 63 return result; 64 } 65 66 private Bounds calculateBounds() { 67 Bounds result = null; 68 for (GpxTrackSegment segment: segments) { 69 Bounds segBounds = segment.getBounds(); 70 if (segBounds != null) { 71 if (result == null) { 72 result = new Bounds(segBounds); 73 } else { 74 result.extend(segBounds); 75 } 76 } 77 } 78 return result; 79 } 80 81 @Override 82 public Map<String, Object> getAttributes() { 83 return attr; 84 } 85 86 @Override 87 public Bounds getBounds() { 88 return bounds == null ? null : new Bounds(bounds); 89 } 90 91 @Override 92 public double length() { 93 return length; 94 } 95 96 @Override 97 public Collection<GpxTrackSegment> getSegments() { 98 return segments; 99 } 100 101 @Override 102 public int hashCode() { 103 return 31 * super.hashCode() + ((segments == null) ? 0 : segments.hashCode()); 104 } 105 106 @Override 107 public boolean equals(Object obj) { 108 if (this == obj) 109 return true; 110 if (!super.equals(obj)) 111 return false; 112 if (getClass() != obj.getClass()) 113 return false; 114 ImmutableGpxTrack other = (ImmutableGpxTrack) obj; 115 if (segments == null) { 116 if (other.segments != null) 117 return false; 118 } else if (!segments.equals(other.segments)) 119 return false; 120 return true; 34 public ImmutableGpxTrack(List<IGpxTrackSegment> segments, Map<String, Object> attributes) { 35 super(segments, attributes); 121 36 } 122 37 } -
trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java
r12186 r15496 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 9 import org.openstreetmap.josm.data.Bounds;10 5 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 { 15 16 private final List<WayPoint> wayPoints; 17 private final Bounds bounds; 18 private final double length; 10 @Deprecated 11 public class ImmutableGpxTrackSegment extends GpxTrackSegment { 19 12 20 13 /** … … 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(); 28 } 29 30 private Bounds calculateBounds() { 31 Bounds result = null; 32 for (WayPoint wpt: wayPoints) { 33 if (result == null) { 34 result = new Bounds(wpt.getCoor()); 35 } else { 36 result.extend(wpt.getCoor()); 37 } 38 } 39 return result; 40 } 41 42 private double calculateLength() { 43 double result = 0.0; // in meters 44 WayPoint last = null; 45 for (WayPoint tpt : wayPoints) { 46 if (last != null) { 47 Double d = last.getCoor().greatCircleDistance(tpt.getCoor()); 48 if (!d.isNaN() && !d.isInfinite()) { 49 result += d; 50 } 51 } 52 last = tpt; 53 } 54 return result; 55 } 56 57 @Override 58 public Bounds getBounds() { 59 return bounds == null ? null : new Bounds(bounds); 60 } 61 62 @Override 63 public Collection<WayPoint> getWayPoints() { 64 return Collections.unmodifiableList(wayPoints); 65 } 66 67 @Override 68 public double length() { 69 return length; 70 } 71 72 @Override 73 public int getUpdateCount() { 74 return 0; 75 } 76 77 @Override 78 public int hashCode() { 79 return 31 + ((wayPoints == null) ? 0 : wayPoints.hashCode()); 80 } 81 82 @Override 83 public boolean equals(Object obj) { 84 if (this == obj) 85 return true; 86 if (obj == null) 87 return false; 88 if (getClass() != obj.getClass()) 89 return false; 90 ImmutableGpxTrackSegment other = (ImmutableGpxTrackSegment) obj; 91 if (wayPoints == null) { 92 if (other.wayPoints != null) 93 return false; 94 } else if (!wayPoints.equals(other.wayPoints)) 95 return false; 96 return true; 18 super(wayPoints); 97 19 } 98 20 } -
trunk/src/org/openstreetmap/josm/data/gpx/Line.java
r14451 r15496 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.awt.Color; 4 5 import java.util.Collection; 5 6 import java.util.Iterator; … … 14 15 private final Collection<WayPoint> waypoints; 15 16 private final boolean unordered; 17 private final Color color; 16 18 17 19 /** … … 19 21 * @param waypoints collection of waypoints 20 22 * @param attributes track/route attributes 23 * @param color color of the track 24 * @since 15496 21 25 */ 22 public Line(Collection<WayPoint> waypoints, Map<String, Object> attributes) { 26 public Line(Collection<WayPoint> waypoints, Map<String, Object> attributes, Color color) { 27 this.color = color; 23 28 this.waypoints = Objects.requireNonNull(waypoints); 24 29 unordered = attributes.isEmpty() && waypoints.stream().allMatch(x -> x.get(GpxConstants.PT_TIME) == null); … … 29 34 * @param trackSegment track segment 30 35 * @param trackAttributes track attributes 36 * @param color color of the track 37 * @since 15496 31 38 */ 32 public Line( GpxTrackSegment trackSegment, Map<String, Object> trackAttributes) {33 this(trackSegment.getWayPoints(), trackAttributes );39 public Line(IGpxTrackSegment trackSegment, Map<String, Object> trackAttributes, Color color) { 40 this(trackSegment.getWayPoints(), trackAttributes, color); 34 41 } 35 42 … … 39 46 */ 40 47 public Line(GpxRoute route) { 41 this(route.routePoints, route.attr );48 this(route.routePoints, route.attr, null); 42 49 } 43 50 … … 48 55 public boolean isUnordered() { 49 56 return unordered; 57 } 58 59 /** 60 * Returns the track/route color 61 * @return the color 62 * @since 15496 63 */ 64 public Color getColor() { 65 return color; 50 66 } 51 67 -
trunk/src/org/openstreetmap/josm/data/gpx/WithAttributes.java
r10906 r15496 5 5 import java.util.HashMap; 6 6 import java.util.Map; 7 import java.util.Objects; 7 8 8 9 /** … … 20 21 */ 21 22 public Map<String, Object> attr = new HashMap<>(0); 23 24 /** 25 * The "exts" collection contains all extensions. 26 */ 27 private final GpxExtensionCollection exts = new GpxExtensionCollection(this); 22 28 23 29 /** … … 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 * … … 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; 86 } 87 88 @Override 89 public GpxExtensionCollection getExtensions() { 90 return exts; 91 91 } 92 92 93 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 … … 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 } -
trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
r15418 r15496 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; … … 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 */ … … 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 -
trunk/src/org/openstreetmap/josm/data/preferences/NamedColorProperty.java
r13543 r15496 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; … … 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 -
trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
r15457 r15496 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; … … 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 … … 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; … … 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()); -
trunk/src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java
r14387 r15496 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 } … … 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)); … … 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()); -
trunk/src/org/openstreetmap/josm/gui/io/importexport/FileExporter.java
r14950 r15496 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 15496 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 -
trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java
r14950 r15496 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; … … 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)) … … 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. … … 147 168 JosmTextField keywords = new JosmTextField(); 148 169 keywords.setText(gpxData.getString(META_KEYWORDS)); 149 p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL)); 170 p.add(keywords, GBC.eol().fill(GBC.HORIZONTAL)); 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)); 150 185 151 186 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), … … 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); … … 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); 177 } 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()); 227 } 228 ColorFormat cFormat = null; 229 if (colors.isSelected()) { 230 cFormat = garmin.isSelected() ? ColorFormat.GPXX : ColorFormat.GPXD; 231 } 232 233 gpxData = getGpxData(layer, file); 178 234 179 235 // add author and copyright details to the gpx data … … 204 260 } 205 261 206 try (OutputStream fo = Compression.getCompressedFileOutputStream(file); GpxWriter writer = new GpxWriter(fo)) { 207 writer.write(gpxData); 208 } 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(); 267 } 268 } 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); 209 277 } 210 278 -
trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java
r14761 r15496 150 150 if (markerLayer.data.isEmpty()) { 151 151 markerLayer = null; 152 } else { 153 gpxLayer.setLinkedMarkerLayer(markerLayer); 152 154 } 153 155 } -
trunk/src/org/openstreetmap/josm/gui/layer/CustomizeColor.java
r14153 r15496 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 … … 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 /** … … 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 } … … 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 … … 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")}; … … 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 } -
trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
r15421 r15496 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; … … 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; … … 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; … … 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; … … 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 … … 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 /** … … 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 123 } 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; 113 138 } 114 139 … … 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 } … … 340 372 public void setAssociatedFile(File file) { 341 373 data.storageFile = file; 374 } 375 376 /** 377 * @return the linked MarkerLayer (imported from the same file) 378 * @since 15496 379 */ 380 public MarkerLayer getLinkedMarkerLayer() { 381 return linkedMarkerLayer; 382 } 383 384 /** 385 * @param linkedMarkerLayer the linked MarkerLayer 386 * @since 15496 387 */ 388 public void setLinkedMarkerLayer(MarkerLayer linkedMarkerLayer) { 389 this.linkedMarkerLayer = linkedMarkerLayer; 342 390 } 343 391 … … 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 -
trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
r15371 r15496 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; … … 165 162 private File associatedFile; 166 163 167 private final ValueChangeListener<Object> invalidateListener = change -> invalidate();168 164 private boolean isDestroyed; 169 165 … … 199 195 200 196 /** 201 * Gets the color property to use for this layer. 202 * @return The color property. 203 * @since 10824 204 */ 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 } 212 } 213 214 /** 215 * Gets the color property that stores the default color for this layer. 216 * @return The property or <code>null</code> if this layer is not colored. 217 * @since 10824 218 */ 219 protected NamedColorProperty getBaseColorProperty() { 197 * @return whether the layer has / can handle colors. 198 * @since 15496 199 */ 200 public boolean hasColor() { 201 return false; 202 } 203 204 /** 205 * Return the current color of the layer 206 * @return null when not present or not supported 207 * @since 15496 208 */ 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 } 228 } 229 230 private void removeColorPropertyListener() { 231 AbstractProperty<Color> colorProperty = getColorProperty(); 232 if (colorProperty != null) { 233 colorProperty.removeListener(invalidateListener); 234 } 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 15496 217 */ 218 public void setColor(Color color) { 235 219 } 236 220 … … 303 287 isDestroyed = true; 304 288 // Override in subclasses if needed 305 removeColorPropertyListener();306 289 } 307 290 … … 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(""); … … 348 328 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name); 349 329 } 350 351 // re-add listener352 addColorPropertyListener();353 330 invalidate(); 354 331 } … … 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 } -
trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
r15422 r15496 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; … … 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; … … 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<>(); … … 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); 786 } 787 788 List<WayPoint> trkseg = null; 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 } 814 } 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 }794 if (trkseg == null) {795 trkseg = new ArrayList<>();796 trk.add(trkseg);797 823 } 798 824 if (!n.isTagged() || containsOnlyGpxTags(n)) { … … 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 } -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
r15247 r15496 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; … … 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()) { -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java
r14337 r15496 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; … … 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; … … 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; … … 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; … … 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; … … 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 } … … 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 … … 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; … … 145 218 @Override 146 219 public boolean isCellEditable(int rowIndex, int colIndex) { 147 return false; 220 return colIndex <= 1; 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 } 148 242 } 149 243 }; … … 181 275 t.addMouseListener(urlOpener); 182 276 t.setFillsViewportHeight(true); 277 t.putClientProperty("terminateEditOnFocusLost", true); 183 278 return t; 184 279 } … … 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(); … … 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); … … 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); … … 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 } -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java
r15419 r15496 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 … … 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; … … 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 … … 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); … … 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 } … … 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} … … 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; 181 } 182 if (listPos == null || !listPos.contains(listkey)) { 183 p.put(key, null); 138 184 } 139 185 } -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/CustomizeDrawingAction.java
r14153 r15496 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; … … 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 … … 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)); … … 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 -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java
r15363 r15496 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; … … 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()) { -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackAction.java
r14427 r15496 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; … … 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()); -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
r15292 r15496 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 … … 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; … … 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; … … 66 67 67 68 /** 68 * The colorthat is used for drawing GPX points.69 * @since 1 082470 */ 71 public static final NamedColorProperty DEFAULT_COLOR = new NamedColorProperty(marktr("gps point"), Color.magenta);69 * The default color property that is used for drawing GPX points. 70 * @since 15496 71 */ 72 public static final NamedColorProperty DEFAULT_COLOR_PROPERTY = new NamedColorProperty(marktr("gps point"), Color.magenta); 72 73 73 74 private final GpxData data; … … 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; … … 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 … … 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; … … 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; … … 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; … … 269 271 } 270 272 271 private static String specName(String layerName) {272 return "layer " + layerName;273 }274 275 /**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 273 /** 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) { … … 305 286 } 306 287 307 /** Reads generic color from preferences (usually gray) 308 * @return the color 288 private String opt(String key) { 289 return GPXSettingsPanel.getLayerPref(layer, key); 290 } 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 300 /** 301 * Read all drawing-related settings from preferences 309 302 **/ 310 public static Color getGenericColor() { 311 return DEFAULT_COLOR.get(); 312 } 313 314 /** 315 * Read all drawing-related settings from preferences 316 * @param layerName layer name used to access its specific preferences 317 **/ 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); 324 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"); 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); 331 } 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); 316 maxLineLength = optInt("lines.max-length"); 317 lines = l == 2; //draw only for 2 (all) 318 } 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); … … 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) { … … 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 … … 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 … … 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); … … 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; … … 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) { … … 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 … … 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; … … 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 … … 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 … … 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); 799 800 g.setColor(customColoringTransparent); 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); 784 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); … … 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); … … 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 ) { … … 1494 1483 computeCacheColorUsed = neutralColor; 1495 1484 computeCacheColored = colored; 1496 computeCache ColorTracksTune = colorTracksTune;1485 computeCacheVelocityTune = velocityTune; 1497 1486 computeCacheColorDynamic = colorModeDynamic; 1498 1487 computeCacheHeatMapDrawColorTableIdx = heatMapDrawColorTableIdx; -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportAudioAction.java
r14456 r15496 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; … … 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(); … … 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)) { … … 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()) { … … 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()); -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java
r13419 r15496 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 … … 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 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/DefaultMarkerProducers.java
r12620 r15496 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; … … 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); -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
r14823 r15496 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; … … 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; … … 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 … … 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 … … 245 174 this.dataProvider = dataProvider; 246 175 this.text = text; 176 177 Preferences.main().addKeyPreferenceChangeListener("draw.rawgps." + getTextTemplateKey(), l -> updateText()); 247 178 } 248 179 … … 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()) { … … 373 304 } 374 305 375 protected TemplateEntryProperty getTextTemplate() { 376 return TemplateEntryProperty.forMarker(parentLayer.getName()); 306 protected String getTextTemplateKey() { 307 return "markers.pattern"; 308 } 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; 377 326 } 378 327 … … 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); 392 393 cachedText = sb.toString(); 394 textVersion = property.getUpdateCount(); 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 } 395 352 } 396 return cachedText; 397 } 353 StringBuilder sb = new StringBuilder(); 354 template.appendText(sb, this); 355 cachedText = sb.toString(); 356 357 } 358 return cachedText; 359 } 360 361 /** 362 * Called when the template changes 363 */ 364 public void updateText() { 365 cachedText = null; 366 cachedDefaultTemplate = null; 367 cachedTemplates = new HashMap<>(); 398 368 } 399 369 -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
r14456 r15496 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; … … 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; … … 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; … … 76 78 private Marker currentMarker; 77 79 public AudioMarker syncAudioMarker; 78 79 private static final Color DEFAULT_COLOR = Color.magenta; 80 private static final NamedColorProperty COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), DEFAULT_COLOR); 80 private Color color, realcolor; 81 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 /** … … 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 */ … … 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); … … 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; … … 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")); 459 } 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()); 455 487 } 456 488 … … 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 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
r14456 r15496 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; … … 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()) { -
trunk/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java
r15121 r15496 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) … … 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 } … … 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 … … 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(); -
trunk/src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java
r15247 r15496 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; … … 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; … … 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")}); … … 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 99 100 /** 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 105 */ 106 public GPXSettingsPanel(String layerName, boolean local, boolean nonlocal) { 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 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 139 /** 140 * Constructs a new {@code GPXSettingsPanel} for the given layers. 141 * @param layers the GPX layers 142 */ 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(); … … 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 167 } 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); 124 295 } 125 296 … … 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 } … … 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 } … … 243 416 // colorTracks 244 417 ButtonGroup colorGroup = new ButtonGroup(); 245 if ( layerName != null) {418 if (!global) { 246 419 colorGroup.add(colorTypeGlobal); 247 420 } … … 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.")); … … 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 } … … 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 }); … … 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 … … 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); … … 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); … … 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 } 411 } 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)); 574 Logging.warn("Unknown line type: " + lines); 575 } 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); … … 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; … … 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)); 468 } 469 } 470 471 /** 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 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")); 615 } 616 updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, pref("markers.pattern")); 617 updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, pref("markers.audio.pattern")); 618 619 } 620 621 /** 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 = ""; 481 } 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); 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()); 630 } 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()); 507 } 508 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()); 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()); 656 } 657 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);538 } 539 Config.getPref().putBoolean("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected());683 putPref("colormode", 0); 684 } 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()); 692 693 if (!global && layers != null && !layers.isEmpty()) { 694 layers.forEach(l -> l.data.invalidate()); 695 } 546 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++) { -
trunk/src/org/openstreetmap/josm/io/GpxReader.java
r14456 r15496 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; … … 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; … … 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<>(); … … 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(); 98 } 99 100 @Override 101 public void startPrefixMapping(String prefix, String uri) throws SAXException { 102 data.getNamespaces().add(new XMLNamespace(prefix, uri)); 92 103 } 93 104 … … 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: … … 160 182 states.push(currentState); 161 183 currentState = State.EXT; 162 currentExtensions = new Extensions();163 184 break; 164 185 case "gpx": … … 179 200 states.push(currentState); 180 201 currentState = State.EXT; 181 currentExtensions = new Extensions();182 202 break; 183 203 case "copyright": … … 229 249 states.push(currentState); 230 250 currentState = State.EXT; 231 currentExtensions = new Extensions();232 251 break; 233 252 default: // Do nothing … … 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; … … 251 276 states.push(currentState); 252 277 currentState = State.EXT; 253 currentExtensions = new Extensions();254 278 break; 255 279 default: // Do nothing … … 271 295 states.push(currentState); 272 296 currentState = State.EXT; 273 currentExtensions = new Extensions(); 274 break; 275 default: // Do nothing 297 break; 298 default: // Do nothing 299 } 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); 276 306 } 277 307 break; … … 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 } … … 360 389 break; 361 390 default: 362 //TODO: parse extensions363 391 } 364 392 break; … … 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 … … 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; … … 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": … … 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; … … 520 559 } 521 560 } 561 accumulator.setLength(0); 522 562 } 523 563 … … 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 } -
trunk/src/org/openstreetmap/josm/io/GpxWriter.java
r14459 r15496 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; … … 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; … … 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; … … 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; 81 } 82 } 83 } 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()); 84 126 85 127 out.println("<?xml version='1.0' encoding='UTF-8'?>"); 86 128 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\">"); 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(); 137 } 138 } 139 } 140 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(); … … 105 158 gpxLink(link); 106 159 } 107 }108 } else if (META_EXTENSIONS.equals(key)) {109 Extensions extensions = (Extensions) obj.get(key);110 if (extensions != null) {111 gpxExtensions(extensions);112 160 } 113 161 } else { … … 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 } … … 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)); … … 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 } … … 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); … … 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); … … 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 + '>'); … … 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 … … 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 … … 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); … … 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 } … … 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 } -
trunk/src/org/openstreetmap/josm/io/nmea/NmeaReader.java
r15247 r15496 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; … … 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) { -
trunk/src/org/openstreetmap/josm/io/rtklib/RtkLibPosReader.java
r15343 r15496 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; … … 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 } -
trunk/src/org/openstreetmap/josm/io/session/GenericSessionExporter.java
r15404 r15496 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 }
Note:
See TracChangeset
for help on using the changeset viewer.