Ticket #16796: GpxColors-v0.13.diff
File GpxColors-v0.13.diff, 103.6 KB (added by , 6 years ago) |
---|
-
data/gpx-drawing-extensions-1.0.xsd
1 <?xml version="1.0" encoding="UTF-8"?> 2 <schema targetNamespace="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0" 3 elementFormDefault="qualified" 4 xmlns="http://www.w3.org/2001/XMLSchema" 5 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 6 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 7 xmlns:gpxd="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0" 8 xsi:schemaLocation="https://josm.openstreetmap.de/gpx-drawing-extensions-1.0 https://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd"> 9 10 <xsd:annotation> 11 <xsd:documentation> 12 This schema defines drawing extensions for the GPX 1.1 schema (http://www.topografix.com/GPX/1/1/gpx.xsd). 13 Elements in this schema should be used as child elements of the "extensions" element defined by the GPX schema. 14 </xsd:documentation> 15 </xsd:annotation> 16 17 <!-- Elements --> 18 19 <xsd:element name="color" type="gpxd:hexColor_type"> 20 <xsd:annotation> 21 <xsd:documentation> 22 The color of the element, i.e. #RRGGBB or #RRGGBBAA. 23 Note that applications should apply possible alpha values to the lines and opacity to the whole track. This means that overlapping parts of the 24 track with alpha values will look more intense than individual lines, whereas the opacity affects the whole track including overlapping parts. 25 </xsd:documentation> 26 </xsd:annotation> 27 </xsd:element> 28 29 <xsd:element name="opacity" type="gpxd:opacity_type"> 30 <xsd:annotation> 31 <xsd:documentation> 32 The opacity of the element between 0.00 and 1.00. 33 </xsd:documentation> 34 </xsd:annotation> 35 </xsd:element> 36 37 <xsd:element name="width" type="xsd:positiveInteger"> 38 <xsd:annotation> 39 <xsd:documentation> 40 The width of the line in pixels, applications may use a width relative to this value if required. 41 </xsd:documentation> 42 </xsd:annotation> 43 </xsd:element> 44 45 <xsd:element name="dashPattern" type="gpxd:dashPattern_type"> 46 <xsd:annotation> 47 <xsd:documentation> 48 The dash pattern of the line, see gpxd:dashPattern_type. Should always be relative to the width. 49 </xsd:documentation> 50 </xsd:annotation> 51 </xsd:element> 52 53 <!-- Types --> 54 55 <xsd:simpleType name="hexColor_type"> 56 <xsd:annotation> 57 <xsd:documentation> 58 The hexColor_type must be a # followed by a 6 or 8-digit hex representation of the color (with or without the alpha value). 59 </xsd:documentation> 60 </xsd:annotation> 61 <xsd:restriction base="xsd:string"> 62 <xsd:pattern value="\#([a-fA-F0-9]{6}|[a-fA-F0-9]{8})" /> 63 <xsd:whiteSpace value="collapse" /> 64 </xsd:restriction> 65 </xsd:simpleType> 66 67 <xsd:simpleType name="opacity_type"> 68 <xsd:annotation> 69 <xsd:documentation> 70 The opacity_type must be a decimal value between 0 and 1. 71 </xsd:documentation> 72 </xsd:annotation> 73 <xsd:restriction base="xsd:decimal"> 74 <xsd:minInclusive value="0" /> 75 <xsd:maxInclusive value="1" /> 76 </xsd:restriction> 77 </xsd:simpleType> 78 79 <xsd:simpleType name="dashPattern_type"> 80 <xsd:annotation> 81 <xsd:documentation> 82 The dashPattern_type can be 83 - a representation of the pattern as y-n-y-n-... with y being the relative length of the line that is 84 visible and n being the relative length of the line that is hidden to create a dashed / dotted line. 85 Has to have an even number of segments (at least two) and can contain multi-digit numbers. 86 - one of the following predefined values: 87 none, dash-long, dash-medium, dash-short, dot-sparse, dot-normal, dot-dense, dash-dot, dash-dot-dot 88 </xsd:documentation> 89 </xsd:annotation> 90 <xsd:restriction base="xsd:string"> <!-- use string based pattern instead of enum because both pattern and enums are allowed --> 91 <xsd:pattern value="\d+\-\d+(\-\d+\-\d+)*" /> <!-- pattern, see documentation above --> 92 <xsd:pattern value="none" /> <!-- 1-0, default value/line --> 93 <xsd:pattern value="dash-long" /> <!-- 6-2 --> 94 <xsd:pattern value="dash-medium" /> <!-- 4-4 --> 95 <xsd:pattern value="dash-short" /> <!-- 2-6 --> 96 <xsd:pattern value="dot-sparse" /> <!-- 1-4 --> 97 <xsd:pattern value="dot-normal" /> <!-- 1-2 --> 98 <xsd:pattern value="dot-dense" /> <!-- 1-1 --> 99 <xsd:pattern value="dash-dot" /> <!-- 4-2-1-2 --> 100 <xsd:pattern value="dash-dot-dot" /><!-- 4-2-1-2-1-2 --> 101 </xsd:restriction> 102 </xsd:simpleType> 103 104 </schema> 105 No newline at end of file -
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.0"; 129 /** 130 * Location of the XSD schema for JOSM GPX extensions 131 */ 132 String XML_XSD_EXTENSIONS_JOSM = Config.getUrls().getXMLBase() + "/gpx-extensions-1.0.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
65 65 * Addidionaly waypoints for this file. 66 66 */ 67 67 private final ArrayList<WayPoint> privateWaypoints = new ArrayList<>(); 68 private final GpxTrackChangeListener proxy = e -> fireInvalidate(); 68 private final GpxTrackChangeListener proxy = e -> {fireInvalidate(); modified = true;}; 69 private boolean modified = false; 69 70 70 71 /** 71 72 * Tracks. Access is discouraged, use {@link #getTracks()} to read. … … 888 889 private Line next; 889 890 private final boolean[] trackVisibility; 890 891 private Map<String, Object> trackAttributes; 892 private GpxTrack curTrack; 891 893 892 894 /** 893 895 * Constructs a new {@code LinesIterator}. … … 921 923 private Line getNext() { 922 924 if (itTracks != null) { 923 925 if (itTrackSegments != null && itTrackSegments.hasNext()) { 924 return new Line(itTrackSegments.next(), trackAttributes );926 return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor()); 925 927 } else { 926 928 while (itTracks.hasNext()) { 927 GpxTrack nxtTrack = itTracks.next();928 trackAttributes = nxtTrack.getAttributes();929 curTrack = itTracks.next(); 930 trackAttributes = curTrack.getAttributes(); 929 931 idxTracks++; 930 932 if (trackVisibility != null && !trackVisibility[idxTracks]) 931 933 continue; 932 itTrackSegments = nxtTrack.getSegments().iterator();934 itTrackSegments = curTrack.getSegments().iterator(); 933 935 if (itTrackSegments.hasNext()) { 934 return new Line(itTrackSegments.next(), trackAttributes );936 return new Line(itTrackSegments.next(), trackAttributes, curTrack.getColor()); 935 937 } 936 938 } 937 939 // if we get here, all the Tracks are finished; Continue with Routes … … 1067 1069 return source; 1068 1070 } 1069 1071 } 1072 1073 /** 1074 * @return whether anything (i.e. colors) have been modified 1075 */ 1076 public boolean isModified() { 1077 return modified; 1078 } 1070 1079 } -
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.eop().fill(GBC.HORIZONTAL).insets(20, 0, 0, 0)); 159 160 151 161 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 152 162 tr("Export options"), 153 163 tr("Export and Save"), tr("Cancel")) … … 154 164 .setButtonIcons("exportgpx", "cancel") 155 165 .setContent(p); 156 166 167 colors.addActionListener(l -> { 168 garmin.setEnabled(colors.isSelected()); 169 }); 170 171 garmin.addActionListener(l -> { 172 if (garmin.isSelected() && 173 !ConditionalOptionPaneUtil.showConfirmationDialog( 174 "gpx_color_garmin", 175 ed, 176 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>")), 177 tr("Information"), 178 JOptionPane.OK_CANCEL_OPTION, 179 JOptionPane.INFORMATION_MESSAGE, 180 JOptionPane.OK_OPTION)) { 181 garmin.setSelected(false); 182 } 183 }); 184 157 185 if (ed.showDialog().getValue() != 1) { 158 186 setCanceled(true); 159 187 return; … … 167 195 if (!copyright.getText().isEmpty()) { 168 196 Config.getPref().put("lastCopyright", copyright.getText()); 169 197 } 198 Config.getPref().putBoolean("gpx.export.colors", colors.isSelected()); 199 Config.getPref().putBoolean("gpx.export.colors.garmin", garmin.isSelected()); 200 String colorFormat = null; 201 if (colors.isSelected()) { 202 colorFormat = garmin.isSelected() 203 ? GpxConstants.EXTENSIONS_GARMIN 204 : GpxConstants.EXTENSIONS_DRAWING; 205 } 170 206 171 207 if (layer instanceof OsmDataLayer) { 172 208 gpxData = ((OsmDataLayer) layer).toGpxData(); … … 203 239 gpxData.put(META_KEYWORDS, keywords.getText()); 204 240 } 205 241 206 try (OutputStream fo = Compression.getCompressedFileOutputStream(file); GpxWriter writer = new GpxWriter(fo)) { 207 writer.write(gpxData); 242 try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) { 243 GpxWriter w = new GpxWriter(fo); 244 w.write(gpxData, colorFormat); 245 w.close(); 246 fo.flush(); 247 } catch (IOException | InvalidPathException ex) { 248 Logging.error(ex); 208 249 } 209 250 } 210 251 -
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(l -> l.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; … … 54 54 /** 55 55 * A layer that displays data from a Gpx file / the OSM gpx downloads. 56 56 */ 57 public class GpxLayer extends Layer implements ExpertModeChangeListener {57 public class GpxLayer extends AbstractModifiableLayer implements ExpertModeChangeListener { 58 58 59 59 /** GPX data */ 60 60 public GpxData data; … … 108 108 } 109 109 110 110 @Override 111 protected NamedColorProperty getBaseColorProperty() { 112 return GpxDrawHelper.DEFAULT_COLOR; 111 public Color getColor() { 112 Color[] c = data.getTracks().stream().map(t -> t.getColor()).distinct().toArray(Color[]::new); 113 return c.length == 1 ? c[0] : null; //only return if exactly one distinct color present 113 114 } 114 115 116 @Override 117 public void setColor(Color color) { 118 for (GpxTrack trk : data.getTracks()) { 119 trk.setColor(color); 120 } 121 } 122 123 @Override 124 public boolean hasColor() { 125 return true; 126 } 127 115 128 /** 116 129 * Returns a human readable string that shows the timespan of the given track 117 130 * @param trk The GPX track for which timespan is displayed … … 478 491 public void expertChanged(boolean isExpert) { 479 492 this.isExpertMode = isExpert; 480 493 } 494 495 @Override 496 public boolean isModified() { 497 return data.isModified(); 498 } 481 499 482 500 @Override 501 public boolean requiresSaveToFile() { 502 return isModified() && isLocalFile(); 503 } 504 505 @Override 483 506 public String getChangesetSourceTag() { 484 507 // no i18n for international values 485 508 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 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 break; 153 case 1: 154 return; 155 case 2: 156 tracks.stream().forEach(t -> t.setColor(null)); 157 break; 158 } 159 table.repaint(); 160 } 161 125 162 /** 126 163 * Builds an non-editable table whose 5th column will open a browser when double clicked. 127 164 * The table will fill its parent. … … 138 175 if (c instanceof JComponent) { 139 176 JComponent jc = (JComponent) c; 140 177 jc.setToolTipText(getValueAt(row, col).toString()); 178 if (content.length > row 179 && content[row].length > 5 180 && content[row][5] instanceof GpxTrack) { 181 Color color = ((GpxTrack) content[row][5]).getColor(); 182 if (color != null) { 183 double brightness = Math.sqrt(Math.pow(color.getRed(), 2) * .241 184 + Math.pow(color.getGreen(), 2) * .691 185 + Math.pow(color.getBlue(), 2) * .068); 186 if (brightness > 250) { 187 color = color.darker(); 188 } 189 if (isRowSelected(row)) { 190 jc.setBackground(color); 191 if (brightness <= 130) { 192 jc.setForeground(Color.WHITE); 193 } else { 194 jc.setForeground(Color.BLACK); 195 } 196 } else { 197 if (brightness > 200) { 198 color = color.darker(); //brightness >250 is darkened twice on purpose 199 } 200 jc.setForeground(color); 201 jc.setBackground(Color.WHITE); 202 } 203 } 204 } 141 205 } 142 206 return c; 143 207 } … … 144 208 145 209 @Override 146 210 public boolean isCellEditable(int rowIndex, int colIndex) { 147 return false;211 return colIndex <= 1; 148 212 } 213 214 @Override 215 public void tableChanged(TableModelEvent e) { 216 super.tableChanged(e); 217 int col = e.getColumn(); 218 int row = e.getFirstRow(); 219 if (row >= 0 && row < content.length && col >= 0 && col <= 1) { 220 Object t = content[row][5]; 221 String val = (String) getValueAt(row, col); 222 if (t != null && t instanceof GpxTrack) { 223 GpxTrack trk = (GpxTrack) t; 224 if (col == 0) { 225 trk.put("name", val); 226 } else { 227 trk.put("desc", val); 228 } 229 } else { 230 throw new InvalidArgumentException(); 231 } 232 } 233 } 149 234 }; 150 235 // define how to sort row 151 236 TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<>(); … … 249 334 250 335 msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. " + 251 336 "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>")),337 "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 338 GBC.eop().fill(GBC.HORIZONTAL)); 254 339 // build table 255 340 final boolean[] trackVisibilityBackup = layer.trackVisibility.clone(); 256 table = buildTable(buildTableContents()); 341 Object[][] content = buildTableContents(); 342 table = buildTable(content); 257 343 selectVisibleTracksInTable(); 258 344 listenToSelectionChanges(); 259 345 // make the table scrollable … … 262 348 263 349 int v = 1; 264 350 // 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"); 351 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 352 tr("Set track visibility for {0}", layer.getName()), 353 tr("Set color for selected tracks..."), tr("Show all"), tr("Show selected only"), tr("Cancel")) { 354 @Override 355 protected void buttonAction(int buttonIndex, ActionEvent evt) { 356 if (buttonIndex == 0) { 357 List<GpxTrack> trks = new ArrayList<>(); 358 for (int i : table.getSelectedRows()) { 359 Object trk = content[i][5]; 360 if (trk != null && trk instanceof GpxTrack) { 361 trks.add((GpxTrack) trk); 362 } 363 } 364 showColorDialog(trks); 365 } else { 366 super.buttonAction(buttonIndex, evt); 367 } 368 } 369 }; 370 ed.setButtonIcons("colorchooser", "eye", "dialogs/filter", "cancel"); 268 371 ed.setContent(msg, false); 269 372 ed.setDefaultButton(2); 270 373 ed.setCancelButton(3); … … 275 378 dateFilter.saveInPrefs(); 276 379 v = ed.getValue(); 277 380 // cancel for unknown buttons and copy back original settings 278 if (v != 1 && v != 2) {381 if (v != 2 && v != 3) { 279 382 layer.trackVisibility = Arrays.copyOf(trackVisibilityBackup, layer.trackVisibility.length); 280 383 MainApplication.getMap().repaint(); 281 384 return; 282 385 } 283 // set visibility ( 1 = show all, 2= filter). If no tracks are selected386 // set visibility (2 = show all, 3 = filter). If no tracks are selected 284 387 // set all of them visible and... 285 388 ListSelectionModel s = table.getSelectionModel(); 286 final boolean all = v == 1|| s.isSelectionEmpty();389 final boolean all = v == 2 || s.isSelectionEmpty(); 287 390 for (int i = 0; i < layer.trackVisibility.length; i++) { 288 391 layer.trackVisibility[table.convertRowIndexToModel(i)] = all || s.isSelectedIndex(i); 289 392 } … … 290 393 // layer has been changed 291 394 layer.invalidate(); 292 395 // ...sync with layer visibility instead to avoid having two ways to hide everything 293 layer.setVisible(v == 1|| !s.isSelectionEmpty());396 layer.setVisible(v == 2 || !s.isSelectionEmpty()); 294 397 } 295 398 } -
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/GpxDrawHelper.java
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; … … 273 273 } 274 274 275 275 /** 276 * Get the default color for gps tracks for specified layer277 * @param layerName name of the GpxLayer278 * @param ignoreCustom do not use preferences279 * @return the color or null if the color is not constant280 */281 public Color getColor(String layerName, boolean ignoreCustom) {282 if (ignoreCustom || getColorMode(layerName) == ColorMode.NONE) {283 return DEFAULT_COLOR.getChildColor(284 NamedColorProperty.COLOR_CATEGORY_LAYER,285 layerName,286 DEFAULT_COLOR.getName()).get();287 } else {288 return null;289 }290 }291 292 /**293 276 * Read coloring mode for specified layer from preferences 294 277 * @param layerName name of the GpxLayer 295 278 * @return coloring mode … … 304 287 return ColorMode.NONE; 305 288 } 306 289 307 /** Reads generic color from preferences (usually gray)308 * @return the color309 **/310 public static Color getGenericColor() {311 return DEFAULT_COLOR.get();312 }313 314 290 /** 315 291 * Read all drawing-related settings from preferences 316 292 * @param layerName layer name used to access its specific preferences … … 352 328 353 329 // shrink to range 354 330 heatMapDrawGain = Utils.clamp(heatMapDrawGain, -10, 10); 355 356 neutralColor = getColor(layerName, true); 331 neutralColor = DEFAULT_COLOR_PROPERTY.get(); 357 332 velocityScale.setNoDataColor(neutralColor); 358 333 dateScale.setNoDataColor(neutralColor); 359 334 hdopScale.setNoDataColor(neutralColor); … … 594 569 } 595 570 for (WayPoint trkPnt : segment) { 596 571 LatLon c = trkPnt.getCoor(); 597 trkPnt.customColoring = neutralColor;572 trkPnt.customColoring = segment.getColor(); 598 573 if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) { 599 574 continue; 600 575 } … … 642 617 } 643 618 } else { // make sure we reset outdated data 644 619 trkPnt.drawLine = false; 645 color = neutralColor;620 color = segment.getColor(); 646 621 } 647 622 if (color != null) { 648 623 trkPnt.customColoring = color; -
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("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
77 77 public AudioMarker syncAudioMarker; 78 78 79 79 private static final Color DEFAULT_COLOR = Color.magenta; 80 private static final NamedColorProperty COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), DEFAULT_COLOR); 80 /** 81 * The color that is used for drawing markers. 82 */ 83 public static final NamedColorProperty DEFAULT_COLOR_PROPERTY = new NamedColorProperty(marktr("gps marker"), DEFAULT_COLOR); 81 84 82 85 /** 83 86 * Constructs a new {@code MarkerLayer}. … … 123 126 // audio file) calculate the offset relative to the first marker of 124 127 // that group. This way the user can jump to the corresponding 125 128 // playback positions in a long audio track. 126 Extensions exts = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);127 if ( exts != null && exts.containsKey("offset")) {129 Extensions josmExts = (Extensions) wpt.get(GpxConstants.EXTENSIONS_JOSM); 130 if (josmExts != null && josmExts.containsKey("offset")) { 128 131 try { 129 offset = Double.valueOf( exts.get("offset"));132 offset = Double.valueOf(josmExts.get("offset")); 130 133 } catch (NumberFormatException nfe) { 131 134 Logging.warn(nfe); 132 135 } … … 173 176 } 174 177 175 178 @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 179 public void paint(Graphics2D g, MapView mv, Bounds box) { 187 180 boolean showTextOrIcon = isTextOrIconShown(); 188 g.setColor(getColorProperty().get()); 189 181 g.setColor(DEFAULT_COLOR_PROPERTY.get()); 190 182 if (mousePressed) { 191 183 boolean mousePressedTmp = mousePressed; 192 184 Point mousePos = mv.getMousePosition(); // Get mouse position only when necessary (it's the slowest part of marker layer painting) -
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; … … 24 23 25 24 import org.openstreetmap.josm.actions.ExpertToggleAction; 26 25 import org.openstreetmap.josm.data.PreferencesUtils; 27 import org.openstreetmap.josm.data.preferences.NamedColorProperty;28 26 import org.openstreetmap.josm.gui.MainApplication; 29 27 import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper; 30 28 import org.openstreetmap.josm.gui.layer.markerlayer.Marker; … … 331 329 if (null != dim) { 332 330 // get image size of environment 333 331 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)); 332 colorTypeHeatIconLabel.setIcon(GpxDrawHelper.getColorMapImageIcon( 333 GpxDrawHelper.DEFAULT_COLOR_PROPERTY.get(), 334 colorTypeHeatMapTune.getSelectedIndex(), 335 iconSize)); 343 336 } 344 337 }); 345 338 -
src/org/openstreetmap/josm/io/GpxReader.java
76 76 private State currentState = State.INIT; 77 77 78 78 private GpxLink currentLink; 79 private Extensions currentExtensions;79 private Map<String, String> currentExtensionMap = new HashMap<>(); 80 80 private Stack<State> states; 81 81 private final Stack<String> elements = new Stack<>(); 82 82 … … 159 159 case "extensions": 160 160 states.push(currentState); 161 161 currentState = State.EXT; 162 currentExtensions = new Extensions();163 162 break; 164 163 case "gpx": 165 164 if (atts.getValue("creator") != null && atts.getValue("creator").startsWith("Nokia Sports Tracker")) { … … 178 177 case "extensions": 179 178 states.push(currentState); 180 179 currentState = State.EXT; 181 currentExtensions = new Extensions();182 180 break; 183 181 case "copyright": 184 182 states.push(currentState); … … 228 226 case "extensions": 229 227 states.push(currentState); 230 228 currentState = State.EXT; 231 currentExtensions = new Extensions();232 229 break; 233 230 default: // Do nothing 234 231 } … … 250 247 case "extensions": 251 248 states.push(currentState); 252 249 currentState = State.EXT; 253 currentExtensions = new Extensions();254 250 break; 255 251 default: // Do nothing 256 252 } … … 270 266 case "extensions": 271 267 states.push(currentState); 272 268 currentState = State.EXT; 273 currentExtensions = new Extensions();274 269 break; 275 270 default: // Do nothing 276 271 } … … 349 344 if ((currentState == State.METADATA && "metadata".equals(localName)) || 350 345 (currentState == State.GPX && "gpx".equals(localName))) { 351 346 convertUrlToLink(data.attr); 352 if (currentExtensions != null && !currentExtensions.isEmpty()) { 353 data.put(META_EXTENSIONS, currentExtensions); 354 } 347 data.addExtensions(currentExtensionMap); 355 348 currentState = states.pop(); 356 349 } 357 350 break; … … 464 457 case "wpt": 465 458 currentState = states.pop(); 466 459 convertUrlToLink(currentWayPoint.attr); 467 if (currentExtensions != null && !currentExtensions.isEmpty()) { 468 currentWayPoint.put(META_EXTENSIONS, currentExtensions); 469 } 460 currentWayPoint.addExtensions(currentExtensionMap); 470 461 data.waypoints.add(currentWayPoint); 462 currentExtensionMap = new HashMap<>(); 471 463 break; 472 464 default: // Do nothing 473 465 } … … 483 475 case "trk": 484 476 currentState = states.pop(); 485 477 convertUrlToLink(currentTrackAttr); 486 data.addTrack(new ImmutableGpxTrack(currentTrack, currentTrackAttr)); 478 ImmutableGpxTrack trk = new ImmutableGpxTrack(currentTrack, currentTrackAttr, currentExtensionMap); 479 data.addTrack(trk); 480 currentExtensionMap = new HashMap<>(); 487 481 break; 488 482 case "name": 489 483 case "cmt": … … 499 493 } 500 494 break; 501 495 case EXT: 496 String acc; 502 497 if ("extensions".equals(localName)) { 503 498 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()); 499 } else if ((acc = accumulator.toString().trim()).length() > 0) { 500 currentExtensionMap.put(qName, acc); 507 501 } 508 502 break; 509 503 default: … … 519 513 default: // Do nothing 520 514 } 521 515 } 516 accumulator.setLength(0); 522 517 } 523 518 524 519 @Override … … 525 520 public void endDocument() throws SAXException { 526 521 if (!states.empty()) 527 522 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"))) {523 Extensions josmMetaExt = (Extensions) data.get(EXTENSIONS_JOSM); 524 if (josmMetaExt != null && "true".equals(josmMetaExt.get("from-server"))) { 530 525 data.fromServer = true; 531 526 } 532 527 gpxData = data; -
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); 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 */ 83 public void write(GpxData data, String colorFormat) { 67 84 this.data = data; 68 85 // We write JOSM specific meta information into gpx 'extensions' elements. 69 86 // In particular it is noted whether the gpx data is from the OSM server … … 71 88 // and some extra synchronization info for export of AudioMarkers. 72 89 // It is checked in advance, if any extensions are used, so we know whether 73 90 // a namespace declaration is necessary. 74 boolean hasExtensions = data.fromServer; 75 if (!hasExtensions) { 91 92 boolean hasJosmExtension = data.fromServer, 93 hasGpxxExtension = false, 94 hasGpxdExtension = false, 95 searchGpxx = EXTENSIONS_GARMIN.equals(colorFormat), 96 searchGpxd = EXTENSIONS_DRAWING.equals(colorFormat); 97 98 if (!hasJosmExtension) { 76 99 for (WayPoint wpt : data.waypoints) { 77 Extensions extensions = (Extensions) wpt.get( META_EXTENSIONS);100 Extensions extensions = (Extensions) wpt.get(EXTENSIONS_JOSM); 78 101 if (extensions != null && !extensions.isEmpty()) { 79 has Extensions= true;102 hasJosmExtension = true; 80 103 break; 81 104 } 82 105 } 83 106 } 84 107 108 if (searchGpxx || searchGpxd) { 109 110 HashMap<Color, String> closestColorCache = new HashMap<>(); 111 112 for (GpxTrack trk : data.getTracks()) { 113 Extensions gpxx = (Extensions) trk.get(EXTENSIONS_GARMIN), 114 gpxd = (Extensions) trk.get(EXTENSIONS_DRAWING); 115 String gpxxC = null, gpxdC = null; 116 117 if (gpxd != null) { 118 gpxdC = gpxd.get("color"); 119 } 120 if (gpxx != null) { 121 gpxxC = gpxx.get("displaycolor"); 122 } 123 124 if (searchGpxd && gpxdC == null && gpxx != null && gpxxC != null) { 125 //Convert GPXX to GPXD 126 Color c = GARMIN_COLORS.get(gpxxC); 127 if (c != null) { 128 //Remove GPXX 129 trk.removeExtensionKey(EXTENSIONS_GARMIN, "displaycolor"); 130 //Put GPXD 131 trk.setColor(c); 132 hasGpxdExtension = true; 133 } else { 134 Logging.warn("Could not read garmin color: " + gpxxC); 135 } 136 } else if (searchGpxx && gpxxC == null && gpxd != null && gpxdC != null) { 137 //Convert GPXD to GPXX 138 try { 139 Color c = Color.decode(gpxdC); 140 //Remove GPXD 141 trk.removeExtensionKey(EXTENSIONS_DRAWING, "color"); 142 //Put GPXX 143 String colorString = null; 144 double closestDiff = -1; 145 146 if (closestColorCache.containsKey(c)) { 147 colorString = closestColorCache.get(c); 148 } else { 149 //find closest garmin color 150 for (Entry<String, Color> e : GARMIN_COLORS.entrySet()) { 151 double diff = colorDist(e.getValue(), c); 152 if (closestDiff < 0 || diff < closestDiff) { 153 colorString = e.getKey(); 154 closestDiff = diff; 155 if (closestDiff == 0) break; 156 } 157 } 158 closestColorCache.put(c, colorString); 159 } 160 trk.addExtensionKey(EXTENSIONS_GARMIN, "DisplayColor", colorString); 161 hasGpxxExtension = true; 162 } catch (NumberFormatException ex) { 163 Logging.warn("Could not read gpxd color: " + gpxdC); 164 } 165 } 166 //Must be checked again because of conversion above 167 if (!hasGpxdExtension && gpxd != null && !gpxd.isEmpty()) { 168 hasGpxdExtension = true; 169 } 170 if (!hasGpxxExtension && gpxx != null && !gpxx.isEmpty()) { 171 hasGpxxExtension = true; 172 } 173 } 174 } 175 85 176 out.println("<?xml version='1.0' encoding='UTF-8'?>"); 86 177 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\">"); 178 179 String schemaLocations = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"; 180 181 if (hasJosmExtension) { 182 extlinks.put(GpxConstants.EXTENSIONS_JOSM, new SimpleEntry<>(GpxConstants.XML_URI_EXTENSIONS_JOSM, GpxConstants.XML_XSD_EXTENSIONS_JOSM)); 183 } 184 if (hasGpxdExtension) { 185 extlinks.put(GpxConstants.EXTENSIONS_DRAWING, new SimpleEntry<>(GpxConstants.XML_URI_EXTENSIONS_DRAWING, GpxConstants.XML_XSD_EXTENSIONS_DRAWING)); 186 } 187 if (hasGpxxExtension) { 188 extlinks.put(GpxConstants.EXTENSIONS_GARMIN, new SimpleEntry<>(GpxConstants.XML_URI_EXTENSIONS_GARMIN, GpxConstants.XML_XSD_EXTENSIONS_GARMIN)); 189 } 190 191 for (Entry<String, Entry<String, String>> e : extlinks.entrySet()) { 192 String k = e.getKey(); 193 Entry<String, String> v = e.getValue(); 194 if (!k.startsWith(EXTENSIONS_PREFIX)) { 195 throw new Extensions.InvalidExtensionException(); 196 } 197 out.println(String.format(" xmlns:%s=\"%s\"", k.substring(k.lastIndexOf(".") + 1), v.getKey())); 198 schemaLocations += " " + v.getKey() + " " + v.getValue(); 199 } 200 201 out.println(" xmlns:xsi=\""+XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI+"\""); 202 out.println(String.format(" xsi:schemaLocation=\"%s\">", schemaLocations)); 90 203 indent = " "; 91 204 writeMetaData(); 92 205 writeWayPoints(); … … 97 210 } 98 211 99 212 private void writeAttr(IWithAttributes obj, List<String> keys) { 213 List<Extensions> allExtensions = new ArrayList<>(); 100 214 for (String key : keys) { 101 215 if (META_LINKS.equals(key)) { 102 216 Collection<GpxLink> lValue = obj.<GpxLink>getCollection(key); … … 105 219 gpxLink(link); 106 220 } 107 221 } 108 } else if (META_EXTENSIONS.equals(key)) { 109 Extensions extensions = (Extensions) obj.get(key); 110 if (extensions != null) { 111 gpxExtensions(extensions); 222 } else if (key != null && key.startsWith(EXTENSIONS_PREFIX)) { 223 Extensions ex = (Extensions) obj.get(key); 224 if (ex != null && !ex.isEmpty()) { 225 //ex.setType(key); 226 allExtensions.add(ex); 112 227 } 113 228 } else { 114 229 String value = obj.getString(key); … … 126 241 } 127 242 } 128 243 } 244 gpxExtensions(allExtensions); 129 245 } 130 246 247 double colorDist(Color c1, Color c2) { 248 // Simple Euclidean distance between two colors 249 return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(), 2) 250 + Math.pow(c1.getGreen() - c2.getGreen(), 2) 251 + Math.pow(c1.getBlue() - c2.getBlue(), 2)); 252 } 253 254 131 255 private void writeMetaData() { 132 256 Map<String, Object> attr = data.attr; 133 257 openln("metadata"); … … 318 442 } 319 443 } 320 444 321 private void gpxExtensions( Extensions extensions) {322 if ( extensions != null && !extensions.isEmpty()) {445 private void gpxExtensions(List<Extensions> allExtensions) { 446 if (!allExtensions.isEmpty()) { 323 447 openln("extensions"); 324 for (Entry<String, String> e : extensions.entrySet()) { 325 simpleTag("josm:" + e.getKey(), e.getValue()); 448 for (Extensions extensions : allExtensions) { 449 if (extlinks.containsKey(extensions.getType())) { 450 //making sure links were actually added / filter if no colors are to be saved at all. 451 //TODO: probably makes more sense to do the conversion here instead of in the beginning 452 //note that the colors will actually change during the conversion to GPXX 453 boolean garmin = EXTENSIONS_GARMIN.equals(extensions.getType()); 454 if (garmin) { //allow nested Garmin TrackExtension. Not ideal but does the job. 455 openln("gpxx:TrackExtension"); 456 } 457 for (Entry<String, String> e : extensions.entrySet()) { 458 simpleTag(extensions.getPrefix() + e.getKey(), e.getValue()); 459 } 460 if (garmin) { 461 closeln("gpxx:TrackExtension"); 462 } 463 } 326 464 } 327 465 closeln("extensions"); 328 466 } -
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 }