source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/AbstractMapRenderer.java@ 11992

Last change on this file since 11992 was 11992, checked in by Don-vip, 7 years ago

fix #13665 - handle deleted nodes in way rendering

  • Property svn:eol-style set to native
File size: 9.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4import java.awt.Color;
5import java.awt.Graphics2D;
6import java.awt.geom.GeneralPath;
7import java.awt.geom.Path2D;
8import java.awt.geom.Rectangle2D;
9import java.util.Iterator;
10
11import org.openstreetmap.josm.Main;
12import org.openstreetmap.josm.data.osm.BBox;
13import org.openstreetmap.josm.data.osm.DataSet;
14import org.openstreetmap.josm.data.osm.Node;
15import org.openstreetmap.josm.data.osm.Way;
16import org.openstreetmap.josm.data.osm.WaySegment;
17import org.openstreetmap.josm.gui.MapViewState;
18import org.openstreetmap.josm.gui.MapViewState.MapViewPoint;
19import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
20import org.openstreetmap.josm.gui.NavigatableComponent;
21import org.openstreetmap.josm.tools.CheckParameterUtil;
22
23/**
24 * <p>Abstract common superclass for {@link Rendering} implementations.</p>
25 * @since 4087
26 */
27public abstract class AbstractMapRenderer implements Rendering {
28
29 /** the graphics context to which the visitor renders OSM objects */
30 protected final Graphics2D g;
31 /** the map viewport - provides projection and hit detection functionality */
32 protected final NavigatableComponent nc;
33
34 /**
35 * The {@link MapViewState} to use to convert between coordinates.
36 */
37 protected final MapViewState mapState;
38
39 /** if true, the paint visitor shall render OSM objects such that they
40 * look inactive. Example: rendering of data in an inactive layer using light gray as color only. */
41 protected boolean isInactiveMode;
42 /** Color Preference for background */
43 protected Color backgroundColor;
44 /** Color Preference for inactive objects */
45 protected Color inactiveColor;
46 /** Color Preference for selected objects */
47 protected Color selectedColor;
48 /** Color Preference for members of selected relations */
49 protected Color relationSelectedColor;
50 /** Color Preference for nodes */
51 protected Color nodeColor;
52
53 /** Color Preference for hightlighted objects */
54 protected Color highlightColor;
55 /** Preference: size of virtual nodes (0 displayes display) */
56 protected int virtualNodeSize;
57 /** Preference: minimum space (displayed way length) to display virtual nodes */
58 protected int virtualNodeSpace;
59
60 /** Preference: minimum space (displayed way length) to display segment numbers */
61 protected int segmentNumberSpace;
62
63 /**
64 * <p>Creates an abstract paint visitor</p>
65 *
66 * @param g the graphics context. Must not be null.
67 * @param nc the map viewport. Must not be null.
68 * @param isInactiveMode if true, the paint visitor shall render OSM objects such that they
69 * look inactive. Example: rendering of data in an inactive layer using light gray as color only.
70 * @throws IllegalArgumentException if {@code g} is null
71 * @throws IllegalArgumentException if {@code nc} is null
72 */
73 public AbstractMapRenderer(Graphics2D g, NavigatableComponent nc, boolean isInactiveMode) {
74 CheckParameterUtil.ensureParameterNotNull(g);
75 CheckParameterUtil.ensureParameterNotNull(nc);
76 this.g = g;
77 this.nc = nc;
78 this.mapState = nc.getState();
79 this.isInactiveMode = isInactiveMode;
80 }
81
82 /**
83 * Draw the node as small square with the given color.
84 *
85 * @param n The node to draw.
86 * @param color The color of the node.
87 * @param size size in pixels
88 * @param fill determines if the square mmust be filled
89 */
90 public abstract void drawNode(Node n, Color color, int size, boolean fill);
91
92 /**
93 * Draw an number of the order of the two consecutive nodes within the
94 * parents way
95 *
96 * @param p1 First point of the way segment.
97 * @param p2 Second point of the way segment.
98 * @param orderNumber The number of the segment in the way.
99 * @param clr The color to use for drawing the text.
100 * @since 10827
101 */
102 protected void drawOrderNumber(MapViewPoint p1, MapViewPoint p2, int orderNumber, Color clr) {
103 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {
104 String on = Integer.toString(orderNumber);
105 int strlen = on.length();
106 double centerX = (p1.getInViewX()+p2.getInViewX())/2;
107 double centerY = (p1.getInViewY()+p2.getInViewY())/2;
108 double x = centerX - 4*strlen;
109 double y = centerY + 4;
110
111 if (virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace)) {
112 y = centerY - virtualNodeSize - 3;
113 }
114
115 g.setColor(backgroundColor);
116 g.fill(new Rectangle2D.Double(x-1, y-12, 8*strlen+1, 14));
117 g.setColor(clr);
118 g.drawString(on, (int) x, (int) y);
119 }
120 }
121
122 /**
123 * Draws virtual nodes.
124 *
125 * @param data The data set being rendered.
126 * @param bbox The bounding box being displayed.
127 */
128 public void drawVirtualNodes(DataSet data, BBox bbox) {
129 if (virtualNodeSize == 0 || data == null || bbox == null)
130 return;
131 // print normal virtual nodes
132 GeneralPath path = new GeneralPath();
133 for (Way osm : data.searchWays(bbox)) {
134 if (osm.isUsable() && !osm.isDisabledAndHidden() && !osm.isDisabled()) {
135 visitVirtual(path, osm);
136 }
137 }
138 g.setColor(nodeColor);
139 g.draw(path);
140 try {
141 // print highlighted virtual nodes. Since only the color changes, simply
142 // drawing them over the existing ones works fine (at least in their current simple style)
143 path = new GeneralPath();
144 for (WaySegment wseg: data.getHighlightedVirtualNodes()) {
145 if (wseg.way.isUsable() && !wseg.way.isDisabled()) {
146 visitVirtual(path, wseg.toWay());
147 }
148 }
149 g.setColor(highlightColor);
150 g.draw(path);
151 } catch (ArrayIndexOutOfBoundsException e) {
152 // Silently ignore any ArrayIndexOutOfBoundsException that may be raised
153 // if the way has changed while being rendered (fix #7979)
154 // TODO: proper solution ?
155 // Idea from bastiK:
156 // avoid the WaySegment class and add another data class with { Way way; Node firstNode, secondNode; int firstIdx; }.
157 // On read, it would first check, if the way still has firstIdx+2 nodes, then check if the corresponding way nodes are still
158 // the same and report changes in a more controlled manner.
159 Main.trace(e);
160 }
161 }
162
163 /**
164 * Reads the color definitions from preferences. This function is <code>public</code>, so that
165 * color names in preferences can be displayed even without calling the wireframe display before.
166 */
167 public void getColors() {
168 this.backgroundColor = PaintColors.BACKGROUND.get();
169 this.inactiveColor = PaintColors.INACTIVE.get();
170 this.selectedColor = PaintColors.SELECTED.get();
171 this.relationSelectedColor = PaintColors.RELATIONSELECTED.get();
172 this.nodeColor = PaintColors.NODE.get();
173 this.highlightColor = PaintColors.HIGHLIGHT.get();
174 }
175
176 /**
177 * Reads all the settings from preferences. Calls the @{link #getColors}
178 * function.
179 *
180 * @param virtual <code>true</code> if virtual nodes are used
181 */
182 protected void getSettings(boolean virtual) {
183 this.virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;
184 this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
185 this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
186 getColors();
187 }
188
189 /**
190 * Checks if a way segemnt is large enough for additional information display.
191 *
192 * @param p1 First point of the way segment.
193 * @param p2 Second point of the way segment.
194 * @param space The free space to check against.
195 * @return <code>true</code> if segment is larger than required space
196 * @since 10827
197 */
198 public static boolean isLargeSegment(MapViewPoint p1, MapViewPoint p2, int space) {
199 return p1.oneNormInView(p2) > space;
200 }
201
202 /**
203 * Checks if segment is visible in display.
204 *
205 * @param p1 First point of the way segment.
206 * @param p2 Second point of the way segment.
207 * @return <code>true</code> if segment may be visible.
208 * @since 10827
209 */
210 protected boolean isSegmentVisible(MapViewPoint p1, MapViewPoint p2) {
211 MapViewRectangle view = mapState.getViewArea();
212 // not outside in the same direction
213 return (p1.getOutsideRectangleFlags(view) & p2.getOutsideRectangleFlags(view)) == 0;
214 }
215
216 /**
217 * Creates path for drawing virtual nodes for one way.
218 *
219 * @param path The path to append drawing to.
220 * @param w The ways to draw node for.
221 * @since 10827
222 */
223 public void visitVirtual(Path2D path, Way w) {
224 Iterator<Node> it = w.getNodes().iterator();
225 MapViewPoint lastP = null;
226 while (it.hasNext()) {
227 Node n = it.next();
228 if (n.isLatLonKnown()) {
229 MapViewPoint p = mapState.getPointFor(n);
230 if (lastP != null && isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace)) {
231 double x = (p.getInViewX()+lastP.getInViewX())/2;
232 double y = (p.getInViewY()+lastP.getInViewY())/2;
233 path.moveTo(x-virtualNodeSize, y);
234 path.lineTo(x+virtualNodeSize, y);
235 path.moveTo(x, y-virtualNodeSize);
236 path.lineTo(x, y+virtualNodeSize);
237 }
238 lastP = p;
239 }
240 }
241 }
242}
Note: See TracBrowser for help on using the repository browser.