source: josm/trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java@ 4172

Last change on this file since 4172 was 4172, checked in by stoecker, 13 years ago

remove unused icons, make connection timouts configurable and increase them a bit to handle JOSM server delays

  • Property svn:eol-style set to native
File size: 12.2 KB
Line 
1// License: GPL. Copyright 2008 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.layer.markerlayer;
3
4import java.awt.Graphics;
5import java.awt.Point;
6import java.awt.event.ActionEvent;
7import java.awt.event.ActionListener;
8import java.io.File;
9import java.net.MalformedURLException;
10import java.net.URL;
11import java.util.Collection;
12import java.util.HashMap;
13import java.util.LinkedList;
14import java.util.Map;
15
16import javax.swing.Icon;
17
18import org.openstreetmap.josm.data.coor.CachedLatLon;
19import org.openstreetmap.josm.data.coor.EastNorth;
20import org.openstreetmap.josm.data.coor.LatLon;
21import org.openstreetmap.josm.data.gpx.GpxData;
22import org.openstreetmap.josm.data.gpx.GpxLink;
23import org.openstreetmap.josm.data.gpx.WayPoint;
24import org.openstreetmap.josm.data.preferences.IntegerProperty;
25import org.openstreetmap.josm.gui.MapView;
26import org.openstreetmap.josm.tools.ImageProvider;
27
28/**
29 * Basic marker class. Requires a position, and supports
30 * a custom icon and a name.
31 *
32 * This class is also used to create appropriate Marker-type objects
33 * when waypoints are imported.
34 *
35 * It hosts a public list object, named makers, containing implementations of
36 * the MarkerMaker interface. Whenever a Marker needs to be created, each
37 * object in makers is called with the waypoint parameters (Lat/Lon and tag
38 * data), and the first one to return a Marker object wins.
39 *
40 * By default, one the list contains one default "Maker" implementation that
41 * will create AudioMarkers for .wav files, ImageMarkers for .png/.jpg/.jpeg
42 * files, and WebMarkers for everything else. (The creation of a WebMarker will
43 * fail if there's no vaild URL in the <link> tag, so it might still make sense
44 * to add Makers for such waypoints at the end of the list.)
45 *
46 * The default implementation only looks at the value of the <link> tag inside
47 * the <wpt> tag of the GPX file.
48 *
49 * <h2>HowTo implement a new Marker</h2>
50 * <ul>
51 * <li> Subclass Marker or ButtonMarker and override <code>containsPoint</code>
52 * if you like to respond to user clicks</li>
53 * <li> Override paint, if you want a custom marker look (not "a label and a symbol")</li>
54 * <li> Implement MarkerCreator to return a new instance of your marker class</li>
55 * <li> In you plugin constructor, add an instance of your MarkerCreator
56 * implementation either on top or bottom of Marker.markerProducers.
57 * Add at top, if your marker should overwrite an current marker or at bottom
58 * if you only add a new marker style.</li>
59 * </ul>
60 *
61 * @author Frederik Ramm <frederik@remote.org>
62 */
63public class Marker implements ActionListener {
64 public final String text;
65 public final Map<String,String> textMap = new HashMap<String,String>();
66 public final Icon symbol;
67 public final MarkerLayer parentLayer;
68 public double time; /* absolute time of marker since epoch */
69 public double offset; /* time offset in seconds from the gpx point from which it was derived,
70 may be adjusted later to sync with other data, so not final */
71
72 private CachedLatLon coor;
73
74 public final void setCoor(LatLon coor) {
75 if(this.coor == null) {
76 this.coor = new CachedLatLon(coor);
77 } else {
78 this.coor.setCoor(coor);
79 }
80 }
81
82 public final LatLon getCoor() {
83 return coor;
84 }
85
86 public final void setEastNorth(EastNorth eastNorth) {
87 coor.setEastNorth(eastNorth);
88 }
89
90 public final EastNorth getEastNorth() {
91 return coor.getEastNorth();
92 }
93
94 /**
95 * Plugins can add their Marker creation stuff at the bottom or top of this list
96 * (depending on whether they want to override default behaviour or just add new
97 * stuff).
98 */
99 public static LinkedList<MarkerProducers> markerProducers = new LinkedList<MarkerProducers>();
100
101 private static final IntegerProperty PROP_LABEL = new IntegerProperty("draw.rawgps.layer.wpt", 0 );
102 private static final String[] labelAttributes = new String[] {"name", "desc"};
103
104 // Add one Maker specifying the default behaviour.
105 static {
106 Marker.markerProducers.add(new MarkerProducers() {
107 @SuppressWarnings("unchecked")
108 public Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) {
109 String uri = null;
110 // cheapest way to check whether "link" object exists and is a non-empty
111 // collection of GpxLink objects...
112 try {
113 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get(GpxData.META_LINKS)) {
114 uri = oneLink.uri;
115 break;
116 }
117 } catch (Exception ex) {}
118
119 // Try a relative file:// url, if the link is not in an URL-compatible form
120 if (relativePath != null && uri != null && !isWellFormedAddress(uri)) {
121 uri = new File(relativePath.getParentFile(), uri).toURI().toString();
122 }
123
124 Map<String,String> nameDesc = new HashMap<String,String>();
125 for(String attribute : labelAttributes) {
126 if (wpt.attr.containsKey(attribute)) {
127 nameDesc.put(attribute, wpt.getString(attribute));
128 }
129 }
130
131 if (uri == null) {
132 String symbolName = wpt.getString("symbol");
133 if (symbolName == null) {
134 symbolName = wpt.getString("sym");
135 }
136 return new Marker(wpt.getCoor(), nameDesc, symbolName, parentLayer, time, offset);
137 }
138 else if (uri.endsWith(".wav"))
139 return AudioMarker.create(wpt.getCoor(), getText(nameDesc), uri, parentLayer, time, offset);
140 else if (uri.endsWith(".png") || uri.endsWith(".jpg") || uri.endsWith(".jpeg") || uri.endsWith(".gif"))
141 return ImageMarker.create(wpt.getCoor(), uri, parentLayer, time, offset);
142 else
143 return WebMarker.create(wpt.getCoor(), uri, parentLayer, time, offset);
144 }
145
146 private boolean isWellFormedAddress(String link) {
147 try {
148 new URL(link);
149 return true;
150 } catch (MalformedURLException x) {
151 return false;
152 }
153 }
154 });
155 }
156
157 public Marker(LatLon ll, String text, String iconName, MarkerLayer parentLayer, double time, double offset) {
158 setCoor(ll);
159 if (text == null || text.length() == 0) {
160 this.text = null;
161 }
162 else {
163 this.text = text;
164 }
165 this.offset = offset;
166 this.time = time;
167 this.symbol = ImageProvider.getIfAvailable("markers",iconName);
168 this.parentLayer = parentLayer;
169 }
170
171 public Marker(LatLon ll, Map<String,String> textMap, String iconName, MarkerLayer parentLayer, double time, double offset) {
172 setCoor(ll);
173 if (textMap != null) {
174 this.textMap.clear();
175 this.textMap.putAll(textMap);
176 }
177
178 this.text = null;
179 this.offset = offset;
180 this.time = time;
181 // /* ICON(markers/) */"Bridge"
182 // /* ICON(markers/) */"Crossing"
183 this.symbol = ImageProvider.getIfAvailable("markers",iconName);
184 this.parentLayer = parentLayer;
185 }
186
187 /**
188 * Checks whether the marker display area contains the given point.
189 * Markers not interested in mouse clicks may always return false.
190 *
191 * @param p The point to check
192 * @return <code>true</code> if the marker "hotspot" contains the point.
193 */
194 public boolean containsPoint(Point p) {
195 return false;
196 }
197
198 /**
199 * Called when the mouse is clicked in the marker's hotspot. Never
200 * called for markers which always return false from containsPoint.
201 *
202 * @param ev A dummy ActionEvent
203 */
204 public void actionPerformed(ActionEvent ev) {
205 }
206
207 /**
208 * Paints the marker.
209 * @param g graphics context
210 * @param mv map view
211 * @param mousePressed true if the left mouse button is pressed
212 */
213 public void paint(Graphics g, MapView mv, boolean mousePressed, boolean showTextOrIcon) {
214 Point screen = mv.getPoint(getEastNorth());
215 if (symbol != null && showTextOrIcon) {
216 symbol.paintIcon(mv, g, screen.x-symbol.getIconWidth()/2, screen.y-symbol.getIconHeight()/2);
217 } else {
218 g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2);
219 g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2);
220 }
221
222 String labelText = getText();
223 if ((labelText != null) && showTextOrIcon) {
224 g.drawString(labelText, screen.x+4, screen.y+2);
225 }
226 }
227
228 /**
229 * Returns an object of class Marker or one of its subclasses
230 * created from the parameters given.
231 *
232 * @param wpt waypoint data for marker
233 * @param relativePath An path to use for constructing relative URLs or
234 * <code>null</code> for no relative URLs
235 * @param offset double in seconds as the time offset of this marker from
236 * the GPX file from which it was derived (if any).
237 * @return a new Marker object
238 */
239 public static Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) {
240 for (MarkerProducers maker : Marker.markerProducers) {
241 Marker marker = maker.createMarker(wpt, relativePath, parentLayer, time, offset);
242 if (marker != null)
243 return marker;
244 }
245 return null;
246 }
247
248 /**
249 * Returns an AudioMarker derived from this Marker and the provided uri
250 * Subclasses of specific marker types override this to return null as they can't
251 * be turned into AudioMarkers. This includes AudioMarkers themselves, as they
252 * already have audio.
253 *
254 * @param uri uri of wave file
255 * @return AudioMarker
256 */
257
258 public AudioMarker audioMarkerFromMarker(String uri) {
259 AudioMarker audioMarker = AudioMarker.create(getCoor(), this.getText(), uri, this.parentLayer, this.time, this.offset);
260 return audioMarker;
261 }
262
263 /**
264 * Returns the Text which should be displayed, depending on chosen preference
265 * @return Text
266 */
267 public String getText() {
268 if (this.text != null )
269 return this.text;
270 else
271 return getText(this.textMap);
272 }
273
274 /**
275 * Returns the Text which should be displayed, depending on chosen preference.
276 * The possible attributes are read from textMap.
277 *
278 * @param textMap A map with available texts/attributes
279 * @return Text
280 */
281 private static String getText(Map<String,String> textMap) {
282 String text = "";
283
284 if (textMap != null && !textMap.isEmpty()) {
285 switch(PROP_LABEL.get())
286 {
287 // name
288 case 1:
289 {
290 if (textMap.containsKey("name")) {
291 text = textMap.get("name");
292 }
293 break;
294 }
295
296 // desc
297 case 2:
298 {
299 if (textMap.containsKey("desc")) {
300 text = textMap.get("desc");
301 }
302 break;
303 }
304
305 // auto
306 case 0:
307 // both
308 case 3:
309 {
310 if (textMap.containsKey("name")) {
311 text = textMap.get("name");
312
313 if (textMap.containsKey("desc")) {
314 if (PROP_LABEL.get() != 0 || !text.equals(textMap.get("desc"))) {
315 text += " - " + textMap.get("desc");
316 }
317 }
318 }
319 else if (textMap.containsKey("desc")) {
320 text = textMap.get("desc");
321 }
322 break;
323 }
324
325 // none
326 case 4:
327 default:
328 {
329 text = "";
330 break;
331 }
332 }
333 }
334
335 return text;
336 }
337}
Note: See TracBrowser for help on using the repository browser.