source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/SimplePaintVisitor.java@ 3994

Last change on this file since 3994 was 3994, checked in by mjulius, 13 years ago

fix #6113 - removing "mandatory" arrows on some types of ways
Since oneway arrows are now different from way direction arrows they need to be treated separately.
They now can be turned on and off independently.

  • Property svn:eol-style set to native
File size: 18.8 KB
Line 
1/* License: GPL. Copyright 2007 by Immanuel Scholz and others */
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4/* To enable debugging or profiling remove the double / signs */
5
6import java.awt.BasicStroke;
7import java.awt.Color;
8import java.awt.Graphics2D;
9import java.awt.Point;
10import java.awt.Polygon;
11import java.awt.Rectangle;
12import java.awt.RenderingHints;
13import java.awt.Stroke;
14import java.awt.geom.GeneralPath;
15import java.awt.geom.Point2D;
16import java.util.Collection;
17import java.util.Iterator;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.Bounds;
21import org.openstreetmap.josm.data.osm.BBox;
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.visitor.AbstractVisitor;
29import org.openstreetmap.josm.gui.NavigatableComponent;
30
31/**
32 * A visitor that paints a simple scheme of every primitive it visits to a
33 * previous set graphic environment.
34 *
35 * @author imi
36 */
37public class SimplePaintVisitor extends AbstractVisitor implements PaintVisitor {
38 /**
39 * The environment to paint to.
40 */
41 protected Graphics2D g;
42 /**
43 * MapView to get screen coordinates.
44 */
45 protected NavigatableComponent nc;
46
47 public boolean inactive;
48
49 /**
50 * Preferences
51 */
52 protected Color inactiveColor;
53 protected Color selectedColor;
54 protected Color nodeColor;
55 protected Color dfltWayColor;
56 protected Color relationColor;
57 protected Color untaggedWayColor;
58 protected Color incompleteColor;
59 protected Color backgroundColor;
60 protected Color highlightColor;
61 protected Color taggedColor;
62 protected Color connectionColor;
63 protected Color taggedConnectionColor;
64 protected boolean showDirectionArrow;
65 protected boolean showOnewayArrow;
66 protected boolean showHeadArrowOnly;
67 protected boolean showOrderNumber;
68 protected boolean fillSelectedNode;
69 protected boolean fillUnselectedNode;
70 protected boolean fillTaggedNode;
71 protected boolean fillConnectionNode;
72 protected int selectedNodeSize;
73 protected int unselectedNodeSize;
74 protected int connectionNodeSize;
75 protected int taggedNodeSize;
76 protected int defaultSegmentWidth;
77 protected int virtualNodeSize;
78 protected int virtualNodeSpace;
79 protected int segmentNumberSpace;
80
81 /**
82 * Draw subsequent segments of same color as one Path
83 */
84 protected Color currentColor = null;
85 protected GeneralPath currentPath = new GeneralPath();
86
87 public void getColors()
88 {
89 inactiveColor = PaintColors.INACTIVE.get();
90 selectedColor = PaintColors.SELECTED.get();
91 nodeColor = PaintColors.NODE.get();
92 dfltWayColor = PaintColors.DEFAULT_WAY.get();
93 relationColor = PaintColors.RELATION.get();
94 untaggedWayColor = PaintColors.UNTAGGED_WAY.get();
95 incompleteColor = PaintColors.INCOMPLETE_WAY.get();
96 backgroundColor = PaintColors.BACKGROUND.get();
97 highlightColor = PaintColors.HIGHLIGHT.get();
98 taggedColor = PaintColors.TAGGED.get();
99 connectionColor = PaintColors.CONNECTION.get();
100
101 if (taggedColor != nodeColor) {
102 taggedConnectionColor = taggedColor;
103 } else {
104 taggedConnectionColor = connectionColor;
105 }
106 }
107
108 protected void getSettings(boolean virtual) {
109 MapPaintSettings settings = MapPaintSettings.INSTANCE;
110 showDirectionArrow = settings.isShowDirectionArrow();
111 showOnewayArrow = settings.isShowOnewayArrow();
112 showHeadArrowOnly = settings.isShowHeadArrowOnly();
113 showOrderNumber = settings.isShowOrderNumber();
114 selectedNodeSize = settings.getSelectedNodeSize();
115 unselectedNodeSize = settings.getUnselectedNodeSize();
116 connectionNodeSize = settings.getConnectionNodeSize();
117 taggedNodeSize = settings.getTaggedNodeSize();
118 defaultSegmentWidth = settings.getDefaultSegmentWidth();
119 fillSelectedNode = settings.isFillSelectedNode();
120 fillUnselectedNode = settings.isFillUnselectedNode();
121 fillConnectionNode = settings.isFillConnectionNode();
122 fillTaggedNode = settings.isFillTaggedNode();
123 virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;
124 virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
125 segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
126 getColors();
127
128 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
129 Main.pref.getBoolean("mappaint.wireframe.use-antialiasing", false) ?
130 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
131 }
132
133 DataSet ds;
134 public void visitAll(DataSet data, boolean virtual, Bounds bounds) {
135 BBox bbox = new BBox(bounds);
136 this.ds = data;
137 //boolean profiler = Main.pref.getBoolean("simplepaint.profiler",false);
138 //long profilerStart = java.lang.System.currentTimeMillis();
139 //long profilerLast = profilerStart;
140 //int profilerN = 0;
141 //if(profiler)
142 // System.out.println("Simplepaint Profiler");
143
144 getSettings(virtual);
145
146 //if(profiler)
147 //{
148 // System.out.format("Prepare : %4dms\n", (java.lang.System.currentTimeMillis()-profilerLast));
149 // profilerLast = java.lang.System.currentTimeMillis();
150 //}
151
152 /* draw tagged ways first, then untagged ways. takes
153 time to iterate through list twice, OTOH does not
154 require changing the colour while painting... */
155 //profilerN = 0;
156 for (final OsmPrimitive osm: data.searchRelations(bbox)) {
157 if (!osm.isDeleted() && !ds.isSelected(osm) && !osm.isDisabledAndHidden()) {
158 osm.visit(this);
159 // profilerN++;
160 }
161 }
162
163 //if(profiler)
164 //{
165 // System.out.format("Relations: %4dms, n=%5d\n", (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
166 // profilerLast = java.lang.System.currentTimeMillis();
167 //}
168
169 //profilerN = 0;
170 for (final OsmPrimitive osm:data.searchWays(bbox)){
171 if (!osm.isDeleted() && !ds.isSelected(osm) && !osm.isDisabledAndHidden() && osm.isTagged()) {
172 osm.visit(this);
173 // profilerN++;
174 }
175 }
176 displaySegments();
177
178 for (final OsmPrimitive osm:data.searchWays(bbox)){
179 if (!osm.isDeleted() && !ds.isSelected(osm) && !osm.isDisabledAndHidden() && !osm.isTagged()) {
180 osm.visit(this);
181 // profilerN++;
182 }
183 }
184 displaySegments();
185
186 //if(profiler)
187 //{
188 // System.out.format("Ways : %4dms, n=%5d\n",
189 // (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
190 // profilerLast = java.lang.System.currentTimeMillis();
191 //}
192
193 //profilerN = 0;
194 for (final OsmPrimitive osm : data.getSelected()) {
195 if (!osm.isDeleted()) {
196 osm.visit(this);
197 // profilerN++;
198 }
199 }
200 displaySegments();
201
202 //if(profiler)
203 //{
204 // System.out.format("Selected : %4dms, n=%5d\n", (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
205 // profilerLast = java.lang.System.currentTimeMillis();
206 //}
207
208 //profilerN = 0;
209 for (final OsmPrimitive osm: data.searchNodes(bbox)) {
210 if (!osm.isDeleted() && !ds.isSelected(osm) && !osm.isDisabledAndHidden())
211 {
212 osm.visit(this);
213 // profilerN++;
214 }
215 }
216
217 //if(profiler)
218 //{
219 // System.out.format("Nodes : %4dms, n=%5d\n",
220 // (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
221 // profilerLast = java.lang.System.currentTimeMillis();
222 //}
223
224 drawVirtualNodes(data.searchWays(bbox));
225
226 //if(profiler)
227 //{
228 // System.out.format("All : %4dms\n", (profilerLast-profilerStart));
229 //}
230 }
231
232 private static final int max(int a, int b, int c, int d) {
233 return Math.max(Math.max(a, b), Math.max(c, d));
234 }
235
236 /**
237 * Draw a small rectangle.
238 * White if selected (as always) or red otherwise.
239 *
240 * @param n The node to draw.
241 */
242 public void visit(Node n) {
243 if (n.isIncomplete()) return;
244
245 if (n.isHighlighted()) {
246 drawNode(n, highlightColor, selectedNodeSize, fillSelectedNode);
247 } else {
248 Color color;
249
250 if (inactive || n.isDisabled()) {
251 color = inactiveColor;
252 } else if (ds.isSelected(n)) {
253 color = selectedColor;
254 } else if (n.isConnectionNode()) {
255 if (n.isTagged()) {
256 color = taggedConnectionColor;
257 } else {
258 color = connectionColor;
259 }
260 } else {
261 if (n.isTagged()) {
262 color = taggedColor;
263 } else {
264 color = nodeColor;
265 }
266 }
267
268 final int size = max((ds.isSelected(n) ? selectedNodeSize : 0),
269 (n.isTagged() ? taggedNodeSize : 0),
270 (n.isConnectionNode() ? connectionNodeSize : 0),
271 unselectedNodeSize);
272
273 final boolean fill = (ds.isSelected(n) && fillSelectedNode) ||
274 (n.isTagged() && fillTaggedNode) ||
275 (n.isConnectionNode() && fillConnectionNode) ||
276 fillUnselectedNode;
277
278 drawNode(n, color, size, fill);
279 }
280 }
281
282 public static boolean isLargeSegment(Point2D p1, Point2D p2, int space)
283 {
284 double xd = Math.abs(p1.getX()-p2.getX());
285 double yd = Math.abs(p1.getY()-p2.getY());
286 return (xd+yd > space);
287 }
288
289 public void drawVirtualNodes(Collection<Way> ways) {
290
291 if (virtualNodeSize != 0) {
292 GeneralPath path = new GeneralPath();
293 for (Way osm: ways){
294 if (osm.isUsable() && !osm.isDisabledAndHidden() && !osm.isDisabled()) {
295 visitVirtual(path, osm);
296 }
297 }
298 g.setColor(nodeColor);
299 g.draw(path);
300 }
301 }
302
303 public void visitVirtual(GeneralPath path, Way w) {
304 Iterator<Node> it = w.getNodes().iterator();
305 if (it.hasNext()) {
306 Point lastP = nc.getPoint(it.next());
307 while(it.hasNext())
308 {
309 Point p = nc.getPoint(it.next());
310 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))
311 {
312 int x = (p.x+lastP.x)/2;
313 int y = (p.y+lastP.y)/2;
314 path.moveTo(x-virtualNodeSize, y);
315 path.lineTo(x+virtualNodeSize, y);
316 path.moveTo(x, y-virtualNodeSize);
317 path.lineTo(x, y+virtualNodeSize);
318 }
319 lastP = p;
320 }
321 }
322 }
323
324 /**
325 * Draw a darkblue line for all segments.
326 * @param w The way to draw.
327 */
328 public void visit(Way w) {
329 if (w.isIncomplete() || w.getNodesCount() < 2)
330 return;
331
332 /* show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
333 (even if the tag is negated as in oneway=false) or the way is selected */
334
335 boolean showThisDirectionArrow = ds.isSelected(w) || showDirectionArrow;
336 /* head only takes over control if the option is true,
337 the direction should be shown at all and not only because it's selected */
338 boolean showOnlyHeadArrowOnly = showThisDirectionArrow && !ds.isSelected(w) && showHeadArrowOnly;
339 Color wayColor;
340
341 if (inactive || w.isDisabled()) {
342 wayColor = inactiveColor;
343 } else if(w.isHighlighted()) {
344 wayColor = highlightColor;
345 } else if(ds.isSelected(w)) {
346 wayColor = selectedColor;
347 } else if (!w.isTagged()) {
348 wayColor = untaggedWayColor;
349 } else {
350 wayColor = dfltWayColor;
351 }
352
353 Iterator<Node> it = w.getNodes().iterator();
354 if (it.hasNext()) {
355 Point lastP = nc.getPoint(it.next());
356 for (int orderNumber = 1; it.hasNext(); orderNumber++) {
357 Point p = nc.getPoint(it.next());
358 drawSegment(lastP, p, wayColor,
359 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
360 if (showOrderNumber) {
361 drawOrderNumber(lastP, p, orderNumber);
362 }
363 lastP = p;
364 }
365 }
366 }
367
368 private Stroke relatedWayStroke = new BasicStroke(
369 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
370 public void visit(Relation r) {
371 if (r.isIncomplete()) return;
372
373 Color col;
374 if (inactive || r.isDisabled()) {
375 col = inactiveColor;
376 } else if (ds.isSelected(r)) {
377 col = selectedColor;
378 } else {
379 col = relationColor;
380 }
381 g.setColor(col);
382
383 for (RelationMember m : r.getMembers()) {
384 if (m.getMember().isIncomplete() || m.getMember().isDeleted()) {
385 continue;
386 }
387
388 if (m.isNode()) {
389 Point p = nc.getPoint(m.getNode());
390 if (p.x < 0 || p.y < 0
391 || p.x > nc.getWidth() || p.y > nc.getHeight()) {
392 continue;
393 }
394
395 g.drawOval(p.x-3, p.y-3, 6, 6);
396 } else if (m.isWay()) {
397 GeneralPath path = new GeneralPath();
398
399 boolean first = true;
400 for (Node n : m.getWay().getNodes()) {
401 if (n.isIncomplete() || n.isDeleted()) {
402 continue;
403 }
404 Point p = nc.getPoint(n);
405 if (first) {
406 path.moveTo(p.x, p.y);
407 first = false;
408 } else {
409 path.lineTo(p.x, p.y);
410 }
411 }
412
413 g.draw(relatedWayStroke.createStrokedShape(path));
414 }
415 }
416 }
417
418 /**
419 * Draw an number of the order of the two consecutive nodes within the
420 * parents way
421 */
422 protected void drawOrderNumber(Point p1, Point p2, int orderNumber) {
423 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {
424 String on = Integer.toString(orderNumber);
425 int strlen = on.length();
426 int x = (p1.x+p2.x)/2 - 4*strlen;
427 int y = (p1.y+p2.y)/2 + 4;
428
429 if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace))
430 {
431 y = (p1.y+p2.y)/2 - virtualNodeSize - 3;
432 }
433
434 displaySegments(); /* draw nodes on top! */
435 Color c = g.getColor();
436 g.setColor(backgroundColor);
437 g.fillRect(x-1, y-12, 8*strlen+1, 14);
438 g.setColor(c);
439 g.drawString(on, x, y);
440 }
441 }
442
443 /**
444 * Draw the node as small rectangle with the given color.
445 *
446 * @param n The node to draw.
447 * @param color The color of the node.
448 */
449 public void drawNode(Node n, Color color, int size, boolean fill) {
450 if (size > 1) {
451 int radius = size / 2;
452 Point p = nc.getPoint(n);
453 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
454 || (p.y > nc.getHeight()))
455 return;
456 g.setColor(color);
457 if (fill) {
458 g.fillRect(p.x - radius, p.y - radius, size, size);
459 g.drawRect(p.x - radius, p.y - radius, size, size);
460 } else {
461 g.drawRect(p.x - radius, p.y - radius, size, size);
462 }
463 }
464 }
465
466 private static final double PHI = Math.toRadians(20);
467 private static final double cosPHI = Math.cos(PHI);
468 private static final double sinPHI = Math.sin(PHI);
469
470 protected void drawSegment(GeneralPath path, Point p1, Point p2, boolean showDirection) {
471 Rectangle bounds = g.getClipBounds();
472 bounds.grow(100, 100); // avoid arrow heads at the border
473 LineClip clip = new LineClip(p1, p2, bounds);
474 if (clip.execute()) {
475 p1 = clip.getP1();
476 p2 = clip.getP2();
477 path.moveTo(p1.x, p1.y);
478 path.lineTo(p2.x, p2.y);
479
480 if (showDirection) {
481 final double l = 10. / p1.distance(p2);
482
483 final double sx = l * (p1.x - p2.x);
484 final double sy = l * (p1.y - p2.y);
485
486 path.lineTo (p2.x + (int) Math.round(cosPHI * sx - sinPHI * sy), p2.y + (int) Math.round(sinPHI * sx + cosPHI * sy));
487 path.moveTo (p2.x + (int) Math.round(cosPHI * sx + sinPHI * sy), p2.y + (int) Math.round(- sinPHI * sx + cosPHI * sy));
488 path.lineTo(p2.x, p2.y);
489 }
490 }
491 }
492
493 /**
494 * Draw a line with the given color.
495 */
496 protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) {
497 if (col != currentColor) {
498 displaySegments(col);
499 }
500 drawSegment(currentPath, p1, p2, showDirection);
501 }
502
503 protected boolean isSegmentVisible(Point p1, Point p2) {
504 if ((p1.x < 0) && (p2.x < 0)) return false;
505 if ((p1.y < 0) && (p2.y < 0)) return false;
506 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;
507 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;
508 return true;
509 }
510
511 protected boolean isPolygonVisible(Polygon polygon) {
512 Rectangle bounds = polygon.getBounds();
513 if (bounds.width == 0 && bounds.height == 0) return false;
514 if (bounds.x > nc.getWidth()) return false;
515 if (bounds.y > nc.getHeight()) return false;
516 if (bounds.x + bounds.width < 0) return false;
517 if (bounds.y + bounds.height < 0) return false;
518 return true;
519 }
520
521 public void setGraphics(Graphics2D g) {
522 this.g = g;
523 }
524
525 public void setNavigatableComponent(NavigatableComponent nc) {
526 this.nc = nc;
527 }
528
529 protected void displaySegments() {
530 displaySegments(null);
531 }
532 protected void displaySegments(Color newColor) {
533 if (currentPath != null) {
534 g.setColor(currentColor);
535 g.draw(currentPath);
536 currentPath = new GeneralPath();
537 currentColor = newColor;
538 }
539 }
540
541 public void setInactive(boolean inactive) {
542 this.inactive = inactive;
543 }
544}
Note: See TracBrowser for help on using the repository browser.