source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java@ 6340

Last change on this file since 6340 was 6280, checked in by Don-vip, 11 years ago

fix #9131 - Mappaint style icons from URLs hang with clockface

  • Property svn:eol-style set to native
File size: 15.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint;
3
4import static org.openstreetmap.josm.tools.Utils.equal;
5
6import java.awt.BasicStroke;
7import java.awt.Color;
8import java.awt.Image;
9import java.awt.Rectangle;
10import java.awt.Stroke;
11
12import org.openstreetmap.josm.Main;
13import org.openstreetmap.josm.data.osm.Node;
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
17import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
18import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProvider;
19import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.SimpleBoxProvider;
20import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
21import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
22import org.openstreetmap.josm.tools.Utils;
23
24/**
25 * applies for Nodes and turn restriction relations
26 */
27public class NodeElemStyle extends ElemStyle implements StyleKeys {
28 public final MapImage mapImage;
29 public final Symbol symbol;
30
31 private Image enabledNodeIcon;
32 private Image disabledNodeIcon;
33
34 private boolean enabledNodeIconIsTemporary;
35 private boolean disabledNodeIconIsTemporary;
36
37 public enum SymbolShape { SQUARE, CIRCLE, TRIANGLE, PENTAGON, HEXAGON, HEPTAGON, OCTAGON, NONAGON, DECAGON }
38
39 public static class Symbol {
40 public SymbolShape symbol;
41 public int size;
42 public Stroke stroke;
43 public Color strokeColor;
44 public Color fillColor;
45
46 public Symbol(SymbolShape symbol, int size, Stroke stroke, Color strokeColor, Color fillColor) {
47 if (stroke != null && strokeColor == null)
48 throw new IllegalArgumentException();
49 if (stroke == null && fillColor == null)
50 throw new IllegalArgumentException();
51 this.symbol = symbol;
52 this.size = size;
53 this.stroke = stroke;
54 this.strokeColor = strokeColor;
55 this.fillColor = fillColor;
56 }
57
58 @Override
59 public boolean equals(Object obj) {
60 if (obj == null || getClass() != obj.getClass())
61 return false;
62 final Symbol other = (Symbol) obj;
63 return symbol == other.symbol &&
64 size == other.size &&
65 equal(stroke, other.stroke) &&
66 equal(strokeColor, other.strokeColor) &&
67 equal(fillColor, other.fillColor);
68 }
69
70 @Override
71 public int hashCode() {
72 int hash = 7;
73 hash = 67 * hash + symbol.hashCode();
74 hash = 67 * hash + size;
75 hash = 67 * hash + (stroke != null ? stroke.hashCode() : 0);
76 hash = 67 * hash + (strokeColor != null ? strokeColor.hashCode() : 0);
77 hash = 67 * hash + (fillColor != null ? fillColor.hashCode() : 0);
78 return hash;
79 }
80
81 @Override
82 public String toString() {
83 return "symbol=" + symbol + " size=" + size +
84 (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
85 (fillColor != null ? (" fillColor=" + fillColor) : "");
86 }
87 }
88
89 public static final NodeElemStyle SIMPLE_NODE_ELEMSTYLE;
90 static {
91 MultiCascade mc = new MultiCascade();
92 mc.getOrCreateCascade("default");
93 SIMPLE_NODE_ELEMSTYLE = create(new Environment(null, mc, "default", null), 4.1f, true);
94 if (SIMPLE_NODE_ELEMSTYLE == null) throw new AssertionError();
95 }
96
97 public static final StyleList DEFAULT_NODE_STYLELIST = new StyleList(NodeElemStyle.SIMPLE_NODE_ELEMSTYLE);
98 public static final StyleList DEFAULT_NODE_STYLELIST_TEXT = new StyleList(NodeElemStyle.SIMPLE_NODE_ELEMSTYLE, BoxTextElemStyle.SIMPLE_NODE_TEXT_ELEMSTYLE);
99
100 protected NodeElemStyle(Cascade c, MapImage mapImage, Symbol symbol, float default_major_z_index) {
101 super(c, default_major_z_index);
102 this.mapImage = mapImage;
103 this.symbol = symbol;
104 }
105
106 public static NodeElemStyle create(Environment env) {
107 return create(env, 4f, false);
108 }
109
110 private static NodeElemStyle create(Environment env, float default_major_z_index, boolean allowDefault) {
111 Cascade c = env.mc.getCascade(env.layer);
112
113 MapImage mapImage = createIcon(env, ICON_KEYS);
114 Symbol symbol = null;
115 if (mapImage == null) {
116 symbol = createSymbol(env);
117 }
118
119 // optimization: if we neither have a symbol, nor a mapImage
120 // we don't have to check for the remaining style properties and we don't
121 // have to allocate a node element style.
122 if (!allowDefault && symbol == null && mapImage == null) return null;
123
124 return new NodeElemStyle(c, mapImage, symbol, default_major_z_index);
125 }
126
127 public static MapImage createIcon(final Environment env, final String[] keys) {
128 Cascade c = env.mc.getCascade(env.layer);
129
130 final IconReference iconRef = c.get(keys[ICON_IMAGE_IDX], null, IconReference.class);
131 if (iconRef == null)
132 return null;
133
134 Cascade c_def = env.mc.getCascade("default");
135
136 Float widthOnDefault = c_def.get(keys[ICON_WIDTH_IDX], null, Float.class);
137 if (widthOnDefault != null && widthOnDefault <= 0) {
138 widthOnDefault = null;
139 }
140 Float widthF = getWidth(c, keys[ICON_WIDTH_IDX], widthOnDefault);
141
142 Float heightOnDefault = c_def.get(keys[ICON_HEIGHT_IDX], null, Float.class);
143 if (heightOnDefault != null && heightOnDefault <= 0) {
144 heightOnDefault = null;
145 }
146 Float heightF = getWidth(c, keys[ICON_HEIGHT_IDX], heightOnDefault);
147
148 int width = widthF == null ? -1 : Math.round(widthF);
149 int height = heightF == null ? -1 : Math.round(heightF);
150
151 final MapImage mapImage = new MapImage(iconRef.iconName, iconRef.source);
152
153 mapImage.width = width;
154 mapImage.height = height;
155
156 mapImage.alpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.icon-image-alpha", 255))));
157 Integer pAlpha = Utils.color_float2int(c.get(keys[ICON_OPACITY_IDX], null, float.class));
158 if (pAlpha != null) {
159 mapImage.alpha = pAlpha;
160 }
161 return mapImage;
162 }
163
164 private static Symbol createSymbol(Environment env) {
165 Cascade c = env.mc.getCascade(env.layer);
166 Cascade c_def = env.mc.getCascade("default");
167
168 SymbolShape shape;
169 Keyword shapeKW = c.get("symbol-shape", null, Keyword.class);
170 if (shapeKW == null)
171 return null;
172 if (equal(shapeKW.val, "square")) {
173 shape = SymbolShape.SQUARE;
174 } else if (equal(shapeKW.val, "circle")) {
175 shape = SymbolShape.CIRCLE;
176 } else if (equal(shapeKW.val, "triangle")) {
177 shape = SymbolShape.TRIANGLE;
178 } else if (equal(shapeKW.val, "pentagon")) {
179 shape = SymbolShape.PENTAGON;
180 } else if (equal(shapeKW.val, "hexagon")) {
181 shape = SymbolShape.HEXAGON;
182 } else if (equal(shapeKW.val, "heptagon")) {
183 shape = SymbolShape.HEPTAGON;
184 } else if (equal(shapeKW.val, "octagon")) {
185 shape = SymbolShape.OCTAGON;
186 } else if (equal(shapeKW.val, "nonagon")) {
187 shape = SymbolShape.NONAGON;
188 } else if (equal(shapeKW.val, "decagon")) {
189 shape = SymbolShape.DECAGON;
190 } else
191 return null;
192
193 Float sizeOnDefault = c_def.get("symbol-size", null, Float.class);
194 if (sizeOnDefault != null && sizeOnDefault <= 0) {
195 sizeOnDefault = null;
196 }
197 Float size = getWidth(c, "symbol-size", sizeOnDefault);
198
199 if (size == null) {
200 size = 10f;
201 }
202
203 if (size <= 0)
204 return null;
205
206 Float strokeWidthOnDefault = getWidth(c_def, "symbol-stroke-width", null);
207 Float strokeWidth = getWidth(c, "symbol-stroke-width", strokeWidthOnDefault);
208
209 Color strokeColor = c.get("symbol-stroke-color", null, Color.class);
210
211 if (strokeWidth == null && strokeColor != null) {
212 strokeWidth = 1f;
213 } else if (strokeWidth != null && strokeColor == null) {
214 strokeColor = Color.ORANGE;
215 }
216
217 Stroke stroke = null;
218 if (strokeColor != null) {
219 float strokeAlpha = c.get("symbol-stroke-opacity", 1f, Float.class);
220 strokeColor = new Color(strokeColor.getRed(), strokeColor.getGreen(),
221 strokeColor.getBlue(), Utils.color_float2int(strokeAlpha));
222 stroke = new BasicStroke(strokeWidth);
223 }
224
225 Color fillColor = c.get("symbol-fill-color", null, Color.class);
226 if (stroke == null && fillColor == null) {
227 fillColor = Color.BLUE;
228 }
229
230 if (fillColor != null) {
231 float fillAlpha = c.get("symbol-fill-opacity", 1f, Float.class);
232 fillColor = new Color(fillColor.getRed(), fillColor.getGreen(),
233 fillColor.getBlue(), Utils.color_float2int(fillAlpha));
234 }
235
236 return new Symbol(shape, Math.round(size), stroke, strokeColor, fillColor);
237 }
238
239 @Override
240 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, StyledMapRenderer painter, boolean selected, boolean member) {
241 if (primitive instanceof Node) {
242 Node n = (Node) primitive;
243 if (mapImage != null && painter.isShowIcons()) {
244 final Image nodeIcon;
245 if (painter.isInactiveMode() || n.isDisabled()) {
246 if (disabledNodeIcon == null || disabledNodeIconIsTemporary) {
247 disabledNodeIcon = mapImage.getDisplayedNodeIcon(true);
248 disabledNodeIconIsTemporary = mapImage.isTemporary();
249 }
250 nodeIcon = disabledNodeIcon;
251 } else {
252 if (enabledNodeIcon == null || enabledNodeIconIsTemporary) {
253 enabledNodeIcon = mapImage.getDisplayedNodeIcon(false);
254 enabledNodeIconIsTemporary = mapImage.isTemporary();
255 }
256 nodeIcon = enabledNodeIcon;
257 }
258 painter.drawNodeIcon(n, nodeIcon, Utils.color_int2float(mapImage.alpha), selected, member);
259 } else if (symbol != null) {
260 Color fillColor = symbol.fillColor;
261 if (fillColor != null) {
262 if (painter.isInactiveMode() || n.isDisabled()) {
263 fillColor = settings.getInactiveColor();
264 } else if (selected) {
265 fillColor = settings.getSelectedColor(fillColor.getAlpha());
266 } else if (member) {
267 fillColor = settings.getRelationSelectedColor(fillColor.getAlpha());
268 }
269 }
270 Color strokeColor = symbol.strokeColor;
271 if (strokeColor != null) {
272 if (painter.isInactiveMode() || n.isDisabled()) {
273 strokeColor = settings.getInactiveColor();
274 } else if (selected) {
275 strokeColor = settings.getSelectedColor(strokeColor.getAlpha());
276 } else if (member) {
277 strokeColor = settings.getRelationSelectedColor(strokeColor.getAlpha());
278 }
279 }
280 painter.drawNodeSymbol(n, symbol, fillColor, strokeColor);
281 } else {
282 Color color;
283 boolean isConnection = n.isConnectionNode();
284
285 if (painter.isInactiveMode() || n.isDisabled()) {
286 color = settings.getInactiveColor();
287 } else if (selected) {
288 color = settings.getSelectedColor();
289 } else if (member) {
290 color = settings.getRelationSelectedColor();
291 } else if (isConnection) {
292 if (n.isTagged()) {
293 color = settings.getTaggedConnectionColor();
294 } else {
295 color = settings.getConnectionColor();
296 }
297 } else {
298 if (n.isTagged()) {
299 color = settings.getTaggedColor();
300 } else {
301 color = settings.getNodeColor();
302 }
303 }
304
305 final int size = Utils.max((selected ? settings.getSelectedNodeSize() : 0),
306 (n.isTagged() ? settings.getTaggedNodeSize() : 0),
307 (isConnection ? settings.getConnectionNodeSize() : 0),
308 settings.getUnselectedNodeSize());
309
310 final boolean fill = (selected && settings.isFillSelectedNode()) ||
311 (n.isTagged() && settings.isFillTaggedNode()) ||
312 (isConnection && settings.isFillConnectionNode()) ||
313 settings.isFillUnselectedNode();
314
315 painter.drawNode(n, color, size, fill);
316
317 }
318 } else if (primitive instanceof Relation && mapImage != null) {
319 painter.drawRestriction((Relation) primitive, mapImage);
320 }
321 }
322
323 public BoxProvider getBoxProvider() {
324 if (mapImage != null)
325 return mapImage.getBoxProvider();
326 else if (symbol != null)
327 return new SimpleBoxProvider(new Rectangle(-symbol.size/2, -symbol.size/2, symbol.size, symbol.size));
328 else {
329 // This is only executed once, so no performance concerns.
330 // However, it would be better, if the settings could be changed at runtime.
331 int size = Utils.max(
332 Main.pref.getInteger("mappaint.node.selected-size", 5),
333 Main.pref.getInteger("mappaint.node.unselected-size", 3),
334 Main.pref.getInteger("mappaint.node.connection-size", 5),
335 Main.pref.getInteger("mappaint.node.tagged-size", 3)
336 );
337 return new SimpleBoxProvider(new Rectangle(-size/2, -size/2, size, size));
338 }
339 }
340
341 @Override
342 public int hashCode() {
343 int hash = super.hashCode();
344 hash = 17 * hash + (mapImage != null ? mapImage.hashCode() : 0);
345 hash = 17 * hash + (symbol != null ? symbol.hashCode() : 0);
346 return hash;
347 }
348
349 @Override
350 public boolean equals(Object obj) {
351 if (obj == null || getClass() != obj.getClass())
352 return false;
353 if (!super.equals(obj))
354 return false;
355
356 final NodeElemStyle other = (NodeElemStyle) obj;
357 // we should get the same image object due to caching
358 if (!equal(mapImage, other.mapImage))
359 return false;
360 if (!equal(symbol, other.symbol))
361 return false;
362 return true;
363 }
364
365 @Override
366 public String toString() {
367 StringBuilder s = new StringBuilder("NodeElemStyle{");
368 s.append(super.toString());
369 if (mapImage != null) {
370 s.append(" icon=[" + mapImage + "]");
371 }
372 if (symbol != null) {
373 s.append(" symbol=[" + symbol + "]");
374 }
375 s.append('}');
376 return s.toString();
377 }
378}
Note: See TracBrowser for help on using the repository browser.