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

Last change on this file since 4005 was 4005, checked in by bastiK, 13 years ago

mapcss: MapPaintVisitor rework (more flexibility with z-index)

  • Property svn:eol-style set to native
File size: 18.6 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.Stroke;
10
11import javax.swing.GrayFilter;
12import javax.swing.ImageIcon;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.data.osm.Node;
16import org.openstreetmap.josm.data.osm.OsmPrimitive;
17import org.openstreetmap.josm.data.osm.Relation;
18import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
19import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
20import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
21import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
22import org.openstreetmap.josm.tools.CheckParameterUtil;
23import org.openstreetmap.josm.tools.Pair;
24import org.openstreetmap.josm.tools.Utils;
25
26/**
27 * applies for Nodes and turn restriction relations
28 */
29public class NodeElemStyle extends ElemStyle {
30 //static private final Logger logger = Logger.getLogger(NodeElemStyle.class.getName());
31
32 public ImageIcon icon;
33 public int iconAlpha;
34 public Symbol symbol;
35 public NodeTextElement text;
36
37 private ImageIcon disabledIcon;
38
39 public enum SymbolShape { SQUARE, CIRCLE, TRIANGLE, PENTAGON, HEXAGON, HEPTAGON, OCTAGON, NONAGON, DECAGON }
40 public enum HorizontalTextAlignment { LEFT, CENTER, RIGHT }
41 public enum VerticalTextAlignment { ABOVE, TOP, CENTER, BOTTOM, BELOW }
42
43 public static class Symbol {
44 public SymbolShape symbol;
45 public int size;
46 public Stroke stroke;
47 public Color strokeColor;
48 public Color fillColor;
49
50 public Symbol(SymbolShape symbol, int size, Stroke stroke, Color strokeColor, Color fillColor) {
51 if (stroke != null && strokeColor == null)
52 throw new IllegalArgumentException();
53 if (stroke == null && fillColor == null)
54 throw new IllegalArgumentException();
55 this.symbol = symbol;
56 this.size = size;
57 this.stroke = stroke;
58 this.strokeColor = strokeColor;
59 this.fillColor = fillColor;
60 }
61
62 @Override
63 public boolean equals(Object obj) {
64 if (obj == null || getClass() != obj.getClass())
65 return false;
66 final Symbol other = (Symbol) obj;
67 return symbol == other.symbol &&
68 size == other.size &&
69 equal(stroke, other.stroke) &&
70 equal(strokeColor, other.strokeColor) &&
71 equal(fillColor, other.fillColor);
72 }
73
74 @Override
75 public int hashCode() {
76 int hash = 7;
77 hash = 67 * hash + symbol.hashCode();
78 hash = 67 * hash + size;
79 hash = 67 * hash + (stroke != null ? stroke.hashCode() : 0);
80 hash = 67 * hash + (strokeColor != null ? strokeColor.hashCode() : 0);
81 hash = 67 * hash + (fillColor != null ? fillColor.hashCode() : 0);
82 return hash;
83 }
84
85 @Override
86 public String toString() {
87 return "symbol=" + symbol + " size=" + size +
88 (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
89 (fillColor != null ? (" fillColor=" + fillColor) : "");
90 }
91 }
92
93 public static class NodeTextElement extends TextElement {
94 public HorizontalTextAlignment hAlign;
95 public VerticalTextAlignment vAlign;
96
97 public NodeTextElement(TextElement text, HorizontalTextAlignment hAlign, VerticalTextAlignment vAlign) {
98 super(text);
99 CheckParameterUtil.ensureParameterNotNull(hAlign);
100 CheckParameterUtil.ensureParameterNotNull(vAlign);
101 this.hAlign = hAlign;
102 this.vAlign = vAlign;
103 }
104
105 @Override
106 public boolean equals(Object obj) {
107 if (!super.equals(obj))
108 return false;
109 if (obj == null || getClass() != obj.getClass())
110 return false;
111 final NodeTextElement other = (NodeTextElement) obj;
112 return hAlign == other.hAlign &&
113 vAlign == other.vAlign;
114 }
115
116 @Override
117 public int hashCode() {
118 int hash = super.hashCode();
119 hash = 97 * hash + hAlign.hashCode();
120 hash = 97 * hash + vAlign.hashCode();
121 return hash;
122 }
123
124 @Override
125 public String toString() {
126 return "NodeTextElement{" + toStringImpl() + '}';
127 }
128
129 protected String toStringImpl() {
130 return super.toStringImpl() + " hAlign=" + hAlign + " vAlign=" + vAlign;
131 }
132 }
133
134 public static final NodeElemStyle SIMPLE_NODE_ELEMSTYLE;
135 static {
136 MultiCascade mc = new MultiCascade();
137 Cascade c = mc.getOrCreateCascade("default");
138 c.put("text", Keyword.AUTO);
139 SIMPLE_NODE_ELEMSTYLE = create(new Environment(null, mc, "default", null), true);
140 }
141
142 protected NodeElemStyle(Cascade c, ImageIcon icon, Integer iconAlpha, Symbol symbol, NodeTextElement text) {
143 super(c, 1000f);
144 this.icon = icon;
145 this.iconAlpha = iconAlpha == null ? 0 : iconAlpha;
146 this.symbol = symbol;
147 this.text = text;
148 }
149
150 public static NodeElemStyle create(Environment env) {
151 return create(env, false);
152 }
153
154 /*
155 * Caches the default text color from the preferences.
156 *
157 * FIXME: the cache isn't updated if the user changes the preference during a JOSM
158 * session. There should be preference listener updating this cache.
159 */
160 static private Color DEFAULT_TEXT_COLOR = null;
161 static private void initDefaultParameters() {
162 if (DEFAULT_TEXT_COLOR != null) return;
163 DEFAULT_TEXT_COLOR = PaintColors.TEXT.get();
164 }
165
166 private static NodeElemStyle create(Environment env, boolean allowOnlyText) {
167 initDefaultParameters();
168 Cascade c = env.mc.getCascade(env.layer);
169
170 Pair<ImageIcon, Integer> icon = createIcon(env);
171 Symbol symbol = null;
172 if (icon == null) {
173 symbol = createSymbol(env);
174 }
175
176 NodeTextElement text = null;
177 TextElement te = TextElement.create(c, DEFAULT_TEXT_COLOR, symbol == null && icon == null);
178 // optimization: if we neither have a symbol, nor an icon, nor a text element
179 // we don't have to check for the remaining style properties and we don't
180 // have to allocate a node element style.
181 if (symbol == null && icon == null && te == null) return null;
182
183 if (te != null) {
184 HorizontalTextAlignment hAlign = HorizontalTextAlignment.RIGHT;
185 Keyword hAlignKW = c.get("text-anchor-horizontal", Keyword.RIGHT, Keyword.class);
186 if (equal(hAlignKW.val, "left")) {
187 hAlign = HorizontalTextAlignment.LEFT;
188 } else if (equal(hAlignKW.val, "center")) {
189 hAlign = HorizontalTextAlignment.CENTER;
190 } else if (equal(hAlignKW.val, "right")) {
191 hAlign = HorizontalTextAlignment.RIGHT;
192 }
193 VerticalTextAlignment vAlign = VerticalTextAlignment.BOTTOM;
194 String vAlignStr = c.get("text-anchor-vertical", Keyword.BOTTOM, Keyword.class).val;
195 if (equal(vAlignStr, "above")) {
196 vAlign = VerticalTextAlignment.ABOVE;
197 } else if (equal(vAlignStr, "top")) {
198 vAlign = VerticalTextAlignment.TOP;
199 } else if (equal(vAlignStr, "center")) {
200 vAlign = VerticalTextAlignment.CENTER;
201 } else if (equal(vAlignStr, "bottom")) {
202 vAlign = VerticalTextAlignment.BOTTOM;
203 } else if (equal(vAlignStr, "below")) {
204 vAlign = VerticalTextAlignment.BELOW;
205 }
206 text = new NodeTextElement(te, hAlign, vAlign);
207 }
208
209 return new NodeElemStyle(c,
210 icon == null ? null : icon.a,
211 icon == null ? null : icon.b,
212 symbol,
213 text);
214 }
215
216 private static Pair<ImageIcon, Integer> createIcon(Environment env) {
217 Cascade c = env.mc.getCascade(env.layer);
218 Cascade c_def = env.mc.getCascade("default");
219
220 IconReference iconRef = c.get("icon-image", null, IconReference.class);
221 if (iconRef == null)
222 return null;
223
224 ImageIcon icon = null;
225 int iconAlpha = 0;
226
227 icon = MapPaintStyles.getIcon(iconRef, false);
228 if (icon == null)
229 return new Pair<ImageIcon, Integer>(MapPaintStyles.getNoIcon_Icon(iconRef.source, false), 255);
230 else {
231 Float sizeOnDefault = c_def.get("icon-size", null, Float.class);
232 if (sizeOnDefault != null && sizeOnDefault <= 0) {
233 sizeOnDefault = null;
234 }
235 Float sizeF = getWidth(c, "icon-size", sizeOnDefault);
236
237 if (sizeF != null) {
238 if (sizeF <= 0)
239 return null;
240 int size = Math.round(sizeF);
241 int width = icon.getIconWidth();
242 int height = icon.getIconHeight();
243 if (Math.max(width, height) != size) {
244 if (width >= height) {
245 width = size;
246 height = -1;
247 } else {
248 width = -1;
249 height = size;
250 }
251 icon.setImage(icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH));
252 }
253 }
254 iconAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.icon-image-alpha", 255))));
255 Integer pAlpha = Utils.color_float2int(c.get("icon-opacity", null, float.class));
256 if (pAlpha != null) {
257 iconAlpha = pAlpha;
258 }
259
260 return new Pair<ImageIcon, Integer>(icon, iconAlpha);
261 }
262 }
263
264 private static Symbol createSymbol(Environment env) {
265 Cascade c = env.mc.getCascade(env.layer);
266 Cascade c_def = env.mc.getCascade("default");
267
268 SymbolShape shape;
269 Keyword shapeKW = c.get("symbol-shape", null, Keyword.class);
270 if (shapeKW == null)
271 return null;
272 if (equal(shapeKW.val, "square")) {
273 shape = SymbolShape.SQUARE;
274 } else if (equal(shapeKW.val, "circle")) {
275 shape = SymbolShape.CIRCLE;
276 } else if (equal(shapeKW.val, "triangle")) {
277 shape = SymbolShape.TRIANGLE;
278 } else if (equal(shapeKW.val, "pentagon")) {
279 shape = SymbolShape.PENTAGON;
280 } else if (equal(shapeKW.val, "hexagon")) {
281 shape = SymbolShape.HEXAGON;
282 } else if (equal(shapeKW.val, "heptagon")) {
283 shape = SymbolShape.HEPTAGON;
284 } else if (equal(shapeKW.val, "octagon")) {
285 shape = SymbolShape.OCTAGON;
286 } else if (equal(shapeKW.val, "nonagon")) {
287 shape = SymbolShape.NONAGON;
288 } else if (equal(shapeKW.val, "decagon")) {
289 shape = SymbolShape.DECAGON;
290 } else
291 return null;
292
293 Float sizeOnDefault = c_def.get("symbol-size", null, Float.class);
294 if (sizeOnDefault != null && sizeOnDefault <= 0) {
295 sizeOnDefault = null;
296 }
297 Float size = getWidth(c, "symbol-size", sizeOnDefault);
298
299 if (size == null) {
300 size = 10f;
301 }
302
303 if (size <= 0)
304 return null;
305
306 Float strokeWidthOnDefault = getWidth(c_def, "symbol-stroke-width", null);
307 Float strokeWidth = getWidth(c, "symbol-stroke-width", strokeWidthOnDefault);
308
309 Color strokeColor = c.get("symbol-stroke-color", null, Color.class);
310
311 if (strokeWidth == null && strokeColor != null) {
312 strokeWidth = 1f;
313 } else if (strokeWidth != null && strokeColor == null) {
314 strokeColor = Color.ORANGE;
315 }
316
317 Stroke stroke = null;
318 if (strokeColor != null) {
319 float strokeAlpha = c.get("symbol-stroke-opacity", 1f, Float.class);
320 strokeColor = new Color(strokeColor.getRed(), strokeColor.getGreen(),
321 strokeColor.getBlue(), Utils.color_float2int(strokeAlpha));
322 stroke = new BasicStroke(strokeWidth);
323 }
324
325 Color fillColor = c.get("symbol-fill-color", null, Color.class);
326 if (stroke == null && fillColor == null) {
327 fillColor = Color.BLUE;
328 }
329
330 if (fillColor != null) {
331 float fillAlpha = c.get("symbol-fill-opacity", 1f, Float.class);
332 fillColor = new Color(fillColor.getRed(), fillColor.getGreen(),
333 fillColor.getBlue(), Utils.color_float2int(fillAlpha));
334 }
335
336 return new Symbol(shape, Math.round(size), stroke, strokeColor, fillColor);
337 }
338
339 @Override
340 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, MapPainter painter, boolean selected, boolean member) {
341 if (primitive instanceof Node) {
342 Node n = (Node) primitive;
343 if (icon != null && painter.isShowIcons()) {
344 painter.drawNodeIcon(n, (painter.isInactive() || n.isDisabled()) ? getDisabledIcon() : icon,
345 Utils.color_int2float(iconAlpha), selected, member, text);
346 } else if (symbol != null) {
347 Color fillColor = symbol.fillColor;
348 if (fillColor != null) {
349 if (n.isHighlighted()) {
350 fillColor = settings.getHighlightColor();
351 } else {
352 if (painter.isInactive() || n.isDisabled()) {
353 fillColor = settings.getInactiveColor();
354 } else if (selected) {
355 fillColor = settings.getSelectedColor(fillColor.getAlpha());
356 } else if (member) {
357 fillColor = settings.getRelationSelectedColor(fillColor.getAlpha());
358 }
359 }
360 }
361 Color strokeColor = symbol.strokeColor;
362 if (strokeColor != null) {
363 if (n.isHighlighted()) {
364 strokeColor = settings.getHighlightColor();
365 } else {
366 if (painter.isInactive() || n.isDisabled()) {
367 strokeColor = settings.getInactiveColor();
368 } else if (selected) {
369 strokeColor = settings.getSelectedColor(strokeColor.getAlpha());
370 } else if (member) {
371 strokeColor = settings.getRelationSelectedColor(strokeColor.getAlpha());
372 }
373 }
374 }
375 painter.drawNodeSymbol(n, symbol, fillColor, strokeColor, text);
376 } else {
377 if (n.isHighlighted()) {
378 painter.drawNode(n, settings.getHighlightColor(), settings.getSelectedNodeSize(), settings.isFillSelectedNode(), text);
379 } else {
380 Color color;
381 boolean isConnection = n.isConnectionNode();
382
383 if (painter.isInactive() || n.isDisabled()) {
384 color = settings.getInactiveColor();
385 } else if (selected) {
386 color = settings.getSelectedColor();
387 } else if (member) {
388 color = settings.getRelationSelectedColor();
389 } else if (isConnection) {
390 if (n.isTagged()) {
391 color = settings.getTaggedConnectionColor();
392 } else {
393 color = settings.getConnectionColor();
394 }
395 } else {
396 if (n.isTagged()) {
397 color = settings.getTaggedColor();
398 } else {
399 color = settings.getNodeColor();
400 }
401 }
402
403 final int size = Utils.max((selected ? settings.getSelectedNodeSize() : 0),
404 (n.isTagged() ? settings.getTaggedNodeSize() : 0),
405 (isConnection ? settings.getConnectionNodeSize() : 0),
406 settings.getUnselectedNodeSize());
407
408 final boolean fill = (selected && settings.isFillSelectedNode()) ||
409 (n.isTagged() && settings.isFillTaggedNode()) ||
410 (isConnection && settings.isFillConnectionNode()) ||
411 settings.isFillUnselectedNode();
412
413 painter.drawNode(n, color, size, fill, text);
414 }
415 }
416 } else if (primitive instanceof Relation && icon != null) {
417 painter.drawRestriction((Relation) primitive, this);
418 }
419 }
420
421 public ImageIcon getDisabledIcon() {
422 if (disabledIcon != null)
423 return disabledIcon;
424 if (icon == null)
425 return null;
426 return disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(icon.getImage()));
427 }
428
429 @Override
430 public int hashCode() {
431 int hash = super.hashCode();
432 hash = 17 * hash + (icon != null ? icon.getImage().hashCode() : 0);
433 hash = 17 * hash + iconAlpha;
434 hash = 17 * hash + (symbol != null ? symbol.hashCode() : 0);
435 hash = 17 * hash + (text != null ? text.hashCode() : 0);
436 return hash;
437 }
438
439 @Override
440 public boolean equals(Object obj) {
441 if (obj == null || getClass() != obj.getClass())
442 return false;
443 if (!super.equals(obj))
444 return false;
445
446 final NodeElemStyle other = (NodeElemStyle) obj;
447 // we should get the same image object due to caching
448 if (icon != other.icon && (icon == null || other.icon == null || icon.getImage() != other.icon.getImage()))
449 return false;
450 if (this.iconAlpha != other.iconAlpha)
451 return false;
452 if (!equal(symbol, other.symbol))
453 return false;
454 if (!equal(text, other.text))
455 return false;
456 return true;
457 }
458
459
460 @Override
461 public String toString() {
462 StringBuilder s = new StringBuilder("NodeElemStyle{");
463 s.append(super.toString());
464 if (icon != null) {
465 s.append(" icon=" + icon + " iconAlpha=" + iconAlpha);
466 }
467 if (symbol != null) {
468 s.append(" symbol=[" + symbol + "]");
469 }
470 if (text != null) {
471 s.append(" text=[" + text.toStringImpl() + "]");
472 }
473 s.append('}');
474 return s.toString();
475 }
476
477}
Note: See TracBrowser for help on using the repository browser.