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

Last change on this file since 2667 was 2666, checked in by jttt, 14 years ago

Minor mappaint cleanup, use constants for colors

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