source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java@ 11809

Last change on this file since 11809 was 11809, checked in by Don-vip, 7 years ago

findbugs - BC_UNCONFIRMED_CAST

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.styleelement;
3
4import java.awt.BasicStroke;
5import java.awt.Color;
6import java.util.Arrays;
7import java.util.Objects;
8import java.util.Optional;
9
10import org.openstreetmap.josm.Main;
11import org.openstreetmap.josm.data.osm.Node;
12import org.openstreetmap.josm.data.osm.OsmPrimitive;
13import org.openstreetmap.josm.data.osm.Way;
14import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
15import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
16import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
17import org.openstreetmap.josm.gui.mappaint.Cascade;
18import org.openstreetmap.josm.gui.mappaint.Environment;
19import org.openstreetmap.josm.gui.mappaint.Keyword;
20import org.openstreetmap.josm.gui.mappaint.MultiCascade;
21import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
22import org.openstreetmap.josm.tools.Utils;
23
24/**
25 * This is the style definition for a simple line.
26 */
27public class LineElement extends StyleElement {
28 /**
29 * The default style for any untagged way.
30 */
31 public static final LineElement UNTAGGED_WAY = createSimpleLineStyle(null, false);
32
33 private BasicStroke line;
34 public Color color;
35 public Color dashesBackground;
36 public float offset;
37 public float realWidth; // the real width of this line in meter
38 public boolean wayDirectionArrows;
39
40 private BasicStroke dashesLine;
41
42 public enum LineType {
43 NORMAL("", 3f),
44 CASING("casing-", 2f),
45 LEFT_CASING("left-casing-", 2.1f),
46 RIGHT_CASING("right-casing-", 2.1f);
47
48 public final String prefix;
49 public final float defaultMajorZIndex;
50
51 LineType(String prefix, float defaultMajorZindex) {
52 this.prefix = prefix;
53 this.defaultMajorZIndex = defaultMajorZindex;
54 }
55 }
56
57 protected LineElement(Cascade c, float defaultMajorZindex, BasicStroke line, Color color, BasicStroke dashesLine,
58 Color dashesBackground, float offset, float realWidth, boolean wayDirectionArrows) {
59 super(c, defaultMajorZindex);
60 this.line = line;
61 this.color = color;
62 this.dashesLine = dashesLine;
63 this.dashesBackground = dashesBackground;
64 this.offset = offset;
65 this.realWidth = realWidth;
66 this.wayDirectionArrows = wayDirectionArrows;
67 }
68
69 @Override
70 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
71 boolean selected, boolean outermember, boolean member) {
72 /* show direction arrows, if draw.segment.relevant_directions_only is not set,
73 the way is tagged with a direction key
74 (even if the tag is negated as in oneway=false) or the way is selected */
75 boolean showOrientation;
76 if (defaultSelectedHandling) {
77 showOrientation = !isModifier && (selected || paintSettings.isShowDirectionArrow()) && !paintSettings.isUseRealWidth();
78 } else {
79 showOrientation = wayDirectionArrows;
80 }
81 boolean showOneway = !isModifier && !selected &&
82 !paintSettings.isUseRealWidth() &&
83 paintSettings.isShowOnewayArrow() && primitive.hasDirectionKeys();
84 boolean onewayReversed = primitive.reversedDirection();
85 /* head only takes over control if the option is true,
86 the direction should be shown at all and not only because it's selected */
87 boolean showOnlyHeadArrowOnly = showOrientation && !selected && paintSettings.isShowHeadArrowOnly();
88 Node lastN;
89
90 Color myDashedColor = dashesBackground;
91 BasicStroke myLine = line, myDashLine = dashesLine;
92 if (realWidth > 0 && paintSettings.isUseRealWidth() && !showOrientation) {
93 float myWidth = (int) (100 / (float) (painter.getCircum() / realWidth));
94 if (myWidth < line.getLineWidth()) {
95 myWidth = line.getLineWidth();
96 }
97 myLine = new BasicStroke(myWidth, line.getEndCap(), line.getLineJoin(),
98 line.getMiterLimit(), line.getDashArray(), line.getDashPhase());
99 if (dashesLine != null) {
100 myDashLine = new BasicStroke(myWidth, dashesLine.getEndCap(), dashesLine.getLineJoin(),
101 dashesLine.getMiterLimit(), dashesLine.getDashArray(), dashesLine.getDashPhase());
102 }
103 }
104
105 Color myColor = color;
106 if (defaultSelectedHandling && selected) {
107 myColor = paintSettings.getSelectedColor(color.getAlpha());
108 } else if (member || outermember) {
109 myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
110 } else if (primitive.isDisabled()) {
111 myColor = paintSettings.getInactiveColor();
112 myDashedColor = paintSettings.getInactiveColor();
113 }
114
115 if (primitive instanceof Way) {
116 Way w = (Way) primitive;
117 painter.drawWay(w, myColor, myLine, myDashLine, myDashedColor, offset, showOrientation,
118 showOnlyHeadArrowOnly, showOneway, onewayReversed);
119
120 if ((paintSettings.isShowOrderNumber() || (paintSettings.isShowOrderNumberOnSelectedWay() && selected))
121 && !painter.isInactiveMode()) {
122 int orderNumber = 0;
123 lastN = null;
124 for (Node n : w.getNodes()) {
125 if (lastN != null) {
126 orderNumber++;
127 painter.drawOrderNumber(lastN, n, orderNumber, myColor);
128 }
129 lastN = n;
130 }
131 }
132 }
133 }
134
135 @Override
136 public boolean isProperLineStyle() {
137 return !isModifier;
138 }
139
140 public String linejoinToString(int linejoin) {
141 switch (linejoin) {
142 case BasicStroke.JOIN_BEVEL: return "bevel";
143 case BasicStroke.JOIN_ROUND: return "round";
144 case BasicStroke.JOIN_MITER: return "miter";
145 default: return null;
146 }
147 }
148
149 public String linecapToString(int linecap) {
150 switch (linecap) {
151 case BasicStroke.CAP_BUTT: return "none";
152 case BasicStroke.CAP_ROUND: return "round";
153 case BasicStroke.CAP_SQUARE: return "square";
154 default: return null;
155 }
156 }
157
158 @Override
159 public boolean equals(Object obj) {
160 if (obj == null || getClass() != obj.getClass())
161 return false;
162 if (!super.equals(obj))
163 return false;
164 final LineElement other = (LineElement) obj;
165 return offset == other.offset &&
166 realWidth == other.realWidth &&
167 wayDirectionArrows == other.wayDirectionArrows &&
168 Objects.equals(line, other.line) &&
169 Objects.equals(color, other.color) &&
170 Objects.equals(dashesLine, other.dashesLine) &&
171 Objects.equals(dashesBackground, other.dashesBackground);
172 }
173
174 @Override
175 public int hashCode() {
176 return Objects.hash(super.hashCode(), line, color, dashesBackground, offset, realWidth, wayDirectionArrows, dashesLine);
177 }
178
179 @Override
180 public String toString() {
181 return "LineElemStyle{" + super.toString() + "width=" + line.getLineWidth() +
182 " realWidth=" + realWidth + " color=" + Utils.toString(color) +
183 " dashed=" + Arrays.toString(line.getDashArray()) +
184 (line.getDashPhase() == 0 ? "" : " dashesOffses=" + line.getDashPhase()) +
185 " dashedColor=" + Utils.toString(dashesBackground) +
186 " linejoin=" + linejoinToString(line.getLineJoin()) +
187 " linecap=" + linecapToString(line.getEndCap()) +
188 (offset == 0 ? "" : " offset=" + offset) +
189 '}';
190 }
191
192 /**
193 * Creates a simple line with default widt.
194 * @param color The color to use
195 * @param isAreaEdge If this is an edge for an area. Edges are drawn at lower Z-Index.
196 * @return The line style.
197 */
198 public static LineElement createSimpleLineStyle(Color color, boolean isAreaEdge) {
199 MultiCascade mc = new MultiCascade();
200 Cascade c = mc.getOrCreateCascade("default");
201 c.put(WIDTH, Keyword.DEFAULT);
202 c.put(COLOR, color != null ? color : PaintColors.UNTAGGED.get());
203 c.put(OPACITY, 1f);
204 if (isAreaEdge) {
205 c.put(Z_INDEX, -3f);
206 }
207 Way w = new Way();
208 return createLine(new Environment(w, mc, "default", null));
209 }
210
211 public static LineElement createLine(Environment env) {
212 return createImpl(env, LineType.NORMAL);
213 }
214
215 public static LineElement createLeftCasing(Environment env) {
216 LineElement leftCasing = createImpl(env, LineType.LEFT_CASING);
217 if (leftCasing != null) {
218 leftCasing.isModifier = true;
219 }
220 return leftCasing;
221 }
222
223 public static LineElement createRightCasing(Environment env) {
224 LineElement rightCasing = createImpl(env, LineType.RIGHT_CASING);
225 if (rightCasing != null) {
226 rightCasing.isModifier = true;
227 }
228 return rightCasing;
229 }
230
231 public static LineElement createCasing(Environment env) {
232 LineElement casing = createImpl(env, LineType.CASING);
233 if (casing != null) {
234 casing.isModifier = true;
235 }
236 return casing;
237 }
238
239 private static LineElement createImpl(Environment env, LineType type) {
240 Cascade c = env.mc.getCascade(env.layer);
241 Cascade cDef = env.mc.getCascade("default");
242 Float width = computeWidth(type, c, cDef);
243 if (width == null)
244 return null;
245
246 float realWidth = computeRealWidth(env, type, c);
247
248 Float offset = computeOffset(type, c, cDef, width);
249
250 int alpha = 255;
251 Color color = c.get(type.prefix + COLOR, null, Color.class);
252 if (color != null) {
253 alpha = color.getAlpha();
254 }
255 if (type == LineType.NORMAL && color == null) {
256 color = c.get(FILL_COLOR, null, Color.class);
257 }
258 if (color == null) {
259 color = PaintColors.UNTAGGED.get();
260 }
261
262 Integer pAlpha = Utils.colorFloat2int(c.get(type.prefix + OPACITY, null, Float.class));
263 if (pAlpha != null) {
264 alpha = pAlpha;
265 }
266 color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
267
268 float[] dashes = c.get(type.prefix + DASHES, null, float[].class, true);
269 if (dashes != null) {
270 boolean hasPositive = false;
271 for (float f : dashes) {
272 if (f > 0) {
273 hasPositive = true;
274 }
275 if (f < 0) {
276 dashes = null;
277 break;
278 }
279 }
280 if (!hasPositive || (dashes != null && dashes.length == 0)) {
281 dashes = null;
282 }
283 }
284 float dashesOffset = c.get(type.prefix + DASHES_OFFSET, 0f, Float.class);
285 Color dashesBackground = c.get(type.prefix + DASHES_BACKGROUND_COLOR, null, Color.class);
286 if (dashesBackground != null) {
287 pAlpha = Utils.colorFloat2int(c.get(type.prefix + DASHES_BACKGROUND_OPACITY, null, Float.class));
288 if (pAlpha != null) {
289 alpha = pAlpha;
290 }
291 dashesBackground = new Color(dashesBackground.getRed(), dashesBackground.getGreen(),
292 dashesBackground.getBlue(), alpha);
293 }
294
295 Integer cap = null;
296 Keyword capKW = c.get(type.prefix + LINECAP, null, Keyword.class);
297 if (capKW != null) {
298 if ("none".equals(capKW.val)) {
299 cap = BasicStroke.CAP_BUTT;
300 } else if ("round".equals(capKW.val)) {
301 cap = BasicStroke.CAP_ROUND;
302 } else if ("square".equals(capKW.val)) {
303 cap = BasicStroke.CAP_SQUARE;
304 }
305 }
306 if (cap == null) {
307 cap = dashes != null ? BasicStroke.CAP_BUTT : BasicStroke.CAP_ROUND;
308 }
309
310 Integer join = null;
311 Keyword joinKW = c.get(type.prefix + LINEJOIN, null, Keyword.class);
312 if (joinKW != null) {
313 if ("round".equals(joinKW.val)) {
314 join = BasicStroke.JOIN_ROUND;
315 } else if ("miter".equals(joinKW.val)) {
316 join = BasicStroke.JOIN_MITER;
317 } else if ("bevel".equals(joinKW.val)) {
318 join = BasicStroke.JOIN_BEVEL;
319 }
320 }
321 if (join == null) {
322 join = BasicStroke.JOIN_ROUND;
323 }
324
325 float miterlimit = c.get(type.prefix + MITERLIMIT, 10f, Float.class);
326 if (miterlimit < 1f) {
327 miterlimit = 10f;
328 }
329
330 BasicStroke line = new BasicStroke(width, cap, join, miterlimit, dashes, dashesOffset);
331 BasicStroke dashesLine = null;
332
333 if (dashes != null && dashesBackground != null) {
334 float[] dashes2 = new float[dashes.length];
335 System.arraycopy(dashes, 0, dashes2, 1, dashes.length - 1);
336 dashes2[0] = dashes[dashes.length-1];
337 dashesLine = new BasicStroke(width, cap, join, miterlimit, dashes2, dashes2[0] + dashesOffset);
338 }
339
340 boolean wayDirectionArrows = c.get(type.prefix + WAY_DIRECTION_ARROWS, env.osm.isSelected(), Boolean.class);
341
342 return new LineElement(c, type.defaultMajorZIndex, line, color, dashesLine, dashesBackground,
343 offset, realWidth, wayDirectionArrows);
344 }
345
346 private static Float computeWidth(LineType type, Cascade c, Cascade cDef) {
347 Float width;
348 switch (type) {
349 case NORMAL:
350 width = getWidth(c, WIDTH, getWidth(cDef, WIDTH, null));
351 break;
352 case CASING:
353 Float casingWidth = c.get(type.prefix + WIDTH, null, Float.class, true);
354 if (casingWidth == null) {
355 RelativeFloat relCasingWidth = c.get(type.prefix + WIDTH, null, RelativeFloat.class, true);
356 if (relCasingWidth != null) {
357 casingWidth = relCasingWidth.val / 2;
358 }
359 }
360 if (casingWidth == null)
361 return null;
362 width = Optional.ofNullable(getWidth(c, WIDTH, getWidth(cDef, WIDTH, null))).orElse(0f) + 2 * casingWidth;
363 break;
364 case LEFT_CASING:
365 case RIGHT_CASING:
366 width = getWidth(c, type.prefix + WIDTH, null);
367 break;
368 default:
369 throw new AssertionError();
370 }
371 return width;
372 }
373
374 private static float computeRealWidth(Environment env, LineType type, Cascade c) {
375 float realWidth = c.get(type.prefix + REAL_WIDTH, 0f, Float.class);
376 if (realWidth > 0 && MapPaintSettings.INSTANCE.isUseRealWidth()) {
377
378 /* if we have a "width" tag, try use it */
379 String widthTag = Optional.ofNullable(env.osm.get("width")).orElseGet(() -> env.osm.get("est_width"));
380 if (widthTag != null) {
381 try {
382 realWidth = Float.parseFloat(widthTag);
383 } catch (NumberFormatException nfe) {
384 Main.warn(nfe);
385 }
386 }
387 }
388 return realWidth;
389 }
390
391 private static Float computeOffset(LineType type, Cascade c, Cascade cDef, Float width) {
392 Float offset = c.get(OFFSET, 0f, Float.class);
393 switch (type) {
394 case NORMAL:
395 break;
396 case CASING:
397 offset += c.get(type.prefix + OFFSET, 0f, Float.class);
398 break;
399 case LEFT_CASING:
400 case RIGHT_CASING:
401 Float baseWidthOnDefault = getWidth(cDef, WIDTH, null);
402 Float baseWidth = getWidth(c, WIDTH, baseWidthOnDefault);
403 if (baseWidth == null || baseWidth < 2f) {
404 baseWidth = 2f;
405 }
406 float casingOffset = c.get(type.prefix + OFFSET, 0f, Float.class);
407 casingOffset += baseWidth / 2 + width / 2;
408 /* flip sign for the right-casing-offset */
409 if (type == LineType.RIGHT_CASING) {
410 casingOffset *= -1f;
411 }
412 offset += casingOffset;
413 break;
414 }
415 return offset;
416 }
417}
Note: See TracBrowser for help on using the repository browser.