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

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

support rendering of IPrimitive

  • Property svn:eol-style set to native
File size: 16.8 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.INode;
21import org.openstreetmap.josm.data.osm.IPrimitive;
22import org.openstreetmap.josm.data.osm.IRelation;
23import org.openstreetmap.josm.data.osm.IRelationMember;
24import org.openstreetmap.josm.data.osm.IWay;
25import org.openstreetmap.josm.data.osm.OsmData;
26import org.openstreetmap.josm.data.osm.WaySegment;
27import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
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 PrimitiveVisitor {
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 /** Helper variable for {@link #drawSegment} */
87 private static final ArrowPaintHelper ARROW_PAINT_HELPER = new ArrowPaintHelper(Utils.toRadians(20), 10);
88
89 /** Helper variable for {@link #visit(IRelation)} */
90 private final Stroke relatedWayStroke = new BasicStroke(
91 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
92 private MapViewRectangle viewClip;
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 showOrderNumberOnSelectedWay = settings.isShowOrderNumberOnSelectedWay();
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 Config.getPref().getBoolean("mappaint.wireframe.use-antialiasing", false) ?
145 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
146 }
147
148 @Override
149 public void render(OsmData<?, ?, ?, ?> data, boolean virtual, Bounds bounds) {
150 BBox bbox = bounds.toBBox();
151 Rectangle clip = g.getClipBounds();
152 clip.grow(50, 50);
153 viewClip = mapState.getViewArea(clip);
154 getSettings(virtual);
155
156 for (final IRelation<?> rel : data.searchRelations(bbox)) {
157 if (rel.isDrawable() && !rel.isSelected() && !rel.isDisabledAndHidden()) {
158 rel.accept(this);
159 }
160 }
161
162 // draw tagged ways first, then untagged ways, then highlighted ways
163 List<IWay<?>> highlightedWays = new ArrayList<>();
164 List<IWay<?>> untaggedWays = new ArrayList<>();
165
166 for (final IWay<?> way : data.searchWays(bbox)) {
167 if (way.isDrawable() && !way.isSelected() && !way.isDisabledAndHidden()) {
168 if (way.isHighlighted()) {
169 highlightedWays.add(way);
170 } else if (!way.isTagged()) {
171 untaggedWays.add(way);
172 } else {
173 way.accept(this);
174 }
175 }
176 }
177 displaySegments();
178
179 // Display highlighted ways after the other ones (fix #8276)
180 List<IWay<?>> specialWays = new ArrayList<>(untaggedWays);
181 specialWays.addAll(highlightedWays);
182 for (final IWay<?> way : specialWays) {
183 way.accept(this);
184 }
185 specialWays.clear();
186 displaySegments();
187
188 for (final IPrimitive osm : data.getSelected()) {
189 if (osm.isDrawable()) {
190 osm.accept(this);
191 }
192 }
193 displaySegments();
194
195 for (final INode osm: data.searchNodes(bbox)) {
196 if (osm.isDrawable() && !osm.isSelected() && !osm.isDisabledAndHidden()) {
197 osm.accept(this);
198 }
199 }
200 drawVirtualNodes(data, bbox);
201
202 // draw highlighted way segments over the already drawn ways. Otherwise each
203 // way would have to be checked if it contains a way segment to highlight when
204 // in most of the cases there won't be more than one segment. Since the wireframe
205 // renderer does not feature any transparency there should be no visual difference.
206 for (final WaySegment wseg : data.getHighlightedWaySegments()) {
207 drawSegment(mapState.getPointFor(wseg.getFirstNode()), mapState.getPointFor(wseg.getSecondNode()), highlightColor, false);
208 }
209 displaySegments();
210 }
211
212 /**
213 * Helper function to calculate maximum of 4 values.
214 *
215 * @param a First value
216 * @param b Second value
217 * @param c Third value
218 * @param d Fourth value
219 * @return maximumof {@code a}, {@code b}, {@code c}, {@code d}
220 */
221 private static int max(int a, int b, int c, int d) {
222 return Math.max(Math.max(a, b), Math.max(c, d));
223 }
224
225 /**
226 * Draw a small rectangle.
227 * White if selected (as always) or red otherwise.
228 *
229 * @param n The node to draw.
230 */
231 @Override
232 public void visit(INode n) {
233 if (n.isIncomplete()) return;
234
235 if (n.isHighlighted()) {
236 drawNode(n, highlightColor, selectedNodeSize, fillSelectedNode);
237 } else {
238 Color color;
239
240 if (isInactiveMode || n.isDisabled()) {
241 color = inactiveColor;
242 } else if (n.isSelected()) {
243 color = selectedColor;
244 } else if (n.isMemberOfSelected()) {
245 color = relationSelectedColor;
246 } else if (n.isConnectionNode()) {
247 if (isNodeTagged(n)) {
248 color = taggedConnectionColor;
249 } else {
250 color = connectionColor;
251 }
252 } else {
253 if (isNodeTagged(n)) {
254 color = taggedColor;
255 } else {
256 color = nodeColor;
257 }
258 }
259
260 final int size = max(n.isSelected() ? selectedNodeSize : 0,
261 isNodeTagged(n) ? taggedNodeSize : 0,
262 n.isConnectionNode() ? connectionNodeSize : 0,
263 unselectedNodeSize);
264
265 final boolean fill = (n.isSelected() && fillSelectedNode) ||
266 (isNodeTagged(n) && fillTaggedNode) ||
267 (n.isConnectionNode() && fillConnectionNode) ||
268 fillUnselectedNode;
269
270 drawNode(n, color, size, fill);
271 }
272 }
273
274 private static boolean isNodeTagged(INode n) {
275 return n.isTagged() || n.isAnnotated();
276 }
277
278 /**
279 * Draw a line for all way segments.
280 * @param w The way to draw.
281 */
282 @Override
283 public void visit(IWay<?> w) {
284 if (w.isIncomplete() || w.getNodesCount() < 2)
285 return;
286
287 /* show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
288 (even if the tag is negated as in oneway=false) or the way is selected */
289
290 boolean showThisDirectionArrow = w.isSelected() || showDirectionArrow;
291 /* head only takes over control if the option is true,
292 the direction should be shown at all and not only because it's selected */
293 boolean showOnlyHeadArrowOnly = showThisDirectionArrow && showHeadArrowOnly && !w.isSelected();
294 Color wayColor;
295
296 if (isInactiveMode || w.isDisabled()) {
297 wayColor = inactiveColor;
298 } else if (w.isHighlighted()) {
299 wayColor = highlightColor;
300 } else if (w.isSelected()) {
301 wayColor = selectedColor;
302 } else if (w.isMemberOfSelected()) {
303 wayColor = relationSelectedColor;
304 } else if (!w.isTagged()) {
305 wayColor = untaggedWayColor;
306 } else {
307 wayColor = dfltWayColor;
308 }
309
310 Iterator<? extends INode> it = w.getNodes().iterator();
311 if (it.hasNext()) {
312 MapViewPoint lastP = mapState.getPointFor(it.next());
313 int lastPOutside = lastP.getOutsideRectangleFlags(viewClip);
314 for (int orderNumber = 1; it.hasNext(); orderNumber++) {
315 MapViewPoint p = mapState.getPointFor(it.next());
316 int pOutside = p.getOutsideRectangleFlags(viewClip);
317 if ((pOutside & lastPOutside) == 0) {
318 drawSegment(lastP, p, wayColor,
319 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
320 if ((showOrderNumber || (showOrderNumberOnSelectedWay && w.isSelected())) && !isInactiveMode) {
321 drawOrderNumber(lastP, p, orderNumber, g.getColor());
322 }
323 }
324 lastP = p;
325 lastPOutside = pOutside;
326 }
327 }
328 }
329
330 /**
331 * Draw objects used in relations.
332 * @param r The relation to draw.
333 */
334 @Override
335 public void visit(IRelation<?> r) {
336 if (r.isIncomplete()) return;
337
338 Color col;
339 if (isInactiveMode || r.isDisabled()) {
340 col = inactiveColor;
341 } else if (r.isSelected()) {
342 col = selectedColor;
343 } else if (r.isMultipolygon() && r.isMemberOfSelected()) {
344 col = relationSelectedColor;
345 } else {
346 col = relationColor;
347 }
348 g.setColor(col);
349
350 for (IRelationMember<?> m : r.getMembers()) {
351 if (m.getMember().isIncomplete() || !m.getMember().isDrawable()) {
352 continue;
353 }
354
355 if (m.isNode()) {
356 MapViewPoint p = mapState.getPointFor((INode) m.getMember());
357 if (p.isInView()) {
358 g.draw(new Ellipse2D.Double(p.getInViewX()-4, p.getInViewY()-4, 9, 9));
359 }
360
361 } else if (m.isWay()) {
362 GeneralPath path = new GeneralPath();
363
364 boolean first = true;
365 for (INode n : ((IWay<?>) m.getMember()).getNodes()) {
366 if (!n.isDrawable()) {
367 continue;
368 }
369 MapViewPoint p = mapState.getPointFor(n);
370 if (first) {
371 path.moveTo(p.getInViewX(), p.getInViewY());
372 first = false;
373 } else {
374 path.lineTo(p.getInViewX(), p.getInViewY());
375 }
376 }
377
378 g.draw(relatedWayStroke.createStrokedShape(path));
379 }
380 }
381 }
382
383 @Override
384 public void drawNode(INode n, Color color, int size, boolean fill) {
385 if (size > 1) {
386 MapViewPoint p = mapState.getPointFor(n);
387 if (!p.isInView())
388 return;
389 int radius = size / 2;
390 Double shape = new Rectangle2D.Double(p.getInViewX() - radius, p.getInViewY() - radius, size, size);
391 g.setColor(color);
392 if (fill) {
393 g.fill(shape);
394 }
395 g.draw(shape);
396 }
397 }
398
399 /**
400 * Draw a line with the given color.
401 *
402 * @param path The path to append this segment.
403 * @param mv1 First point of the way segment.
404 * @param mv2 Second point of the way segment.
405 * @param showDirection <code>true</code> if segment direction should be indicated
406 * @since 10827
407 */
408 protected void drawSegment(MapPath2D path, MapViewPoint mv1, MapViewPoint mv2, boolean showDirection) {
409 path.moveTo(mv1);
410 path.lineTo(mv2);
411 if (showDirection) {
412 ARROW_PAINT_HELPER.paintArrowAt(path, mv2, mv1);
413 }
414 }
415
416 /**
417 * Draw a line with the given color.
418 *
419 * @param p1 First point of the way segment.
420 * @param p2 Second point of the way segment.
421 * @param col The color to use for drawing line.
422 * @param showDirection <code>true</code> if segment direction should be indicated.
423 * @since 10827
424 */
425 protected void drawSegment(MapViewPoint p1, MapViewPoint p2, Color col, boolean showDirection) {
426 if (!col.equals(currentColor)) {
427 displaySegments(col);
428 }
429 drawSegment(currentPath, p1, p2, showDirection);
430 }
431
432 /**
433 * Finally display all segments in currect path.
434 */
435 protected void displaySegments() {
436 displaySegments(null);
437 }
438
439 /**
440 * Finally display all segments in currect path.
441 *
442 * @param newColor This color is set after the path is drawn.
443 */
444 protected void displaySegments(Color newColor) {
445 if (currentPath != null) {
446 g.setColor(currentColor);
447 g.draw(currentPath);
448 currentPath = new MapPath2D();
449 currentColor = newColor;
450 }
451 }
452}
Note: See TracBrowser for help on using the repository browser.