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

Last change on this file since 2264 was 2264, checked in by stoecker, 15 years ago

applied #3676 - patch by Dave Hansen - cleanup selection handling interface

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