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

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

Checkstyle 6.19: enable SingleSpaceSeparator and fix violations

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