source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/WireframeMapRenderer.java@ 9067

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

sonar - Immutable Field

  • Property svn:eol-style set to native
File size: 17.2 KB
RevLine 
[8378]1// License: GPL. For details, see LICENSE file.
[2666]2package org.openstreetmap.josm.data.osm.visitor.paint;
[221]3
[759]4import java.awt.BasicStroke;
[221]5import java.awt.Color;
[315]6import java.awt.Graphics2D;
[221]7import java.awt.Point;
8import java.awt.Rectangle;
[768]9import java.awt.RenderingHints;
[759]10import java.awt.Stroke;
[315]11import java.awt.geom.GeneralPath;
[5652]12import java.util.ArrayList;
[768]13import java.util.Iterator;
[5652]14import java.util.List;
[221]15
16import org.openstreetmap.josm.Main;
[2450]17import org.openstreetmap.josm.data.Bounds;
[3126]18import org.openstreetmap.josm.data.osm.BBox;
[4087]19import org.openstreetmap.josm.data.osm.Changeset;
[221]20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
[768]23import org.openstreetmap.josm.data.osm.Relation;
[1862]24import org.openstreetmap.josm.data.osm.RelationMember;
[221]25import org.openstreetmap.josm.data.osm.Way;
[4327]26import org.openstreetmap.josm.data.osm.WaySegment;
[4087]27import org.openstreetmap.josm.data.osm.visitor.Visitor;
[221]28import org.openstreetmap.josm.gui.NavigatableComponent;
29
30/**
[4087]31 * A map renderer that paints a simple scheme of every primitive it visits to a
[221]32 * previous set graphic environment.
[7549]33 * @since 23
[221]34 */
[4087]35public class WireframeMapRenderer extends AbstractMapRenderer implements Visitor {
[805]36
[5421]37 /** Color Preference for ways not matching any other group */
[1169]38 protected Color dfltWayColor;
[5421]39 /** Color Preference for relations */
[1169]40 protected Color relationColor;
[5421]41 /** Color Preference for untagged ways */
[1169]42 protected Color untaggedWayColor;
[5421]43 /** Color Preference for tagged nodes */
[3165]44 protected Color taggedColor;
[5421]45 /** Color Preference for multiply connected nodes */
[3180]46 protected Color connectionColor;
[5421]47 /** Color Preference for tagged and multiply connected nodes */
[3180]48 protected Color taggedConnectionColor;
[5421]49 /** Preference: should directional arrows be displayed */
[1169]50 protected boolean showDirectionArrow;
[5421]51 /** Preference: should arrows for oneways be displayed */
[3994]52 protected boolean showOnewayArrow;
[5421]53 /** Preference: should only the last arrow of a way be displayed */
[1289]54 protected boolean showHeadArrowOnly;
[8325]55 /** Preference: should the segment numbers of ways be displayed */
[1169]56 protected boolean showOrderNumber;
[5421]57 /** Preference: should selected nodes be filled */
[1169]58 protected boolean fillSelectedNode;
[5421]59 /** Preference: should unselected nodes be filled */
[1169]60 protected boolean fillUnselectedNode;
[5421]61 /** Preference: should tagged nodes be filled */
[3164]62 protected boolean fillTaggedNode;
[5421]63 /** Preference: should multiply connected nodes be filled */
[3180]64 protected boolean fillConnectionNode;
[5421]65 /** Preference: size of selected nodes */
[3164]66 protected int selectedNodeSize;
[5421]67 /** Preference: size of unselected nodes */
[3164]68 protected int unselectedNodeSize;
[5421]69 /** Preference: size of multiply connected nodes */
[3180]70 protected int connectionNodeSize;
[5421]71 /** Preference: size of tagged nodes */
[3180]72 protected int taggedNodeSize;
[768]73
[5421]74 /** Color cache to draw subsequent segments of same color as one <code>Path</code>. */
[8840]75 protected Color currentColor;
[5421]76 /** Path store to draw subsequent segments of same color as one <code>Path</code>. */
[1169]77 protected GeneralPath currentPath = new GeneralPath();
[5421]78 /**
79 * <code>DataSet</code> passed to the @{link render} function to overcome the argument
80 * limitations of @{link Visitor} interface. Only valid until end of rendering call.
81 */
82 private DataSet ds;
[343]83
[5881]84 /** Helper variable for {@link #drawSegment} */
[5421]85 private static final double PHI = Math.toRadians(20);
[5881]86 /** Helper variable for {@link #drawSegment} */
[5421]87 private static final double cosPHI = Math.cos(PHI);
[5881]88 /** Helper variable for {@link #drawSegment} */
[5421]89 private static final double sinPHI = Math.sin(PHI);
90
[5652]91 /** Helper variable for {@link #visit(Relation)} */
[9067]92 private final Stroke relatedWayStroke = new BasicStroke(
[5421]93 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
94
[4087]95 /**
[5421]96 * Creates an wireframe render
[6069]97 *
[5421]98 * @param g the graphics context. Must not be null.
99 * @param nc the map viewport. Must not be null.
100 * @param isInactiveMode if true, the paint visitor shall render OSM objects such that they
101 * look inactive. Example: rendering of data in an inactive layer using light gray as color only.
[8291]102 * @throws IllegalArgumentException if {@code g} is null
103 * @throws IllegalArgumentException if {@code nc} is null
[4087]104 */
105 public WireframeMapRenderer(Graphics2D g, NavigatableComponent nc, boolean isInactiveMode) {
106 super(g, nc, isInactiveMode);
107 }
108
[5571]109 @Override
110 public void getColors() {
111 super.getColors();
[2666]112 dfltWayColor = PaintColors.DEFAULT_WAY.get();
113 relationColor = PaintColors.RELATION.get();
114 untaggedWayColor = PaintColors.UNTAGGED_WAY.get();
[4952]115 highlightColor = PaintColors.HIGHLIGHT_WIREFRAME.get();
[3165]116 taggedColor = PaintColors.TAGGED.get();
[3180]117 connectionColor = PaintColors.CONNECTION.get();
118
119 if (taggedColor != nodeColor) {
120 taggedConnectionColor = taggedColor;
121 } else {
122 taggedConnectionColor = connectionColor;
123 }
[1221]124 }
125
[5571]126 @Override
[2466]127 protected void getSettings(boolean virtual) {
[5571]128 super.getSettings(virtual);
[3164]129 MapPaintSettings settings = MapPaintSettings.INSTANCE;
130 showDirectionArrow = settings.isShowDirectionArrow();
[3994]131 showOnewayArrow = settings.isShowOnewayArrow();
[3164]132 showHeadArrowOnly = settings.isShowHeadArrowOnly();
133 showOrderNumber = settings.isShowOrderNumber();
134 selectedNodeSize = settings.getSelectedNodeSize();
135 unselectedNodeSize = settings.getUnselectedNodeSize();
[3180]136 connectionNodeSize = settings.getConnectionNodeSize();
137 taggedNodeSize = settings.getTaggedNodeSize();
[3164]138 fillSelectedNode = settings.isFillSelectedNode();
139 fillUnselectedNode = settings.isFillUnselectedNode();
[3180]140 fillConnectionNode = settings.isFillConnectionNode();
[3164]141 fillTaggedNode = settings.isFillTaggedNode();
[775]142
[2659]143 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
[3895]144 Main.pref.getBoolean("mappaint.wireframe.use-antialiasing", false) ?
[2025]145 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
[1169]146 }
[768]147
[5421]148 /**
149 * Renders the dataset for display.
150 *
151 * @param data <code>DataSet</code> to display
152 * @param virtual <code>true</code> if virtual nodes are used
153 * @param bounds display boundaries
154 */
[6084]155 @Override
[4087]156 public void render(DataSet data, boolean virtual, Bounds bounds) {
[6203]157 BBox bbox = bounds.toBBox();
[2264]158 this.ds = data;
[1169]159 getSettings(virtual);
[6069]160
[5652]161 for (final Relation rel : data.searchRelations(bbox)) {
162 if (rel.isDrawable() && !ds.isSelected(rel) && !rel.isDisabledAndHidden()) {
[6010]163 rel.accept(this);
[1251]164 }
[2381]165 }
[759]166
[5652]167 // draw tagged ways first, then untagged ways, then highlighted ways
[7005]168 List<Way> highlightedWays = new ArrayList<>();
169 List<Way> untaggedWays = new ArrayList<>();
[5652]170
[8510]171 for (final Way way : data.searchWays(bbox)) {
[5657]172 if (way.isDrawable() && !ds.isSelected(way) && !way.isDisabledAndHidden()) {
[5652]173 if (way.isHighlighted()) {
174 highlightedWays.add(way);
175 } else if (!way.isTagged()) {
176 untaggedWays.add(way);
177 } else {
[6010]178 way.accept(this);
[5652]179 }
[1251]180 }
[2381]181 }
[1169]182 displaySegments();
[6069]183
[5652]184 // Display highlighted ways after the other ones (fix #8276)
[7022]185 List<Way> specialWays = new ArrayList<>(untaggedWays);
186 specialWays.addAll(highlightedWays);
[8510]187 for (final Way way : specialWays) {
[7022]188 way.accept(this);
[2381]189 }
[7022]190 specialWays.clear();
191 displaySegments();
[6069]192
[3685]193 for (final OsmPrimitive osm : data.getSelected()) {
[5351]194 if (osm.isDrawable()) {
[6010]195 osm.accept(this);
[1251]196 }
[3685]197 }
[1169]198 displaySegments();
[857]199
[3126]200 for (final OsmPrimitive osm: data.searchNodes(bbox)) {
[8395]201 if (osm.isDrawable() && !ds.isSelected(osm) && !osm.isDisabledAndHidden()) {
[6010]202 osm.accept(this);
[1251]203 }
[2381]204 }
[5571]205 drawVirtualNodes(data, bbox);
[4327]206
207 // draw highlighted way segments over the already drawn ways. Otherwise each
208 // way would have to be checked if it contains a way segment to highlight when
209 // in most of the cases there won't be more than one segment. Since the wireframe
210 // renderer does not feature any transparency there should be no visual difference.
[5571]211 for (final WaySegment wseg : data.getHighlightedWaySegments()) {
[4327]212 drawSegment(nc.getPoint(wseg.getFirstNode()), nc.getPoint(wseg.getSecondNode()), highlightColor, false);
213 }
214 displaySegments();
[1169]215 }
[221]216
[5421]217 /**
218 * Helper function to calculate maximum of 4 values.
219 *
220 * @param a First value
221 * @param b Second value
222 * @param c Third value
223 * @param d Fourth value
224 */
[8512]225 private static int max(int a, int b, int c, int d) {
[3180]226 return Math.max(Math.max(a, b), Math.max(c, d));
227 }
228
[1169]229 /**
230 * Draw a small rectangle.
231 * White if selected (as always) or red otherwise.
232 *
233 * @param n The node to draw.
234 */
[5421]235 @Override
[1169]236 public void visit(Node n) {
[2578]237 if (n.isIncomplete()) return;
[357]238
[3164]239 if (n.isHighlighted()) {
[3180]240 drawNode(n, highlightColor, selectedNodeSize, fillSelectedNode);
[2025]241 } else {
[3180]242 Color color;
243
[4087]244 if (isInactiveMode || n.isDisabled()) {
[3180]245 color = inactiveColor;
[7549]246 } else if (n.isSelected()) {
[3180]247 color = selectedColor;
[7549]248 } else if (n.isMemberOfSelected()) {
249 color = relationSelectedColor;
[3180]250 } else if (n.isConnectionNode()) {
[5754]251 if (isNodeTagged(n)) {
[3180]252 color = taggedConnectionColor;
253 } else {
254 color = connectionColor;
255 }
256 } else {
[5754]257 if (isNodeTagged(n)) {
[3180]258 color = taggedColor;
259 } else {
260 color = nodeColor;
261 }
262 }
263
[8345]264 final int size = max(ds.isSelected(n) ? selectedNodeSize : 0,
265 isNodeTagged(n) ? taggedNodeSize : 0,
266 n.isConnectionNode() ? connectionNodeSize : 0,
[3594]267 unselectedNodeSize);
[3180]268
269 final boolean fill = (ds.isSelected(n) && fillSelectedNode) ||
[5754]270 (isNodeTagged(n) && fillTaggedNode) ||
[3594]271 (n.isConnectionNode() && fillConnectionNode) ||
272 fillUnselectedNode;
[3180]273
274 drawNode(n, color, size, fill);
[2025]275 }
[1169]276 }
[6069]277
[8870]278 private static boolean isNodeTagged(Node n) {
[5754]279 return n.isTagged() || n.isAnnotated();
280 }
[221]281
[5421]282 /**
283 * Draw a line for all way segments.
[1169]284 * @param w The way to draw.
285 */
[5421]286 @Override
[1169]287 public void visit(Way w) {
[2578]288 if (w.isIncomplete() || w.getNodesCount() < 2)
[1169]289 return;
[357]290
[1415]291 /* show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key
292 (even if the tag is negated as in oneway=false) or the way is selected */
[597]293
[3994]294 boolean showThisDirectionArrow = ds.isSelected(w) || showDirectionArrow;
[1415]295 /* head only takes over control if the option is true,
296 the direction should be shown at all and not only because it's selected */
[2264]297 boolean showOnlyHeadArrowOnly = showThisDirectionArrow && !ds.isSelected(w) && showHeadArrowOnly;
[1169]298 Color wayColor;
[805]299
[4087]300 if (isInactiveMode || w.isDisabled()) {
[1169]301 wayColor = inactiveColor;
[7549]302 } else if (w.isHighlighted()) {
[1409]303 wayColor = highlightColor;
[7549]304 } else if (w.isSelected()) {
[1409]305 wayColor = selectedColor;
[7549]306 } else if (w.isMemberOfSelected()) {
307 wayColor = relationSelectedColor;
[1499]308 } else if (!w.isTagged()) {
[1169]309 wayColor = untaggedWayColor;
310 } else {
311 wayColor = dfltWayColor;
312 }
[221]313
[1898]314 Iterator<Node> it = w.getNodes().iterator();
[1169]315 if (it.hasNext()) {
[1725]316 Point lastP = nc.getPoint(it.next());
[1169]317 for (int orderNumber = 1; it.hasNext(); orderNumber++) {
[1725]318 Point p = nc.getPoint(it.next());
[1409]319 drawSegment(lastP, p, wayColor,
[2025]320 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
[4871]321 if (showOrderNumber && !isInactiveMode) {
[5571]322 drawOrderNumber(lastP, p, orderNumber, g.getColor());
[2025]323 }
[1169]324 lastP = p;
325 }
326 }
327 }
[221]328
[5421]329 /**
330 * Draw objects used in relations.
331 * @param r The relation to draw.
332 */
333 @Override
[1169]334 public void visit(Relation r) {
[2578]335 if (r.isIncomplete()) return;
[759]336
[1169]337 Color col;
[4087]338 if (isInactiveMode || r.isDisabled()) {
[1169]339 col = inactiveColor;
[7555]340 } else if (r.isSelected()) {
[1169]341 col = selectedColor;
[7555]342 } else if (r.isMultipolygon() && r.isMemberOfSelected()) {
343 col = relationSelectedColor;
[1169]344 } else {
345 col = relationColor;
346 }
347 g.setColor(col);
[759]348
[1925]349 for (RelationMember m : r.getMembers()) {
[5351]350 if (m.getMember().isIncomplete() || !m.getMember().isDrawable()) {
[2025]351 continue;
352 }
[759]353
[1938]354 if (m.isNode()) {
355 Point p = nc.getPoint(m.getNode());
[1169]356 if (p.x < 0 || p.y < 0
[2025]357 || p.x > nc.getWidth() || p.y > nc.getHeight()) {
358 continue;
359 }
[759]360
[7549]361 g.drawOval(p.x-4, p.y-4, 9, 9);
[1938]362 } else if (m.isWay()) {
[1169]363 GeneralPath path = new GeneralPath();
[759]364
[1169]365 boolean first = true;
[1938]366 for (Node n : m.getWay().getNodes()) {
[5351]367 if (!n.isDrawable()) {
[2025]368 continue;
369 }
[1725]370 Point p = nc.getPoint(n);
[1169]371 if (first) {
372 path.moveTo(p.x, p.y);
373 first = false;
374 } else {
375 path.lineTo(p.x, p.y);
376 }
377 }
[759]378
[2659]379 g.draw(relatedWayStroke.createStrokedShape(path));
[1169]380 }
381 }
382 }
[805]383
[5421]384 /**
385 * Visitor for changesets not used in this class
386 * @param cs The changeset for inspection.
387 */
[4087]388 @Override
389 public void visit(Changeset cs) {/* ignore */}
390
[5571]391 @Override
[3180]392 public void drawNode(Node n, Color color, int size, boolean fill) {
[1169]393 if (size > 1) {
[3180]394 int radius = size / 2;
[1725]395 Point p = nc.getPoint(n);
[1169]396 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth())
397 || (p.y > nc.getHeight()))
398 return;
399 g.setColor(color);
400 if (fill) {
401 g.fillRect(p.x - radius, p.y - radius, size, size);
402 g.drawRect(p.x - radius, p.y - radius, size, size);
[2025]403 } else {
[1169]404 g.drawRect(p.x - radius, p.y - radius, size, size);
[2025]405 }
[1169]406 }
407 }
[221]408
[5421]409 /**
410 * Draw a line with the given color.
411 *
412 * @param path The path to append this segment.
413 * @param p1 First point of the way segment.
414 * @param p2 Second point of the way segment.
415 * @param showDirection <code>true</code> if segment direction should be indicated
416 */
[2663]417 protected void drawSegment(GeneralPath path, Point p1, Point p2, boolean showDirection) {
[3685]418 Rectangle bounds = g.getClipBounds();
419 bounds.grow(100, 100); // avoid arrow heads at the border
420 LineClip clip = new LineClip(p1, p2, bounds);
421 if (clip.execute()) {
[3243]422 p1 = clip.getP1();
423 p2 = clip.getP2();
[2663]424 path.moveTo(p1.x, p1.y);
425 path.lineTo(p2.x, p2.y);
426
427 if (showDirection) {
[3243]428 final double l = 10. / p1.distance(p2);
429
430 final double sx = l * (p1.x - p2.x);
431 final double sy = l * (p1.y - p2.y);
432
[8419]433 path.lineTo(p2.x + (int) Math.round(cosPHI * sx - sinPHI * sy), p2.y + (int) Math.round(sinPHI * sx + cosPHI * sy));
[8444]434 path.moveTo(p2.x + (int) Math.round(cosPHI * sx + sinPHI * sy), p2.y + (int) Math.round(-sinPHI * sx + cosPHI * sy));
[2663]435 path.lineTo(p2.x, p2.y);
436 }
437 }
438 }
439
[1169]440 /**
441 * Draw a line with the given color.
[5421]442 *
443 * @param p1 First point of the way segment.
444 * @param p2 Second point of the way segment.
445 * @param col The color to use for drawing line.
446 * @param showDirection <code>true</code> if segment direction should be indicated.
[1169]447 */
448 protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) {
[2025]449 if (col != currentColor) {
450 displaySegments(col);
451 }
[2663]452 drawSegment(currentPath, p1, p2, showDirection);
[1169]453 }
[805]454
[5421]455 /**
456 * Finally display all segments in currect path.
457 */
[1169]458 protected void displaySegments() {
459 displaySegments(null);
460 }
[5421]461
462 /**
463 * Finally display all segments in currect path.
464 *
465 * @param newColor This color is set after the path is drawn.
466 */
[1169]467 protected void displaySegments(Color newColor) {
468 if (currentPath != null) {
469 g.setColor(currentColor);
[2659]470 g.draw(currentPath);
[1169]471 currentPath = new GeneralPath();
472 currentColor = newColor;
473 }
474 }
[221]475}
Note: See TracBrowser for help on using the repository browser.