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

Last change on this file since 8133 was 8133, checked in by bastiK, 10 years ago

see #11227 - MapCSS: rendering slow because of frequent preferences lookup

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