source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java@ 608

Last change on this file since 608 was 608, checked in by framm, 16 years ago
  • new extrude mode allows creation of rectangular shapes
  • new AlignInRectangle function
  • additional information in status bar about length, heading, and angle of segment being drawn
  • helper line from last node to mouse cursor (disable with edit.helper-line=false)
File size: 13.2 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm.visitor;
3
4import java.awt.BasicStroke;
5import java.awt.Color;
6import java.awt.Font;
7import java.awt.Graphics;
8import java.awt.Graphics2D;
9import java.awt.Point;
10import java.awt.Polygon;
11import java.awt.Rectangle;
12import java.awt.Stroke;
13import java.awt.geom.GeneralPath;
14import java.util.Collection;
15import java.util.LinkedList;
16
17import javax.swing.ImageIcon;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.Preferences;
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.Way;
26import org.openstreetmap.josm.gui.NavigatableComponent;
27import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
28import org.openstreetmap.josm.gui.mappaint.ElemStyle;
29import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
30import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
31import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
32import org.openstreetmap.josm.tools.ColorHelper;
33
34public class MapPaintVisitor implements Visitor {
35
36 protected boolean useRealWidth;
37 protected boolean zoomLevelDisplay;
38 protected boolean fillAreas;
39 protected int fillAlpha;
40 protected Color untaggedColor;
41 protected Color textColor;
42 protected boolean currentDashed = false;
43 protected int currentWidth = 0;
44 protected Stroke currentStroke = null;
45 protected static final Font orderFont = new Font("Helvetica", Font.PLAIN, 8);
46
47 public boolean inactive;
48
49 /**
50 * The environment to paint to.
51 */
52 protected Graphics g;
53
54 /**
55 * MapView to get screen coordinates.
56 */
57 protected NavigatableComponent nc;
58
59 /**
60 * Draw subsequent segments of same color as one Path
61 */
62 protected Color currentColor = null;
63 protected GeneralPath currentPath = new GeneralPath();
64
65 protected static final double PHI = Math.toRadians(20);
66
67 /**
68 * Preferences
69 */
70 protected Color inactiveColor;
71 protected Color selectedColor;
72 protected Color nodeColor;
73 protected Color dfltWayColor;
74 protected Color untaggedWayColor;
75 protected Color incompleteColor;
76 protected Color backgroundColor;
77 protected boolean showDirectionArrow;
78 protected boolean showRelevantDirectionsOnly;
79 protected boolean showOrderNumber;
80
81 public final static Color darkerblue = new Color(0,0,96);
82 public final static Color darkblue = new Color(0,0,128);
83
84 protected boolean isZoomOk(ElemStyle e) {
85 double circum = Main.map.mapView.getScale()*100*Main.proj.scaleFactor()*40041455; // circumference of the earth in meter
86
87 /* show everything if the user wishes so */
88 if (!zoomLevelDisplay) {
89 return true;
90 }
91
92 if (e == null) {
93 /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
94 if (circum < 1500)
95 return true;
96 return false;
97 }
98
99 // formula to calculate a map scale: natural size / map size = scale
100 // example: 876000mm (876m as displayed) / 22mm (roughly estimated screen size of legend bar) = 39818
101 //
102 // so the exact "correcting value" below depends only on the screen size and resolution
103 // XXX - do we need a Preference setting for this (if things vary widely)?
104 /*System.out.println(
105 "Circum: " + circum +
106 " max: " + e.getMaxScale() + "(" + e.getMaxScale()/22 + ")" +
107 " min:" + e.getMinScale() + "(" + e.getMinScale()/22 + ")");*/
108 if(circum>=e.getMaxScale() / 22 || circum<e.getMinScale() / 22)
109 return false;
110 return true;
111 }
112
113 /**
114 * Draw a small rectangle.
115 * White if selected (as always) or red otherwise.
116 *
117 * @param n The node to draw.
118 */
119 public void visit(Node n) {
120 ElemStyle nodeStyle = MapPaintStyles.getStyle(n);
121 if (nodeStyle!=null) {
122 if (nodeStyle instanceof IconElemStyle) {
123 if (isZoomOk(nodeStyle)) {
124 drawNode(n, ((IconElemStyle)nodeStyle).getIcon(), ((IconElemStyle)nodeStyle).doAnnotate());
125 }
126 } else {
127 // throw some sort of exception
128 }
129 } else {
130 drawNode(n, n.selected ? selectedColor : nodeColor);
131 }
132 }
133
134 /**
135 * Draw a line for all segments, according to tags.
136 * @param w The way to draw.
137 */
138 public void visit(Way w) {
139 double circum = Main.map.mapView.getScale()*100*Main.proj.scaleFactor()*40041455; // circumference of the earth in meter
140 // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
141 // (even if the tag is negated as in oneway=false) or the way is selected
142 boolean showDirection = w.selected ||
143 ((!useRealWidth) && (showDirectionArrow
144 && (!showRelevantDirectionsOnly || w.hasDirectionKeys)));
145
146 Color colour = untaggedColor;
147 int width = 2;
148 int realWidth = 0; //the real width of the element in meters
149 boolean dashed = false;
150 boolean area = false;
151 ElemStyle wayStyle = MapPaintStyles.getStyle(w);
152
153 if(!isZoomOk(wayStyle)) {
154 return;
155 }
156
157 if(wayStyle!=null)
158 {
159 if(wayStyle instanceof LineElemStyle)
160 {
161 colour = ((LineElemStyle)wayStyle).colour;
162 width = ((LineElemStyle)wayStyle).width;
163 realWidth = ((LineElemStyle)wayStyle).realWidth;
164 dashed = ((LineElemStyle)wayStyle).dashed;
165 }
166 else if (wayStyle instanceof AreaElemStyle)
167 {
168 colour = ((AreaElemStyle)wayStyle).getColour();
169 area = true;
170 }
171 }
172
173 if (area && fillAreas)
174 drawWayAsArea(w, colour);
175 int orderNumber = 0;
176
177 Node lastN = null;
178 for (Node n : w.nodes) {
179 if (lastN == null) {
180 lastN = n;
181 continue;
182 }
183 orderNumber++;
184 // drawSegment(lastN, n, w.selected && !inactive ? selectedColor : wayColor, showDirectionArrow);
185
186 if (area && fillAreas)
187 //Draw segments in a different colour so direction arrows show against the fill
188 drawSeg(lastN, n, w.selected ? selectedColor : untaggedColor, showDirection, width, true);
189 else
190 if (area)
191 drawSeg(lastN, n, w.selected ? selectedColor : colour, showDirection, width, true);
192 else
193 if (realWidth > 0 && useRealWidth && !showDirection){
194 int tmpWidth = (int) (100 / (float) (circum / realWidth));
195 if (tmpWidth > width) width = tmpWidth;
196 }
197
198 drawSeg(lastN, n, w.selected ? selectedColor : colour, showDirection, width, dashed);
199
200 if (showOrderNumber)
201 drawOrderNumber(lastN, n, orderNumber);
202
203 lastN = n;
204 }
205 }
206
207 public void visit(Relation e) {
208 // relations are not (yet?) drawn.
209 }
210
211 // This assumes that all segments are aligned in the same direction!
212 protected void drawWayAsArea(Way w, Color colour)
213 {
214 Polygon polygon = new Polygon();
215 Point p;
216 // set the opacity (alpha) level of the filled polygon
217 Color coloura = new Color( colour.getRed(), colour.getGreen(), colour.getBlue(), fillAlpha);
218
219 for (Node n : w.nodes)
220 {
221 p = nc.getPoint(n.eastNorth);
222 polygon.addPoint(p.x,p.y);
223 }
224
225 g.setColor( w.selected ?
226 selectedColor : coloura);
227
228 g.fillPolygon(polygon);
229 }
230
231 // NEW
232 protected void drawNode(Node n, ImageIcon icon, boolean annotate) {
233 Point p = nc.getPoint(n.eastNorth);
234 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;
235 int w = icon.getIconWidth(), h=icon.getIconHeight();
236 icon.paintIcon ( Main.map.mapView, g, p.x-w/2, p.y-h/2 );
237 String name = (n.keys==null) ? null : n.keys.get("name");
238 if (name!=null && annotate)
239 {
240 g.setColor(textColor);
241 Font defaultFont = g.getFont();
242 g.setFont (orderFont);
243 g.drawString (name, p.x+w/2+2, p.y+h/2+2);
244 g.setFont(defaultFont);
245 }
246 if (n.selected)
247 {
248 g.setColor ( selectedColor );
249 g.drawRect (p.x-w/2-2,p.y-w/2-2, w+4, h+4);
250 }
251 }
252
253 /**
254 * Draw a line with the given color.
255 */
256 protected void drawSegment(Node n1, Node n2, Color col, boolean showDirection) {
257 if (useRealWidth && showDirection) showDirection = false;
258 drawSeg(n1, n2, col, showDirection, 1, false);
259 }
260
261 private void drawSeg(Node n1, Node n2, Color col, boolean showDirection, int width, boolean dashed) {
262 if (col != currentColor || width != currentWidth || dashed != currentDashed) {
263 displaySegments(col, width, dashed);
264 }
265 Point p1 = nc.getPoint(n1.eastNorth);
266 Point p2 = nc.getPoint(n2.eastNorth);
267
268 // checking if this segment is visible
269 if ((p1.x < 0) && (p2.x < 0)) return ;
270 if ((p1.y < 0) && (p2.y < 0)) return ;
271 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return ;
272 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return ;
273 //if (ls.selected)
274 // col = selectedColor;
275 //g.setColor(col);
276 //g.setWidth(width);
277 //if (dashed)
278 // g2d.setStroke(new BasicStroke(width,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0,new float[] {9},0));
279 //else
280 // g2d.setStroke(new BasicStroke(width,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
281
282 //g.drawLine(p1.x, p1.y, p2.x, p2.y);
283 currentPath.moveTo(p1.x, p1.y);
284 currentPath.lineTo(p2.x, p2.y);
285
286 if (showDirection) {
287 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI;
288 //g.drawLine(p2.x,p2.y, (int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
289 //g.drawLine(p2.x,p2.y, (int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
290 currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI)));
291 currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI)));
292 currentPath.lineTo(p2.x, p2.y);
293 }
294 //g2d.setStroke(new BasicStroke(1));
295
296 }
297
298 protected void displaySegments() {
299 displaySegments(null, 0, false);
300 }
301
302 protected void displaySegments(Color newColor, int newWidth, boolean newDash) {
303
304 if (currentPath != null) {
305 Graphics2D g2d = (Graphics2D)g;
306 g2d.setColor(inactive ? inactiveColor : currentColor);
307 if (currentStroke == null) {
308 if (currentDashed)
309 g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0,new float[] {9},0));
310 else
311 g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
312 }
313 g2d.draw(currentPath);
314 g2d.setStroke(new BasicStroke(1));
315
316 currentPath = new GeneralPath();
317 currentColor = newColor;
318 currentWidth = newWidth;
319 currentDashed = newDash;
320 currentStroke = null;
321 }
322 }
323
324 /**
325 * Draw the node as small rectangle with the given color.
326 *
327 * @param n The node to draw.
328 * @param color The color of the node.
329 */
330 public void drawNode(Node n, Color color) {
331 if(isZoomOk(null)) {
332 Point p = nc.getPoint(n.eastNorth);
333 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;
334 g.setColor(color);
335 g.drawRect(p.x-1, p.y-1, 2, 2);
336 }
337 }
338
339 // NW 111106 Overridden from SimplePaintVisitor in josm-1.4-nw1
340 // Shows areas before non-areas
341 public void visitAll(DataSet data) {
342 inactiveColor = Preferences.getPreferencesColor("inactive", Color.DARK_GRAY);
343 selectedColor = Preferences.getPreferencesColor("selected", Color.YELLOW);
344 nodeColor = Preferences.getPreferencesColor("node", Color.RED);
345 dfltWayColor = Preferences.getPreferencesColor("way", darkblue);
346 incompleteColor = Preferences.getPreferencesColor("incomplete way", darkerblue);
347 backgroundColor = Preferences.getPreferencesColor("background", Color.BLACK);
348 untaggedColor = Preferences.getPreferencesColor("untagged",Color.GRAY);
349 textColor = Preferences.getPreferencesColor ("text", Color.WHITE);
350 showDirectionArrow = Main.pref.getBoolean("draw.segment.direction");
351 showRelevantDirectionsOnly = Main.pref.getBoolean("draw.segment.relevant_directions_only");
352 showOrderNumber = Main.pref.getBoolean("draw.segment.order_number");
353 useRealWidth = Main.pref.getBoolean("mappaint.useRealWidth",false);
354 zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay",false);
355 fillAreas = Main.pref.getBoolean("mappaint.fillareas", true);
356
357 /* XXX - there must be a better way to get a bounded Integer pref! */
358 try {
359 fillAlpha = Integer.valueOf(Main.pref.get("mappaint.fillalpha", "50"));
360 if (fillAlpha < 0) {
361 fillAlpha = 0;
362 }
363 if (fillAlpha > 255) {
364 fillAlpha = 255;
365 }
366 } catch (NumberFormatException nfe) {
367 fillAlpha = 50;
368 }
369
370 Collection<Way> noAreaWays = new LinkedList<Way>();
371
372 for (final OsmPrimitive osm : data.ways)
373 if (!osm.incomplete && !osm.deleted && MapPaintStyles.isArea(osm))
374 osm.visit(this);
375 else if (!osm.deleted && !osm.incomplete)
376 noAreaWays.add((Way)osm);
377
378 for (final OsmPrimitive osm : noAreaWays)
379 osm.visit(this);
380
381 for (final OsmPrimitive osm : data.nodes)
382 if (!osm.incomplete && !osm.deleted)
383 osm.visit(this);
384
385 for (final OsmPrimitive osm : data.getSelected())
386 if (!osm.incomplete && !osm.deleted){
387 osm.visit(this);
388 }
389 displaySegments();
390 }
391
392 /**
393 * Draw an number of the order of the two consecutive nodes within the
394 * parents way
395 */
396 protected void drawOrderNumber(Node n1, Node n2, int orderNumber) {
397 int strlen = (""+orderNumber).length();
398 Point p1 = nc.getPoint(n1.eastNorth);
399 Point p2 = nc.getPoint(n2.eastNorth);
400 int x = (p1.x+p2.x)/2 - 4*strlen;
401 int y = (p1.y+p2.y)/2 + 4;
402
403 Rectangle screen = g.getClipBounds();
404 if (screen.contains(x,y)) {
405 Color c = g.getColor();
406 g.setColor(backgroundColor);
407 g.fillRect(x-1, y-12, 8*strlen+1, 14);
408 g.setColor(c);
409 g.drawString(""+orderNumber, x, y);
410 }
411 }
412
413 public void setGraphics(Graphics g) {
414 this.g = g;
415 }
416
417 public void setNavigatableComponent(NavigatableComponent nc) {
418 this.nc = nc;
419 }
420}
Note: See TracBrowser for help on using the repository browser.