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

Last change on this file since 12966 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 17.0 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.data.Bounds;
19import org.openstreetmap.josm.data.osm.BBox;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Relation;
24import org.openstreetmap.josm.data.osm.RelationMember;
25import org.openstreetmap.josm.data.osm.Way;
26import org.openstreetmap.josm.data.osm.WaySegment;
27import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
28import org.openstreetmap.josm.gui.MapViewState.MapViewPoint;
29import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
30import org.openstreetmap.josm.gui.NavigatableComponent;
31import org.openstreetmap.josm.gui.draw.MapPath2D;
32import org.openstreetmap.josm.spi.preferences.Config;
33import org.openstreetmap.josm.tools.Utils;
34
35/**
36 * A map renderer that paints a simple scheme of every primitive it visits to a
37 * previous set graphic environment.
38 * @since 23
39 */
40public class WireframeMapRenderer extends AbstractMapRenderer implements OsmPrimitiveVisitor {
41
42 /** Color Preference for ways not matching any other group */
43 protected Color dfltWayColor;
44 /** Color Preference for relations */
45 protected Color relationColor;
46 /** Color Preference for untagged ways */
47 protected Color untaggedWayColor;
48 /** Color Preference for tagged nodes */
49 protected Color taggedColor;
50 /** Color Preference for multiply connected nodes */
51 protected Color connectionColor;
52 /** Color Preference for tagged and multiply connected nodes */
53 protected Color taggedConnectionColor;
54 /** Preference: should directional arrows be displayed */
55 protected boolean showDirectionArrow;
56 /** Preference: should arrows for oneways be displayed */
57 protected boolean showOnewayArrow;
58 /** Preference: should only the last arrow of a way be displayed */
59 protected boolean showHeadArrowOnly;
60 /** Preference: should the segment numbers of ways be displayed */
61 protected boolean showOrderNumber;
62 /** Preference: should the segment numbers of the selected be displayed */
63 protected boolean showOrderNumberOnSelectedWay;
64 /** Preference: should selected nodes be filled */
65 protected boolean fillSelectedNode;
66 /** Preference: should unselected nodes be filled */
67 protected boolean fillUnselectedNode;
68 /** Preference: should tagged nodes be filled */
69 protected boolean fillTaggedNode;
70 /** Preference: should multiply connected nodes be filled */
71 protected boolean fillConnectionNode;
72 /** Preference: size of selected nodes */
73 protected int selectedNodeSize;
74 /** Preference: size of unselected nodes */
75 protected int unselectedNodeSize;
76 /** Preference: size of multiply connected nodes */
77 protected int connectionNodeSize;
78 /** Preference: size of tagged nodes */
79 protected int taggedNodeSize;
80
81 /** Color cache to draw subsequent segments of same color as one <code>Path</code>. */
82 protected Color currentColor;
83 /** Path store to draw subsequent segments of same color as one <code>Path</code>. */
84 protected MapPath2D currentPath = new MapPath2D();
85 /**
86 * <code>DataSet</code> passed to the @{link render} function to overcome the argument
87 * limitations of @{link Visitor} interface. Only valid until end of rendering call.
88 */
89 private DataSet ds;
90
91 /** Helper variable for {@link #drawSegment} */
92 private static final ArrowPaintHelper ARROW_PAINT_HELPER = new ArrowPaintHelper(Utils.toRadians(20), 10);
93
94 /** Helper variable for {@link #visit(Relation)} */
95 private final Stroke relatedWayStroke = new BasicStroke(
96 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
97 private MapViewRectangle viewClip;
98
99 /**
100 * Creates an wireframe render
101 *
102 * @param g the graphics context. Must not be null.
103 * @param nc the map viewport. Must not be null.
104 * @param isInactiveMode if true, the paint visitor shall render OSM objects such that they
105 * look inactive. Example: rendering of data in an inactive layer using light gray as color only.
106 * @throws IllegalArgumentException if {@code g} is null
107 * @throws IllegalArgumentException if {@code nc} is null
108 */
109 public WireframeMapRenderer(Graphics2D g, NavigatableComponent nc, boolean isInactiveMode) {
110 super(g, nc, isInactiveMode);
111 }
112
113 @Override
114 public void getColors() {
115 super.getColors();
116 dfltWayColor = PaintColors.DEFAULT_WAY.get();
117 relationColor = PaintColors.RELATION.get();
118 untaggedWayColor = PaintColors.UNTAGGED_WAY.get();
119 highlightColor = PaintColors.HIGHLIGHT_WIREFRAME.get();
120 taggedColor = PaintColors.TAGGED.get();
121 connectionColor = PaintColors.CONNECTION.get();
122
123 if (!taggedColor.equals(nodeColor)) {
124 taggedConnectionColor = taggedColor;
125 } else {
126 taggedConnectionColor = connectionColor;
127 }
128 }
129
130 @Override
131 protected void getSettings(boolean virtual) {
132 super.getSettings(virtual);
133 MapPaintSettings settings = MapPaintSettings.INSTANCE;
134 showDirectionArrow = settings.isShowDirectionArrow();
135 showOnewayArrow = settings.isShowOnewayArrow();
136 showHeadArrowOnly = settings.isShowHeadArrowOnly();
137 showOrderNumber = settings.isShowOrderNumber();
138 showOrderNumberOnSelectedWay = settings.isShowOrderNumberOnSelectedWay();
139 selectedNodeSize = settings.getSelectedNodeSize();
140 unselectedNodeSize = settings.getUnselectedNodeSize();
141 connectionNodeSize = settings.getConnectionNodeSize();
142 taggedNodeSize = settings.getTaggedNodeSize();
143 fillSelectedNode = settings.isFillSelectedNode();
144 fillUnselectedNode = settings.isFillUnselectedNode();
145 fillConnectionNode = settings.isFillConnectionNode();
146 fillTaggedNode = settings.isFillTaggedNode();
147
148 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
149 Config.getPref().getBoolean("mappaint.wireframe.use-antialiasing", false) ?
150 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
151 }
152
153 @Override
154 public void render(DataSet data, boolean virtual, Bounds bounds) {
155 BBox bbox = bounds.toBBox();
156 this.ds = data;
157 Rectangle clip = g.getClipBounds();
158 clip.grow(50, 50);
159 viewClip = mapState.getViewArea(clip);
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 osm.accept(this);
204 }
205 }
206 drawVirtualNodes(data, bbox);
207
208 // draw highlighted way segments over the already drawn ways. Otherwise each
209 // way would have to be checked if it contains a way segment to highlight when
210 // in most of the cases there won't be more than one segment. Since the wireframe
211 // renderer does not feature any transparency there should be no visual difference.
212 for (final WaySegment wseg : data.getHighlightedWaySegments()) {
213 drawSegment(mapState.getPointFor(wseg.getFirstNode()), mapState.getPointFor(wseg.getSecondNode()), highlightColor, false);
214 }
215 displaySegments();
216 }
217
218 /**
219 * Helper function to calculate maximum of 4 values.
220 *
221 * @param a First value
222 * @param b Second value
223 * @param c Third value
224 * @param d Fourth value
225 * @return maximumof {@code a}, {@code b}, {@code c}, {@code d}
226 */
227 private static 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 (n.isSelected()) {
249 color = selectedColor;
250 } else if (n.isMemberOfSelected()) {
251 color = relationSelectedColor;
252 } else if (n.isConnectionNode()) {
253 if (isNodeTagged(n)) {
254 color = taggedConnectionColor;
255 } else {
256 color = connectionColor;
257 }
258 } else {
259 if (isNodeTagged(n)) {
260 color = taggedColor;
261 } else {
262 color = nodeColor;
263 }
264 }
265
266 final int size = max(ds.isSelected(n) ? selectedNodeSize : 0,
267 isNodeTagged(n) ? taggedNodeSize : 0,
268 n.isConnectionNode() ? connectionNodeSize : 0,
269 unselectedNodeSize);
270
271 final boolean fill = (ds.isSelected(n) && fillSelectedNode) ||
272 (isNodeTagged(n) && fillTaggedNode) ||
273 (n.isConnectionNode() && fillConnectionNode) ||
274 fillUnselectedNode;
275
276 drawNode(n, color, size, fill);
277 }
278 }
279
280 private static boolean isNodeTagged(Node n) {
281 return n.isTagged() || n.isAnnotated();
282 }
283
284 /**
285 * Draw a line for all way segments.
286 * @param w The way to draw.
287 */
288 @Override
289 public void visit(Way w) {
290 if (w.isIncomplete() || w.getNodesCount() < 2)
291 return;
292
293 /* show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
294 (even if the tag is negated as in oneway=false) or the way is selected */
295
296 boolean showThisDirectionArrow = ds.isSelected(w) || showDirectionArrow;
297 /* head only takes over control if the option is true,
298 the direction should be shown at all and not only because it's selected */
299 boolean showOnlyHeadArrowOnly = showThisDirectionArrow && showHeadArrowOnly && !ds.isSelected(w);
300 Color wayColor;
301
302 if (isInactiveMode || w.isDisabled()) {
303 wayColor = inactiveColor;
304 } else if (w.isHighlighted()) {
305 wayColor = highlightColor;
306 } else if (w.isSelected()) {
307 wayColor = selectedColor;
308 } else if (w.isMemberOfSelected()) {
309 wayColor = relationSelectedColor;
310 } else if (!w.isTagged()) {
311 wayColor = untaggedWayColor;
312 } else {
313 wayColor = dfltWayColor;
314 }
315
316 Iterator<Node> it = w.getNodes().iterator();
317 if (it.hasNext()) {
318 MapViewPoint lastP = mapState.getPointFor(it.next());
319 int lastPOutside = lastP.getOutsideRectangleFlags(viewClip);
320 for (int orderNumber = 1; it.hasNext(); orderNumber++) {
321 MapViewPoint p = mapState.getPointFor(it.next());
322 int pOutside = p.getOutsideRectangleFlags(viewClip);
323 if ((pOutside & lastPOutside) == 0) {
324 drawSegment(lastP, p, wayColor,
325 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
326 if ((showOrderNumber || (showOrderNumberOnSelectedWay && w.isSelected())) && !isInactiveMode) {
327 drawOrderNumber(lastP, p, orderNumber, g.getColor());
328 }
329 }
330 lastP = p;
331 lastPOutside = pOutside;
332 }
333 }
334 }
335
336 /**
337 * Draw objects used in relations.
338 * @param r The relation to draw.
339 */
340 @Override
341 public void visit(Relation r) {
342 if (r.isIncomplete()) return;
343
344 Color col;
345 if (isInactiveMode || r.isDisabled()) {
346 col = inactiveColor;
347 } else if (r.isSelected()) {
348 col = selectedColor;
349 } else if (r.isMultipolygon() && r.isMemberOfSelected()) {
350 col = relationSelectedColor;
351 } else {
352 col = relationColor;
353 }
354 g.setColor(col);
355
356 for (RelationMember m : r.getMembers()) {
357 if (m.getMember().isIncomplete() || !m.getMember().isDrawable()) {
358 continue;
359 }
360
361 if (m.isNode()) {
362 MapViewPoint p = mapState.getPointFor(m.getNode());
363 if (p.isInView()) {
364 g.draw(new Ellipse2D.Double(p.getInViewX()-4, p.getInViewY()-4, 9, 9));
365 }
366
367 } else if (m.isWay()) {
368 GeneralPath path = new GeneralPath();
369
370 boolean first = true;
371 for (Node n : m.getWay().getNodes()) {
372 if (!n.isDrawable()) {
373 continue;
374 }
375 MapViewPoint p = mapState.getPointFor(n);
376 if (first) {
377 path.moveTo(p.getInViewX(), p.getInViewY());
378 first = false;
379 } else {
380 path.lineTo(p.getInViewX(), p.getInViewY());
381 }
382 }
383
384 g.draw(relatedWayStroke.createStrokedShape(path));
385 }
386 }
387 }
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 path.moveTo(mv1);
416 path.lineTo(mv2);
417 if (showDirection) {
418 ARROW_PAINT_HELPER.paintArrowAt(path, mv2, mv1);
419 }
420 }
421
422 /**
423 * Draw a line with the given color.
424 *
425 * @param p1 First point of the way segment.
426 * @param p2 Second point of the way segment.
427 * @param col The color to use for drawing line.
428 * @param showDirection <code>true</code> if segment direction should be indicated.
429 * @since 10827
430 */
431 protected void drawSegment(MapViewPoint p1, MapViewPoint p2, Color col, boolean showDirection) {
432 if (!col.equals(currentColor)) {
433 displaySegments(col);
434 }
435 drawSegment(currentPath, p1, p2, showDirection);
436 }
437
438 /**
439 * Finally display all segments in currect path.
440 */
441 protected void displaySegments() {
442 displaySegments(null);
443 }
444
445 /**
446 * Finally display all segments in currect path.
447 *
448 * @param newColor This color is set after the path is drawn.
449 */
450 protected void displaySegments(Color newColor) {
451 if (currentPath != null) {
452 g.setColor(currentColor);
453 g.draw(currentPath);
454 currentPath = new MapPath2D();
455 currentColor = newColor;
456 }
457 }
458}
Note: See TracBrowser for help on using the repository browser.