Ticket #16796: GpxColors-v0.16.diff
| File GpxColors-v0.16.diff, 159.6 KB (added by , 7 years ago) |
|---|
-
data/gpx-drawing-extensions-1.0.xsd
1 <?xml version="1.0" encoding="UTF-8"?> 2 <schema targetNamespace="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0" 3 elementFormDefault="qualified" 4 xmlns="http://www.w3.org/2001/XMLSchema" 5 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 6 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 7 xmlns:gpxd="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0" 8 xsi:schemaLocation="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0 https://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd"> 9 10 <xsd:annotation> 11 <xsd:documentation> 12 This schema defines drawing extensions for the GPX 1.1 schema (http://www.topografix.com/GPX/1/1/gpx.xsd). 13 Elements in this schema should be used as child elements of the "extensions" element defined by the GPX schema. 14 </xsd:documentation> 15 </xsd:annotation> 16 17 <!-- Elements --> 18 19 <xsd:element name="color" type="gpxd:hexColor_type"> 20 <xsd:annotation> 21 <xsd:documentation> 22 The color of the element, i.e. #RRGGBB or #RRGGBBAA. 23 Note that applications should apply possible alpha values to the lines and opacity to the whole track. This means that overlapping parts of the 24 track with alpha values will look more intense than individual lines, whereas the opacity affects the whole track including overlapping parts. 25 </xsd:documentation> 26 </xsd:annotation> 27 </xsd:element> 28 29 <xsd:element name="opacity" type="gpxd:opacity_type"> 30 <xsd:annotation> 31 <xsd:documentation> 32 The opacity of the element between 0.00 and 1.00. 33 </xsd:documentation> 34 </xsd:annotation> 35 </xsd:element> 36 37 <xsd:element name="width" type="xsd:positiveInteger"> 38 <xsd:annotation> 39 <xsd:documentation> 40 The width of the line in pixels, applications may use a width relative to this value if required. 41 </xsd:documentation> 42 </xsd:annotation> 43 </xsd:element> 44 45 <xsd:element name="dashPattern" type="gpxd:dashPattern_type"> 46 <xsd:annotation> 47 <xsd:documentation> 48 The dash pattern of the line, see gpxd:dashPattern_type. Should always be relative to the width. 49 </xsd:documentation> 50 </xsd:annotation> 51 </xsd:element> 52 53 <!-- Types --> 54 55 <xsd:simpleType name="hexColor_type"> 56 <xsd:annotation> 57 <xsd:documentation> 58 The hexColor_type must be a # followed by a 6 or 8-digit hex representation of the color (with or without the alpha value). 59 </xsd:documentation> 60 </xsd:annotation> 61 <xsd:restriction base="xsd:string"> 62 <xsd:pattern value="\#([a-fA-F0-9]{6}|[a-fA-F0-9]{8})" /> 63 <xsd:whiteSpace value="collapse" /> 64 </xsd:restriction> 65 </xsd:simpleType> 66 67 <xsd:simpleType name="opacity_type"> 68 <xsd:annotation> 69 <xsd:documentation> 70 The opacity_type must be a decimal value between 0 and 1. 71 </xsd:documentation> 72 </xsd:annotation> 73 <xsd:restriction base="xsd:decimal"> 74 <xsd:minInclusive value="0" /> 75 <xsd:maxInclusive value="1" /> 76 </xsd:restriction> 77 </xsd:simpleType> 78 79 <xsd:simpleType name="dashPattern_type"> 80 <xsd:annotation> 81 <xsd:documentation> 82 The dashPattern_type can be 83 - a representation of the pattern as y-n-y-n-... with y being the relative length of the line that is 84 visible and n being the relative length of the line that is hidden to create a dashed / dotted line. 85 Has to have an even number of segments (at least two) and can contain multi-digit numbers. 86 - one of the following predefined values: 87 none, dash-long, dash-medium, dash-short, dot-sparse, dot-normal, dot-dense, dash-dot, dash-dot-dot 88 </xsd:documentation> 89 </xsd:annotation> 90 <xsd:restriction base="xsd:string"> <!-- use string based pattern instead of enum because both pattern and enums are allowed --> 91 <xsd:pattern value="\d+\-\d+(\-\d+\-\d+)*" /> <!-- pattern, see documentation above --> 92 <xsd:pattern value="none" /> <!-- 1-0, default value/line --> 93 <xsd:pattern value="dash-long" /> <!-- 6-2 --> 94 <xsd:pattern value="dash-medium" /> <!-- 4-4 --> 95 <xsd:pattern value="dash-short" /> <!-- 2-6 --> 96 <xsd:pattern value="dot-sparse" /> <!-- 1-4 --> 97 <xsd:pattern value="dot-normal" /> <!-- 1-2 --> 98 <xsd:pattern value="dot-dense" /> <!-- 1-1 --> 99 <xsd:pattern value="dash-dot" /> <!-- 4-2-1-2 --> 100 <xsd:pattern value="dash-dot-dot" /> <!-- 4-2-1-2-1-2 --> 101 </xsd:restriction> 102 </xsd:simpleType> 103 104 </schema> 105 No newline at end of file -
data/gpx-extensions-1.1.xsd
1 <?xml version="1.0" encoding="UTF-8"?> 2 <schema targetNamespace="http://josm.openstreetmap.de/gpx-extensions-1.1" 3 elementFormDefault="qualified" 4 xmlns="http://www.w3.org/2001/XMLSchema" 5 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 6 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 7 xmlns:josm="http://josm.openstreetmap.de/gpx-extensions-1.1" 8 xsi:schemaLocation="http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd"> 9 10 <!-- true, if gpx data has been downloaded from the osm server --> 11 <!-- it this case, JOSM improves the rendering of clouds of anonymous TrackPoints --> 12 <element name="from-server" type="boolean"/> 13 14 <!-- the following properties are only set for marker layer export --> 15 <element name="offset" type="decimal"/> 16 <element name="sync-offset" type="decimal"/> 17 <element name="text" type="string" /> 18 19 <xsd:element name="layerPreferences" type="josm:preferences_type"> 20 <xsd:annotation> 21 <xsd:documentation> 22 The layerPreferences contain the preferences that can be set for the layer, e.g. in the "Customize track drawing" dialog in JOSM. 23 </xsd:documentation> 24 </xsd:annotation> 25 </xsd:element> 26 27 <xsd:complexType name="preferences_type"> 28 <xsd:sequence> 29 <xsd:element name="entry" type="josm:entry_type" minOccurs="0" /> 30 </xsd:sequence> 31 </xsd:complexType> 32 33 <xsd:complexType name="entry_type"> 34 <xsd:attribute name="key" type="xsd:string" use="required" /> 35 <xsd:attribute name="value" type="xsd:string" use="required" /> 36 </xsd:complexType> 37 38 39 </schema> 40 No newline at end of file -
src/org/openstreetmap/josm/actions/ShowStatusReportAction.java
293 293 text.append(reportHeader); 294 294 295 295 Preferences.main().getAllSettings().forEach((key, setting) -> { 296 if (key.startsWith("marker.show") 297 || "file-open.history".equals(key) 296 if ("file-open.history".equals(key) 298 297 || "download.overpass.query".equals(key) 299 298 || "download.overpass.queries".equals(key) 300 299 || key.contains("username") -
src/org/openstreetmap/josm/data/gpx/Extensions.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.util. LinkedHashMap;4 import java.util.TreeMap; 5 5 6 import org.apache.commons.jcs.access.exception.InvalidArgumentException; 7 6 8 /** 7 * Data class for extensions in a GPX-File.9 * Data class for extensions of a specific type in a GPX-File (keys are case-insensitive). 8 10 */ 9 public class Extensions extends LinkedHashMap<String, String> {11 public final class Extensions extends TreeMap<String, String> { 10 12 11 13 private static final long serialVersionUID = 1L; 14 private String type; 12 15 13 16 /** 14 17 * Constructs a new {@code Extensions}. 18 * @param extensionType type of the extension, can be any <code>GpxConstants.EXTENSIONS_*</code> 15 19 */ 16 public Extensions() { 17 super(); 20 public Extensions(String extensionType) { 21 super(String.CASE_INSENSITIVE_ORDER); 22 if (!extensionType.startsWith(GpxConstants.EXTENSIONS_PREFIX)) 23 throw new InvalidExtensionException(); 24 type = extensionType; 18 25 } 26 27 /** 28 * Returns the prefix for the XML namespace. 29 * @return the prefix including the <code>:</code> 30 */ 31 public String getPrefix() { 32 return type.substring(type.lastIndexOf(".") + 1) + ":"; 33 } 34 35 /** 36 * @return type of the extension 37 */ 38 public String getType() { 39 return type; 40 } 41 42 /** 43 * InvalidExtensionException is thrown if the Extension value does not match the expected format 44 * @see GpxConstants#EXTENSIONS_PREFIX 45 */ 46 public static class InvalidExtensionException extends InvalidArgumentException { 47 /** 48 * Constructs a new {@link InvalidExtensionException} with a default error message 49 */ 50 public InvalidExtensionException() { 51 super("The extensionType must start with the extensions prefix."); 52 } 53 /** 54 * Constructs a new {@link InvalidExtensionException} with the given error message 55 * @param message 56 */ 57 public InvalidExtensionException(String message) { 58 super(message); 59 } 60 } 61 19 62 } -
src/org/openstreetmap/josm/data/gpx/GpxConstants.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.awt.Color; 4 5 import java.util.Arrays; 5 6 import java.util.Collection; 6 7 import java.util.Collections; 7 8 import java.util.List; 9 import java.util.Map; 10 import java.util.TreeMap; 8 11 9 12 import org.openstreetmap.josm.data.Bounds; 10 13 import org.openstreetmap.josm.spi.preferences.Config; … … 95 98 * @see GpxData#getMetaBounds() 96 99 */ 97 100 String META_BOUNDS = META_PREFIX + "bounds"; 101 98 102 /** 99 * A constant for the metadata hash map: the extension data. This is a {@link Extensions} object 100 * @see GpxData#addExtension(String, String) 103 * Prefix used for all extension values. 104 * Note that all extension values <b>must</b> end with the according XML namespace prefix (i.e <code>.josm</code> or <code>.gpxd</code>) 105 */ 106 String EXTENSIONS_PREFIX = "extensions."; 107 108 /** 109 * The JOSM extension data (josm:*). This is a {@link Extensions} object 110 * @see GpxData#addExtensionKey(String, String, String) 101 111 * @see GpxData#get(String) 102 112 */ 103 String META_EXTENSIONS = META_PREFIX + "extensions";113 String EXTENSIONS_JOSM = EXTENSIONS_PREFIX + "josm"; 104 114 105 115 /** 106 * A namespace for josm GPX extensions116 * The GPX drawing extension data (gpxd:*). This is a {@link Extensions} object 107 117 */ 108 String JOSM_EXTENSIONS_NAMESPACE_URI = Config.getUrls().getXMLBase() + "/gpx-extensions-1.0";118 String EXTENSIONS_DRAWING = EXTENSIONS_PREFIX + "gpxd"; 109 119 120 /** 121 * The Garmin GPX extension data (gpxx:*). This is a {@link Extensions} object 122 */ 123 String EXTENSIONS_GARMIN = EXTENSIONS_PREFIX + "gpxx"; 124 125 /** 126 * Namespace for JOSM GPX extensions 127 */ 128 String XML_URI_EXTENSIONS_JOSM = Config.getUrls().getXMLBase() + "/gpx-extensions-1.1"; 129 /** 130 * Location of the XSD schema for JOSM GPX extensions 131 */ 132 String XML_XSD_EXTENSIONS_JOSM = Config.getUrls().getXMLBase() + "/gpx-extensions-1.1.xsd"; 133 134 /** 135 * Namespace for GPX drawing extensions 136 */ 137 String XML_URI_EXTENSIONS_DRAWING = Config.getUrls().getXMLBase() + "/gpx-drawing-extensions-1.0"; 138 /** 139 * Location of the XSD schema for GPX drawing extensions 140 */ 141 String XML_XSD_EXTENSIONS_DRAWING = Config.getUrls().getXMLBase() + "/gpx-drawing-extensions-1.0.xsd"; 142 143 /** 144 * Namespace for Garmin GPX extensions 145 */ 146 String XML_URI_EXTENSIONS_GARMIN = "http://www.garmin.com/xmlschemas/GpxExtensions/v3"; 147 /** 148 * Location of the XSD schema for GPX drawing extensions 149 */ 150 String XML_XSD_EXTENSIONS_GARMIN = "http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd"; 151 110 152 /** Elevation (in meters) of the point. */ 111 153 String PT_ELE = "ele"; 112 154 … … 154 196 */ 155 197 List<String> WPT_KEYS = Collections.unmodifiableList(Arrays.asList(PT_ELE, PT_TIME, PT_MAGVAR, PT_GEOIDHEIGHT, 156 198 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)); 199 PT_FIX, PT_SAT, PT_HDOP, PT_VDOP, PT_PDOP, PT_AGEOFDGPSDATA, PT_DGPSID, 200 EXTENSIONS_JOSM, EXTENSIONS_DRAWING, EXTENSIONS_GARMIN)); 158 201 159 202 /** 160 203 * Ordered list of all possible route and track keys. 161 204 */ 162 205 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)); 206 GPX_NAME, GPX_CMT, GPX_DESC, GPX_SRC, META_LINKS, "number", PT_TYPE, 207 EXTENSIONS_JOSM, EXTENSIONS_DRAWING, EXTENSIONS_GARMIN)); 164 208 165 209 /** 210 * Possible extension namespaces 211 */ 212 List<String> SUPPORTED_EXTENSION_NAPMESPACES = Collections.unmodifiableList(Arrays.asList( 213 "josm", "gpxx", "gpxd")); 214 215 /** 216 * Map with all supported Garmin colors 217 */ 218 Map<String, Color> GARMIN_COLORS = Collections.unmodifiableMap( 219 new TreeMap<String, Color>(String.CASE_INSENSITIVE_ORDER) {{ 220 put("Black", Color.BLACK); 221 put("DarkRed", new Color(139, 0, 0)); 222 put("DarkGreen", new Color(0, 100, 0)); 223 put("DarkYellow", new Color(255, 170, 0)); 224 put("DarkBlue", new Color(0, 0, 139)); 225 put("DarkMagenta", new Color(139, 0, 139)); 226 put("DarkCyan", new Color(0, 139, 139)); 227 put("LightGray", Color.LIGHT_GRAY); 228 put("DarkGray", Color.DARK_GRAY); 229 put("Red", Color.RED); 230 put("Green", Color.GREEN); 231 put("Yellow", Color.YELLOW); 232 put("Blue", Color.BLUE); 233 put("Magenta", Color.MAGENTA); 234 put("Cyan", Color.CYAN); 235 put("White", Color.WHITE); 236 put("Transparent", new Color(0, 0, 0, 255)); 237 }}); 238 239 /** 166 240 * Possible fix values. NMEA 0183 Version 4.00 167 241 */ 168 242 Collection<String> FIX_VALUES = Collections.unmodifiableList( -
src/org/openstreetmap/josm/data/gpx/GpxData.java
54 54 public String creator; 55 55 56 56 /** 57 * The layer specific prefs formerly saved in the preferences, e.g. drawing options. 58 * NOT the segment specific settings (e.g. color, width) 59 */ 60 public Map<String, String> layerPrefs = new HashMap<String, String>() {}; 61 62 /** 57 63 * A list of tracks this file consists of 58 64 */ 59 65 private final ArrayList<GpxTrack> privateTracks = new ArrayList<>(); … … 65 71 * Addidionaly waypoints for this file. 66 72 */ 67 73 private final ArrayList<WayPoint> privateWaypoints = new ArrayList<>(); 68 private final GpxTrackChangeListener proxy = e -> fireInvalidate(); 74 private final GpxTrackChangeListener proxy = e -> {fireInvalidate(); setModified();}; 75 private boolean modified = false; 69 76 70 77 /** 71 78 * Tracks. Access is discouraged, use {@link #getTracks()} to read. … … 888 895 private Line next; 889 896 private final boolean[] trackVisibility; 890 897 private Map<String, Object> trackAttributes; 898 private GpxTrack curTrack; 891 899 892 900 /** 893 901 * Constructs a new {@code LinesIterator}. … … 921 929 private Line getNext() { 922 930 if (itTracks != null) { 923 931 if (itTrackSegments != null && itTrackSegments.hasNext()) { 924 return new Line(itTrackSegments.next(), trackAttributes );932 return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor()); 925 933 } else { 926 934 while (itTracks.hasNext()) { 927 GpxTrack nxtTrack = itTracks.next();928 trackAttributes = nxtTrack.getAttributes();935 curTrack = itTracks.next(); 936 trackAttributes = curTrack.getAttributes(); 929 937 idxTracks++; 930 938 if (trackVisibility != null && !trackVisibility[idxTracks]) 931 939 continue; 932 itTrackSegments = nxtTrack.getSegments().iterator();940 itTrackSegments = curTrack.getSegments().iterator(); 933 941 if (itTrackSegments.hasNext()) { 934 return new Line(itTrackSegments.next(), trackAttributes );942 return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor()); 935 943 } 936 944 } 937 945 // if we get here, all the Tracks are finished; Continue with Routes … … 981 989 return false; 982 990 } else if (!dataSources.equals(other.dataSources)) 983 991 return false; 992 if (layerPrefs == null) { 993 if (other.layerPrefs != null) 994 return false; 995 } else if (!layerPrefs.equals(other.layerPrefs)) 996 return false; 984 997 if (privateRoutes == null) { 985 998 if (other.privateRoutes != null) 986 999 return false; … … 1067 1080 return source; 1068 1081 } 1069 1082 } 1083 1084 /** 1085 * @return whether anything (i.e. colors) have been modified 1086 */ 1087 public boolean isModified() { 1088 return modified; 1089 } 1090 1091 /** 1092 * Sets the modified flag to true. 1093 */ 1094 public void setModified() { 1095 setModified(true); 1096 } 1097 1098 /** 1099 * Sets the modified flag to the value. 1100 * @param value 1101 */ 1102 public void setModified(boolean value) { 1103 modified = value; 1104 } 1070 1105 } -
src/org/openstreetmap/josm/data/gpx/GpxTrack.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.awt.Color; 4 5 import java.util.Collection; 5 6 import java.util.Map; 6 7 … … 7 8 import org.openstreetmap.josm.data.Bounds; 8 9 9 10 /** 10 * Read-only gpx track. Implementations doesn't have to be immutable, but should always be thread safe.11 * Gpx track. Implementations doesn't have to be immutable, but should always be thread safe. 11 12 * @since 444 12 13 */ 13 14 public interface GpxTrack extends IWithAttributes { … … 37 38 double length(); 38 39 39 40 /** 41 * Gets the color of this track. 42 * @return The color, <code>null</code> if not set or not supported by the implementation. 43 * @since xxx 44 */ 45 default Color getColor() { 46 return null; 47 } 48 49 /** 50 * Sets the color of this track. Not necessarily supported by all implementations. 51 * @param color 52 * @since xxx 53 */ 54 default void setColor(Color color) { 55 return; 56 } 57 58 /** 40 59 * Add a listener that listens to changes in the GPX track. 41 60 * @param l The listener 42 61 */ -
src/org/openstreetmap/josm/data/gpx/IWithAttributes.java
2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 4 import java.util.Collection; 5 import java.util.Map; 5 6 6 7 /** 7 8 * Object with attributes (in the context of GPX data). … … 50 51 void put(String key, Object value); 51 52 52 53 /** 53 * Add a key / value pair that is not part of the GPX schema as an extension .54 * 54 * Add a key / value pair that is not part of the GPX schema as an extension and the extension attribute if not already present. 55 * @param extensionType type of the extension, can be any <code>GpxConstants.EXTENSIONS_*</code> or a string of format <code>"extensions.<i>xmlns_prefix</i>"</code> 55 56 * @param key the key 56 57 * @param value the value 58 * @see GpxConstants#EXTENSIONS_PREFIX 57 59 */ 58 void addExtension (String key, String value);60 void addExtensionKey(String extensionType, String key, String value); 59 61 62 /** 63 * Add key / value pairs that are not part of the GPX schema as extensions. 64 * @param extensionMap the <code>Map<String, String></code> with the key being the qualified name including prefix of the namespace 65 * @see GpxConstants#EXTENSIONS_PREFIX 66 * @see Extensions 67 */ 68 void addExtensions(Map<String, String> extensionMap); 69 70 /** 71 * Removes a key from the extension if present and the extension if empty 72 * @param extensionType type of the extension, can be any <code>GpxConstants.EXTENSIONS_*</code> or a string of format <code>"extensions.<i>xmlns_prefix</i>"</code> 73 * @param key the key, not case-sensitive 74 * @see GpxConstants#EXTENSIONS_PREFIX 75 */ 76 void removeExtensionKey(String extensionType, String key); 77 60 78 } -
src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import java.awt.Color; 4 5 import java.util.ArrayList; 5 6 import java.util.Collection; 6 7 import java.util.Collections; … … 9 10 import java.util.Map; 10 11 11 12 import org.openstreetmap.josm.data.Bounds; 13 import org.openstreetmap.josm.tools.ListenerList; 14 import org.openstreetmap.josm.tools.Logging; 12 15 13 16 /** 14 17 * Immutable GPX track. 18 * Note that the color attributes are not immutable and may be modified by the user. 15 19 * @since 2907 16 20 */ 17 21 public class ImmutableGpxTrack extends WithAttributes implements GpxTrack { … … 19 23 private final List<GpxTrackSegment> segments; 20 24 private final double length; 21 25 private final Bounds bounds; 26 private Color colorCache; 27 private final ListenerList<GpxTrackChangeListener> listeners = ListenerList.create(); 22 28 23 29 /** 24 * Constructs a new {@code ImmutableGpxTrack} .30 * Constructs a new {@code ImmutableGpxTrack} and adds the extensions. 25 31 * @param trackSegs track segments 26 32 * @param attributes track attributes 33 * @param extensionMap map of extensions 27 34 */ 28 public ImmutableGpxTrack(Collection<Collection<WayPoint>> trackSegs, Map<String, Object> attributes ) {35 public ImmutableGpxTrack(Collection<Collection<WayPoint>> trackSegs, Map<String, Object> attributes, Map<String, String> extensionMap) { 29 36 List<GpxTrackSegment> newSegments = new ArrayList<>(); 30 37 for (Collection<WayPoint> trackSeg: trackSegs) { 31 38 if (trackSeg != null && !trackSeg.isEmpty()) { … … 32 39 newSegments.add(new ImmutableGpxTrackSegment(trackSeg)); 33 40 } 34 41 } 35 this.attr = Collections.unmodifiableMap(new HashMap<>(attributes)); 42 this.attr = new HashMap<>(attributes); 43 if (extensionMap != null) { 44 addExtensions(extensionMap); 45 } 36 46 this.segments = Collections.unmodifiableList(newSegments); 37 47 this.length = calculateLength(); 38 48 this.bounds = calculateBounds(); … … 39 49 } 40 50 41 51 /** 52 * Constructs a new {@code ImmutableGpxTrack}. 53 * @param trackSegs track segments 54 * @param attributes track attributes 55 */ 56 public ImmutableGpxTrack(Collection<Collection<WayPoint>> trackSegs, Map<String, Object> attributes) { 57 this(trackSegs, attributes, null); 58 } 59 60 /** 42 61 * Constructs a new {@code ImmutableGpxTrack} from {@code GpxTrackSegment} objects. 43 62 * @param segments The segments to build the track from. Input is not deep-copied, 44 63 * which means the caller may reuse the same segments to build … … 48 67 * @since 13210 49 68 */ 50 69 public ImmutableGpxTrack(List<GpxTrackSegment> segments, Map<String, Object> attributes) { 51 this.attr = Collections.unmodifiableMap(new HashMap<>(attributes));70 this.attr = new HashMap<>(attributes); 52 71 this.segments = Collections.unmodifiableList(segments); 53 72 this.length = calculateLength(); 54 73 this.bounds = calculateBounds(); … … 79 98 } 80 99 81 100 @Override 101 public void setColor(Color color) { 102 setColorAttr(color); 103 colorCache = color; 104 } 105 106 private void setColorAttr(Color color) { 107 removeExtensionKey(GpxConstants.EXTENSIONS_GARMIN, "displaycolor"); 108 if (color != null) { 109 addExtensionKey(EXTENSIONS_DRAWING, "color", String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue())); 110 } else { 111 removeExtensionKey(EXTENSIONS_DRAWING, "color"); 112 } 113 } 114 115 @Override 116 public Color getColor() { 117 if (colorCache == null) { 118 colorCache = getColorFromAttr(attr); 119 } 120 return colorCache; 121 } 122 123 124 private Color getColorFromAttr(Map<String, Object> attr) { 125 Extensions gpxd = (Extensions) attr.get(GpxConstants.EXTENSIONS_DRAWING); 126 if (gpxd != null) { 127 String cs = gpxd.get("color"); 128 try { 129 return Color.decode(cs); 130 } catch (NumberFormatException ex) { 131 Logging.warn("Could not read gpxd color: " + cs); 132 } 133 } else { 134 Extensions gpxx = (Extensions) attr.get(GpxConstants.EXTENSIONS_GARMIN); 135 if (gpxx != null) { 136 String cs = gpxx.get("displaycolor"); 137 if (cs != null) { 138 Color cc = GpxConstants.GARMIN_COLORS.get(cs); 139 if (cc != null) { 140 return cc; 141 } 142 } 143 Logging.warn("Could not read garmin color: " + cs); 144 } 145 } 146 return null; 147 } 148 149 @Override 150 public void addExtensionKey(String extensionType, String key, String value) { 151 colorCache = null; 152 super.addExtensionKey(extensionType, key, value); 153 listeners.fireEvent(l -> l.gpxDataChanged(new GpxTrackChangeEvent(this))); 154 } 155 156 @Override 157 public void removeExtensionKey(String extensionType, String key) { 158 colorCache = null; 159 super.removeExtensionKey(extensionType, key); 160 listeners.fireEvent(l -> l.gpxDataChanged(new GpxTrackChangeEvent(this))); 161 } 162 163 @Override 82 164 public Map<String, Object> getAttributes() { 83 165 return attr; 84 166 } … … 119 201 return false; 120 202 return true; 121 203 } 204 205 @Override 206 public void addListener(GpxTrackChangeListener l) { 207 listeners.addListener(l); 208 } 209 210 @Override 211 public void removeListener(GpxTrackChangeListener l) { 212 listeners.removeListener(l); 213 } 214 122 215 } -
src/org/openstreetmap/josm/data/gpx/Line.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.util.Collection; 5 import java.util.Iterator; 6 import java.util.Map; 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.gpx; 3 4 import java.awt.Color; 5 import java.util.Collection; 6 import java.util.Iterator; 7 import java.util.Map; 7 8 import java.util.Objects; 8 9 9 10 /** … … 10 11 * Line represents a linear collection of GPX waypoints with the ordered/unordered distinction. 11 12 * @since 14451 12 13 */ 13 public class Line implements Collection<WayPoint> { 14 private final Collection<WayPoint> waypoints; 15 private final boolean unordered; 14 public class Line implements Collection<WayPoint> { 15 private final Collection<WayPoint> waypoints; 16 private final boolean unordered; 17 private final Color color; 18 19 /** 20 * Constructs a new {@code Line}. 21 * @param waypoints collection of waypoints 22 * @param attributes track/route attributes 23 * @param color color of the track 24 * 25 */ 26 public Line(Collection<WayPoint> waypoints, Map<String, Object> attributes, Color color) { 27 this.color = color; 28 this.waypoints = Objects.requireNonNull(waypoints); 29 unordered = attributes.isEmpty() && waypoints.stream().allMatch(x -> x.get(GpxConstants.PT_TIME) == null); 30 } 16 31 17 32 /** 18 33 * Constructs a new {@code Line}. 19 * @param waypoints collection of waypoints20 * @param attributes track/routeattributes21 * /22 public Line(Collection<WayPoint> waypoints, Map<String, Object> attributes) {23 this.waypoints = Objects.requireNonNull(waypoints);24 unordered = attributes.isEmpty() && waypoints.stream().allMatch(x -> x.get(GpxConstants.PT_TIME) == null);25 } 26 27 /** 34 * @param trackSegment track segment 35 * @param trackAttributes track attributes 36 * @param color color of the track 37 */ 38 public Line(GpxTrackSegment trackSegment, Map<String, Object> trackAttributes, Color color) { 39 this(trackSegment.getWayPoints(), trackAttributes, color); 40 } 41 42 /** 28 43 * Constructs a new {@code Line}. 29 * @param trackSegment track segment 30 * @param trackAttributes track attributes 31 */ 32 public Line(GpxTrackSegment trackSegment, Map<String, Object> trackAttributes) { 33 this(trackSegment.getWayPoints(), trackAttributes); 34 } 35 36 /** 37 * Constructs a new {@code Line}. 38 * @param route route 39 */ 40 public Line(GpxRoute route) { 41 this(route.routePoints, route.attr); 42 } 43 44 /** 44 * @param route route 45 */ 46 public Line(GpxRoute route) { 47 this(route.routePoints, route.attr, null); 48 } 49 50 /** 45 51 * Determines if waypoints are ordered. 46 52 * @return {@code true} if waypoints are ordered 47 53 */ 48 54 public boolean isUnordered() { 49 return unordered; 55 return unordered; 56 } 57 58 /** 59 * Returns the track/route color 60 * @return the color 61 */ 62 public Color getColor() { 63 return color; 64 } 65 66 @Override 67 public int size() { 68 return waypoints.size(); 50 69 } 51 70 52 71 @Override 53 public int size() {54 return waypoints.size();55 }56 57 @Override58 72 public boolean isEmpty() { 59 73 return waypoints.isEmpty(); 60 74 } -
src/org/openstreetmap/josm/data/gpx/WithAttributes.java
4 4 import java.util.Collection; 5 5 import java.util.HashMap; 6 6 import java.util.Map; 7 import java.util.Map.Entry; 7 8 8 9 /** 9 10 * Default implementation for IWithAttributes. … … 64 65 65 66 /** 66 67 * Put a key / value pair as a new attribute. 67 *68 68 * Overrides key / value pair with the same key (if present). 69 69 * 70 70 * @param key the key … … 75 75 attr.put(key, value); 76 76 } 77 77 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 78 @Override 85 public void addExtension(String key, String value) { 86 if (!attr.containsKey(META_EXTENSIONS)) { 87 attr.put(META_EXTENSIONS, new Extensions()); 79 public void addExtensionKey(String extensionType, String key, String value) { 80 Extensions ext; 81 if (!attr.containsKey(extensionType)) { 82 ext = new Extensions(extensionType); 83 attr.put(extensionType, ext); 84 } else { 85 ext = (Extensions) attr.get(extensionType); 88 86 } 89 Extensions ext = (Extensions) attr.get(META_EXTENSIONS);90 87 ext.put(key, value); 91 88 } 92 89 93 90 @Override 91 public void addExtensions(Map<String, String> extensionMap) { 92 for (Entry<String, String> e : extensionMap.entrySet()) { 93 String k = e.getKey(); 94 int dot = k.indexOf(":"); 95 if (dot != -1) { 96 addExtensionKey(GpxConstants.EXTENSIONS_PREFIX + k.substring(0, dot), k.substring(dot + 1), e.getValue()); 97 } 98 } 99 } 100 101 @Override 102 public void removeExtensionKey(String extensionType, String key) { 103 Extensions ext = (Extensions) attr.get(extensionType); 104 if (ext != null) { 105 ext.remove(key); 106 if (ext.isEmpty()) { 107 attr.remove(extensionType); 108 } 109 } 110 } 111 112 @Override 94 113 public int hashCode() { 95 114 return 31 + ((attr == null) ? 0 : attr.hashCode()); 96 115 } … … 111 130 return false; 112 131 return true; 113 132 } 133 114 134 } -
src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.Color;7 6 import java.awt.Component; 8 7 import java.awt.Dimension; 9 8 import java.awt.Font; … … 17 16 import java.util.ArrayList; 18 17 import java.util.Arrays; 19 18 import java.util.List; 20 import java.util.O bjects;19 import java.util.Optional; 21 20 import java.util.concurrent.CopyOnWriteArrayList; 22 21 23 22 import javax.swing.AbstractAction; … … 41 40 import org.openstreetmap.josm.actions.MergeLayerAction; 42 41 import org.openstreetmap.josm.data.coor.EastNorth; 43 42 import org.openstreetmap.josm.data.imagery.OffsetBookmark; 44 import org.openstreetmap.josm.data.preferences.AbstractProperty;45 43 import org.openstreetmap.josm.gui.MainApplication; 46 44 import org.openstreetmap.josm.gui.MapFrame; 47 45 import org.openstreetmap.josm.gui.MapView; … … 583 581 label.setFont(label.getFont().deriveFont(Font.BOLD)); 584 582 } 585 583 if (Config.getPref().getBoolean("dialog.layer.colorname", true)) { 586 AbstractProperty<Color> prop = layer.getColorProperty(); 587 Color c = prop == null ? null : prop.get(); 588 if (c == null || model.getLayers().stream() 589 .map(Layer::getColorProperty) 590 .filter(Objects::nonNull) 591 .map(AbstractProperty::get) 592 .noneMatch(oc -> oc != null && !oc.equals(c))) { 593 /* not more than one color, don't use coloring */ 594 label.setForeground(UIManager.getColor(isSelected ? "Table.selectionForeground" : "Table.foreground")); 595 } else { 596 label.setForeground(c); 597 } 584 label.setForeground(Optional 585 .ofNullable(layer.getColor()) 586 .orElse(UIManager.getColor(isSelected ? "Table.selectionForeground" : "Table.foreground"))); 598 587 } 599 588 label.setIcon(layer.getIcon()); 600 589 label.setToolTipText(layer.getToolTipText()); -
src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java
593 593 List<Layer> layers = layerSupplier.get(); 594 594 for (Layer l : layers) { 595 595 if (l instanceof GpxLayer) { 596 l. getColorProperty().put(color);596 l.setColor(color); 597 597 } 598 598 } 599 599 highlightColor(color); … … 602 602 add(colorPanel, GBC.std().weight(1, 1).fill().insets(5)); 603 603 panels.put(color, colorPanel); 604 604 605 List<Color> colors = layerSupplier.get().stream().map(l -> l.getColor Property().get()).distinct().collect(Collectors.toList());605 List<Color> colors = layerSupplier.get().stream().map(l -> l.getColor()).distinct().collect(Collectors.toList()); 606 606 if (colors.size() == 1) { 607 607 highlightColor(colors.get(0)); 608 608 } … … 611 611 @Override 612 612 public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) { 613 613 List<Color> colors = layers.stream().filter(l -> l instanceof GpxLayer) 614 .map(l -> ((GpxLayer) l).getColor Property().get())614 .map(l -> ((GpxLayer) l).getColor()) 615 615 .distinct() 616 616 .collect(Collectors.toList()); 617 617 if (colors.size() == 1) { -
src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java
25 25 26 26 import org.openstreetmap.josm.data.gpx.GpxConstants; 27 27 import org.openstreetmap.josm.data.gpx.GpxData; 28 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 28 29 import org.openstreetmap.josm.gui.ExtendedDialog; 29 30 import org.openstreetmap.josm.gui.MainApplication; 30 31 import org.openstreetmap.josm.gui.layer.GpxLayer; … … 146 147 p.add(new JLabel(tr("Keywords")), GBC.eol()); 147 148 JosmTextField keywords = new JosmTextField(); 148 149 keywords.setText(gpxData.getString(META_KEYWORDS)); 149 p.add(keywords, GBC.eo p().fill(GBC.HORIZONTAL));150 p.add(keywords, GBC.eol().fill(GBC.HORIZONTAL)); 150 151 152 boolean sel = Config.getPref().getBoolean("gpx.export.colors", true); 153 JCheckBox colors = new JCheckBox(tr("Save track colors in GPX file"), sel); 154 p.add(colors, GBC.eol().fill(GBC.HORIZONTAL)); 155 JCheckBox garmin = new JCheckBox(tr("Use Garmin compatible GPX extensions"), 156 Config.getPref().getBoolean("gpx.export.colors.garmin", false)); 157 garmin.setEnabled(sel); 158 p.add(garmin, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 0)); 159 160 boolean hasPrefs = !gpxData.layerPrefs.isEmpty(); 161 JCheckBox layerPrefs = new JCheckBox(tr("Save layer specific preferences"), 162 hasPrefs && Config.getPref().getBoolean("gpx.export.prefs", true)); 163 layerPrefs.setEnabled(hasPrefs); 164 p.add(layerPrefs, GBC.eop().fill(GBC.HORIZONTAL)); 165 151 166 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 152 167 tr("Export options"), 153 168 tr("Export and Save"), tr("Cancel")) … … 154 169 .setButtonIcons("exportgpx", "cancel") 155 170 .setContent(p); 156 171 172 colors.addActionListener(l -> { 173 garmin.setEnabled(colors.isSelected()); 174 }); 175 176 garmin.addActionListener(l -> { 177 if (garmin.isSelected() && 178 !ConditionalOptionPaneUtil.showConfirmationDialog( 179 "gpx_color_garmin", 180 ed, 181 new JLabel(tr("<html>Garmin track extensions only support 16 colors. If you continue, the closest supported<br>track color will be used and information such as width and opacity will be lost.</html>")), 182 tr("Information"), 183 JOptionPane.OK_CANCEL_OPTION, 184 JOptionPane.INFORMATION_MESSAGE, 185 JOptionPane.OK_OPTION)) { 186 garmin.setSelected(false); 187 } 188 }); 189 157 190 if (ed.showDialog().getValue() != 1) { 158 191 setCanceled(true); 159 192 return; … … 167 200 if (!copyright.getText().isEmpty()) { 168 201 Config.getPref().put("lastCopyright", copyright.getText()); 169 202 } 203 Config.getPref().putBoolean("gpx.export.colors", colors.isSelected()); 204 Config.getPref().putBoolean("gpx.export.colors.garmin", garmin.isSelected()); 205 if (hasPrefs) { 206 Config.getPref().putBoolean("gpx.export.prefs", layerPrefs.isSelected()); 207 } 208 String colorFormat = null; 209 if (colors.isSelected()) { 210 colorFormat = garmin.isSelected() 211 ? GpxConstants.EXTENSIONS_GARMIN 212 : GpxConstants.EXTENSIONS_DRAWING; 213 } 170 214 171 215 if (layer instanceof OsmDataLayer) { 172 216 gpxData = ((OsmDataLayer) layer).toGpxData(); … … 203 247 gpxData.put(META_KEYWORDS, keywords.getText()); 204 248 } 205 249 206 try (OutputStream fo = Compression.getCompressedFileOutputStream(file); GpxWriter writer = new GpxWriter(fo)) { 207 writer.write(gpxData); 250 try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) { 251 GpxWriter w = new GpxWriter(fo); 252 w.write(gpxData, colorFormat, layerPrefs.isSelected()); 253 w.close(); 254 fo.flush(); 255 gpxData.setModified(false); 208 256 } 209 257 } 210 258 -
src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java
149 149 markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer); 150 150 if (markerLayer.data.isEmpty()) { 151 151 markerLayer = null; 152 } else { 153 gpxLayer.linkedMarkerLayer = markerLayer; 152 154 } 153 155 } 154 156 Runnable postLayerTask = () -> { -
src/org/openstreetmap/josm/gui/layer/CustomizeColor.java
18 18 import javax.swing.JMenuItem; 19 19 import javax.swing.JOptionPane; 20 20 21 import org.openstreetmap.josm.data.preferences.AbstractProperty;22 21 import org.openstreetmap.josm.gui.MainApplication; 23 22 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 24 23 import org.openstreetmap.josm.gui.layer.Layer.LayerAction; 25 24 import org.openstreetmap.josm.gui.layer.Layer.MultiLayerAction; 26 import org.openstreetmap.josm.tools.CheckParameterUtil;27 25 import org.openstreetmap.josm.tools.ImageProvider; 28 26 29 27 /** … … 33 31 * of a certain {@link GpxLayer} or {@link org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer}. 34 32 */ 35 33 public class CustomizeColor extends AbstractAction implements LayerAction, MultiLayerAction { 36 private final transient List< AbstractProperty<Color>> colors;34 private final transient List<Layer> colorLayers; 37 35 38 36 /** 39 37 * Constructs a new {@code CustomizeColor} for a given list of layers. … … 42 40 public CustomizeColor(List<Layer> l) { 43 41 super(tr("Customize Color")); 44 42 new ImageProvider("colorchooser").getResource().attachImageIcon(this, true); 45 colors = l.stream().map(Layer::getColorProperty).collect(Collectors.toList()); 46 CheckParameterUtil.ensureThat(colors.stream().allMatch(Objects::nonNull), "All layers must have colors."); 43 colorLayers = l.stream().filter(Objects::nonNull).filter(Layer::hasColor).collect(Collectors.toList()); 47 44 putValue("help", ht("/Action/LayerCustomizeColor")); 48 45 } 49 46 … … 57 54 58 55 @Override 59 56 public boolean supportLayers(List<Layer> layers) { 60 return layers.stream().allMatch( l -> l.getColorProperty() != null);57 return layers.stream().allMatch(Layer::hasColor); 61 58 } 62 59 63 60 @Override … … 72 69 73 70 @Override 74 71 public void actionPerformed(ActionEvent e) { 75 Color cl = color s.stream().map(AbstractProperty::get).filter(Objects::nonNull).findAny().orElse(Color.GRAY);72 Color cl = colorLayers.stream().filter(Objects::nonNull).map(Layer::getColor).filter(Objects::nonNull).findAny().orElse(Color.GRAY); 76 73 JColorChooser c = new JColorChooser(cl); 77 74 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 78 75 int answer = JOptionPane.showOptionDialog( … … 87 84 ); 88 85 switch (answer) { 89 86 case 0: 90 color s.stream().forEach(prop -> prop.put(c.getColor()));87 colorLayers.stream().forEach(l -> l.setColor(c.getColor())); 91 88 break; 92 89 case 1: 93 90 return; 94 91 case 2: 95 color s.stream().forEach(prop -> prop.put(null));92 colorLayers.stream().forEach(l -> l.setColor(null)); 96 93 break; 97 94 } 98 95 // TODO: Make the layer dialog listen to property change events so that this is not needed any more. -
src/org/openstreetmap/josm/gui/layer/GpxLayer.java
4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 import static org.openstreetmap.josm.tools.I18n.trn; 6 6 7 import java.awt.Color; 7 8 import java.awt.Dimension; 8 9 import java.awt.Graphics2D; 9 10 import java.awt.event.ActionEvent; … … 31 32 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener; 32 33 import org.openstreetmap.josm.data.gpx.GpxTrack; 33 34 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 34 import org.openstreetmap.josm.data.preferences.NamedColorProperty;35 35 import org.openstreetmap.josm.data.projection.Projection; 36 36 import org.openstreetmap.josm.gui.MapView; 37 37 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; … … 46 46 import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction; 47 47 import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction; 48 48 import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction; 49 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 50 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 49 51 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 50 52 import org.openstreetmap.josm.tools.ImageProvider; 51 53 import org.openstreetmap.josm.tools.Utils; … … 54 56 /** 55 57 * A layer that displays data from a Gpx file / the OSM gpx downloads. 56 58 */ 57 public class GpxLayer extends Layer implements ExpertModeChangeListener {59 public class GpxLayer extends AbstractModifiableLayer implements ExpertModeChangeListener { 58 60 59 61 /** GPX data */ 60 62 public GpxData data; 61 63 private final boolean isLocalFile; 62 64 private boolean isExpertMode; 65 63 66 /** 67 * The MarkerLayer imported from the same file. 68 */ 69 public MarkerLayer linkedMarkerLayer; 70 71 /** 64 72 * used by {@link ChooseTrackVisibilityAction} to determine which tracks to show/hide 65 73 * 66 74 * Call {@link #invalidate()} after each change! … … 108 116 } 109 117 110 118 @Override 111 protected NamedColorProperty getBaseColorProperty() { 112 return GpxDrawHelper.DEFAULT_COLOR; 119 public Color getColor() { 120 Color[] c = data.getTracks().stream().map(t -> t.getColor()).distinct().toArray(Color[]::new); 121 return c.length == 1 ? c[0] : null; //only return if exactly one distinct color present 113 122 } 114 123 124 @Override 125 public void setColor(Color color) { 126 for (GpxTrack trk : data.getTracks()) { 127 trk.setColor(color); 128 } 129 GPXSettingsPanel.putLayerPrefLocal(this, "colormode", "0"); 130 } 131 132 @Override 133 public boolean hasColor() { 134 return true; 135 } 136 115 137 /** 116 138 * Returns a human readable string that shows the timespan of the given track 117 139 * @param trk The GPX track for which timespan is displayed … … 480 502 } 481 503 482 504 @Override 505 public boolean isModified() { 506 return data.isModified(); 507 } 508 509 @Override 510 public boolean requiresSaveToFile() { 511 return isModified() && isLocalFile(); 512 } 513 514 @Override 483 515 public String getChangesetSourceTag() { 484 516 // no i18n for international values 485 517 return "survey"; -
src/org/openstreetmap/josm/gui/layer/Layer.java
25 25 import org.openstreetmap.josm.actions.SaveAsAction; 26 26 import org.openstreetmap.josm.data.ProjectionBounds; 27 27 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 28 import org.openstreetmap.josm.data.preferences.AbstractProperty;29 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener;30 import org.openstreetmap.josm.data.preferences.NamedColorProperty;31 28 import org.openstreetmap.josm.data.projection.Projection; 32 29 import org.openstreetmap.josm.data.projection.ProjectionChangeListener; 33 30 import org.openstreetmap.josm.gui.MainApplication; … … 164 161 */ 165 162 private File associatedFile; 166 163 167 private final ValueChangeListener<Object> invalidateListener = change -> invalidate();168 164 private boolean isDestroyed; 169 165 170 166 /** … … 198 194 public abstract Icon getIcon(); 199 195 200 196 /** 201 * Gets the color property to use for this layer. 202 * @return The color property. 203 * @since 10824 197 * @return whether the layer has / can handle colors. 204 198 */ 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 } 199 public boolean hasColor() { 200 return false; 212 201 } 213 202 214 203 /** 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 204 * Return the current color of the layer 205 * @return null when not present or not supported 218 206 */ 219 p rotected NamedColorProperty getBaseColorProperty() {207 public Color getColor() { 220 208 return null; 221 209 } 222 210 223 private void addColorPropertyListener() {224 AbstractProperty<Color> colorProperty = getColorProperty();225 if (colorProperty != null) {226 colorProperty.addListener(invalidateListener);227 }211 /** 212 * Sets the color for this layer. Nothing happens if not supported by the layer 213 * @param color the color to be set, <code>null</code> for default 214 */ 215 public void setColor(Color color) { 228 216 } 229 217 230 private void removeColorPropertyListener() {231 AbstractProperty<Color> colorProperty = getColorProperty();232 if (colorProperty != null) {233 colorProperty.removeListener(invalidateListener);234 }235 }236 237 218 /** 238 219 * @return A small tooltip hint about some statistics for this layer. 239 220 */ … … 302 283 } 303 284 isDestroyed = true; 304 285 // Override in subclasses if needed 305 removeColorPropertyListener();306 286 } 307 287 308 288 /** … … 339 319 * @param name the name. If null, the name is set to the empty string. 340 320 */ 341 321 public void setName(String name) { 342 if (this.name != null) {343 removeColorPropertyListener();344 }345 322 String oldValue = this.name; 346 323 this.name = Optional.ofNullable(name).orElse(""); 347 324 if (!this.name.equals(oldValue)) { 348 325 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name); 349 326 } 350 351 // re-add listener352 addColorPropertyListener();353 327 invalidate(); 354 328 } 355 329 -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
31 31 import java.util.LinkedHashMap; 32 32 import java.util.List; 33 33 import java.util.Map; 34 import java.util.Map.Entry; 34 35 import java.util.Optional; 35 36 import java.util.Set; 36 37 import java.util.concurrent.CopyOnWriteArrayList; … … 780 781 Collection<Collection<WayPoint>> trk = new ArrayList<>(); 781 782 Map<String, Object> trkAttr = new HashMap<>(); 782 783 783 String name = gpxVal(w, "name"); 784 if (name != null) { 785 trkAttr.put("name", name); 784 Map<String, String> trkExts = new HashMap<>(); 785 for (Entry<String, String> e : w.getKeys().entrySet()) { 786 //String k = e.getKey().replaceFirst("/^" + GpxConstants.GPX_PREFIX + "/", ""); 787 String k = e.getKey().startsWith(GpxConstants.GPX_PREFIX) ? e.getKey().substring(GpxConstants.GPX_PREFIX.length()) : e.getKey(); 788 String v = e.getValue(); 789 if (GpxConstants.RTE_TRK_KEYS.contains(k)) { 790 trkAttr.put(k, v); 791 } else if (GpxConstants.SUPPORTED_EXTENSION_NAPMESPACES.stream().anyMatch( 792 s -> k.startsWith(s + ":"))) { 793 trkExts.put(k, v); 794 } 786 795 } 787 788 796 List<WayPoint> trkseg = null; 789 797 for (Node n : w.getNodes()) { 790 798 if (!n.isUsable()) { … … 801 809 trkseg.add(nodeToWayPoint(n)); 802 810 } 803 811 804 gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr ));812 gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr, trkExts)); 805 813 }); 806 814 } 807 815 -
src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java
4 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 6 7 import java.awt.Color; 7 8 import java.awt.Component; 8 9 import java.awt.Dimension; 9 10 import java.awt.GridBagLayout; … … 12 13 import java.awt.event.MouseEvent; 13 14 import java.awt.event.MouseListener; 14 15 import java.io.Serializable; 16 import java.util.ArrayList; 15 17 import java.util.Arrays; 16 18 import java.util.Comparator; 19 import java.util.List; 17 20 import java.util.Map; 21 import java.util.Objects; 18 22 import java.util.Optional; 19 23 20 24 import javax.swing.AbstractAction; 25 import javax.swing.JColorChooser; 21 26 import javax.swing.JComponent; 22 27 import javax.swing.JLabel; 28 import javax.swing.JOptionPane; 23 29 import javax.swing.JPanel; 24 30 import javax.swing.JScrollPane; 25 31 import javax.swing.JTable; 26 32 import javax.swing.JToggleButton; 27 33 import javax.swing.ListSelectionModel; 34 import javax.swing.event.TableModelEvent; 28 35 import javax.swing.table.DefaultTableModel; 29 36 import javax.swing.table.TableCellRenderer; 30 37 import javax.swing.table.TableRowSorter; 31 38 39 import org.apache.commons.jcs.access.exception.InvalidArgumentException; 32 40 import org.openstreetmap.josm.data.SystemOfMeasurement; 33 41 import org.openstreetmap.josm.data.gpx.GpxConstants; 34 42 import org.openstreetmap.josm.data.gpx.GpxTrack; … … 55 63 * @param layer The associated GPX layer 56 64 */ 57 65 public ChooseTrackVisibilityAction(final GpxLayer layer) { 58 super(tr("Choose visible tracks"));66 super(tr("Choose track visibility and colors")); 59 67 new ImageProvider("dialogs/filter").getResource().attachImageIcon(this, true); 60 68 this.layer = layer; 61 69 putValue("help", ht("/Action/ChooseTrackVisibility")); … … 116 124 String time = GpxLayer.getTimespanForTrack(trk); 117 125 TrackLength length = new TrackLength(trk.length()); 118 126 String url = (String) Optional.ofNullable(attr.get("url")).orElse(""); 119 tracks[i] = new Object[]{name, desc, time, length, url };127 tracks[i] = new Object[]{name, desc, time, length, url, trk}; 120 128 i++; 121 129 } 122 130 return tracks; 123 131 } 124 132 133 private void showColorDialog(List<GpxTrack> tracks) { 134 Color cl = tracks.stream().filter(Objects::nonNull) 135 .map(GpxTrack::getColor).filter(Objects::nonNull) 136 .findAny().orElse(GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get()); 137 JColorChooser c = new JColorChooser(cl); 138 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 139 int answer = JOptionPane.showOptionDialog( 140 MainApplication.getMainFrame(), 141 c, 142 tr("Choose a color"), 143 JOptionPane.OK_CANCEL_OPTION, 144 JOptionPane.PLAIN_MESSAGE, 145 null, 146 options, 147 options[0] 148 ); 149 switch (answer) { 150 case 0: 151 tracks.stream().forEach(t -> t.setColor(c.getColor())); 152 layer.data.layerPrefs.put("colors", "0"); //set Colormode to none 153 break; 154 case 1: 155 return; 156 case 2: 157 tracks.stream().forEach(t -> t.setColor(null)); 158 break; 159 } 160 table.repaint(); 161 } 162 125 163 /** 126 164 * Builds an non-editable table whose 5th column will open a browser when double clicked. 127 165 * The table will fill its parent. … … 138 176 if (c instanceof JComponent) { 139 177 JComponent jc = (JComponent) c; 140 178 jc.setToolTipText(getValueAt(row, col).toString()); 179 if (content.length > row 180 && content[row].length > 5 181 && content[row][5] instanceof GpxTrack) { 182 Color color = ((GpxTrack) content[row][5]).getColor(); 183 if (color != null) { 184 double brightness = Math.sqrt(Math.pow(color.getRed(), 2) * .241 185 + Math.pow(color.getGreen(), 2) * .691 186 + Math.pow(color.getBlue(), 2) * .068); 187 if (brightness > 250) { 188 color = color.darker(); 189 } 190 if (isRowSelected(row)) { 191 jc.setBackground(color); 192 if (brightness <= 130) { 193 jc.setForeground(Color.WHITE); 194 } else { 195 jc.setForeground(Color.BLACK); 196 } 197 } else { 198 if (brightness > 200) { 199 color = color.darker(); //brightness >250 is darkened twice on purpose 200 } 201 jc.setForeground(color); 202 jc.setBackground(Color.WHITE); 203 } 204 } 205 } 141 206 } 142 207 return c; 143 208 } … … 144 209 145 210 @Override 146 211 public boolean isCellEditable(int rowIndex, int colIndex) { 147 return false;212 return colIndex <= 1; 148 213 } 214 215 @Override 216 public void tableChanged(TableModelEvent e) { 217 super.tableChanged(e); 218 int col = e.getColumn(); 219 int row = e.getFirstRow(); 220 if (row >= 0 && row < content.length && col >= 0 && col <= 1) { 221 Object t = content[row][5]; 222 String val = (String) getValueAt(row, col); 223 if (t != null && t instanceof GpxTrack) { 224 GpxTrack trk = (GpxTrack) t; 225 if (col == 0) { 226 trk.put("name", val); 227 } else { 228 trk.put("desc", val); 229 } 230 } else { 231 throw new InvalidArgumentException(); 232 } 233 } 234 } 149 235 }; 150 236 // define how to sort row 151 237 TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<>(); … … 249 335 250 336 msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. " + 251 337 "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>")),338 "The map is updated live in the background. Open the URLs by double clicking them, edit name and description by double clicking the cell.</html>")), 253 339 GBC.eop().fill(GBC.HORIZONTAL)); 254 340 // build table 255 341 final boolean[] trackVisibilityBackup = layer.trackVisibility.clone(); 256 table = buildTable(buildTableContents()); 342 Object[][] content = buildTableContents(); 343 table = buildTable(content); 257 344 selectVisibleTracksInTable(); 258 345 listenToSelectionChanges(); 259 346 // make the table scrollable … … 262 349 263 350 int v = 1; 264 351 // 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"); 352 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 353 tr("Set track visibility for {0}", layer.getName()), 354 tr("Set color for selected tracks..."), tr("Show all"), tr("Show selected only"), tr("Cancel")) { 355 @Override 356 protected void buttonAction(int buttonIndex, ActionEvent evt) { 357 if (buttonIndex == 0) { 358 List<GpxTrack> trks = new ArrayList<>(); 359 for (int i : table.getSelectedRows()) { 360 Object trk = content[i][5]; 361 if (trk != null && trk instanceof GpxTrack) { 362 trks.add((GpxTrack) trk); 363 } 364 } 365 showColorDialog(trks); 366 } else { 367 super.buttonAction(buttonIndex, evt); 368 } 369 } 370 }; 371 ed.setButtonIcons("colorchooser", "eye", "dialogs/filter", "cancel"); 268 372 ed.setContent(msg, false); 269 373 ed.setDefaultButton(2); 270 374 ed.setCancelButton(3); … … 275 379 dateFilter.saveInPrefs(); 276 380 v = ed.getValue(); 277 381 // cancel for unknown buttons and copy back original settings 278 if (v != 1 && v != 2) {382 if (v != 2 && v != 3) { 279 383 layer.trackVisibility = Arrays.copyOf(trackVisibilityBackup, layer.trackVisibility.length); 280 384 MainApplication.getMap().repaint(); 281 385 return; 282 386 } 283 // set visibility ( 1 = show all, 2= filter). If no tracks are selected387 // set visibility (2 = show all, 3 = filter). If no tracks are selected 284 388 // set all of them visible and... 285 389 ListSelectionModel s = table.getSelectionModel(); 286 final boolean all = v == 1|| s.isSelectionEmpty();390 final boolean all = v == 2 || s.isSelectionEmpty(); 287 391 for (int i = 0; i < layer.trackVisibility.length; i++) { 288 392 layer.trackVisibility[table.convertRowIndexToModel(i)] = all || s.isSelectedIndex(i); 289 393 } … … 290 394 // layer has been changed 291 395 layer.invalidate(); 292 396 // ...sync with layer visibility instead to avoid having two ways to hide everything 293 layer.setVisible(v == 1|| !s.isSelectionEmpty());397 layer.setVisible(v == 2 || !s.isSelectionEmpty()); 294 398 } 295 399 } -
src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java
10 10 import java.util.Collection; 11 11 import java.util.Date; 12 12 import java.util.List; 13 import java.util.Map; 13 14 import java.util.Map.Entry; 14 15 15 16 import javax.swing.BorderFactory; … … 19 20 import javax.swing.JPanel; 20 21 import javax.swing.JRadioButton; 21 22 23 import org.openstreetmap.josm.data.gpx.Extensions; 22 24 import org.openstreetmap.josm.data.gpx.GpxConstants; 23 25 import org.openstreetmap.josm.data.gpx.GpxTrack; 24 26 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; … … 25 27 import org.openstreetmap.josm.data.gpx.WayPoint; 26 28 import org.openstreetmap.josm.data.osm.DataSet; 27 29 import org.openstreetmap.josm.data.osm.Node; 30 import org.openstreetmap.josm.data.osm.OsmPrimitive; 28 31 import org.openstreetmap.josm.data.osm.Way; 29 32 import org.openstreetmap.josm.gui.ExtendedDialog; 30 33 import org.openstreetmap.josm.gui.MainApplication; … … 64 67 List<Node> nodes = new ArrayList<>(); 65 68 for (WayPoint p : segment.getWayPoints()) { 66 69 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 } 84 } 70 addAttributes(p.attr, n, keys, check, none); 85 71 ds.addPrimitive(n); 86 72 nodes.add(n); 87 73 } 88 74 Way w = new Way(); 89 75 w.setNodes(nodes); 76 addAttributes(trk.getAttributes(), w, keys, check, none); 90 77 ds.addPrimitive(w); 91 78 } 92 79 } … … 122 109 return ds; 123 110 } 124 111 112 private static void addAttributes(Map<String, Object> attr, OsmPrimitive n, List<String> keys, boolean check, boolean none) { 113 for (Entry<String, Object> entry : attr.entrySet()) { 114 String key = entry.getKey(); 115 Object obj = entry.getValue(); 116 if (check && !keys.contains(key) && (obj instanceof String || obj instanceof Number || obj instanceof Date)) { 117 keys.add(key); 118 } 119 if (!none && (obj instanceof String || obj instanceof Number)) { 120 // only convert when required 121 n.put(GpxConstants.GPX_PREFIX + key, obj.toString()); 122 } else if (!none && obj instanceof Extensions) { 123 Extensions ext = (Extensions) obj; 124 String pre = ext.getPrefix(); 125 for (Entry<String, String> e : ext.entrySet()) { 126 String k = pre + e.getKey(); 127 if (check && !keys.contains(k)) { 128 keys.add(k); 129 } 130 n.put(k, e.getValue()); 131 } 132 } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) { 133 // timestamps should always be converted 134 Date date = (Date) obj; 135 if (!none) { //... but the tag will only be set when required 136 n.put(GpxConstants.GPX_PREFIX + key, DateUtils.fromDate(date)); 137 } 138 n.setTimestamp(date); 139 } 140 } 141 } 142 125 143 /** 126 144 * Filters the tags of the given {@link DataSet} 127 145 * @param ds The {@link DataSet} … … 131 149 */ 132 150 public DataSet filterDataSet(DataSet ds, List<String> listPos) { 133 151 Collection<Node> nodes = ds.getNodes(); 134 for ( Node n : nodes) {135 for (String key : n.keySet()) {152 for (OsmPrimitive p : ds.getPrimitives(p -> p instanceof Node || p instanceof Way)) { 153 for (String key : p.keySet()) { 136 154 if (listPos == null || !listPos.contains(key.substring(GpxConstants.GPX_PREFIX.length()))) { 137 n.put(key, null);155 p.put(key, null); 138 156 } 139 157 } 140 158 } -
src/org/openstreetmap/josm/gui/layer/gpx/CustomizeDrawingAction.java
9 9 import java.awt.event.ActionEvent; 10 10 import java.util.LinkedList; 11 11 import java.util.List; 12 import java.util.stream.Collectors; 12 13 13 14 import javax.swing.AbstractAction; 14 15 import javax.swing.Action; … … 59 60 60 61 @Override 61 62 public boolean supportLayers(List<Layer> layers) { 62 for (Layer layer : layers) { 63 if (!(layer instanceof GpxLayer)) { 64 return false; 65 } 66 } 67 return true; 63 return layers.stream().allMatch(l -> l instanceof GpxLayer); 68 64 } 69 65 70 66 @Override … … 79 75 80 76 @Override 81 77 public void actionPerformed(ActionEvent e) { 82 boolean hasLocal = false; 83 boolean hasNonlocal = false; 84 for (Layer layer : layers) { 85 if (layer instanceof GpxLayer) { 86 if (((GpxLayer) layer).isLocalFile()) { 87 hasLocal = true; 88 } else { 89 hasNonlocal = true; 90 } 91 } 92 } 93 GPXSettingsPanel panel = new GPXSettingsPanel(layers.get(0).getName(), hasLocal, hasNonlocal); 78 GPXSettingsPanel panel = new GPXSettingsPanel(layers.stream().filter(l -> l instanceof GpxLayer).map(l -> (GpxLayer) l).collect(Collectors.toList())); 94 79 JScrollPane scrollpane = GuiHelper.embedInVerticalScrollPane(panel); 95 80 scrollpane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 96 81 int screenHeight = GuiHelper.getScreenSize().height; … … 103 88 if (answer == JOptionPane.CANCEL_OPTION || answer == JOptionPane.CLOSED_OPTION) { 104 89 return; 105 90 } 106 for (Layer layer : layers) { 107 // save preferences for all layers 108 boolean f = false; 109 if (layer instanceof GpxLayer) { 110 f = ((GpxLayer) layer).isLocalFile(); 111 } 112 panel.savePreferences(layer.getName(), f); 113 } 114 MainApplication.getMap().repaint(); 91 panel.savePreferences(); 92 MainApplication.getMainPanel().repaint(); 93 layers.stream().forEach(l -> l.invalidate()); 115 94 } 116 95 117 96 } -
src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
32 32 import javax.swing.ImageIcon; 33 33 34 34 import org.openstreetmap.josm.data.Bounds; 35 import org.openstreetmap.josm.data.PreferencesUtils;36 35 import org.openstreetmap.josm.data.SystemOfMeasurement; 37 36 import org.openstreetmap.josm.data.SystemOfMeasurement.SoMChangeListener; 38 37 import org.openstreetmap.josm.data.coor.LatLon; … … 51 50 import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent; 52 51 import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent; 53 52 import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener; 53 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 54 54 import org.openstreetmap.josm.io.CachedFile; 55 55 import org.openstreetmap.josm.spi.preferences.Config; 56 56 import org.openstreetmap.josm.tools.ColorScale; … … 68 68 * The color that is used for drawing GPX points. 69 69 * @since 10824 70 70 */ 71 public static final NamedColorProperty DEFAULT_COLOR = new NamedColorProperty(marktr("gps point"), Color.magenta);71 public static final NamedColorProperty DEFAULT_COLOR_PROPERTY = new NamedColorProperty(marktr("gps point"), Color.magenta); 72 72 73 73 private final GpxData data; 74 74 private final GpxLayer layer; … … 78 78 // use alpha blending for line draw 79 79 private boolean alphaLines; 80 80 // draw direction arrows on the lines 81 private boolean direction;81 private boolean arrows; 82 82 /** width of line for paint **/ 83 83 private int lineWidth; 84 84 /** don't draw lines if longer than x meters **/ … … 90 90 private int largesize; 91 91 private boolean hdopCircle; 92 92 /** paint direction arrow with alternate math. may be faster **/ 93 private boolean a lternateDirection;93 private boolean arrowsFast; 94 94 /** don't draw arrows nearer to each other than this **/ 95 95 private int delta; 96 96 private double minTrackDurationForTimeColoring; … … 106 106 private Color computeCacheColorUsed; 107 107 private boolean computeCacheColorDynamic; 108 108 private ColorMode computeCacheColored; 109 private int computeCache ColorTracksTune;109 private int computeCacheVelocityTune; 110 110 private int computeCacheHeatMapDrawColorTableIdx; 111 111 private boolean computeCacheHeatMapDrawPointMode; 112 112 private int computeCacheHeatMapDrawGain; … … 116 116 /** Mode of the line coloring **/ 117 117 private ColorMode colored; 118 118 /** max speed for coloring - allows to tweak line coloring for different speed levels. **/ 119 private int colorTracksTune;119 private int velocityTune; 120 120 private boolean colorModeDynamic; 121 121 private Color neutralColor; 122 122 private int largePointAlpha; … … 127 127 private ColorScale hdopScale; 128 128 private ColorScale qualityScale; 129 129 private ColorScale dateScale; 130 private ColorScale directionScale;130 private ColorScale arrowScale; 131 131 132 132 /** Opacity for hdop points **/ 133 133 private int hdopAlpha; … … 145 145 146 146 /** heat map parameters **/ 147 147 148 // enabled or not (override by settings)149 private boolean heatMapEnabled;150 148 // draw small extra line 151 149 private boolean heatMapDrawExtraLine; 152 150 // used index for color table (parameter) … … 199 197 hdopScale = ColorScale.createHSBScale(256).makeReversed().addTitle(tr("HDOP")); 200 198 qualityScale = ColorScale.createFixedScale(rtkLibQualityColors).addTitle(tr("Quality")); 201 199 dateScale = ColorScale.createHSBScale(256).addTitle(tr("Time")); 202 directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction"));200 arrowScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction")); 203 201 204 202 systemOfMeasurementChanged(null, null); 205 203 } … … 268 266 setupColors(); 269 267 } 270 268 271 private static String specName(String layerName) {272 return "layer " + layerName;273 }274 275 269 /** 276 * Get the default color for gps tracks for specified layer277 * @param layerName name of the GpxLayer278 * @param ignoreCustom do not use preferences279 * @return the color or null if the color is not constant280 */281 public Color getColor(String layerName, boolean ignoreCustom) {282 if (ignoreCustom || getColorMode(layerName) == ColorMode.NONE) {283 return DEFAULT_COLOR.getChildColor(284 NamedColorProperty.COLOR_CATEGORY_LAYER,285 layerName,286 DEFAULT_COLOR.getName()).get();287 } else {288 return null;289 }290 }291 292 /**293 270 * Read coloring mode for specified layer from preferences 294 * @param layerName name of the GpxLayer295 271 * @return coloring mode 296 272 */ 297 public ColorMode getColorMode( String layerName) {273 public ColorMode getColorMode() { 298 274 try { 299 int i = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colors", specName(layerName), 0); 275 int i = optInt("colormode"); 276 if (i == -1) i = 0; //global 300 277 return ColorMode.fromIndex(i); 301 278 } catch (IndexOutOfBoundsException e) { 302 279 Logging.warn(e); … … 304 281 return ColorMode.NONE; 305 282 } 306 283 307 /** Reads generic color from preferences (usually gray) 308 * @return the color 309 **/ 310 public static Color getGenericColor() { 311 return DEFAULT_COLOR.get(); 284 private String opt(String key) { 285 return GPXSettingsPanel.getLayerPref(layer, key); 312 286 } 313 287 288 private boolean optBool(String key) { 289 return Boolean.parseBoolean(opt(key)); 290 } 291 292 private int optInt(String key) { 293 return GPXSettingsPanel.getLayerPrefInt(layer, key); 294 } 295 314 296 /** 315 297 * Read all drawing-related settings from preferences 316 * @param layerName layer name used to access its specific preferences317 298 **/ 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);299 public void readPreferences() { 300 forceLines = optBool("lines.force"); 301 arrows = optBool("lines.arrows"); 302 arrowsFast = optBool("lines.arrows.fast"); 303 lineWidth = optInt("lines.width"); 304 alphaLines = optBool("lines.alpha-blend"); 324 305 306 int l = optInt("lines"); 325 307 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);308 maxLineLength = optInt("lines.max-length.local"); 309 lines = l != 0; //draw for -1 (default), 1 (local) and 2 (all) 328 310 } 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);311 maxLineLength = optInt("lines.max-length"); 312 lines = l == 2; //draw only for 2 (all) 331 313 } 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); 314 large = optBool("points.large"); 315 largesize = optInt("points.large.size"); 316 hdopCircle = optBool("points.hdopcircle"); 317 colored = getColorMode(); 318 delta = optInt("lines.arrows.min-distance"); 319 velocityTune = optInt("colormode.velocity.tune"); 320 colorModeDynamic = optBool("colormode.dynamic-range"); 340 321 /* good HDOP's are between 1 and 3, very bad HDOP's go into 3 digit values */ 341 322 hdoprange = Config.getPref().getInt("hdop.range", 7); 342 323 minTrackDurationForTimeColoring = Config.getPref().getInt("draw.rawgps.date-coloring-min-dt", 60); … … 343 324 largePointAlpha = Config.getPref().getInt("draw.rawgps.large.alpha", -1) & 0xFF; 344 325 345 326 // 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); 327 heatMapDrawExtraLine = optBool("colormode.heatmap.line-extra"); 328 heatMapDrawColorTableIdx = optInt("colormode.heatmap.colormap"); 329 heatMapDrawPointMode = optBool("colormode.heatmap.use-points"); 330 heatMapDrawGain = optInt("colormode.heatmap.gain"); 331 heatMapDrawLowerLimit = optInt("colormode.heatmap.lower-limit"); 352 332 353 333 // shrink to range 354 334 heatMapDrawGain = Utils.clamp(heatMapDrawGain, -10, 10); 355 356 neutralColor = getColor(layerName, true); 335 neutralColor = DEFAULT_COLOR_PROPERTY.get(); 357 336 velocityScale.setNoDataColor(neutralColor); 358 337 dateScale.setNoDataColor(neutralColor); 359 338 hdopScale.setNoDataColor(neutralColor); 360 339 qualityScale.setNoDataColor(neutralColor); 361 directionScale.setNoDataColor(neutralColor);340 arrowScale.setNoDataColor(neutralColor); 362 341 363 342 largesize += lineWidth; 364 343 } … … 368 347 Bounds clipBounds = graphics.getClipBounds().getLatLonBoundsBox(); 369 348 List<WayPoint> visibleSegments = listVisibleSegments(clipBounds); 370 349 if (!visibleSegments.isEmpty()) { 371 readPreferences( layer.getName());350 readPreferences(); 372 351 drawAll(graphics.getDefaultGraphics(), graphics.getMapView(), visibleSegments, clipBounds); 373 352 if (graphics.getMapView().getLayerManager().getActiveLayer() == layer) { 374 353 drawColorBar(graphics.getDefaultGraphics(), graphics.getMapView()); … … 462 441 } 463 442 464 443 // global enabled or select via color 465 boolean useHeatMap = heatMapEnabled ||ColorMode.HEATMAP == colored;444 boolean useHeatMap = ColorMode.HEATMAP == colored; 466 445 467 446 // default global alpha level 468 447 float layerAlpha = 1.00f; … … 570 549 } 571 550 oldWp = null; 572 551 } else { // color mode not dynamic 573 velocityScale.setRange(0, colorTracksTune);552 velocityScale.setRange(0, velocityTune); 574 553 hdopScale.setRange(0, hdoprange); 575 554 qualityScale.setRange(1, rtkLibQualityColors.length); 576 555 } … … 594 573 } 595 574 for (WayPoint trkPnt : segment) { 596 575 LatLon c = trkPnt.getCoor(); 597 trkPnt.customColoring = neutralColor;576 trkPnt.customColoring = segment.getColor(); 598 577 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) { 599 578 continue; 600 579 } … … 620 599 break; 621 600 case DIRECTION: 622 601 double dirColor = oldWp.getCoor().bearing(trkPnt.getCoor()); 623 color = directionScale.getColor(dirColor);602 color = arrowScale.getColor(dirColor); 624 603 break; 625 604 case TIME: 626 605 double t = trkPnt.getTime(); … … 642 621 } 643 622 } else { // make sure we reset outdated data 644 623 trkPnt.drawLine = false; 645 color = neutralColor;624 color = segment.getColor(); 646 625 } 647 626 if (color != null) { 648 627 trkPnt.customColoring = color; … … 700 679 /**************************************************************** 701 680 ********** STEP 3b - DRAW NICE ARROWS ************************** 702 681 ****************************************************************/ 703 if (lines && direction && !alternateDirection) {682 if (lines && arrows && !arrowsFast) { 704 683 Point old = null; 705 684 Point oldA = null; // last arrow painted 706 685 for (WayPoint trkPnt : visibleSegments) { … … 730 709 /**************************************************************** 731 710 ********** STEP 3c - DRAW FAST ARROWS ************************** 732 711 ****************************************************************/ 733 if (lines && direction && alternateDirection) {712 if (lines && arrows && arrowsFast) { 734 713 Point old = null; 735 714 Point oldA = null; // last arrow painted 736 715 for (WayPoint trkPnt : visibleSegments) { … … 1480 1459 // CHECKSTYLE.OFF: BooleanExpressionComplexity 1481 1460 if ((computeCacheMaxLineLengthUsed != maxLineLength) 1482 1461 || (computeCacheColored != colored) 1483 || (computeCache ColorTracksTune != colorTracksTune)1462 || (computeCacheVelocityTune != velocityTune) 1484 1463 || (computeCacheColorDynamic != colorModeDynamic) 1485 1464 || (computeCacheHeatMapDrawColorTableIdx != heatMapDrawColorTableIdx) 1486 1465 || (!neutralColor.equals(computeCacheColorUsed) … … 1493 1472 computeCacheInSync = false; 1494 1473 computeCacheColorUsed = neutralColor; 1495 1474 computeCacheColored = colored; 1496 computeCache ColorTracksTune = colorTracksTune;1475 computeCacheVelocityTune = velocityTune; 1497 1476 computeCacheColorDynamic = colorModeDynamic; 1498 1477 computeCacheHeatMapDrawColorTableIdx = heatMapDrawColorTableIdx; 1499 1478 computeCacheHeatMapDrawPointMode = heatMapDrawPointMode; … … 1529 1508 SystemOfMeasurement som = SystemOfMeasurement.getSystemOfMeasurement(); 1530 1509 velocityScale.drawColorBar(g, w-30, 50, 20, 100, som.speedValue); 1531 1510 } else if (colored == ColorMode.DIRECTION) { 1532 directionScale.drawColorBar(g, w-30, 50, 20, 100, 180.0/Math.PI);1511 arrowScale.drawColorBar(g, w-30, 50, 20, 100, 180.0/Math.PI); 1533 1512 } 1534 1513 } 1535 1514 -
src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java
103 103 GpxLink link = new GpxLink(audioUrl.toString()); 104 104 link.type = "audio"; 105 105 wpt.put(GpxConstants.META_LINKS, Collections.singleton(link)); 106 wpt.addExtension ("offset", Double.toString(offset));107 wpt.addExtension ("sync-offset", Double.toString(syncOffset));106 wpt.addExtensionKey(GpxConstants.EXTENSIONS_JOSM, "offset", Double.toString(offset)); 107 wpt.addExtensionKey(GpxConstants.EXTENSIONS_JOSM, "sync-offset", Double.toString(syncOffset)); 108 108 return wpt; 109 109 } 110 110 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/DefaultMarkerProducers.java
45 45 return Collections.singleton(marker); 46 46 } else if (Utils.hasExtension(urlStr, "wav", "mp3", "aac", "aif", "aiff")) { 47 47 final AudioMarker audioMarker = new AudioMarker(wpt.getCoor(), wpt, url, parentLayer, time, offset); 48 Extensions exts = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);49 if ( exts != null && exts.containsKey("offset")) {48 Extensions josmExts = (Extensions) wpt.get(GpxConstants.EXTENSIONS_JOSM); 49 if (josmExts != null && josmExts.containsKey("sync-offset")) { 50 50 try { 51 audioMarker.syncOffset = Double.parseDouble( exts.get("sync-offset"));51 audioMarker.syncOffset = Double.parseDouble(josmExts.get("sync-offset")); 52 52 } catch (NumberFormatException nfe) { 53 53 Logging.warn(nfe); 54 54 } -
src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
257 257 WayPoint wpt = new WayPoint(getCoor()); 258 258 wpt.setTimeInMillis((long) (time * 1000)); 259 259 if (text != null) { 260 wpt.addExtension ("text", text);260 wpt.addExtensionKey(GpxConstants.EXTENSIONS_JOSM, "text", text); 261 261 } else if (dataProvider != null) { 262 262 for (String key : dataProvider.getTemplateKeys()) { 263 263 Object value = dataProvider.getTemplateValue(key, false); -
src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
20 20 import java.util.Collection; 21 21 import java.util.Comparator; 22 22 import java.util.List; 23 import java.util.Optional; 23 24 24 25 import javax.swing.AbstractAction; 25 26 import javax.swing.Action; … … 48 49 import org.openstreetmap.josm.gui.layer.JumpToMarkerActions.JumpToPreviousMarker; 49 50 import org.openstreetmap.josm.gui.layer.Layer; 50 51 import org.openstreetmap.josm.gui.layer.gpx.ConvertFromMarkerLayerAction; 52 import org.openstreetmap.josm.gui.preferences.display.GPXSettingsPanel; 51 53 import org.openstreetmap.josm.io.audio.AudioPlayer; 52 54 import org.openstreetmap.josm.spi.preferences.Config; 53 55 import org.openstreetmap.josm.tools.ImageProvider; … … 75 77 public GpxLayer fromLayer; 76 78 private Marker currentMarker; 77 79 public AudioMarker syncAudioMarker; 80 private Color color, realcolor; 78 81 79 private static final Color DEFAULT_COLOR = Color.magenta; 80 private static final NamedColorProperty COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), DEFAULT_COLOR); 82 /** 83 * The default color that is used for drawing markers. 84 */ 85 public static final NamedColorProperty DEFAULT_COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), Color.magenta); 81 86 82 87 /** 83 88 * Constructs a new {@code MarkerLayer}. … … 94 99 double firstTime = -1.0; 95 100 String lastLinkedFile = ""; 96 101 102 Color c = null; 103 String cs = GPXSettingsPanel.tryGetLayerPrefLocal(indata, "markers.color"); 104 if (cs != null) { 105 try { 106 c = Color.decode(cs); 107 } catch (NumberFormatException ex) { 108 Logging.warn("Could not read marker color: " + cs); 109 } 110 } 111 setPrivateColors(c); 112 97 113 for (WayPoint wpt : indata.waypoints) { 98 114 /* calculate time differences in waypoints */ 99 115 double time = wpt.getTime(); … … 123 139 // audio file) calculate the offset relative to the first marker of 124 140 // that group. This way the user can jump to the corresponding 125 141 // playback positions in a long audio track. 126 Extensions exts = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);127 if ( exts != null && exts.containsKey("offset")) {142 Extensions josmExts = (Extensions) wpt.get(GpxConstants.EXTENSIONS_JOSM); 143 if (josmExts != null && josmExts.containsKey("offset")) { 128 144 try { 129 offset = Double.valueOf( exts.get("offset"));145 offset = Double.valueOf(josmExts.get("offset")); 130 146 } catch (NumberFormatException nfe) { 131 147 Logging.warn(nfe); 132 148 } … … 173 189 } 174 190 175 191 @Override 176 protected NamedColorProperty getBaseColorProperty() {177 return COLOR_PROPERTY;178 }179 180 /* for preferences */181 public static Color getGenericColor() {182 return COLOR_PROPERTY.get();183 }184 185 @Override186 192 public void paint(Graphics2D g, MapView mv, Bounds box) { 187 193 boolean showTextOrIcon = isTextOrIconShown(); 188 g.setColor(getColorProperty().get()); 189 194 g.setColor(realcolor); 190 195 if (mousePressed) { 191 196 boolean mousePressedTmp = mousePressed; 192 197 Point mousePos = mv.getMousePosition(); // Get mouse position only when necessary (it's the slowest part of marker layer painting) … … 450 455 * @return <code>true</code> if text should be shown, <code>false</code> otherwise. 451 456 */ 452 457 private boolean isTextOrIconShown() { 453 String current = Config.getPref().get("marker.show "+getName(), "show"); 454 return "show".equalsIgnoreCase(current); 458 return Boolean.parseBoolean(GPXSettingsPanel.getLayerPref(fromLayer, "markers.show-text")); 455 459 } 456 460 461 @Override 462 public boolean hasColor() { 463 return true; 464 } 465 466 @Override 467 public Color getColor() { 468 return color; 469 } 470 471 @Override 472 public void setColor(Color color) { 473 setPrivateColors(color); 474 if (fromLayer != null) { 475 String cs = null; 476 if (color != null) { 477 cs = String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue()); 478 } 479 GPXSettingsPanel.putLayerPrefLocal(fromLayer, "markers.color", cs); 480 } 481 invalidate(); 482 } 483 484 private void setPrivateColors(Color color) { 485 this.color = color; 486 this.realcolor = Optional.ofNullable(color).orElse(DEFAULT_COLOR_PROPERTY.get()); 487 } 488 457 489 private final class MarkerMouseAdapter extends MouseAdapter { 458 490 @Override 459 491 public void mousePressed(MouseEvent e) { … … 503 535 504 536 @Override 505 537 public void actionPerformed(ActionEvent e) { 506 Config.getPref().put("marker.show "+layer.getName(), layer.isTextOrIconShown() ? "hide" : "show");538 GPXSettingsPanel.putLayerPrefLocal(layer.fromLayer, "markers.show-text", Boolean.toString(!layer.isTextOrIconShown())); 507 539 layer.invalidate(); 508 540 } 509 541 -
src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java
390 390 PaintColors.values(); 391 391 ConflictColors.getColors(); 392 392 Severity.getColors(); 393 MarkerLayer. getGenericColor();394 GpxDrawHelper. getGenericColor();393 MarkerLayer.DEFAULT_COLOR_PROPERTY.get(); 394 GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get(); 395 395 OsmDataLayer.getOutsideColor(); 396 396 MapScaler.getColor(); 397 397 MapStatus.getColors(); -
src/org/openstreetmap/josm/gui/preferences/display/GPXSettingsPanel.java
4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 import static org.openstreetmap.josm.tools.I18n.trc; 6 6 7 import java.awt.Color;8 7 import java.awt.Component; 9 8 import java.awt.Dimension; 10 9 import java.awt.GridBagLayout; 11 10 import java.awt.event.ActionListener; 12 11 import java.util.Enumeration; 12 import java.util.HashMap; 13 import java.util.List; 14 import java.util.Map; 15 import java.util.Optional; 13 16 14 17 import javax.swing.AbstractButton; 15 18 import javax.swing.BorderFactory; … … 22 25 import javax.swing.JRadioButton; 23 26 import javax.swing.JSlider; 24 27 28 import org.apache.commons.jcs.access.exception.InvalidArgumentException; 25 29 import org.openstreetmap.josm.actions.ExpertToggleAction; 26 import org.openstreetmap.josm.data.PreferencesUtils; 27 import org.openstreetmap.josm.data.preferences.NamedColorProperty; 30 import org.openstreetmap.josm.data.gpx.GpxData; 28 31 import org.openstreetmap.josm.gui.MainApplication; 32 import org.openstreetmap.josm.gui.layer.GpxLayer; 29 33 import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper; 30 34 import org.openstreetmap.josm.gui.layer.markerlayer.Marker; 31 35 import org.openstreetmap.josm.gui.layer.markerlayer.Marker.TemplateEntryProperty; … … 67 71 private final JRadioButton colorTypeQuality = new JRadioButton(tr("Quality (RTKLib only, if available)")); 68 72 private final JRadioButton colorTypeTime = new JRadioButton(tr("Track date")); 69 73 private final JRadioButton colorTypeHeatMap = new JRadioButton(tr("Heat Map (dark = few, bright = many)")); 70 private final JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized for named layers)"));74 private final JRadioButton colorTypeNone = new JRadioButton(tr("Single Color (can be customized in the layer manager)")); 71 75 private final JRadioButton colorTypeGlobal = new JRadioButton(tr("Use global settings")); 72 76 private final JosmComboBox<String> colorTypeVelocityTune = new JosmComboBox<>(new String[] {tr("Car"), tr("Bicycle"), tr("Foot")}); 73 77 private final JosmComboBox<String> colorTypeHeatMapTune = new JosmComboBox<>(new String[] { … … 93 97 private final JCheckBox useGpsAntialiasing = new JCheckBox(tr("Smooth GPX graphics (antialiasing)")); 94 98 private final JCheckBox drawLineWithAlpha = new JCheckBox(tr("Draw with Opacity (alpha blending) ")); 95 99 96 private String layerName; 97 private final boolean local; // flag to display LocalOnly checkbox 98 private final boolean nonlocal; // flag to display AllLines checkbox 100 private final List<GpxLayer> layers; 101 private final GpxLayer firstLayer; 102 private final boolean global; // global settings vs. layer specific settings 103 private final boolean hasLocalFile; // flag to display LocalOnly checkbooks 104 private final boolean hasNonLocalFile; // flag to display AllLines checkbox 99 105 106 private final static Map<String, Object> defaults = new HashMap<String, Object>() {{ 107 put("colormode", -1); 108 put("colormode.dynamic-range", false); 109 put("colormode.heatmap.colormap", 0); 110 put("colormode.heatmap.gain", 0); 111 put("colormode.heatmap.line-extra", false); //Einstein only 112 put("colormode.heatmap.lower-limit", 0); 113 put("colormode.heatmap.use-points", false); 114 put("colormode.velocity.tune", 45); 115 put("lines", -1); 116 put("lines.alpha-blend", false); 117 put("lines.arrows", false); 118 put("lines.arrows.fast", false); 119 put("lines.arrows.min-distance", 40); 120 put("lines.force", false); 121 put("lines.max-length", 200); 122 put("lines.max-length.local", -1); 123 put("lines.width", 0); 124 put("markers.color", ""); 125 put("markers.show-text", true); 126 put("points.hdopcircle", false); 127 put("points.large", false); 128 put("points.large.size", 3); //Einstein only 129 }}; 130 100 131 /** 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 132 * Constructs a new {@code GPXSettingsPanel} for the given layers. 133 * @param layers the GPX layers 105 134 */ 106 public GPXSettingsPanel( String layerName, boolean local, boolean nonlocal) {135 public GPXSettingsPanel(List<GpxLayer> layers) { 107 136 super(new GridBagLayout()); 108 this.local = local; 109 this.nonlocal = nonlocal; 110 this.layerName = "layer "+layerName; 137 this.layers = layers; 138 if (layers == null || layers.size() == 0) { 139 throw new InvalidArgumentException("At least one layer required"); 140 } 141 firstLayer = layers.get(0); 142 global = false; 143 hasLocalFile = layers.stream().anyMatch(GpxLayer::isLocalFile); 144 hasNonLocalFile = layers.stream().anyMatch(l -> !l.isLocalFile()); 111 145 initComponents(); 112 146 loadPreferences(); 113 147 } … … 117 151 */ 118 152 public GPXSettingsPanel() { 119 153 super(new GridBagLayout()); 154 layers = null; 155 firstLayer = null; 156 global = hasLocalFile = hasNonLocalFile = true; 120 157 initComponents(); 121 local = false;122 nonlocal = false;123 158 loadPreferences(); // preferences -> controls 124 159 } 125 160 161 /** 162 * Reads the preference for the given layer or the default preference if not available 163 * @param layer the GpxLayer. Can be <code>null</code>, default preference will be returned then 164 * @param key the drawing key to be read, without "draw.rawgps." 165 * @return the value 166 */ 167 public static String getLayerPref(GpxLayer layer, String key) { 168 Object d = defaults.get(key); 169 String ds; 170 if (d != null) { 171 ds = d.toString(); 172 } else { 173 Logging.warn("No default value found for layer preference \"" + key + "\"."); 174 ds = null; 175 } 176 return Optional.ofNullable(tryGetLayerPrefLocal(layer, key)).orElse(Config.getPref().get("draw.rawgps." + key, ds)); 177 } 178 179 /** 180 * Reads the integer preference for the given layer or the default preference if not available 181 * @param layer the GpxLayer. Can be <code>null</code>, default preference will be returned then 182 * @param key the drawing key to be read, without "draw.rawgps." 183 * @return the integer value 184 */ 185 public static int getLayerPrefInt(GpxLayer layer, String key) { 186 try { 187 return Integer.parseInt(getLayerPref(layer, key)); 188 } catch (NumberFormatException ex) { 189 Object d = defaults.get(key); 190 if (d instanceof Integer) { 191 return (int) d; 192 } else { 193 Logging.warn("No valid default value found for layer preference \"" + key + "\"."); 194 return 0; 195 } 196 } 197 } 198 199 /** 200 * Try to read the preference for the given layer 201 * @param layer the GpxLayer 202 * @param key the drawing key to be read, without "draw.rawgps." 203 * @return the value or <code>null</code> if not found 204 */ 205 public static String tryGetLayerPrefLocal(GpxLayer layer, String key) { 206 return layer != null ? tryGetLayerPrefLocal(layer.data, key) : null; 207 } 208 209 /** 210 * Try to read the preference for the given GpxData 211 * @param data the GpxData 212 * @param key the drawing key to be read, without "draw.rawgps." 213 * @return the value or <code>null</code> if not found 214 */ 215 public static String tryGetLayerPrefLocal(GpxData data, String key) { 216 return data != null ? data.layerPrefs.get(key) : null; 217 } 218 219 /** 220 * Puts the preference for the given layers or the default preference if layers is <code>null</code> 221 * @param layers List of <code>GpxLayer</code> to put the drawingOptions 222 * @param key the drawing key to be written, without "draw.rawgps." 223 * @param value (can be <code>null</code> to remove option) 224 */ 225 public static void putLayerPref(List<GpxLayer> layers, String key, Object value) { 226 String v = value == null ? null : value.toString(); 227 if (layers != null) { 228 for (GpxLayer l : layers) { 229 putLayerPrefLocal(l.data, key, v); 230 } 231 } else { 232 Config.getPref().put("draw.rawgps." + key, v); 233 } 234 } 235 236 /** 237 * Puts the preference for the given layer 238 * @param layer <code>GpxLayer</code> to put the drawingOptions 239 * @param key the drawing key to be written, without "draw.rawgps." 240 * @param value the value or <code>null</code> to remove key 241 */ 242 public static void putLayerPrefLocal(GpxLayer layer, String key, String value) { 243 if (layer == null) return; 244 putLayerPrefLocal(layer.data, key, value); 245 } 246 247 /** 248 * Puts the preference for the given layer 249 * @param data <code>GpxData</code> to put the drawingOptions. Must not be <code>null</code> 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(GpxData data, String key, String value) { 254 if (value == null || value.isBlank() || (defaults.get(key) != null && defaults.get(key).toString().equals(value))) { 255 data.layerPrefs.remove(key); 256 } else { 257 data.layerPrefs.put(key, value); 258 } 259 data.setModified(); 260 } 261 262 private String pref(String key) { 263 return getLayerPref(firstLayer, key); 264 } 265 266 private boolean prefBool(String key) { 267 return Boolean.parseBoolean(pref(key)); 268 } 269 270 private int prefInt(String key) { 271 return getLayerPrefInt(firstLayer, key); 272 } 273 274 private int prefIntLocal(String key) { 275 try { 276 return Integer.parseInt(tryGetLayerPrefLocal(firstLayer, key)); 277 } catch (NumberFormatException ex) { 278 return -1; 279 } 280 281 } 282 283 private void putPref(String key, Object value) { 284 putLayerPref(layers, key, value); 285 } 286 126 287 // CHECKSTYLE.OFF: ExecutableStatementCountCheck 127 288 private void initComponents() { 128 289 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 129 290 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)); 291 if (global) { 292 // makeAutoMarkers 293 makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer.")); 294 ExpertToggleAction.addVisibilitySwitcher(makeAutoMarkers); 295 add(makeAutoMarkers, GBC.eol().insets(20, 0, 0, 5)); 296 } 134 297 135 298 // drawRawGpsLines 136 299 ButtonGroup gpsLinesGroup = new ButtonGroup(); 137 if ( layerName != null) {300 if (!global) { 138 301 gpsLinesGroup.add(drawRawGpsLinesGlobal); 139 302 } 140 303 gpsLinesGroup.add(drawRawGpsLinesNone); … … 145 308 146 309 JLabel label = new JLabel(tr("Draw lines between raw GPS points")); 147 310 add(label, GBC.eol().insets(20, 0, 0, 0)); 148 if ( layerName != null) {311 if (!global) { 149 312 add(drawRawGpsLinesGlobal, GBC.eol().insets(40, 0, 0, 0)); 150 313 } 151 314 add(drawRawGpsLinesNone, GBC.eol().insets(40, 0, 0, 0)); 152 if ( layerName == null || local) {315 if (hasLocalFile) { 153 316 add(drawRawGpsLinesLocal, GBC.eol().insets(40, 0, 0, 0)); 154 317 } 155 if ( layerName == null || nonlocal) {318 if (hasNonLocalFile) { 156 319 add(drawRawGpsLinesAll, GBC.eol().insets(40, 0, 0, 0)); 157 320 } 158 321 ExpertToggleAction.addVisibilitySwitcher(label); … … 242 405 243 406 // colorTracks 244 407 ButtonGroup colorGroup = new ButtonGroup(); 245 if ( layerName != null) {408 if (!global) { 246 409 colorGroup.add(colorTypeGlobal); 247 410 } 248 411 colorGroup.add(colorTypeNone); … … 253 416 colorGroup.add(colorTypeTime); 254 417 colorGroup.add(colorTypeHeatMap); 255 418 256 colorTypeNone.setToolTipText(tr("All points and track segments will have the samecolor. Can be customized in Layer Manager."));419 colorTypeNone.setToolTipText(tr("All points and track segments will have their own color. Can be customized in Layer Manager.")); 257 420 colorTypeVelocity.setToolTipText(tr("Colors points and track segments by velocity.")); 258 421 colorTypeDirection.setToolTipText(tr("Colors points and track segments by direction.")); 259 422 colorTypeDilution.setToolTipText( … … 272 435 add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0)); 273 436 274 437 add(new JLabel(tr("Track and Point Coloring")), GBC.eol().insets(20, 0, 0, 0)); 275 if ( layerName != null) {438 if (!global) { 276 439 add(colorTypeGlobal, GBC.eol().insets(40, 0, 0, 0)); 277 440 } 278 441 add(colorTypeNone, GBC.eol().insets(40, 0, 0, 0)); … … 331 494 if (null != dim) { 332 495 // get image size of environment 333 496 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)); 497 colorTypeHeatIconLabel.setIcon(GpxDrawHelper.getColorMapImageIcon( 498 GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get(), 499 colorTypeHeatMapTune.getSelectedIndex(), 500 iconSize)); 343 501 } 344 502 }); 345 503 … … 353 511 add(colorDynamic, GBC.eop().insets(40, 0, 0, 0)); 354 512 ExpertToggleAction.addVisibilitySwitcher(colorDynamic); 355 513 356 if ( layerName == null) {514 if (global) { 357 515 // Setting waypoints for gpx layer doesn't make sense - waypoints are shown in marker layer that has different name - so show 358 516 // this only for global config 359 517 … … 363 521 label.setLabelFor(waypointLabel); 364 522 add(waypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 365 523 waypointLabel.addActionListener(e -> updateWaypointPattern(waypointLabel, waypointLabelPattern)); 366 updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, TemplateEntryProperty.forMarker( layerName));524 updateWaypointLabelCombobox(waypointLabel, waypointLabelPattern, TemplateEntryProperty.forMarker(null)); 367 525 add(waypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5)); 368 526 ExpertToggleAction.addVisibilitySwitcher(label); 369 527 ExpertToggleAction.addVisibilitySwitcher(waypointLabel); … … 379 537 label.setLabelFor(audioWaypointLabel); 380 538 add(audioWaypointLabel, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5)); 381 539 audioWaypointLabel.addActionListener(e -> updateWaypointPattern(audioWaypointLabel, audioWaypointLabelPattern)); 382 updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, TemplateEntryProperty.forAudioMarker( layerName));540 updateWaypointLabelCombobox(audioWaypointLabel, audioWaypointLabelPattern, TemplateEntryProperty.forAudioMarker(null)); 383 541 add(audioWaypointLabelPattern, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 5)); 384 542 ExpertToggleAction.addVisibilitySwitcher(label); 385 543 ExpertToggleAction.addVisibilitySwitcher(audioWaypointLabel); … … 395 553 */ 396 554 public final void loadPreferences() { 397 555 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 556 int lines = global ? prefInt("lines") : prefIntLocal("lines"); 557 if (lines == 2 && hasNonLocalFile) { 558 drawRawGpsLinesAll.setSelected(true); 559 } else if ((lines == 1 && hasLocalFile) || (lines == -1 && global)) { 560 drawRawGpsLinesLocal.setSelected(true); 561 } else if (lines == 0) { 562 drawRawGpsLinesNone.setSelected(true); 563 } else if (lines == -1) { 401 564 drawRawGpsLinesGlobal.setSelected(true); 402 565 } 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 } 566 Logging.warn("Unknown line type: " + lines); 411 567 } 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)); 568 drawRawGpsMaxLineLengthLocal.setText(pref("lines.max-length.local")); 569 drawRawGpsMaxLineLength.setText(pref("lines.max-length")); 570 drawLineWidth.setText(pref("lines.width")); 571 drawLineWithAlpha.setSelected(prefBool("lines.alpha-blend")); 572 forceRawGpsLines.setSelected(prefBool("lines.force")); 573 drawGpsArrows.setSelected(prefBool("lines.arrows")); 574 drawGpsArrowsFast.setSelected(prefBool("lines.arrows.fast")); 575 drawGpsArrowsMinDist.setText(pref("lines.arrows.min-distance")); 576 hdopCircleGpsPoints.setSelected(prefBool("points.hdopcircle")); 577 largeGpsPoints.setSelected(prefBool("points.large")); 433 578 useGpsAntialiasing.setSelected(Config.getPref().getBoolean("mappaint.gpx.use-antialiasing", false)); 434 579 435 580 drawRawGpsLinesActionListener.actionPerformed(null); 436 437 if (layerName != null && Config.getPref().get("draw.rawgps.colors."+layerName).isEmpty()) { 581 if (!global && prefIntLocal("colormode") == -1) { 438 582 colorTypeGlobal.setSelected(true); 439 583 colorDynamic.setSelected(false); 440 584 colorDynamic.setEnabled(false); … … 442 586 colorTypeHeatMapGain.setValue(0); 443 587 colorTypeHeatMapLowerLimit.setValue(0); 444 588 } else { 445 int colorType = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colors", layerName, 0);589 int colorType = prefInt("colormode"); 446 590 switch (colorType) { 447 case 0: colorTypeNone.setSelected(true); break;591 case -1: case 0: colorTypeNone.setSelected(true); break; 448 592 case 1: colorTypeVelocity.setSelected(true); break; 449 593 case 2: colorTypeDilution.setSelected(true); break; 450 594 case 3: colorTypeDirection.setSelected(true); break; … … 453 597 case 6: colorTypeQuality.setSelected(true); break; 454 598 default: Logging.warn("Unknown color type: " + colorType); 455 599 } 456 int ccts = PreferencesUtils.getInteger(Config.getPref(), "draw.rawgps.colorTracksTune", layerName, 45);600 int ccts = prefInt("colormode.velocity.tune"); 457 601 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)); 602 colorTypeHeatMapTune.setSelectedIndex(prefInt("colormode.heatmap.colormap")); 603 colorDynamic.setSelected(prefBool("colormode.dynamic-range")); 604 colorTypeHeatMapPoints.setSelected(prefBool("colormode.heatmap.use-points")); 605 colorTypeHeatMapGain.setValue(prefInt("colormode.heatmap.gain")); 606 colorTypeHeatMapLowerLimit.setValue(prefInt("colormode.heatmap.lower-limit")); 468 607 } 469 608 } 470 609 471 610 /** 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 611 * Save preferences from UI controls, globally or for the specified layers. 475 612 * @return {@code true} when restart is required, {@code false} otherwise 476 613 */ 477 public boolean savePreferences(String layerName, boolean locLayer) { 478 String layerNameDot = ".layer "+layerName; 479 if (layerName == null) { 480 layerNameDot = ""; 614 public boolean savePreferences() { 615 if (global) { 616 Config.getPref().putBoolean("marker.makeautomarkers", makeAutoMarkers.isSelected()); 481 617 } 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); 618 if (!global && drawRawGpsLinesGlobal.isSelected()) { 619 putPref("lines", null); 620 putPref("lines.max-length", null); 621 putPref("lines.max-length.local", null); 622 putPref("lines.force", null); 623 putPref("lines.arrows", null); 624 putPref("lines.arrows.fast", null); 625 putPref("lines.arrows.min-distance", null); 492 626 } 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()); 627 if (drawRawGpsLinesNone.isSelected()) { 628 putPref("lines", 0); 629 } else if (drawRawGpsLinesLocal.isSelected()) { 630 putPref("lines", 1); 631 } else if (drawRawGpsLinesAll.isSelected()) { 632 putPref("lines", 2); 496 633 } 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()); 634 putPref("lines.max-length", drawRawGpsMaxLineLength.getText()); 635 putPref("lines.max-length.local", drawRawGpsMaxLineLengthLocal.getText()); 636 putPref("lines.force", forceRawGpsLines.isSelected()); 637 putPref("lines.arrows", drawGpsArrows.isSelected()); 638 putPref("lines.arrows.fast", drawGpsArrowsFast.isSelected()); 639 putPref("lines.arrows.min-distance", drawGpsArrowsMinDist.getText()); 507 640 } 508 641 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());642 putPref("points.hdopcircle", hdopCircleGpsPoints.isSelected()); 643 putPref("points.large", largeGpsPoints.isSelected()); 644 putPref("lines.width", drawLineWidth.getText()); 645 putPref("lines.alpha-blend", drawLineWithAlpha.isSelected()); 513 646 514 647 Config.getPref().putBoolean("mappaint.gpx.use-antialiasing", useGpsAntialiasing.isSelected()); 515 648 516 TemplateEntryProperty.forMarker( layerName).put(waypointLabelPattern.getText());517 TemplateEntryProperty.forAudioMarker( layerName).put(audioWaypointLabelPattern.getText());649 TemplateEntryProperty.forMarker(null).put(waypointLabelPattern.getText()); 650 TemplateEntryProperty.forAudioMarker(null).put(audioWaypointLabelPattern.getText()); 518 651 519 652 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);653 putPref("colormode", null); 654 putPref("colormode.dynamic-range", null); 655 putPref("colormode.velocity.tune", null); 523 656 return false; 524 657 } else if (colorTypeVelocity.isSelected()) { 525 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 1);658 putPref("colormode", 1); 526 659 } else if (colorTypeDilution.isSelected()) { 527 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 2);660 putPref("colormode", 2); 528 661 } else if (colorTypeDirection.isSelected()) { 529 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 3);662 putPref("colormode", 3); 530 663 } else if (colorTypeTime.isSelected()) { 531 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 4);664 putPref("colormode", 4); 532 665 } else if (colorTypeHeatMap.isSelected()) { 533 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 5);666 putPref("colormode", 5); 534 667 } else if (colorTypeQuality.isSelected()) { 535 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 6);668 putPref("colormode", 6); 536 669 } else { 537 Config.getPref().putInt("draw.rawgps.colors"+layerNameDot, 0);670 putPref("colormode", 0); 538 671 } 539 Config.getPref().putBoolean("draw.rawgps.colors.dynamic"+layerNameDot, colorDynamic.isSelected());672 putPref("colormode.dynamic-range", colorDynamic.isSelected()); 540 673 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());674 putPref("colormode.velocity.tune", ccti == 2 ? 10 : (ccti == 1 ? 20 : 45)); 675 putPref("colormode.heatmap.colormap", colorTypeHeatMapTune.getSelectedIndex()); 676 putPref("colormode.heatmap.use-points", colorTypeHeatMapPoints.isSelected()); 677 putPref("colormode.heatmap.gain", colorTypeHeatMapGain.getValue()); 678 putPref("colormode.heatmap.lower-limit", colorTypeHeatMapLowerLimit.getValue()); 546 679 547 680 return false; 548 681 } 549 682 550 /**551 * Save preferences from UI controls for initial layer or globally552 * @return {@code true} when restart is required, {@code false} otherwise553 */554 public boolean savePreferences() {555 return savePreferences(null, false);556 }557 558 683 private static void updateWaypointLabelCombobox(JosmComboBox<String> cb, JosmTextField tf, TemplateEntryProperty property) { 559 684 String labelPattern = property.getAsString(); 560 685 boolean found = false; -
src/org/openstreetmap/josm/io/GpxReader.java
53 53 RTE, 54 54 TRK, 55 55 EXT, 56 JOSMPREFS, 56 57 AUTHOR, 57 58 LINK, 58 59 TRKSEG, … … 76 77 private State currentState = State.INIT; 77 78 78 79 private GpxLink currentLink; 79 private Extensions currentExtensions;80 private Map<String, String> currentExtensionMap = new HashMap<>(); 80 81 private Stack<State> states; 81 82 private final Stack<String> elements = new Stack<>(); 82 83 … … 159 160 case "extensions": 160 161 states.push(currentState); 161 162 currentState = State.EXT; 162 currentExtensions = new Extensions();163 163 break; 164 164 case "gpx": 165 165 if (atts.getValue("creator") != null && atts.getValue("creator").startsWith("Nokia Sports Tracker")) { … … 178 178 case "extensions": 179 179 states.push(currentState); 180 180 currentState = State.EXT; 181 currentExtensions = new Extensions();182 181 break; 183 182 case "copyright": 184 183 states.push(currentState); … … 228 227 case "extensions": 229 228 states.push(currentState); 230 229 currentState = State.EXT; 231 currentExtensions = new Extensions();232 230 break; 233 231 default: // Do nothing 234 232 } … … 250 248 case "extensions": 251 249 states.push(currentState); 252 250 currentState = State.EXT; 253 currentExtensions = new Extensions();254 251 break; 255 252 default: // Do nothing 256 253 } … … 270 267 case "extensions": 271 268 states.push(currentState); 272 269 currentState = State.EXT; 273 currentExtensions = new Extensions();274 270 break; 275 271 default: // Do nothing 276 272 } 277 273 break; 274 case EXT: 275 if ("layerPreferences".equals(localName)) { 276 states.push(currentState); 277 currentState = State.JOSMPREFS; 278 } 279 break; 280 case JOSMPREFS: 281 String k, v; 282 if ("entry".equals(localName) && (k = atts.getValue("key")) != null && (v = atts.getValue("value")) != null) { 283 data.layerPrefs.put(k, v); 284 } 285 break; 278 286 default: // Do nothing 279 287 } 280 288 accumulator.setLength(0); … … 349 357 if ((currentState == State.METADATA && "metadata".equals(localName)) || 350 358 (currentState == State.GPX && "gpx".equals(localName))) { 351 359 convertUrlToLink(data.attr); 352 if (currentExtensions != null && !currentExtensions.isEmpty()) { 353 data.put(META_EXTENSIONS, currentExtensions); 354 } 360 data.addExtensions(currentExtensionMap); 355 361 currentState = states.pop(); 356 362 } 357 363 break; … … 464 470 case "wpt": 465 471 currentState = states.pop(); 466 472 convertUrlToLink(currentWayPoint.attr); 467 if (currentExtensions != null && !currentExtensions.isEmpty()) { 468 currentWayPoint.put(META_EXTENSIONS, currentExtensions); 469 } 473 currentWayPoint.addExtensions(currentExtensionMap); 470 474 data.waypoints.add(currentWayPoint); 475 currentExtensionMap = new HashMap<>(); 471 476 break; 472 477 default: // Do nothing 473 478 } … … 483 488 case "trk": 484 489 currentState = states.pop(); 485 490 convertUrlToLink(currentTrackAttr); 486 data.addTrack(new ImmutableGpxTrack(currentTrack, currentTrackAttr)); 491 ImmutableGpxTrack trk = new ImmutableGpxTrack(currentTrack, currentTrackAttr, currentExtensionMap); 492 data.addTrack(trk); 493 currentExtensionMap = new HashMap<>(); 487 494 break; 488 495 case "name": 489 496 case "cmt": … … 499 506 } 500 507 break; 501 508 case EXT: 509 String acc; 502 510 if ("extensions".equals(localName)) { 503 511 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()); 512 } else if ((acc = accumulator.toString().trim()).length() > 0) { 513 currentExtensionMap.put(qName, acc); 507 514 } 508 515 break; 516 case JOSMPREFS: 517 if ("layerPreferences".equals(localName)) { 518 currentState = states.pop(); 519 } 520 break; 509 521 default: 510 522 switch (localName) { 511 523 case "wpt": … … 519 531 default: // Do nothing 520 532 } 521 533 } 534 accumulator.setLength(0); 522 535 } 523 536 524 537 @Override … … 525 538 public void endDocument() throws SAXException { 526 539 if (!states.empty()) 527 540 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; 541 Extensions josmMetaExt = (Extensions) data.get(EXTENSIONS_JOSM); 542 if (josmMetaExt != null) { 543 if ("true".equals(josmMetaExt.get("from-server"))) { 544 data.fromServer = true; 545 } 531 546 } 532 547 gpxData = data; 533 548 } -
src/org/openstreetmap/josm/io/GpxWriter.java
3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.Color; 6 7 import java.io.BufferedWriter; 7 8 import java.io.OutputStream; 8 9 import java.io.OutputStreamWriter; 9 10 import java.io.PrintWriter; 10 11 import java.nio.charset.StandardCharsets; 12 import java.util.AbstractMap.SimpleEntry; 13 import java.util.ArrayList; 11 14 import java.util.Collection; 12 15 import java.util.Date; 16 import java.util.HashMap; 13 17 import java.util.List; 14 18 import java.util.Map; 15 19 import java.util.Map.Entry; … … 59 63 private static final int ROUTE_POINT = 1; 60 64 private static final int TRACK_POINT = 2; 61 65 66 private HashMap<String, Entry<String, String>> extlinks = new HashMap<>(); 67 62 68 /** 63 69 * Writes the given GPX data. 64 70 * @param data The data to write 65 71 */ 66 72 public void write(GpxData data) { 73 write(data, EXTENSIONS_DRAWING, true); 74 } 75 76 /** 77 * Writes the given GPX data. 78 * 79 * @param data The data to write 80 * @param colorFormat determines if colors are saved and which extension is to be used, can be 81 * {@link GpxConstants#EXTENSIONS_GARMIN}, {@link GpxConstants#EXTENSIONS_DRAWING} or <code>null</code> 82 * @param savePrefs whether layer specific preferences are saved 83 */ 84 public void write(GpxData data, String colorFormat, boolean savePrefs) { 67 85 this.data = data; 68 86 // We write JOSM specific meta information into gpx 'extensions' elements. 69 87 // In particular it is noted whether the gpx data is from the OSM server … … 71 89 // and some extra synchronization info for export of AudioMarkers. 72 90 // It is checked in advance, if any extensions are used, so we know whether 73 91 // a namespace declaration is necessary. 74 boolean hasExtensions = data.fromServer; 75 if (!hasExtensions) { 92 93 boolean hasJosmExtension = data.fromServer || !data.layerPrefs.isEmpty(), 94 hasGpxxExtension = false, 95 hasGpxdExtension = false, 96 searchGpxx = EXTENSIONS_GARMIN.equals(colorFormat), 97 searchGpxd = EXTENSIONS_DRAWING.equals(colorFormat); 98 99 if (!hasJosmExtension) { 76 100 for (WayPoint wpt : data.waypoints) { 77 Extensions extensions = (Extensions) wpt.get( META_EXTENSIONS);101 Extensions extensions = (Extensions) wpt.get(EXTENSIONS_JOSM); 78 102 if (extensions != null && !extensions.isEmpty()) { 79 has Extensions= true;103 hasJosmExtension = true; 80 104 break; 81 105 } 82 106 } 83 107 } 84 108 109 if (searchGpxx || searchGpxd) { 110 111 HashMap<Color, String> closestColorCache = new HashMap<>(); 112 113 for (GpxTrack trk : data.getTracks()) { 114 Extensions gpxx = (Extensions) trk.get(EXTENSIONS_GARMIN), 115 gpxd = (Extensions) trk.get(EXTENSIONS_DRAWING); 116 String gpxxC = null, gpxdC = null; 117 118 if (gpxd != null) { 119 gpxdC = gpxd.get("color"); 120 } 121 if (gpxx != null) { 122 gpxxC = gpxx.get("displaycolor"); 123 } 124 125 if (searchGpxd && gpxdC == null && gpxx != null && gpxxC != null) { 126 //Convert GPXX to GPXD 127 Color c = GARMIN_COLORS.get(gpxxC); 128 if (c != null) { 129 //Remove GPXX 130 trk.removeExtensionKey(EXTENSIONS_GARMIN, "displaycolor"); 131 //Put GPXD 132 trk.setColor(c); 133 hasGpxdExtension = true; 134 } else { 135 Logging.warn("Could not read garmin color: " + gpxxC); 136 } 137 } else if (searchGpxx && gpxxC == null && gpxd != null && gpxdC != null) { 138 //Convert GPXD to GPXX 139 try { 140 Color c = Color.decode(gpxdC); 141 //Remove GPXD 142 trk.removeExtensionKey(EXTENSIONS_DRAWING, "color"); 143 //Put GPXX 144 String colorString = null; 145 double closestDiff = -1; 146 147 if (closestColorCache.containsKey(c)) { 148 colorString = closestColorCache.get(c); 149 } else { 150 //find closest garmin color 151 for (Entry<String, Color> e : GARMIN_COLORS.entrySet()) { 152 double diff = colorDist(e.getValue(), c); 153 if (closestDiff < 0 || diff < closestDiff) { 154 colorString = e.getKey(); 155 closestDiff = diff; 156 if (closestDiff == 0) break; 157 } 158 } 159 closestColorCache.put(c, colorString); 160 } 161 trk.addExtensionKey(EXTENSIONS_GARMIN, "DisplayColor", colorString); 162 hasGpxxExtension = true; 163 } catch (NumberFormatException ex) { 164 Logging.warn("Could not read gpxd color: " + gpxdC); 165 } 166 } 167 //Must be checked again because of conversion above 168 if (!hasGpxdExtension && gpxd != null && !gpxd.isEmpty()) { 169 hasGpxdExtension = true; 170 } 171 if (!hasGpxxExtension && gpxx != null && !gpxx.isEmpty()) { 172 hasGpxxExtension = true; 173 } 174 } 175 } 176 85 177 out.println("<?xml version='1.0' encoding='UTF-8'?>"); 86 178 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\">"); 179 180 String schemaLocations = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"; 181 182 if (hasJosmExtension) { 183 extlinks.put(GpxConstants.EXTENSIONS_JOSM, new SimpleEntry<>(GpxConstants.XML_URI_EXTENSIONS_JOSM, GpxConstants.XML_XSD_EXTENSIONS_JOSM)); 184 } 185 if (hasGpxdExtension) { 186 extlinks.put(GpxConstants.EXTENSIONS_DRAWING, new SimpleEntry<>(GpxConstants.XML_URI_EXTENSIONS_DRAWING, GpxConstants.XML_XSD_EXTENSIONS_DRAWING)); 187 } 188 if (hasGpxxExtension) { 189 extlinks.put(GpxConstants.EXTENSIONS_GARMIN, new SimpleEntry<>(GpxConstants.XML_URI_EXTENSIONS_GARMIN, GpxConstants.XML_XSD_EXTENSIONS_GARMIN)); 190 } 191 192 for (Entry<String, Entry<String, String>> e : extlinks.entrySet()) { 193 String k = e.getKey(); 194 Entry<String, String> v = e.getValue(); 195 if (!k.startsWith(EXTENSIONS_PREFIX)) { 196 throw new Extensions.InvalidExtensionException(); 197 } 198 out.println(String.format(" xmlns:%s=\"%s\"", k.substring(k.lastIndexOf(".") + 1), v.getKey())); 199 schemaLocations += " " + v.getKey() + " " + v.getValue(); 200 } 201 202 out.println(" xmlns:xsi=\""+XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI+"\""); 203 out.println(String.format(" xsi:schemaLocation=\"%s\">", schemaLocations)); 90 204 indent = " "; 91 writeMetaData( );205 writeMetaData(savePrefs); 92 206 writeWayPoints(); 93 207 writeRoutes(); 94 208 writeTracks(); … … 97 211 } 98 212 99 213 private void writeAttr(IWithAttributes obj, List<String> keys) { 214 List<Extensions> allExtensions = new ArrayList<>(); 100 215 for (String key : keys) { 101 216 if (META_LINKS.equals(key)) { 102 217 Collection<GpxLink> lValue = obj.<GpxLink>getCollection(key); … … 105 220 gpxLink(link); 106 221 } 107 222 } 108 } else if (META_EXTENSIONS.equals(key)) { 109 Extensions extensions = (Extensions) obj.get(key); 110 if (extensions != null) { 111 gpxExtensions(extensions); 223 } else if (key != null && key.startsWith(EXTENSIONS_PREFIX)) { 224 Extensions ex = (Extensions) obj.get(key); 225 if (ex != null && !ex.isEmpty()) { 226 //ex.setType(key); 227 allExtensions.add(ex); 112 228 } 113 229 } else { 114 230 String value = obj.getString(key); … … 126 242 } 127 243 } 128 244 } 245 gpxExtensions(allExtensions); 129 246 } 130 247 131 private void writeMetaData() { 248 double colorDist(Color c1, Color c2) { 249 // Simple Euclidean distance between two colors 250 return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(), 2) 251 + Math.pow(c1.getGreen() - c2.getGreen(), 2) 252 + Math.pow(c1.getBlue() - c2.getBlue(), 2)); 253 } 254 255 256 private void writeMetaData(boolean savePrefs) { 132 257 Map<String, Object> attr = data.attr; 133 258 openln("metadata"); 134 259 … … 147 272 if (attr.containsKey(META_AUTHOR_EMAIL)) { 148 273 String[] tmp = data.getString(META_AUTHOR_EMAIL).split("@"); 149 274 if (tmp.length == 2) { 150 inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+'\"');275 inline("email", "id=\"" + encode(tmp[0]) + "\" domain=\"" + encode(tmp[1]) +'\"'); 151 276 } 152 277 } 153 278 // write the author link … … 158 283 // write the copyright details 159 284 if (attr.containsKey(META_COPYRIGHT_LICENSE) 160 285 || attr.containsKey(META_COPYRIGHT_YEAR)) { 161 openAtt("copyright", "author=\""+ data.get(META_COPYRIGHT_AUTHOR) +'\"');286 openAtt("copyright", "author=\""+ encode(data.get(META_COPYRIGHT_AUTHOR).toString()) +'\"'); 162 287 if (attr.containsKey(META_COPYRIGHT_YEAR)) { 163 288 simpleTag("year", (String) data.get(META_COPYRIGHT_YEAR)); 164 289 } … … 187 312 inline("bounds", b); 188 313 } 189 314 190 if (data.fromServer ) {315 if (data.fromServer || (savePrefs && !data.layerPrefs.isEmpty())) { 191 316 openln("extensions"); 192 simpleTag("josm:from-server", "true"); 317 318 if (data.fromServer) { 319 simpleTag("josm:from-server", "true"); 320 } 321 322 323 if (!data.layerPrefs.isEmpty()) { 324 openln("josm:layerPreferences"); 325 ArrayList<Entry<String, String>> prefs = new ArrayList<>(data.layerPrefs.entrySet()); 326 prefs.sort((o1, o2) -> o1.getKey().compareTo(o2.getKey())); 327 for (Entry<String, String> pref : prefs) { 328 inline("josm:entry", "key=\"" + encode(pref.getKey()) + "\" value=\"" + encode(pref.getValue()) + "\""); 329 } 330 closeln("josm:layerPreferences"); 331 } 332 193 333 closeln("extensions"); 194 334 } 195 335 … … 278 418 */ 279 419 private void gpxLink(GpxLink link) { 280 420 if (link != null) { 281 openAtt("link", "href=\"" + link.uri+ '\"');421 openAtt("link", "href=\"" + encode(link.uri) + '\"'); 282 422 simpleTag("text", link.text); 283 423 simpleTag("type", link.type); 284 424 closeln("link"); … … 318 458 } 319 459 } 320 460 321 private void gpxExtensions( Extensions extensions) {322 if ( extensions != null && !extensions.isEmpty()) {461 private void gpxExtensions(List<Extensions> allExtensions) { 462 if (!allExtensions.isEmpty()) { 323 463 openln("extensions"); 324 for (Entry<String, String> e : extensions.entrySet()) { 325 simpleTag("josm:" + e.getKey(), e.getValue()); 464 for (Extensions extensions : allExtensions) { 465 if (extlinks.containsKey(extensions.getType())) { 466 //making sure links were actually added / filter if no colors are to be saved at all. 467 //TODO: probably makes more sense to do the conversion here instead of in the beginning 468 //note that the colors will actually change during the conversion to GPXX 469 boolean garmin = EXTENSIONS_GARMIN.equals(extensions.getType()); 470 if (garmin) { //allow nested Garmin TrackExtension. Not ideal but does the job. 471 openln("gpxx:TrackExtension"); 472 } 473 for (Entry<String, String> e : extensions.entrySet()) { 474 simpleTag(extensions.getPrefix() + e.getKey(), e.getValue()); 475 } 476 if (garmin) { 477 closeln("gpxx:TrackExtension"); 478 } 479 } 326 480 } 327 481 closeln("extensions"); 328 482 } -
test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java
473 473 public void testEqualsContract() { 474 474 TestUtils.assumeWorkingEqualsVerifier(); 475 475 EqualsVerifier.forClass(GpxData.class).usingGetClass() 476 .withIgnoredFields("attr", "creator", "fromServer", "storageFile", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans" )476 .withIgnoredFields("attr", "creator", "fromServer", "storageFile", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans", "modified") 477 477 .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE)) 478 478 .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create()) 479 479 .verify(); -
test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackTest.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertNull; 6 7 import java.awt.Color; 8 import java.util.ArrayList; 9 import java.util.HashMap; 10 import java.util.Map; 11 4 12 import org.junit.Rule; 5 13 import org.junit.Test; 6 14 import org.openstreetmap.josm.TestUtils; 7 15 import org.openstreetmap.josm.testutils.JOSMTestRules; 16 import org.openstreetmap.josm.tools.ListenerList; 8 17 9 18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 10 19 import nl.jqno.equalsverifier.EqualsVerifier; … … 23 32 public JOSMTestRules test = new JOSMTestRules(); 24 33 25 34 /** 35 * Tests weather the track can read and write colors. 36 */ 37 @Test 38 public void testColors() { 39 GpxTrack trk = new ImmutableGpxTrack(new ArrayList<GpxTrackSegment>(), new HashMap<>()); 40 trk.addExtensions(Map.of("gpxd:color", "#FF0000")); 41 assertEquals(trk.getColor(), Color.RED); 42 trk.addExtensionKey(GpxConstants.EXTENSIONS_DRAWING, "color", "#00FF00"); 43 assertEquals(trk.getColor(), Color.GREEN); 44 trk.removeExtensionKey(GpxConstants.EXTENSIONS_DRAWING, "color"); 45 assertNull(trk.getColor()); 46 trk.addExtensionKey(GpxConstants.EXTENSIONS_GARMIN, "DisplayColor", "Blue"); 47 assertEquals(trk.getColor(), Color.BLUE); 48 trk.setColor(null); 49 assertNull(trk.getColor()); 50 trk.addExtensions(Map.of("gpxx:DisplayColor", "Cyan")); 51 assertEquals(trk.getColor(), Color.CYAN); 52 trk.setColor(Color.YELLOW); 53 assertEquals(trk.getColor(), Color.YELLOW); 54 } 55 56 /** 26 57 * Unit test of methods {@link ImmutableGpxTrack#equals} and {@link ImmutableGpxTrack#hashCode}. 27 58 */ 28 59 @Test … … 30 61 TestUtils.assumeWorkingEqualsVerifier(); 31 62 EqualsVerifier.forClass(ImmutableGpxTrack.class).usingGetClass() 32 63 .suppress(Warning.NONFINAL_FIELDS) 33 .withIgnoredFields("bounds", "length") 64 .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create()) 65 .withIgnoredFields("bounds", "length", "colorCache", "listeners") 34 66 .verify(); 35 67 } 36 68 } -
test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java
3 3 4 4 import static org.junit.Assert.assertEquals; 5 5 import static org.junit.Assert.assertFalse; 6 import static org.junit.Assert.assertNull; 6 7 import static org.junit.Assert.assertTrue; 7 8 8 9 import java.awt.Color; … … 10 11 import java.util.ArrayList; 11 12 import java.util.Collection; 12 13 import java.util.HashMap; 14 import java.util.Map; 13 15 import java.util.TimeZone; 14 16 15 17 import javax.swing.JScrollPane; … … 18 20 import org.junit.Test; 19 21 import org.openstreetmap.josm.TestUtils; 20 22 import org.openstreetmap.josm.data.gpx.GpxData; 23 import org.openstreetmap.josm.data.gpx.GpxTrackSegment; 21 24 import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack; 22 25 import org.openstreetmap.josm.data.gpx.WayPoint; 23 26 import org.openstreetmap.josm.data.osm.DataSet; … … 73 76 @Test 74 77 public void testGpxLayer() throws Exception { 75 78 GpxLayer layer = new GpxLayer(new GpxData(), "foo", false); 79 ImmutableGpxTrack trk = new ImmutableGpxTrack(new ArrayList<GpxTrackSegment>(), new HashMap<>()); 80 trk.addExtensions(Map.of("gpxd:color", "#FF0000")); 81 layer.data.addTrack(trk); 82 76 83 assertEquals("foo", layer.getName()); 77 84 assertFalse(layer.isLocalFile()); 78 assertEquals( Color.MAGENTA, layer.getColorProperty().get());79 assertEquals("<html> 0 tracks(0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText());85 assertEquals(layer.getColor(), Color.RED); 86 assertEquals("<html>1 track (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText()); 80 87 81 88 GpxLayer layer2 = new GpxLayer(new GpxData(), "bar", true); 82 89 assertEquals("bar", layer2.getName()); 83 90 assertTrue(layer2.isLocalFile()); 84 assert Equals(Color.MAGENTA, layer2.getColorProperty().get());91 assertNull(layer2.getColor()); 85 92 assertEquals("<html>0 tracks (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer2.getToolTipText()); 86 93 87 94 assertTrue(layer.checkSaveConditions()); -
test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java
7 7 import static org.junit.Assert.assertNull; 8 8 import static org.junit.Assert.assertTrue; 9 9 10 import java.awt.Color;11 10 import java.io.File; 12 11 13 12 import org.junit.Before; 14 13 import org.junit.Rule; 15 14 import org.junit.Test; 16 import org.openstreetmap.josm.data.preferences.AbstractProperty;17 import org.openstreetmap.josm.data.preferences.NamedColorProperty;18 15 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 19 16 import org.openstreetmap.josm.testutils.JOSMTestRules; 20 17 … … 43 40 } 44 41 45 42 /** 46 * Test {@link Layer#getColorProperty()}47 */48 @Test49 public void testGetColorProperty() {50 assertEquals(null, testLayer.getColorProperty());51 52 AbstractProperty<Color> color = new LayerManagerTest.TestLayer() {53 @Override54 protected NamedColorProperty getBaseColorProperty() {55 return new NamedColorProperty("x", Color.BLACK);56 }57 }.getColorProperty();58 59 assertEquals(Color.BLACK, color.get());60 assertEquals(Color.BLACK, color.getDefaultValue());61 assertEquals("clr.layer.Test Layer.x", color.getKey());62 }63 64 /**65 43 * Test of {@link Layer#isInfoResizable} 66 44 */ 67 45 @Test … … 97 75 testLayer.setName("Test Layer2"); 98 76 assertEquals("Test Layer2", testLayer.getName()); 99 77 100 testLayer = new LayerManagerTest.TestLayer() { 101 @Override 102 public AbstractProperty<Color> getColorProperty() { 103 return new NamedColorProperty("test", Color.RED); 104 } 105 }; 78 testLayer = new LayerManagerTest.TestLayer(); 106 79 107 80 testLayer.setName("Test Layer2"); 108 81 testLayer.setName(null); -
test/unit/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarkerTest.java
45 45 assertEquals("2", marker.getText()); 46 46 WayPoint wpt = marker.convertToWayPoint(); 47 47 assertEquals(LatLon.ZERO, wpt.getCoor()); 48 Extensions ext = (Extensions) wpt.get(GpxConstants. META_EXTENSIONS);48 Extensions ext = (Extensions) wpt.get(GpxConstants.EXTENSIONS_JOSM); 49 49 assertEquals("2.0", ext.get("offset")); 50 50 } 51 51 } -
test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java
3 3 4 4 import static org.junit.Assert.assertEquals; 5 5 import static org.junit.Assert.assertNotNull; 6 import static org.junit.Assert.assertNull; 6 7 import static org.junit.Assert.assertTrue; 7 8 8 import java.awt.Color;9 9 import java.util.Arrays; 10 10 11 11 import org.junit.Before; … … 16 16 import org.openstreetmap.josm.data.gpx.GpxData; 17 17 import org.openstreetmap.josm.data.gpx.GpxLink; 18 18 import org.openstreetmap.josm.data.gpx.WayPoint; 19 import org.openstreetmap.josm.data.osm.DataSet; 19 20 import org.openstreetmap.josm.gui.MainApplication; 21 import org.openstreetmap.josm.gui.MapFrame; 22 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 20 23 import org.openstreetmap.josm.spi.preferences.Config; 21 24 import org.openstreetmap.josm.testutils.JOSMTestRules; 22 25 … … 28 31 public class MarkerLayerTest { 29 32 30 33 /** 31 * Setup tests34 * For creating layers 32 35 */ 33 36 @Rule 34 37 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") … … 47 50 */ 48 51 @Test 49 52 public void testMarkerLayer() { 50 assertEquals(Color.magenta, MarkerLayer.getGenericColor());53 //assertEquals(Color.magenta, MarkerLayer.getGenericColor()); 51 54 MarkerLayer layer = new MarkerLayer(new GpxData(), "foo", null, null); 52 55 MainApplication.getLayerManager().addLayer(layer); 53 56 54 57 assertEquals("foo", layer.getName()); 55 assert Equals(Color.magenta, layer.getColorProperty().get());58 assertNull(layer.getColor()); 56 59 assertNotNull(layer.getIcon()); 57 60 assertEquals("0 markers", layer.getToolTipText()); 58 61 assertEquals("<html>foo consists of 0 markers</html>", layer.getInfoComponent()); … … 61 64 GpxData gpx = new GpxData(); 62 65 WayPoint wpt = new WayPoint(LatLon.ZERO); 63 66 wpt.attr.put(GpxConstants.META_LINKS, Arrays.asList(new GpxLink("https://josm.openstreetmap.de"))); 64 wpt.addExtension ("offset", "1.0");67 wpt.addExtensionKey(GpxConstants.EXTENSIONS_JOSM, "offset", "1.0"); 65 68 gpx.waypoints.add(wpt); 66 69 wpt = new WayPoint(LatLon.ZERO); 67 wpt.addExtension ("offset", "NaN");70 wpt.addExtensionKey(GpxConstants.EXTENSIONS_JOSM, "offset", "NaN"); 68 71 gpx.waypoints.add(wpt); 69 72 layer = new MarkerLayer(gpx, "bar", null, null); 70 73 71 74 assertEquals("bar", layer.getName()); 72 assert Equals(Color.magenta, layer.getColorProperty().get());75 assertNull(layer.getColor()); 73 76 assertNotNull(layer.getIcon()); 74 77 assertEquals("3 markers", layer.getToolTipText()); 75 78 assertEquals("<html>bar consists of 3 markers</html>", layer.getInfoComponent()); 76 79 assertTrue(layer.getMenuEntries().length > 10); 77 80 } 81 82 /** 83 * Unit test of {@code Main.map.mapView.playHeadMarker}. 84 */ 85 @Test 86 public void testPlayHeadMarker() { 87 try { 88 MainApplication.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "", null)); 89 MapFrame map = MainApplication.getMap(); 90 MarkerLayer layer = new MarkerLayer(new GpxData(), null, null, null); 91 assertNull(map.mapView.playHeadMarker); 92 MainApplication.getLayerManager().addLayer(layer); 93 assertNotNull(map.mapView.playHeadMarker); 94 MainApplication.getLayerManager().removeLayer(layer); 95 } finally { 96 if (MainApplication.isDisplayingMapView()) { 97 MainApplication.getMap().mapView.playHeadMarker = null; 98 } 99 } 100 } 78 101 } -
test/unit/org/openstreetmap/josm/io/GpxWriterTest.java
87 87 " <vdop>0.9</vdop>%n" + 88 88 " <pdop>1.2</pdop>%n"); 89 89 } 90 91 /** 92 * Tests if extensions are handled correctly when reading and writing. 93 * Source file contains 4 tracks 94 * - 1x gpxx colors (garmin) 95 * - 1x gpxd colors 96 * - 2x no colors 97 * one of the tracks without colors is assigned a new color 98 * Then the layer is exported twice 99 * - once using gpxx extensions and 100 * - once using gpxd extensions 101 */ 102 @Test 103 public void testExtensions() { 104 //TODO 105 } 106 90 107 }
