source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/WireframeMapRenderer.java@ 7395

Last change on this file since 7395 was 7022, checked in by Don-vip, 10 years ago

see #8465 - enable and fix more warnings

  • Property svn:eol-style set to native
File size: 17.6 KB
Line 
1/* License: GPL. For details, see LICENSE file. */
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4import java.awt.BasicStroke;
5import java.awt.Color;
6import java.awt.Graphics2D;
7import java.awt.Point;
8import java.awt.Polygon;
9import java.awt.Rectangle;
10import java.awt.RenderingHints;
11import java.awt.Stroke;
12import java.awt.geom.GeneralPath;
13import java.util.ArrayList;
14import java.util.Iterator;
15import java.util.List;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.Bounds;
19import org.openstreetmap.josm.data.osm.BBox;
20import org.openstreetmap.josm.data.osm.Changeset;
21import org.openstreetmap.josm.data.osm.DataSet;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Relation;
25import org.openstreetmap.josm.data.osm.RelationMember;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.WaySegment;
28import org.openstreetmap.josm.data.osm.visitor.Visitor;
29import org.openstreetmap.josm.gui.NavigatableComponent;
30
31/**
32 * A map renderer that paints a simple scheme of every primitive it visits to a
33 * previous set graphic environment.
34 */
35public class WireframeMapRenderer extends AbstractMapRenderer implements Visitor {
36
37 /** Color Preference for ways not matching any other group */
38 protected Color dfltWayColor;
39 /** Color Preference for relations */
40 protected Color relationColor;
41 /** Color Preference for untagged ways */
42 protected Color untaggedWayColor;
43 /** Color Preference for tagged nodes */
44 protected Color taggedColor;
45 /** Color Preference for multiply connected nodes */
46 protected Color connectionColor;
47 /** Color Preference for tagged and multiply connected nodes */
48 protected Color taggedConnectionColor;
49 /** Preference: should directional arrows be displayed */
50 protected boolean showDirectionArrow;
51 /** Preference: should arrows for oneways be displayed */
52 protected boolean showOnewayArrow;
53 /** Preference: should only the last arrow of a way be displayed */
54 protected boolean showHeadArrowOnly;
55 /** Preference: should the segement numbers of ways be displayed */
56 protected boolean showOrderNumber;
57 /** Preference: should selected nodes be filled */
58 protected boolean fillSelectedNode;
59 /** Preference: should unselected nodes be filled */
60 protected boolean fillUnselectedNode;
61 /** Preference: should tagged nodes be filled */
62 protected boolean fillTaggedNode;
63 /** Preference: should multiply connected nodes be filled */
64 protected boolean fillConnectionNode;
65 /** Preference: size of selected nodes */
66 protected int selectedNodeSize;
67 /** Preference: size of unselected nodes */
68 protected int unselectedNodeSize;
69 /** Preference: size of multiply connected nodes */
70 protected int connectionNodeSize;
71 /** Preference: size of tagged nodes */
72 protected int taggedNodeSize;
73
74 /** Color cache to draw subsequent segments of same color as one <code>Path</code>. */
75 protected Color currentColor = null;
76 /** Path store to draw subsequent segments of same color as one <code>Path</code>. */
77 protected GeneralPath currentPath = new GeneralPath();
78 /**
79 * <code>DataSet</code> passed to the @{link render} function to overcome the argument
80 * limitations of @{link Visitor} interface. Only valid until end of rendering call.
81 */
82 private DataSet ds;
83
84 /** Helper variable for {@link #drawSegment} */
85 private static final double PHI = Math.toRadians(20);
86 /** Helper variable for {@link #drawSegment} */
87 private static final double cosPHI = Math.cos(PHI);
88 /** Helper variable for {@link #drawSegment} */
89 private static final double sinPHI = Math.sin(PHI);
90
91 /** Helper variable for {@link #visit(Relation)} */
92 private Stroke relatedWayStroke = new BasicStroke(
93 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
94
95 /**
96 * Creates an wireframe render
97 *
98 * @param g the graphics context. Must not be null.
99 * @param nc the map viewport. Must not be null.
100 * @param isInactiveMode if true, the paint visitor shall render OSM objects such that they
101 * look inactive. Example: rendering of data in an inactive layer using light gray as color only.
102 * @throws IllegalArgumentException thrown if {@code g} is null
103 * @throws IllegalArgumentException thrown if {@code nc} is null
104 */
105 public WireframeMapRenderer(Graphics2D g, NavigatableComponent nc, boolean isInactiveMode) {
106 super(g, nc, isInactiveMode);
107 }
108
109 @Override
110 public void getColors() {
111 super.getColors();
112 dfltWayColor = PaintColors.DEFAULT_WAY.get();
113 relationColor = PaintColors.RELATION.get();
114 untaggedWayColor = PaintColors.UNTAGGED_WAY.get();
115 highlightColor = PaintColors.HIGHLIGHT_WIREFRAME.get();
116 taggedColor = PaintColors.TAGGED.get();
117 connectionColor = PaintColors.CONNECTION.get();
118
119 if (taggedColor != nodeColor) {
120 taggedConnectionColor = taggedColor;
121 } else {
122 taggedConnectionColor = connectionColor;
123 }
124 }
125
126 @Override
127 protected void getSettings(boolean virtual) {
128 super.getSettings(virtual);
129 MapPaintSettings settings = MapPaintSettings.INSTANCE;
130 showDirectionArrow = settings.isShowDirectionArrow();
131 showOnewayArrow = settings.isShowOnewayArrow();
132 showHeadArrowOnly = settings.isShowHeadArrowOnly();
133 showOrderNumber = settings.isShowOrderNumber();
134 selectedNodeSize = settings.getSelectedNodeSize();
135 unselectedNodeSize = settings.getUnselectedNodeSize();
136 connectionNodeSize = settings.getConnectionNodeSize();
137 taggedNodeSize = settings.getTaggedNodeSize();
138 fillSelectedNode = settings.isFillSelectedNode();
139 fillUnselectedNode = settings.isFillUnselectedNode();
140 fillConnectionNode = settings.isFillConnectionNode();
141 fillTaggedNode = settings.isFillTaggedNode();
142
143 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
144 Main.pref.getBoolean("mappaint.wireframe.use-antialiasing", false) ?
145 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
146 }
147
148 /**
149 * Renders the dataset for display.
150 *
151 * @param data <code>DataSet</code> to display
152 * @param virtual <code>true</code> if virtual nodes are used
153 * @param bounds display boundaries
154 */
155 @SuppressWarnings("unchecked")
156 @Override
157 public void render(DataSet data, boolean virtual, Bounds bounds) {
158 BBox bbox = bounds.toBBox();
159 this.ds = data;
160 getSettings(virtual);
161
162 for (final Relation rel : data.searchRelations(bbox)) {
163 if (rel.isDrawable() && !ds.isSelected(rel) && !rel.isDisabledAndHidden()) {
164 rel.accept(this);
165 }
166 }
167
168 // draw tagged ways first, then untagged ways, then highlighted ways
169 List<Way> highlightedWays = new ArrayList<>();
170 List<Way> untaggedWays = new ArrayList<>();
171
172 for (final Way way : data.searchWays(bbox)){
173 if (way.isDrawable() && !ds.isSelected(way) && !way.isDisabledAndHidden()) {
174 if (way.isHighlighted()) {
175 highlightedWays.add(way);
176 } else if (!way.isTagged()) {
177 untaggedWays.add(way);
178 } else {
179 way.accept(this);
180 }
181 }
182 }
183 displaySegments();
184
185 // Display highlighted ways after the other ones (fix #8276)
186 List<Way> specialWays = new ArrayList<>(untaggedWays);
187 specialWays.addAll(highlightedWays);
188 for (final Way way : specialWays){
189 way.accept(this);
190 }
191 specialWays.clear();
192 displaySegments();
193
194 for (final OsmPrimitive osm : data.getSelected()) {
195 if (osm.isDrawable()) {
196 osm.accept(this);
197 }
198 }
199 displaySegments();
200
201 for (final OsmPrimitive osm: data.searchNodes(bbox)) {
202 if (osm.isDrawable() && !ds.isSelected(osm) && !osm.isDisabledAndHidden())
203 {
204 osm.accept(this);
205 }
206 }
207 drawVirtualNodes(data, bbox);
208
209 // draw highlighted way segments over the already drawn ways. Otherwise each
210 // way would have to be checked if it contains a way segment to highlight when
211 // in most of the cases there won't be more than one segment. Since the wireframe
212 // renderer does not feature any transparency there should be no visual difference.
213 for (final WaySegment wseg : data.getHighlightedWaySegments()) {
214 drawSegment(nc.getPoint(wseg.getFirstNode()), nc.getPoint(wseg.getSecondNode()), highlightColor, false);
215 }
216 displaySegments();
217 }
218
219 /**
220 * Helper function to calculate maximum of 4 values.
221 *
222 * @param a First value
223 * @param b Second value
224 * @param c Third value
225 * @param d Fourth value
226 */
227 private static final int max(int a, int b, int c, int d) {
228 return Math.max(Math.max(a, b), Math.max(c, d));
229 }
230
231 /**
232 * Draw a small rectangle.
233 * White if selected (as always) or red otherwise.
234 *
235 * @param n The node to draw.
236 */
237 @Override
238 public void visit(Node n) {
239 if (n.isIncomplete()) return;
240
241 if (n.isHighlighted()) {
242 drawNode(n, highlightColor, selectedNodeSize, fillSelectedNode);
243 } else {
244 Color color;
245
246 if (isInactiveMode || n.isDisabled()) {
247 color = inactiveColor;
248 } else if (ds.isSelected(n)) {
249 color = selectedColor;
250 } else if (n.isConnectionNode()) {
251 if (isNodeTagged(n)) {
252 color = taggedConnectionColor;
253 } else {
254 color = connectionColor;
255 }
256 } else {
257 if (isNodeTagged(n)) {
258 color = taggedColor;
259 } else {
260 color = nodeColor;
261 }
262 }
263
264 final int size = max((ds.isSelected(n) ? selectedNodeSize : 0),
265 (isNodeTagged(n) ? taggedNodeSize : 0),
266 (n.isConnectionNode() ? connectionNodeSize : 0),
267 unselectedNodeSize);
268
269 final boolean fill = (ds.isSelected(n) && fillSelectedNode) ||
270 (isNodeTagged(n) && fillTaggedNode) ||
271 (n.isConnectionNode() && fillConnectionNode) ||
272 fillUnselectedNode;
273
274 drawNode(n, color, size, fill);
275 }
276 }
277
278 private boolean isNodeTagged(Node n) {
279 return n.isTagged() || n.isAnnotated();
280 }
281
282 /**
283 * Draw a line for all way segments.
284 * @param w The way to draw.
285 */
286 @Override
287 public void visit(Way w) {
288 if (w.isIncomplete() || w.getNodesCount() < 2)
289 return;
290
291 /* show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
292 (even if the tag is negated as in oneway=false) or the way is selected */
293
294 boolean showThisDirectionArrow = ds.isSelected(w) || showDirectionArrow;
295 /* head only takes over control if the option is true,
296 the direction should be shown at all and not only because it's selected */
297 boolean showOnlyHeadArrowOnly = showThisDirectionArrow && !ds.isSelected(w) && showHeadArrowOnly;
298 Color wayColor;
299
300 if (isInactiveMode || w.isDisabled()) {
301 wayColor = inactiveColor;
302 } else if(w.isHighlighted()) {
303 wayColor = highlightColor;
304 } else if(ds.isSelected(w)) {
305 wayColor = selectedColor;
306 } else if (!w.isTagged()) {
307 wayColor = untaggedWayColor;
308 } else {
309 wayColor = dfltWayColor;
310 }
311
312 Iterator<Node> it = w.getNodes().iterator();
313 if (it.hasNext()) {
314 Point lastP = nc.getPoint(it.next());
315 for (int orderNumber = 1; it.hasNext(); orderNumber++) {
316 Point p = nc.getPoint(it.next());
317 drawSegment(lastP, p, wayColor,
318 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
319 if (showOrderNumber && !isInactiveMode) {
320 drawOrderNumber(lastP, p, orderNumber, g.getColor());
321 }
322 lastP = p;
323 }
324 }
325 }
326
327 /**
328 * Draw objects used in relations.
329 * @param r The relation to draw.
330 */
331 @Override
332 public void visit(Relation r) {
333 if (r.isIncomplete()) return;
334
335 Color col;
336 if (isInactiveMode || r.isDisabled()) {
337 col = inactiveColor;
338 } else if (ds.isSelected(r)) {
339 col = selectedColor;
340 } else {
341 col = relationColor;
342 }
343 g.setColor(col);
344
345 for (RelationMember m : r.getMembers()) {
346 if (m.getMember().isIncomplete() || !m.getMember().isDrawable()) {
347 continue;
348 }
349
350 if (m.isNode()) {
351 Point p = nc.getPoint(m.getNode());
352 if (p.x < 0 || p.y < 0
353 || p.x > nc.getWidth() || p.y > nc.getHeight()) {
354 continue;
355 }
356
357 g.drawOval(p.x-3, p.y-3, 6, 6);
358 } else if (m.isWay()) {
359 GeneralPath path = new GeneralPath();
360
361 boolean first = true;
362 for (Node n : m.getWay().getNodes()) {
363 if (!n.isDrawable()) {
364 continue;
365 }
366 Point p = nc.getPoint(n);
367 if (first) {
368 path.moveTo(p.x, p.y);
369 first = false;
370 } else {
371 path.lineTo(p.x, p.y);
372 }
373 }
374
375 g.draw(relatedWayStroke.createStrokedShape(path));
376 }
377 }
378 }
379
380 /**
381 * Visitor for changesets not used in this class
382 * @param cs The changeset for inspection.
383 */
384 @Override
385 public void visit(Changeset cs) {/* ignore */}
386
387 @Override
388 public void drawNode(Node n, Color color, int size, boolean fill) {
389 if (size > 1) {
390 int radius = size / 2;
391 Point p = nc.getPoint(n);
392 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
393 || (p.y > nc.getHeight()))
394 return;
395 g.setColor(color);
396 if (fill) {
397 g.fillRect(p.x - radius, p.y - radius, size, size);
398 g.drawRect(p.x - radius, p.y - radius, size, size);
399 } else {
400 g.drawRect(p.x - radius, p.y - radius, size, size);
401 }
402 }
403 }
404
405 /**
406 * Draw a line with the given color.
407 *
408 * @param path The path to append this segment.
409 * @param p1 First point of the way segment.
410 * @param p2 Second point of the way segment.
411 * @param showDirection <code>true</code> if segment direction should be indicated
412 */
413 protected void drawSegment(GeneralPath path, Point p1, Point p2, boolean showDirection) {
414 Rectangle bounds = g.getClipBounds();
415 bounds.grow(100, 100); // avoid arrow heads at the border
416 LineClip clip = new LineClip(p1, p2, bounds);
417 if (clip.execute()) {
418 p1 = clip.getP1();
419 p2 = clip.getP2();
420 path.moveTo(p1.x, p1.y);
421 path.lineTo(p2.x, p2.y);
422
423 if (showDirection) {
424 final double l = 10. / p1.distance(p2);
425
426 final double sx = l * (p1.x - p2.x);
427 final double sy = l * (p1.y - p2.y);
428
429 path.lineTo (p2.x + (int) Math.round(cosPHI * sx - sinPHI * sy), p2.y + (int) Math.round(sinPHI * sx + cosPHI * sy));
430 path.moveTo (p2.x + (int) Math.round(cosPHI * sx + sinPHI * sy), p2.y + (int) Math.round(- sinPHI * sx + cosPHI * sy));
431 path.lineTo(p2.x, p2.y);
432 }
433 }
434 }
435
436 /**
437 * Draw a line with the given color.
438 *
439 * @param p1 First point of the way segment.
440 * @param p2 Second point of the way segment.
441 * @param col The color to use for drawing line.
442 * @param showDirection <code>true</code> if segment direction should be indicated.
443 */
444 protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) {
445 if (col != currentColor) {
446 displaySegments(col);
447 }
448 drawSegment(currentPath, p1, p2, showDirection);
449 }
450
451 /**
452 * Checks if a polygon is visible in display.
453 *
454 * @param polygon The polygon to check.
455 * @return <code>true</code> if polygon is visible.
456 */
457 protected boolean isPolygonVisible(Polygon polygon) {
458 Rectangle bounds = polygon.getBounds();
459 if (bounds.width == 0 && bounds.height == 0) return false;
460 if (bounds.x > nc.getWidth()) return false;
461 if (bounds.y > nc.getHeight()) return false;
462 if (bounds.x + bounds.width < 0) return false;
463 if (bounds.y + bounds.height < 0) return false;
464 return true;
465 }
466
467 /**
468 * Finally display all segments in currect path.
469 */
470 protected void displaySegments() {
471 displaySegments(null);
472 }
473
474 /**
475 * Finally display all segments in currect path.
476 *
477 * @param newColor This color is set after the path is drawn.
478 */
479 protected void displaySegments(Color newColor) {
480 if (currentPath != null) {
481 g.setColor(currentColor);
482 g.draw(currentPath);
483 currentPath = new GeneralPath();
484 currentColor = newColor;
485 }
486 }
487}
Note: See TracBrowser for help on using the repository browser.