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

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

Use QuadBuckets in SimplePaintVisitor

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