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

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

see #3475 - patch by Petr Dlouhý - code rework for display filtering

  • Property svn:eol-style set to native
File size: 17.7 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 public void visitAll(DataSet data, Boolean virtual) {
131 //boolean profiler = Main.pref.getBoolean("simplepaint.profiler",false);
132 //long profilerStart = java.lang.System.currentTimeMillis();
133 //long profilerLast = profilerStart;
134 //int profilerN = 0;
135 //if(profiler)
136 // System.out.println("Simplepaint Profiler");
137
138 getSettings(virtual);
139
140 //if(profiler)
141 //{
142 // System.out.format("Prepare : %4dms\n", (java.lang.System.currentTimeMillis()-profilerLast));
143 // profilerLast = java.lang.System.currentTimeMillis();
144 //}
145
146 /* draw tagged ways first, then untagged ways. takes
147 time to iterate through list twice, OTOH does not
148 require changing the colour while painting... */
149 //profilerN = 0;
150 for (final OsmPrimitive osm : data.relations)
151 if (!osm.isDeleted() && !osm.isSelected() && !osm.isFiltered())
152 {
153 osm.visit(this);
154 // profilerN++;
155 }
156
157 //if(profiler)
158 //{
159 // System.out.format("Relations: %4dms, n=%5d\n", (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
160 // profilerLast = java.lang.System.currentTimeMillis();
161 //}
162
163 //profilerN = 0;
164 for (final OsmPrimitive osm : data.ways)
165 if (!osm.isDeleted() && !osm.isSelected() && !osm.isFiltered() && osm.isTagged())
166 {
167 osm.visit(this);
168 // profilerN++;
169 }
170 displaySegments();
171
172 for (final OsmPrimitive osm : data.ways)
173 if (!osm.isDeleted() && !osm.isSelected() && !osm.isFiltered() && !osm.isTagged())
174 {
175 osm.visit(this);
176 // profilerN++;
177 }
178 displaySegments();
179
180 //if(profiler)
181 //{
182 // System.out.format("Ways : %4dms, n=%5d\n",
183 // (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
184 // profilerLast = java.lang.System.currentTimeMillis();
185 //}
186
187 //profilerN = 0;
188 for (final OsmPrimitive osm : data.getSelected())
189 if (!osm.isDeleted())
190 {
191 osm.visit(this);
192 // profilerN++;
193 }
194 displaySegments();
195
196 //if(profiler)
197 //{
198 // System.out.format("Selected : %4dms, n=%5d\n", (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
199 // profilerLast = java.lang.System.currentTimeMillis();
200 //}
201
202 //profilerN = 0;
203 for (final OsmPrimitive osm : data.nodes)
204 if (!osm.isDeleted() && !osm.isSelected() && !osm.isFiltered())
205 {
206 osm.visit(this);
207 // profilerN++;
208 }
209
210 //if(profiler)
211 //{
212 // System.out.format("Nodes : %4dms, n=%5d\n",
213 // (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
214 // profilerLast = java.lang.System.currentTimeMillis();
215 //}
216
217 if(virtualNodeSize != 0)
218 {
219 // profilerN = 0;
220 currentColor = nodeColor;
221 for (final OsmPrimitive osm : data.ways)
222 if (!osm.isDeleted() && !osm.isDisabled() && !osm.isFiltered())
223 {
224 visitVirtual((Way)osm);
225 // profilerN++;
226 }
227 displaySegments();
228
229 // if(profiler)
230 // {
231 // System.out.format("Virtual : %4dms, n=%5d\n", (java.lang.System.currentTimeMillis()-profilerLast), profilerN);
232 // profilerLast = java.lang.System.currentTimeMillis();
233 // }
234 }
235
236 //if(profiler)
237 //{
238 // System.out.format("All : %4dms\n", (profilerLast-profilerStart));
239 //}
240 }
241
242 /**
243 * Draw a small rectangle.
244 * White if selected (as always) or red otherwise.
245 *
246 * @param n The node to draw.
247 */
248 public void visit(Node n) {
249 if (n.incomplete) return;
250
251 if (inactive || n.isDisabled()) {
252 drawNode(n, inactiveColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
253 } else if (n.highlighted) {
254 drawNode(n, highlightColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
255 } else if (n.isSelected()) {
256 drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode);
257 } else if(n.isTagged()) {
258 drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode);
259 } else {
260 drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode);
261 }
262 }
263
264 public static Boolean isLargeSegment(Point p1, Point p2, int space)
265 {
266 int xd = p1.x-p2.x; if(xd < 0) {
267 xd = -xd;
268 }
269 int yd = p1.y-p2.y; if(yd < 0) {
270 yd = -yd;
271 }
272 return (xd+yd > space);
273 }
274
275 public void visitVirtual(Way w) {
276 Iterator<Node> it = w.getNodes().iterator();
277 if (it.hasNext()) {
278 Point lastP = nc.getPoint(it.next());
279 while(it.hasNext())
280 {
281 Point p = nc.getPoint(it.next());
282 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))
283 {
284 int x = (p.x+lastP.x)/2;
285 int y = (p.y+lastP.y)/2;
286 currentPath.moveTo(x-virtualNodeSize, y);
287 currentPath.lineTo(x+virtualNodeSize, y);
288 currentPath.moveTo(x, y-virtualNodeSize);
289 currentPath.lineTo(x, y+virtualNodeSize);
290 }
291 lastP = p;
292 }
293 }
294 }
295
296 /**
297 * Draw a darkblue line for all segments.
298 * @param w The way to draw.
299 */
300 public void visit(Way w) {
301 if (w.incomplete || w.getNodesCount() < 2)
302 return;
303
304 /* show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
305 (even if the tag is negated as in oneway=false) or the way is selected */
306
307 boolean showThisDirectionArrow = w.isSelected()
308 || (showDirectionArrow && (!showRelevantDirectionsOnly || w.hasDirectionKeys()));
309 /* head only takes over control if the option is true,
310 the direction should be shown at all and not only because it's selected */
311 boolean showOnlyHeadArrowOnly = showThisDirectionArrow && !w.isSelected() && showHeadArrowOnly;
312 Color wayColor;
313
314 if (inactive || w.isDisabled()) {
315 wayColor = inactiveColor;
316 } else if(w.highlighted) {
317 wayColor = highlightColor;
318 } else if(w.isSelected()) {
319 wayColor = selectedColor;
320 } else if (!w.isTagged()) {
321 wayColor = untaggedWayColor;
322 } else {
323 wayColor = dfltWayColor;
324 }
325
326 Iterator<Node> it = w.getNodes().iterator();
327 if (it.hasNext()) {
328 Point lastP = nc.getPoint(it.next());
329 for (int orderNumber = 1; it.hasNext(); orderNumber++) {
330 Point p = nc.getPoint(it.next());
331 drawSegment(lastP, p, wayColor,
332 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
333 if (showOrderNumber) {
334 drawOrderNumber(lastP, p, orderNumber);
335 }
336 lastP = p;
337 }
338 }
339 }
340
341 private Stroke relatedWayStroke = new BasicStroke(
342 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
343 public void visit(Relation r) {
344 if (r.incomplete) return;
345
346 Color col;
347 if (inactive || r.isDisabled()) {
348 col = inactiveColor;
349 } else if (r.isSelected()) {
350 col = selectedColor;
351 } else {
352 col = relationColor;
353 }
354 g.setColor(col);
355
356 for (RelationMember m : r.getMembers()) {
357 if (m.getMember().incomplete || m.getMember().isDeleted()) {
358 continue;
359 }
360
361 if (m.isNode()) {
362 Point p = nc.getPoint(m.getNode());
363 if (p.x < 0 || p.y < 0
364 || p.x > nc.getWidth() || p.y > nc.getHeight()) {
365 continue;
366 }
367
368 g.drawOval(p.x-3, p.y-3, 6, 6);
369 } else if (m.isWay()) {
370 GeneralPath path = new GeneralPath();
371
372 boolean first = true;
373 for (Node n : m.getWay().getNodes()) {
374 if (n.incomplete || n.isDeleted()) {
375 continue;
376 }
377 Point p = nc.getPoint(n);
378 if (first) {
379 path.moveTo(p.x, p.y);
380 first = false;
381 } else {
382 path.lineTo(p.x, p.y);
383 }
384 }
385
386 ((Graphics2D) g).draw(relatedWayStroke.createStrokedShape(path));
387 }
388 }
389 }
390
391 /**
392 * Draw an number of the order of the two consecutive nodes within the
393 * parents way
394 */
395 protected void drawOrderNumber(Point p1, Point p2, int orderNumber) {
396 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {
397 String on = Integer.toString(orderNumber);
398 int strlen = on.length();
399 int x = (p1.x+p2.x)/2 - 4*strlen;
400 int y = (p1.y+p2.y)/2 + 4;
401
402 if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace))
403 {
404 y = (p1.y+p2.y)/2 - virtualNodeSize - 3;
405 }
406
407 displaySegments(); /* draw nodes on top! */
408 Color c = g.getColor();
409 g.setColor(backgroundColor);
410 g.fillRect(x-1, y-12, 8*strlen+1, 14);
411 g.setColor(c);
412 g.drawString(on, x, y);
413 }
414 }
415
416 /**
417 * Draw the node as small rectangle with the given color.
418 *
419 * @param n The node to draw.
420 * @param color The color of the node.
421 */
422 public void drawNode(Node n, Color color, int size, int radius, boolean fill) {
423 if (size > 1) {
424 Point p = nc.getPoint(n);
425 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
426 || (p.y > nc.getHeight()))
427 return;
428 g.setColor(color);
429 if (fill) {
430 g.fillRect(p.x - radius, p.y - radius, size, size);
431 g.drawRect(p.x - radius, p.y - radius, size, size);
432 } else {
433 g.drawRect(p.x - radius, p.y - radius, size, size);
434 }
435 }
436 }
437
438 /**
439 * Draw a line with the given color.
440 */
441 protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) {
442 if (col != currentColor) {
443 displaySegments(col);
444 }
445
446 if (isSegmentVisible(p1, p2)) {
447 currentPath.moveTo(p1.x, p1.y);
448 currentPath.lineTo(p2.x, p2.y);
449
450 if (showDirection) {
451 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
452 currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
453 currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
454 currentPath.lineTo(p2.x, p2.y);
455 }
456 }
457 }
458
459 protected boolean isSegmentVisible(Point p1, Point p2) {
460 if ((p1.x < 0) && (p2.x < 0)) return false;
461 if ((p1.y < 0) && (p2.y < 0)) return false;
462 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;
463 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;
464 return true;
465 }
466
467 protected boolean isPolygonVisible(Polygon polygon) {
468 Rectangle bounds = polygon.getBounds();
469 if (bounds.width == 0 && bounds.height == 0) return false;
470 if (bounds.x > nc.getWidth()) return false;
471 if (bounds.y > nc.getHeight()) return false;
472 if (bounds.x + bounds.width < 0) return false;
473 if (bounds.y + bounds.height < 0) return false;
474 return true;
475 }
476
477 public void setGraphics(Graphics g) {
478 this.g = g;
479 }
480
481 public void setNavigatableComponent(NavigatableComponent nc) {
482 this.nc = nc;
483 }
484
485 protected void displaySegments() {
486 displaySegments(null);
487 }
488 protected void displaySegments(Color newColor) {
489 if (currentPath != null) {
490 g.setColor(currentColor);
491 ((Graphics2D) g).draw(currentPath);
492 currentPath = new GeneralPath();
493 currentColor = newColor;
494 }
495 }
496}
Note: See TracBrowser for help on using the repository browser.