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

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

see #13306 - fix wrong version number

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