Ticket #7979: 7979.patch
| File 7979.patch, 132.9 KB (added by , 13 years ago) |
|---|
-
core/src/org/openstreetmap/josm/data/osm/visitor/paint/AbstractMapRenderer.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.osm.visitor.paint; 3 3 4 import java.awt.Color; 4 5 import java.awt.Graphics2D; 6 import java.awt.Point; 7 import java.awt.geom.GeneralPath; 8 import java.awt.geom.Point2D; 9 import java.util.Iterator; 5 10 11 import org.openstreetmap.josm.Main; 12 import org.openstreetmap.josm.data.osm.BBox; 13 import org.openstreetmap.josm.data.osm.DataSet; 14 import org.openstreetmap.josm.data.osm.Node; 15 import org.openstreetmap.josm.data.osm.Way; 16 import org.openstreetmap.josm.data.osm.WaySegment; 6 17 import org.openstreetmap.josm.gui.NavigatableComponent; 7 18 import org.openstreetmap.josm.tools.CheckParameterUtil; 8 19 … … 16 27 protected Graphics2D g; 17 28 /** the map viewport - provides projection and hit detection functionality */ 18 29 protected NavigatableComponent nc; 30 19 31 /** if true, the paint visitor shall render OSM objects such that they 20 * look inactive. Example: rendering of data in an inactive layer using light gray as color only. 21 */ 32 * look inactive. Example: rendering of data in an inactive layer using light gray as color only. */ 22 33 protected boolean isInactiveMode; 34 /** Color Preference for background */ 35 protected Color backgroundColor; 36 /** Color Preference for inactive objects */ 37 protected Color inactiveColor; 38 /** Color Preference for selected objects */ 39 protected Color selectedColor; 40 /** Color Preference for nodes */ 41 protected Color nodeColor; 42 43 /** Color Preference for hightlighted objects */ 44 protected Color highlightColor; 45 /** Preference: size of virtual nodes (0 displayes display) */ 46 protected int virtualNodeSize; 47 /** Preference: minimum space (displayed way length) to display virtual nodes */ 48 protected int virtualNodeSpace; 23 49 50 /** Preference: minimum space (displayed way length) to display segment numbers */ 51 protected int segmentNumberSpace; 52 24 53 /** 25 54 * <p>Creates an abstract paint visitor</p> 26 55 * … … 38 67 this.nc = nc; 39 68 this.isInactiveMode = isInactiveMode; 40 69 } 70 71 /** 72 * Draw the node as small rectangle with the given color. 73 * 74 * @param n The node to draw. 75 * @param color The color of the node. 76 */ 77 public abstract void drawNode(Node n, Color color, int size, boolean fill); 78 79 /** 80 * Draw an number of the order of the two consecutive nodes within the 81 * parents way 82 * 83 * @param p1 First point of the way segment. 84 * @param p2 Second point of the way segment. 85 * @param orderNumber The number of the segment in the way. 86 */ 87 protected void drawOrderNumber(Point p1, Point p2, int orderNumber, Color clr) { 88 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) { 89 String on = Integer.toString(orderNumber); 90 int strlen = on.length(); 91 int x = (p1.x+p2.x)/2 - 4*strlen; 92 int y = (p1.y+p2.y)/2 + 4; 93 94 if (virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace)) { 95 y = (p1.y+p2.y)/2 - virtualNodeSize - 3; 96 } 97 98 g.setColor(backgroundColor); 99 g.fillRect(x-1, y-12, 8*strlen+1, 14); 100 g.setColor(clr); 101 g.drawString(on, x, y); 102 } 103 } 104 105 /** 106 * Draws virtual nodes. 107 * 108 * @param data The data set being rendered. 109 * @param bbox The bounding box being displayed. 110 */ 111 public void drawVirtualNodes(DataSet data, BBox bbox) { 112 if (virtualNodeSize == 0 || data == null || bbox == null) 113 return; 114 // print normal virtual nodes 115 GeneralPath path = new GeneralPath(); 116 for (Way osm : data.searchWays(bbox)) { 117 if (osm.isUsable() && !osm.isDisabledAndHidden() && !osm.isDisabled()) { 118 visitVirtual(path, osm); 119 } 120 } 121 g.setColor(nodeColor); 122 g.draw(path); 123 try { 124 // print highlighted virtual nodes. Since only the color changes, simply 125 // drawing them over the existing ones works fine (at least in their current 126 // simple style) 127 path = new GeneralPath(); 128 for (WaySegment wseg: data.getHighlightedVirtualNodes()) { 129 if (wseg.way.isUsable() && !wseg.way.isDisabled()) { 130 visitVirtual(path, wseg.toWay()); 131 } 132 } 133 g.setColor(highlightColor); 134 g.draw(path); 135 } catch (ArrayIndexOutOfBoundsException e) { 136 // Silently ignore any ArrayIndexOutOfBoundsException that may be raised 137 // if the way has changed while being rendered (fix #7979) 138 } 139 } 140 141 /** 142 * Reads the color definitions from preferences. This function is <code>public</code>, so that 143 * color names in preferences can be displayed even without calling the wireframe display before. 144 */ 145 public void getColors() { 146 this.backgroundColor = PaintColors.BACKGROUND.get(); 147 this.inactiveColor = PaintColors.INACTIVE.get(); 148 this.selectedColor = PaintColors.SELECTED.get(); 149 this.nodeColor = PaintColors.NODE.get(); 150 this.highlightColor = PaintColors.HIGHLIGHT.get(); 151 } 152 153 /** 154 * Reads all the settings from preferences. Calls the @{link #getColors} 155 * function. 156 * 157 * @param virtual <code>true</code> if virtual nodes are used 158 */ 159 protected void getSettings(boolean virtual) { 160 this.virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0; 161 this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70); 162 this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40); 163 getColors(); 164 } 165 166 /** 167 * Checks if a way segemnt is large enough for additional information display. 168 * 169 * @param p1 First point of the way segment. 170 * @param p2 Second point of the way segment. 171 * @param space The free space to check against. 172 * @return <code>true</code> if segment is larger than required space 173 */ 174 public static boolean isLargeSegment(Point2D p1, Point2D p2, int space) { 175 double xd = Math.abs(p1.getX()-p2.getX()); 176 double yd = Math.abs(p1.getY()-p2.getY()); 177 return (xd+yd > space); 178 } 179 180 /** 181 * Checks if segment is visible in display. 182 * 183 * @param p1 First point of the way segment. 184 * @param p2 Second point of the way segment. 185 * @return <code>true</code> if segment is visible. 186 */ 187 protected boolean isSegmentVisible(Point p1, Point p2) { 188 if ((p1.x < 0) && (p2.x < 0)) return false; 189 if ((p1.y < 0) && (p2.y < 0)) return false; 190 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false; 191 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false; 192 return true; 193 } 194 195 /** 196 * Creates path for drawing virtual nodes for one way. 197 * 198 * @param path The path to append drawing to. 199 * @param w The ways to draw node for. 200 */ 201 public void visitVirtual(GeneralPath path, Way w) { 202 Iterator<Node> it = w.getNodes().iterator(); 203 if (it.hasNext()) { 204 Point lastP = nc.getPoint(it.next()); 205 while (it.hasNext()) 206 { 207 Point p = nc.getPoint(it.next()); 208 if (isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace)) 209 { 210 int x = (p.x+lastP.x)/2; 211 int y = (p.y+lastP.y)/2; 212 path.moveTo(x-virtualNodeSize, y); 213 path.lineTo(x+virtualNodeSize, y); 214 path.moveTo(x, y-virtualNodeSize); 215 path.lineTo(x, y+virtualNodeSize); 216 } 217 lastP = p; 218 } 219 } 220 } 41 221 } -
core/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java
1 // License: GPL. For details, see LICENSE file.2 package org.openstreetmap.josm.data.osm.visitor.paint;3 4 import java.awt.AlphaComposite;5 import java.awt.BasicStroke;6 import java.awt.Color;7 import java.awt.Font;8 import java.awt.FontMetrics;9 import java.awt.Graphics2D;10 import java.awt.Image;11 import java.awt.Point;12 import java.awt.Polygon;13 import java.awt.Rectangle;14 import java.awt.Shape;15 import java.awt.TexturePaint;16 import java.awt.font.FontRenderContext;17 import java.awt.font.GlyphVector;18 import java.awt.font.LineMetrics;19 import java.awt.geom.AffineTransform;20 import java.awt.geom.GeneralPath;21 import java.awt.geom.Path2D;22 import java.awt.geom.Point2D;23 import java.awt.geom.Rectangle2D;24 import java.util.Arrays;25 import java.util.Collection;26 import java.util.Iterator;27 import java.util.List;28 29 import javax.swing.ImageIcon;30 31 import org.openstreetmap.josm.Main;32 import org.openstreetmap.josm.data.coor.EastNorth;33 import org.openstreetmap.josm.data.osm.Node;34 import org.openstreetmap.josm.data.osm.OsmPrimitive;35 import org.openstreetmap.josm.data.osm.OsmUtils;36 import org.openstreetmap.josm.data.osm.Relation;37 import org.openstreetmap.josm.data.osm.RelationMember;38 import org.openstreetmap.josm.data.osm.Way;39 import org.openstreetmap.josm.data.osm.WaySegment;40 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;41 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;42 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;43 import org.openstreetmap.josm.gui.NavigatableComponent;44 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle;45 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.HorizontalTextAlignment;46 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.VerticalTextAlignment;47 import org.openstreetmap.josm.gui.mappaint.MapImage;48 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;49 import org.openstreetmap.josm.gui.mappaint.TextElement;50 import org.openstreetmap.josm.tools.ImageProvider;51 import org.openstreetmap.josm.tools.Pair;52 import org.openstreetmap.josm.tools.Utils;53 54 public class MapPainter {55 56 private final Graphics2D g;57 private final NavigatableComponent nc;58 private final boolean inactive;59 private final MapPaintSettings settings;60 private final Collection<WaySegment> highlightWaySegments;61 62 private final boolean useStrokes;63 private final boolean showNames;64 private final boolean showIcons;65 66 private final boolean isOutlineOnly;67 68 private final Color inactiveColor;69 private final Color selectedColor;70 private final Color relationSelectedColor;71 private final Color nodeColor;72 private final Color highlightColor;73 private final Color highlightColorTransparent;74 private final Color backgroundColor;75 76 private final Font orderFont;77 private final int virtualNodeSize;78 private final int virtualNodeSpace;79 private final int segmentNumberSpace;80 81 private final double circum;82 83 private final boolean leftHandTraffic;84 85 private static final double PHI = Math.toRadians(20);86 private static final double cosPHI = Math.cos(PHI);87 private static final double sinPHI = Math.sin(PHI);88 89 public MapPainter(MapPaintSettings settings, Graphics2D g,90 boolean inactive, NavigatableComponent nc, boolean virtual,91 double circum, boolean leftHandTraffic,92 Collection<WaySegment> highlightWaySegments){93 this.settings = settings;94 this.g = g;95 this.inactive = inactive;96 this.nc = nc;97 this.highlightWaySegments = highlightWaySegments;98 this.useStrokes = settings.getUseStrokesDistance() > circum;99 this.showNames = settings.getShowNamesDistance() > circum;100 this.showIcons = settings.getShowIconsDistance() > circum;101 102 this.isOutlineOnly = settings.isOutlineOnly();103 104 this.inactiveColor = PaintColors.INACTIVE.get();105 this.selectedColor = PaintColors.SELECTED.get();106 this.relationSelectedColor = PaintColors.RELATIONSELECTED.get();107 this.nodeColor = PaintColors.NODE.get();108 this.highlightColor = PaintColors.HIGHLIGHT.get();109 this.highlightColorTransparent = new Color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 100);110 this.backgroundColor = PaintColors.getBackgroundColor();111 112 this.orderFont = new Font(Main.pref.get("mappaint.font", "Helvetica"), Font.PLAIN, Main.pref.getInteger("mappaint.fontsize", 8));113 this.virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;114 this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);115 this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);116 117 this.circum = circum;118 this.leftHandTraffic = leftHandTraffic;119 }120 121 /**122 * draw way123 * @param showOrientation show arrows that indicate the technical orientation of124 * the way (defined by order of nodes)125 * @param showOneway show symbols that indicate the direction of the feature,126 * e.g. oneway street or waterway127 * @param onewayReversed for oneway=-1 and similar128 */129 public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor, float offset,130 boolean showOrientation, boolean showHeadArrowOnly,131 boolean showOneway, boolean onewayReversed) {132 133 GeneralPath path = new GeneralPath();134 GeneralPath orientationArrows = showOrientation ? new GeneralPath() : null;135 GeneralPath onewayArrows = showOneway ? new GeneralPath() : null;136 GeneralPath onewayArrowsCasing = showOneway ? new GeneralPath() : null;137 Rectangle bounds = g.getClipBounds();138 bounds.grow(100, 100); // avoid arrow heads at the border139 140 double wayLength = 0;141 Point lastPoint = null;142 boolean initialMoveToNeeded = true;143 List<Node> wayNodes = way.getNodes();144 if (wayNodes.size() < 2) return;145 146 // only highlight the segment if the way itself is not highlighted147 if(!way.isHighlighted()) {148 GeneralPath highlightSegs = null;149 for(WaySegment ws : highlightWaySegments) {150 if(ws.way != way || ws.lowerIndex < offset) {151 continue;152 }153 if(highlightSegs == null) {154 highlightSegs = new GeneralPath();155 }156 157 Point p1 = nc.getPoint(ws.getFirstNode());158 Point p2 = nc.getPoint(ws.getSecondNode());159 highlightSegs.moveTo(p1.x, p1.y);160 highlightSegs.lineTo(p2.x, p2.y);161 }162 163 drawPathHighlight(highlightSegs, line);164 }165 166 167 Iterator<Point> it = new OffsetIterator(wayNodes, offset);168 while (it.hasNext()) {169 Point p = it.next();170 if (lastPoint != null) {171 Point p1 = lastPoint;172 Point p2 = p;173 174 /**175 * Do custom clipping to work around openjdk bug. It leads to176 * drawing artefacts when zooming in a lot. (#4289, #4424)177 * (Looks like int overflow.)178 */179 LineClip clip = new LineClip(p1, p2, bounds);180 if (clip.execute()) {181 if (!p1.equals(clip.getP1())) {182 p1 = clip.getP1();183 path.moveTo(p1.x, p1.y);184 } else if (initialMoveToNeeded) {185 initialMoveToNeeded = false;186 path.moveTo(p1.x, p1.y);187 }188 p2 = clip.getP2();189 path.lineTo(p2.x, p2.y);190 191 /* draw arrow */192 if (showHeadArrowOnly ? !it.hasNext() : showOrientation) {193 final double segmentLength = p1.distance(p2);194 if (segmentLength != 0.0) {195 final double l = (10. + line.getLineWidth()) / segmentLength;196 197 final double sx = l * (p1.x - p2.x);198 final double sy = l * (p1.y - p2.y);199 200 orientationArrows.moveTo (p2.x + cosPHI * sx - sinPHI * sy, p2.y + sinPHI * sx + cosPHI * sy);201 orientationArrows.lineTo(p2.x, p2.y);202 orientationArrows.lineTo (p2.x + cosPHI * sx + sinPHI * sy, p2.y - sinPHI * sx + cosPHI * sy);203 }204 }205 if (showOneway) {206 final double segmentLength = p1.distance(p2);207 if (segmentLength != 0.0) {208 final double nx = (p2.x - p1.x) / segmentLength;209 final double ny = (p2.y - p1.y) / segmentLength;210 211 final double interval = 60;212 // distance from p1213 double dist = interval - (wayLength % interval);214 215 while (dist < segmentLength) {216 for (Pair<Float, GeneralPath> sizeAndPath : Arrays.asList(new Pair[] {217 new Pair<Float, GeneralPath>(3f, onewayArrowsCasing),218 new Pair<Float, GeneralPath>(2f, onewayArrows)})) {219 220 // scale such that border is 1 px221 final double fac = - (onewayReversed ? -1 : 1) * sizeAndPath.a * (1 + sinPHI) / (sinPHI * cosPHI);222 final double sx = nx * fac;223 final double sy = ny * fac;224 225 // Attach the triangle at the incenter and not at the tip.226 // Makes the border even at all sides.227 final double x = p1.x + nx * (dist + (onewayReversed ? -1 : 1) * (sizeAndPath.a / sinPHI));228 final double y = p1.y + ny * (dist + (onewayReversed ? -1 : 1) * (sizeAndPath.a / sinPHI));229 230 sizeAndPath.b.moveTo(x, y);231 sizeAndPath.b.lineTo (x + cosPHI * sx - sinPHI * sy, y + sinPHI * sx + cosPHI * sy);232 sizeAndPath.b.lineTo (x + cosPHI * sx + sinPHI * sy, y - sinPHI * sx + cosPHI * sy);233 sizeAndPath.b.lineTo(x, y);234 }235 dist += interval;236 }237 }238 wayLength += segmentLength;239 }240 }241 }242 lastPoint = p;243 }244 if(way.isHighlighted()) {245 drawPathHighlight(path, line);246 }247 displaySegments(path, orientationArrows, onewayArrows, onewayArrowsCasing, color, line, dashes, dashedColor);248 }249 250 /**251 * Iterates over a list of Way Nodes and returns screen coordinates that252 * represent a line that is shifted by a certain offset perpendicular253 * to the way direction.254 *255 * There is no intention, to handle consecutive duplicate Nodes in a256 * perfect way, but it is should not throw an exception.257 */258 public class OffsetIterator implements Iterator<Point> {259 260 private List<Node> nodes;261 private float offset;262 private int idx;263 264 private Point prev = null;265 /* 'prev0' is a point that has distance 'offset' from 'prev' and the266 * line from 'prev' to 'prev0' is perpendicular to the way segment from267 * 'prev' to the next point.268 */269 private int x_prev0, y_prev0;270 271 public OffsetIterator(List<Node> nodes, float offset) {272 this.nodes = nodes;273 this.offset = offset;274 idx = 0;275 }276 277 @Override278 public boolean hasNext() {279 return idx < nodes.size();280 }281 282 @Override283 public Point next() {284 if (Math.abs(offset) < 0.1f) return nc.getPoint(nodes.get(idx++));285 286 Point current = nc.getPoint(nodes.get(idx));287 288 if (idx == nodes.size() - 1) {289 ++idx;290 return new Point(x_prev0 + current.x - prev.x, y_prev0 + current.y - prev.y);291 }292 293 Point next = nc.getPoint(nodes.get(idx+1));294 295 int dx_next = next.x - current.x;296 int dy_next = next.y - current.y;297 double len_next = Math.sqrt(dx_next*dx_next + dy_next*dy_next);298 299 if (len_next == 0) {300 len_next = 1; // value does not matter, because dy_next and dx_next is 0301 }302 303 int x_current0 = current.x + (int) Math.round(offset * dy_next / len_next);304 int y_current0 = current.y - (int) Math.round(offset * dx_next / len_next);305 306 if (idx==0) {307 ++idx;308 prev = current;309 x_prev0 = x_current0;310 y_prev0 = y_current0;311 return new Point(x_current0, y_current0);312 } else {313 int dx_prev = current.x - prev.x;314 int dy_prev = current.y - prev.y;315 316 // determine intersection of the lines parallel to the two317 // segments318 int det = dx_next*dy_prev - dx_prev*dy_next;319 320 if (det == 0) {321 ++idx;322 prev = current;323 x_prev0 = x_current0;324 y_prev0 = y_current0;325 return new Point(x_current0, y_current0);326 }327 328 int m = dx_next*(y_current0 - y_prev0) - dy_next*(x_current0 - x_prev0);329 330 int cx_ = x_prev0 + Math.round((float)m * dx_prev / det);331 int cy_ = y_prev0 + Math.round((float)m * dy_prev / det);332 ++idx;333 prev = current;334 x_prev0 = x_current0;335 y_prev0 = y_current0;336 return new Point(cx_, cy_);337 }338 }339 340 @Override341 public void remove() {342 throw new UnsupportedOperationException();343 }344 }345 346 private void displaySegments(GeneralPath path, GeneralPath orientationArrows, GeneralPath onewayArrows, GeneralPath onewayArrowsCasing,347 Color color, BasicStroke line, BasicStroke dashes, Color dashedColor) {348 g.setColor(inactive ? inactiveColor : color);349 if (useStrokes) {350 g.setStroke(line);351 }352 g.draw(path);353 354 if(!inactive && useStrokes && dashes != null) {355 g.setColor(dashedColor);356 g.setStroke(dashes);357 g.draw(path);358 }359 360 if (orientationArrows != null) {361 g.setColor(inactive ? inactiveColor : color);362 g.setStroke(new BasicStroke(line.getLineWidth(), line.getEndCap(), BasicStroke.JOIN_MITER, line.getMiterLimit()));363 g.draw(orientationArrows);364 }365 366 if (onewayArrows != null) {367 g.setStroke(new BasicStroke(1, line.getEndCap(), BasicStroke.JOIN_MITER, line.getMiterLimit()));368 g.fill(onewayArrowsCasing);369 g.setColor(inactive ? inactiveColor : backgroundColor);370 g.fill(onewayArrows);371 }372 373 if(useStrokes) {374 g.setStroke(new BasicStroke());375 }376 }377 378 /**379 * highlights a given GeneralPath using the settings from BasicStroke to match the line's380 * style. Width of the highlight is hard coded.381 * @param path382 * @param line383 */384 private void drawPathHighlight(GeneralPath path, BasicStroke line) {385 if(path == null)386 return;387 g.setColor(highlightColorTransparent);388 float w = (line.getLineWidth() + 4);389 while(w >= line.getLineWidth()) {390 g.setStroke(new BasicStroke(w, line.getEndCap(), line.getLineJoin(), line.getMiterLimit()));391 g.draw(path);392 w -= 4;393 }394 }395 396 private boolean isSegmentVisible(Point p1, Point p2) {397 if ((p1.x < 0) && (p2.x < 0)) return false;398 if ((p1.y < 0) && (p2.y < 0)) return false;399 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;400 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;401 return true;402 }403 404 private static Boolean IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG = null;405 406 /**407 * Check, if this System has the GlyphVector double translation bug.408 *409 * With this bug, <code>gv.setGlyphTransform(i, trfm)</code> has a different410 * effect than on most other systems, namely the translation components411 * ("m02" & "m12", {@link AffineTransform}) appear to be twice as large, as412 * they actually are. The rotation is unaffected (scale & shear not tested413 * so far).414 *415 * This bug has only been observed on Mac OS X, see #7841.416 *417 * @return true, if the GlyphVector double translation bug is present on418 * this System419 */420 public static boolean isGlyphVectorDoubleTranslationBug() {421 if (IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG != null)422 return IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG;423 FontRenderContext frc = new FontRenderContext(null, false, false);424 Font font = new Font("Dialog", Font.PLAIN, 12);425 GlyphVector gv = font.createGlyphVector(frc, "x");426 gv.setGlyphTransform(0, AffineTransform.getTranslateInstance(1000, 1000));427 Shape shape = gv.getGlyphOutline(0);428 // x is about 1000 on normal stystems and about 2000 when the bug occurs429 int x = shape.getBounds().x;430 IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG = x > 1500;431 return IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG;432 }433 434 public void drawTextOnPath(Way way, TextElement text) {435 if (way == null || text == null)436 return;437 String name = text.getString(way);438 if (name == null || name.isEmpty())439 return;440 441 Polygon poly = new Polygon();442 Point lastPoint = null;443 Iterator<Node> it = way.getNodes().iterator();444 double pathLength = 0;445 long dx, dy;446 while (it.hasNext()) {447 Node n = it.next();448 Point p = nc.getPoint(n);449 poly.addPoint(p.x, p.y);450 451 if(lastPoint != null) {452 dx = p.x - lastPoint.x;453 dy = p.y - lastPoint.y;454 pathLength += Math.sqrt(dx*dx + dy*dy);455 }456 lastPoint = p;457 }458 459 FontMetrics fontMetrics = g.getFontMetrics(text.font); // if slow, use cache460 Rectangle2D rec = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font)461 462 if (rec.getWidth() > pathLength)463 return;464 465 double t1 = (pathLength/2 - rec.getWidth()/2) / pathLength;466 double t2 = (pathLength/2 + rec.getWidth()/2) / pathLength;467 468 double[] p1 = pointAt(t1, poly, pathLength);469 double[] p2 = pointAt(t2, poly, pathLength);470 471 if (p1 == null || p2 == null)472 return;473 474 double angleOffset;475 double offsetSign;476 double tStart;477 478 if (p1[0] < p2[0] &&479 p1[2] < Math.PI/2 &&480 p1[2] > -Math.PI/2) {481 angleOffset = 0;482 offsetSign = 1;483 tStart = t1;484 } else {485 angleOffset = Math.PI;486 offsetSign = -1;487 tStart = t2;488 }489 490 FontRenderContext frc = g.getFontRenderContext();491 GlyphVector gv = text.font.createGlyphVector(frc, name);492 493 for (int i=0; i<gv.getNumGlyphs(); ++i) {494 Rectangle2D rect = gv.getGlyphLogicalBounds(i).getBounds2D();495 double t = tStart + offsetSign * (rect.getX() + rect.getWidth()/2) / pathLength;496 double[] p = pointAt(t, poly, pathLength);497 if (p != null) {498 AffineTransform trfm = AffineTransform.getTranslateInstance(p[0] - rect.getX(), p[1]);499 trfm.rotate(p[2]+angleOffset);500 double off = -rect.getY() - rect.getHeight()/2 + text.yOffset;501 trfm.translate(-rect.getWidth()/2, off);502 if (isGlyphVectorDoubleTranslationBug()) {503 // scale the translation components by one half504 AffineTransform tmp = AffineTransform.getTranslateInstance(-0.5 * trfm.getTranslateX(), -0.5 * trfm.getTranslateY());505 tmp.concatenate(trfm);506 trfm = tmp;507 }508 gv.setGlyphTransform(i, trfm);509 }510 }511 if (text.haloRadius != null) {512 Shape textOutline = gv.getOutline();513 g.setStroke(new BasicStroke(2*text.haloRadius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));514 g.setColor(text.haloColor);515 g.draw(textOutline);516 g.setStroke(new BasicStroke());517 g.setColor(text.color);518 g.fill(textOutline);519 } else {520 g.setColor(text.color);521 g.drawGlyphVector(gv, 0, 0);522 }523 }524 525 private double[] pointAt(double t, Polygon poly, double pathLength) {526 double totalLen = t * pathLength;527 double curLen = 0;528 long dx, dy;529 double segLen;530 531 // Yes, it is inefficient to iterate from the beginning for each glyph.532 // Can be optimized if it turns out to be slow.533 for (int i = 1; i < poly.npoints; ++i) {534 dx = poly.xpoints[i] - poly.xpoints[i-1];535 dy = poly.ypoints[i] - poly.ypoints[i-1];536 segLen = Math.sqrt(dx*dx + dy*dy);537 if (totalLen > curLen + segLen) {538 curLen += segLen;539 continue;540 }541 return new double[] {542 poly.xpoints[i-1]+(totalLen - curLen)/segLen*dx,543 poly.ypoints[i-1]+(totalLen - curLen)/segLen*dy,544 Math.atan2(dy, dx)};545 }546 return null;547 }548 549 public void drawLinePattern(Way way, Image pattern) {550 final int width = pattern.getWidth(null);551 final int height = pattern.getHeight(null);552 553 Point lastP = null;554 double wayLength = 0;555 556 Iterator<Node> it = way.getNodes().iterator();557 while (it.hasNext()) {558 Node n = it.next();559 Point thisP = nc.getPoint(n);560 561 if (lastP != null) {562 final double segmentLength = thisP.distance(lastP);563 564 final double dx = thisP.x - lastP.x;565 final double dy = thisP.y - lastP.y;566 567 double dist = wayLength == 0 ? 0 : width - (wayLength % width);568 569 AffineTransform saveTransform = g.getTransform();570 g.translate(lastP.x, lastP.y);571 g.rotate(Math.atan2(dy, dx));572 573 if (dist > 0) {574 g.drawImage(pattern, 0, 0, (int) dist, height,575 width - (int) dist, 0, width, height, null);576 }577 while (dist < segmentLength) {578 if (dist + width > segmentLength) {579 g.drawImage(pattern, (int) dist, 0, (int) segmentLength, height,580 0, 0, (int) segmentLength - (int) dist, height, null);581 } else {582 g.drawImage(pattern, (int) dist, 0, nc);583 }584 dist += width;585 }586 g.setTransform(saveTransform);587 588 wayLength += segmentLength;589 }590 lastP = thisP;591 }592 }593 594 public void drawNodeIcon(Node n, Image img, float alpha, boolean selected, boolean member) {595 Point p = nc.getPoint(n);596 597 final int w = img.getWidth(null), h=img.getHeight(null);598 if(n.isHighlighted()) {599 drawPointHighlight(p, Math.max(w, h));600 }601 602 if (alpha != 1f) {603 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));604 }605 g.drawImage(img, p.x-w/2, p.y-h/2, nc);606 g.setPaintMode();607 if (selected || member)608 {609 Color color = null;610 if (inactive || n.isDisabled()) {611 color = inactiveColor;612 } else if (selected) {613 color = selectedColor;614 } else {615 color = relationSelectedColor;616 }617 g.setColor(color);618 g.drawRect(p.x-w/2-2, p.y-h/2-2, w+4, h+4);619 }620 }621 622 private Polygon buildPolygon(Point center, int radius, int sides, double rotation) {623 Polygon polygon = new Polygon();624 for (int i = 0; i < sides; i++) {625 double angle = ((2 * Math.PI / sides) * i) - rotation;626 int x = (int) Math.round(center.x + radius * Math.cos(angle));627 int y = (int) Math.round(center.y + radius * Math.sin(angle));628 polygon.addPoint(x, y);629 }630 return polygon;631 }632 633 private Polygon buildPolygon(Point center, int radius, int sides) {634 return buildPolygon(center, radius, sides, 0.0);635 }636 637 public void drawNodeSymbol(Node n, Symbol s, Color fillColor, Color strokeColor) {638 Point p = nc.getPoint(n);639 int radius = s.size / 2;640 641 if(n.isHighlighted()) {642 drawPointHighlight(p, s.size);643 }644 645 if (fillColor != null) {646 g.setColor(fillColor);647 switch (s.symbol) {648 case SQUARE:649 g.fillRect(p.x - radius, p.y - radius, s.size, s.size);650 break;651 case CIRCLE:652 g.fillOval(p.x - radius, p.y - radius, s.size, s.size);653 break;654 case TRIANGLE:655 g.fillPolygon(buildPolygon(p, radius, 3, Math.PI / 2));656 break;657 case PENTAGON:658 g.fillPolygon(buildPolygon(p, radius, 5, Math.PI / 2));659 break;660 case HEXAGON:661 g.fillPolygon(buildPolygon(p, radius, 6));662 break;663 case HEPTAGON:664 g.fillPolygon(buildPolygon(p, radius, 7, Math.PI / 2));665 break;666 case OCTAGON:667 g.fillPolygon(buildPolygon(p, radius, 8, Math.PI / 8));668 break;669 case NONAGON:670 g.fillPolygon(buildPolygon(p, radius, 9, Math.PI / 2));671 break;672 case DECAGON:673 g.fillPolygon(buildPolygon(p, radius, 10));674 break;675 default:676 throw new AssertionError();677 }678 }679 if (s.stroke != null) {680 g.setStroke(s.stroke);681 g.setColor(strokeColor);682 switch (s.symbol) {683 case SQUARE:684 g.drawRect(p.x - radius, p.y - radius, s.size - 1, s.size - 1);685 break;686 case CIRCLE:687 g.drawOval(p.x - radius, p.y - radius, s.size - 1, s.size - 1);688 break;689 case TRIANGLE:690 g.drawPolygon(buildPolygon(p, radius, 3, Math.PI / 2));691 break;692 case PENTAGON:693 g.drawPolygon(buildPolygon(p, radius, 5, Math.PI / 2));694 break;695 case HEXAGON:696 g.drawPolygon(buildPolygon(p, radius, 6));697 break;698 case HEPTAGON:699 g.drawPolygon(buildPolygon(p, radius, 7, Math.PI / 2));700 break;701 case OCTAGON:702 g.drawPolygon(buildPolygon(p, radius, 8, Math.PI / 8));703 break;704 case NONAGON:705 g.drawPolygon(buildPolygon(p, radius, 9, Math.PI / 2));706 break;707 case DECAGON:708 g.drawPolygon(buildPolygon(p, radius, 10));709 break;710 default:711 throw new AssertionError();712 }713 g.setStroke(new BasicStroke());714 }715 }716 717 /**718 * Draw the node as small rectangle with the given color.719 *720 * @param n The node to draw.721 * @param color The color of the node.722 */723 public void drawNode(Node n, Color color, int size, boolean fill) {724 if(size <= 0 && !n.isHighlighted())725 return;726 727 Point p = nc.getPoint(n);728 729 if(n.isHighlighted()) {730 drawPointHighlight(p, size);731 }732 733 if (size > 1) {734 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return;735 int radius = size / 2;736 737 if (inactive || n.isDisabled()) {738 g.setColor(inactiveColor);739 } else {740 g.setColor(color);741 }742 if (fill) {743 g.fillRect(p.x-radius-1, p.y-radius-1, size + 1, size + 1);744 } else {745 g.drawRect(p.x-radius-1, p.y-radius-1, size, size);746 }747 }748 }749 750 /**751 * highlights a given point by drawing a rounded rectangle around it. Give the752 * size of the object you want to be highlighted, width is added automatically.753 */754 private void drawPointHighlight(Point p, int size) {755 g.setColor(highlightColorTransparent);756 int s = size + 7;757 while(s >= size) {758 int r = (int) Math.floor(s/2);759 g.fillRoundRect(p.x-r, p.y-r, s, s, r, r);760 s -= 4;761 }762 }763 764 public void drawBoxText(Node n, BoxTextElemStyle bs) {765 if (!isShowNames() || bs == null)766 return;767 768 Point p = nc.getPoint(n);769 TextElement text = bs.text;770 String s = text.labelCompositionStrategy.compose(n);771 if (s == null) return;772 773 Font defaultFont = g.getFont();774 g.setFont(text.font);775 776 int x = p.x + text.xOffset;777 int y = p.y + text.yOffset;778 /**779 *780 * left-above __center-above___ right-above781 * left-top| |right-top782 * | |783 * left-center| center-center |right-center784 * | |785 * left-bottom|_________________|right-bottom786 * left-below center-below right-below787 *788 */789 Rectangle box = bs.getBox();790 if (bs.hAlign == HorizontalTextAlignment.RIGHT) {791 x += box.x + box.width + 2;792 } else {793 FontRenderContext frc = g.getFontRenderContext();794 Rectangle2D bounds = text.font.getStringBounds(s, frc);795 int textWidth = (int) bounds.getWidth();796 if (bs.hAlign == HorizontalTextAlignment.CENTER) {797 x -= textWidth / 2;798 } else if (bs.hAlign == HorizontalTextAlignment.LEFT) {799 x -= - box.x + 4 + textWidth;800 } else throw new AssertionError();801 }802 803 if (bs.vAlign == VerticalTextAlignment.BOTTOM) {804 y += box.y + box.height;805 } else {806 FontRenderContext frc = g.getFontRenderContext();807 LineMetrics metrics = text.font.getLineMetrics(s, frc);808 if (bs.vAlign == VerticalTextAlignment.ABOVE) {809 y -= - box.y + metrics.getDescent();810 } else if (bs.vAlign == VerticalTextAlignment.TOP) {811 y -= - box.y - metrics.getAscent();812 } else if (bs.vAlign == VerticalTextAlignment.CENTER) {813 y += (metrics.getAscent() - metrics.getDescent()) / 2;814 } else if (bs.vAlign == VerticalTextAlignment.BELOW) {815 y += box.y + box.height + metrics.getAscent() + 2;816 } else throw new AssertionError();817 }818 if (inactive || n.isDisabled()) {819 g.setColor(inactiveColor);820 } else {821 g.setColor(text.color);822 }823 if (text.haloRadius != null) {824 g.setStroke(new BasicStroke(2*text.haloRadius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));825 g.setColor(text.haloColor);826 FontRenderContext frc = g.getFontRenderContext();827 GlyphVector gv = text.font.createGlyphVector(frc, s);828 Shape textOutline = gv.getOutline(x, y);829 g.draw(textOutline);830 g.setStroke(new BasicStroke());831 g.setColor(text.color);832 g.fill(textOutline);833 } else {834 g.drawString(s, x, y);835 }836 g.setFont(defaultFont);837 }838 839 private Path2D.Double getPath(Way w) {840 Path2D.Double path = new Path2D.Double();841 boolean initial = true;842 for (Node n : w.getNodes())843 {844 Point2D p = n.getEastNorth();845 if (p != null) {846 if (initial) {847 path.moveTo(p.getX(), p.getY());848 initial = false;849 } else {850 path.lineTo(p.getX(), p.getY());851 }852 }853 }854 return path;855 }856 857 public void drawArea(Way w, Color color, MapImage fillImage, TextElement text) {858 drawArea(w, getPath(w), color, fillImage, text);859 }860 861 protected void drawArea(OsmPrimitive osm, Path2D.Double path, Color color, MapImage fillImage, TextElement text) {862 863 Shape area = path.createTransformedShape(nc.getAffineTransform());864 865 if (!isOutlineOnly) {866 if (fillImage == null) {867 if (inactive) {868 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.33f));869 }870 g.setColor(color);871 g.fill(area);872 } else {873 TexturePaint texture = new TexturePaint(fillImage.getImage(),874 // new Rectangle(polygon.xpoints[0], polygon.ypoints[0], fillImage.getWidth(), fillImage.getHeight()));875 new Rectangle(0, 0, fillImage.getWidth(), fillImage.getHeight()));876 g.setPaint(texture);877 Float alpha = Utils.color_int2float(fillImage.alpha);878 if (alpha != 1f) {879 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));880 }881 g.fill(area);882 g.setPaintMode();883 }884 }885 886 if (text != null && isShowNames()) {887 /*888 * abort if we can't compose the label to be rendered889 */890 if (text.labelCompositionStrategy == null) return;891 String name = text.labelCompositionStrategy.compose(osm);892 if (name == null) return;893 894 Rectangle pb = area.getBounds();895 FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache896 Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font)897 898 // Point2D c = getCentroid(polygon);899 // Using the Centroid is Nicer for buildings like: +--------+900 // but this needs to be fast. As most houses are | 42 |901 // boxes anyway, the center of the bounding box +---++---+902 // will have to do. ++903 // Centroids are not optimal either, just imagine a U-shaped house.904 // Point2D c = new Point2D.Double(pb.x + pb.width / 2.0, pb.y + pb.height / 2.0);905 // Rectangle2D.Double centeredNBounds =906 // new Rectangle2D.Double(c.getX() - nb.getWidth()/2,907 // c.getY() - nb.getHeight()/2,908 // nb.getWidth(),909 // nb.getHeight());910 911 Rectangle centeredNBounds = new Rectangle(pb.x + (int)((pb.width - nb.getWidth())/2.0),912 pb.y + (int)((pb.height - nb.getHeight())/2.0),913 (int)nb.getWidth(),914 (int)nb.getHeight());915 916 if ((pb.width >= nb.getWidth() && pb.height >= nb.getHeight()) && // quick check917 area.contains(centeredNBounds) // slow but nice918 ) {919 if (inactive || osm.isDisabled()) {920 g.setColor(inactiveColor);921 } else {922 g.setColor(text.color);923 }924 Font defaultFont = g.getFont();925 g.setFont (text.font);926 g.drawString (name,927 (int)(centeredNBounds.getMinX() - nb.getMinX()),928 (int)(centeredNBounds.getMinY() - nb.getMinY()));929 g.setFont(defaultFont);930 }931 }932 }933 934 public void drawArea(Relation r, Color color, MapImage fillImage, TextElement text) {935 Multipolygon multipolygon = MultipolygonCache.getInstance().get(nc, r);936 if (!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {937 for (PolyData pd : multipolygon.getCombinedPolygons()) {938 Path2D.Double p = pd.get();939 if (!isAreaVisible(p)) {940 continue;941 }942 drawArea(r, p,943 pd.selected ? settings.getRelationSelectedColor(color.getAlpha()) : color,944 fillImage, text);945 }946 }947 }948 949 private boolean isAreaVisible(Path2D.Double area) {950 Rectangle2D bounds = area.getBounds2D();951 if (bounds.isEmpty()) return false;952 Point2D p = nc.getPoint2D(new EastNorth(bounds.getX(), bounds.getY()));953 if (p.getX() > nc.getWidth()) return false;954 if (p.getY() < 0) return false;955 p = nc.getPoint2D(new EastNorth(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()));956 if (p.getX() < 0) return false;957 if (p.getY() > nc.getHeight()) return false;958 return true;959 }960 961 public void drawRestriction(Image img, Point pVia, double vx, double vx2, double vy, double vy2, double angle, boolean selected) {962 /* rotate image with direction last node in from to */963 Image rotatedImg = ImageProvider.createRotatedImage(null , img, angle);964 965 /* scale down image to 16*16 pixels */966 Image smallImg = new ImageIcon(rotatedImg.getScaledInstance(16 , 16, Image.SCALE_SMOOTH)).getImage();967 int w = smallImg.getWidth(null), h=smallImg.getHeight(null);968 g.drawImage(smallImg, (int)(pVia.x+vx+vx2)-w/2, (int)(pVia.y+vy+vy2)-h/2, nc);969 970 if (selected) {971 g.setColor(inactive ? inactiveColor : relationSelectedColor);972 g.drawRect((int)(pVia.x+vx+vx2)-w/2-2,(int)(pVia.y+vy+vy2)-h/2-2, w+4, h+4);973 }974 }975 976 public void drawRestriction(Relation r, MapImage icon) {977 Way fromWay = null;978 Way toWay = null;979 OsmPrimitive via = null;980 981 /* find the "from", "via" and "to" elements */982 for (RelationMember m : r.getMembers())983 {984 if(m.getMember().isIncomplete())985 return;986 else987 {988 if(m.isWay())989 {990 Way w = m.getWay();991 if(w.getNodesCount() < 2) {992 continue;993 }994 995 if("from".equals(m.getRole())) {996 if(fromWay == null) {997 fromWay = w;998 }999 } else if("to".equals(m.getRole())) {1000 if(toWay == null) {1001 toWay = w;1002 }1003 } else if("via".equals(m.getRole())) {1004 if(via == null) {1005 via = w;1006 }1007 }1008 }1009 else if(m.isNode())1010 {1011 Node n = m.getNode();1012 if("via".equals(m.getRole()) && via == null) {1013 via = n;1014 }1015 }1016 }1017 }1018 1019 if (fromWay == null || toWay == null || via == null)1020 return;1021 1022 Node viaNode;1023 if(via instanceof Node)1024 {1025 viaNode = (Node) via;1026 if(!fromWay.isFirstLastNode(viaNode))1027 return;1028 }1029 else1030 {1031 Way viaWay = (Way) via;1032 Node firstNode = viaWay.firstNode();1033 Node lastNode = viaWay.lastNode();1034 Boolean onewayvia = false;1035 1036 String onewayviastr = viaWay.get("oneway");1037 if(onewayviastr != null)1038 {1039 if("-1".equals(onewayviastr)) {1040 onewayvia = true;1041 Node tmp = firstNode;1042 firstNode = lastNode;1043 lastNode = tmp;1044 } else {1045 onewayvia = OsmUtils.getOsmBoolean(onewayviastr);1046 if (onewayvia == null) {1047 onewayvia = false;1048 }1049 }1050 }1051 1052 if(fromWay.isFirstLastNode(firstNode)) {1053 viaNode = firstNode;1054 } else if (!onewayvia && fromWay.isFirstLastNode(lastNode)) {1055 viaNode = lastNode;1056 } else1057 return;1058 }1059 1060 /* find the "direct" nodes before the via node */1061 Node fromNode = null;1062 if(fromWay.firstNode() == via) {1063 fromNode = fromWay.getNode(1);1064 } else {1065 fromNode = fromWay.getNode(fromWay.getNodesCount()-2);1066 }1067 1068 Point pFrom = nc.getPoint(fromNode);1069 Point pVia = nc.getPoint(viaNode);1070 1071 /* starting from via, go back the "from" way a few pixels1072 (calculate the vector vx/vy with the specified length and the direction1073 away from the "via" node along the first segment of the "from" way)1074 */1075 double distanceFromVia=14;1076 double dx = (pFrom.x >= pVia.x) ? (pFrom.x - pVia.x) : (pVia.x - pFrom.x);1077 double dy = (pFrom.y >= pVia.y) ? (pFrom.y - pVia.y) : (pVia.y - pFrom.y);1078 1079 double fromAngle;1080 if(dx == 0.0) {1081 fromAngle = Math.PI/2;1082 } else {1083 fromAngle = Math.atan(dy / dx);1084 }1085 double fromAngleDeg = Math.toDegrees(fromAngle);1086 1087 double vx = distanceFromVia * Math.cos(fromAngle);1088 double vy = distanceFromVia * Math.sin(fromAngle);1089 1090 if(pFrom.x < pVia.x) {1091 vx = -vx;1092 }1093 if(pFrom.y < pVia.y) {1094 vy = -vy;1095 }1096 1097 /* go a few pixels away from the way (in a right angle)1098 (calculate the vx2/vy2 vector with the specified length and the direction1099 90degrees away from the first segment of the "from" way)1100 */1101 double distanceFromWay=10;1102 double vx2 = 0;1103 double vy2 = 0;1104 double iconAngle = 0;1105 1106 if(pFrom.x >= pVia.x && pFrom.y >= pVia.y) {1107 if(!leftHandTraffic) {1108 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));1109 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));1110 } else {1111 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));1112 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));1113 }1114 iconAngle = 270+fromAngleDeg;1115 }1116 if(pFrom.x < pVia.x && pFrom.y >= pVia.y) {1117 if(!leftHandTraffic) {1118 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));1119 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));1120 } else {1121 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));1122 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));1123 }1124 iconAngle = 90-fromAngleDeg;1125 }1126 if(pFrom.x < pVia.x && pFrom.y < pVia.y) {1127 if(!leftHandTraffic) {1128 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90));1129 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90));1130 } else {1131 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90));1132 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90));1133 }1134 iconAngle = 90+fromAngleDeg;1135 }1136 if(pFrom.x >= pVia.x && pFrom.y < pVia.y) {1137 if(!leftHandTraffic) {1138 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180));1139 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180));1140 } else {1141 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg));1142 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg));1143 }1144 iconAngle = 270-fromAngleDeg;1145 }1146 1147 drawRestriction(inactive || r.isDisabled() ? icon.getDisabled() : icon.getImage(),1148 pVia, vx, vx2, vy, vy2, iconAngle, r.isSelected());1149 }1150 1151 public void drawVirtualNodes(Collection<Way> ways, Collection<WaySegment> highlightVirtualNodes) {1152 if (virtualNodeSize == 0)1153 return;1154 // print normal virtual nodes1155 GeneralPath path = new GeneralPath();1156 for (Way osm: ways){1157 if (osm.isUsable() && !osm.isDisabled()) {1158 visitVirtual(path, osm);1159 }1160 }1161 g.setColor(nodeColor);1162 g.draw(path);1163 // print highlighted virtual nodes. Since only the color changes, simply1164 // drawing them over the existing ones works fine (at least in their current1165 // simple style)1166 path = new GeneralPath();1167 for (WaySegment wseg: highlightVirtualNodes){1168 if (wseg.way.isUsable() && !wseg.way.isDisabled()) {1169 visitVirtual(path, wseg.toWay());1170 }1171 }1172 g.setColor(highlightColor);1173 g.draw(path);1174 }1175 1176 public void visitVirtual(GeneralPath path, Way w) {1177 Iterator<Node> it = w.getNodes().iterator();1178 if (it.hasNext()) {1179 Point lastP = nc.getPoint(it.next());1180 while(it.hasNext())1181 {1182 Point p = nc.getPoint(it.next());1183 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))1184 {1185 int x = (p.x+lastP.x)/2;1186 int y = (p.y+lastP.y)/2;1187 path.moveTo(x-virtualNodeSize, y);1188 path.lineTo(x+virtualNodeSize, y);1189 path.moveTo(x, y-virtualNodeSize);1190 path.lineTo(x, y+virtualNodeSize);1191 }1192 lastP = p;1193 }1194 }1195 }1196 1197 private static boolean isLargeSegment(Point p1, Point p2, int space) {1198 int xd = p1.x-p2.x; if(xd < 0) {1199 xd = -xd;1200 }1201 int yd = p1.y-p2.y; if(yd < 0) {1202 yd = -yd;1203 }1204 return (xd+yd > space);1205 }1206 1207 /**1208 * Draw a number of the order of the two consecutive nodes within the1209 * parents way1210 */1211 public void drawOrderNumber(Node n1, Node n2, int orderNumber, Color clr) {1212 Point p1 = nc.getPoint(n1);1213 Point p2 = nc.getPoint(n2);1214 drawOrderNumber(p1, p2, orderNumber, clr);1215 }1216 1217 /**1218 * Draw an number of the order of the two consecutive nodes within the1219 * parents way1220 */1221 protected void drawOrderNumber(Point p1, Point p2, int orderNumber, Color clr) {1222 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) {1223 String on = Integer.toString(orderNumber);1224 int strlen = on.length();1225 int x = (p1.x+p2.x)/2 - 4*strlen;1226 int y = (p1.y+p2.y)/2 + 4;1227 1228 if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace))1229 {1230 y = (p1.y+p2.y)/2 - virtualNodeSize - 3;1231 }1232 1233 g.setColor(backgroundColor);1234 g.fillRect(x-1, y-12, 8*strlen+1, 14);1235 g.setColor(clr);1236 g.drawString(on, x, y);1237 }1238 }1239 1240 public boolean isShowNames() {1241 return showNames;1242 }1243 1244 public double getCircum() {1245 return circum;1246 }1247 1248 public boolean isShowIcons() {1249 return showIcons;1250 }1251 1252 public boolean isInactiveMode() {1253 return inactive;1254 }1255 } -
core/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
1 1 // License: GPL. Copyright 2007 by Immanuel Scholz and others 2 2 package org.openstreetmap.josm.data.osm.visitor.paint; 3 3 4 import java.awt.AlphaComposite; 5 import java.awt.BasicStroke; 6 import java.awt.Color; 7 import java.awt.Font; 8 import java.awt.FontMetrics; 4 9 import java.awt.Graphics2D; 10 import java.awt.Image; 11 import java.awt.Point; 12 import java.awt.Polygon; 13 import java.awt.Rectangle; 5 14 import java.awt.RenderingHints; 15 import java.awt.Shape; 16 import java.awt.TexturePaint; 17 import java.awt.font.FontRenderContext; 18 import java.awt.font.GlyphVector; 19 import java.awt.font.LineMetrics; 20 import java.awt.geom.AffineTransform; 21 import java.awt.geom.GeneralPath; 22 import java.awt.geom.Path2D; 23 import java.awt.geom.Point2D; 24 import java.awt.geom.Rectangle2D; 6 25 import java.util.ArrayList; 26 import java.util.Arrays; 7 27 import java.util.Collection; 8 28 import java.util.Collections; 29 import java.util.Iterator; 9 30 import java.util.List; 10 31 32 import javax.swing.ImageIcon; 33 11 34 import org.openstreetmap.josm.Main; 12 35 import org.openstreetmap.josm.data.Bounds; 36 import org.openstreetmap.josm.data.coor.EastNorth; 13 37 import org.openstreetmap.josm.data.osm.BBox; 14 38 import org.openstreetmap.josm.data.osm.DataSet; 15 39 import org.openstreetmap.josm.data.osm.Node; 16 40 import org.openstreetmap.josm.data.osm.OsmPrimitive; 41 import org.openstreetmap.josm.data.osm.OsmUtils; 17 42 import org.openstreetmap.josm.data.osm.Relation; 43 import org.openstreetmap.josm.data.osm.RelationMember; 18 44 import org.openstreetmap.josm.data.osm.Way; 19 45 import org.openstreetmap.josm.data.osm.WaySegment; 46 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 47 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData; 48 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache; 20 49 import org.openstreetmap.josm.gui.NavigatableComponent; 21 50 import org.openstreetmap.josm.gui.mappaint.AreaElemStyle; 51 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle; 52 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.HorizontalTextAlignment; 53 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.VerticalTextAlignment; 22 54 import org.openstreetmap.josm.gui.mappaint.ElemStyle; 23 55 import org.openstreetmap.josm.gui.mappaint.ElemStyles; 56 import org.openstreetmap.josm.gui.mappaint.MapImage; 24 57 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 25 58 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle; 59 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol; 26 60 import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList; 61 import org.openstreetmap.josm.gui.mappaint.TextElement; 62 import org.openstreetmap.josm.tools.ImageProvider; 63 import org.openstreetmap.josm.tools.Pair; 64 import org.openstreetmap.josm.tools.Utils; 27 65 28 66 /** 29 67 * <p>A map renderer which renders a map according to style rules in a set of style sheets.</p> 30 68 * 31 69 */ 32 public class StyledMapRenderer extends AbstractMapRenderer {70 public class StyledMapRenderer extends AbstractMapRenderer { 33 71 34 private ElemStyles styles; 35 private double circum; 36 private MapPainter painter; 37 private MapPaintSettings paintSettings; 72 /** 73 * Iterates over a list of Way Nodes and returns screen coordinates that 74 * represent a line that is shifted by a certain offset perpendicular 75 * to the way direction. 76 * 77 * There is no intention, to handle consecutive duplicate Nodes in a 78 * perfect way, but it is should not throw an exception. 79 */ 80 private class OffsetIterator implements Iterator<Point> { 38 81 39 private static int FLAG_NORMAL = 0; 40 private static int FLAG_DISABLED = 1; 41 private static int FLAG_MEMBER_OF_SELECTED = 2; 42 private static int FLAG_SELECTED = 4; 82 private List<Node> nodes; 83 private float offset; 84 private int idx; 43 85 44 private static class StyleRecord implements Comparable<StyleRecord> { 45 final ElemStyle style; 46 final OsmPrimitive osm; 47 final int flags; 86 private Point prev = null; 87 /* 'prev0' is a point that has distance 'offset' from 'prev' and the 88 * line from 'prev' to 'prev0' is perpendicular to the way segment from 89 * 'prev' to the next point. 90 */ 91 private int x_prev0, y_prev0; 48 92 49 public StyleRecord(ElemStyle style, OsmPrimitive osm, int flags) {50 this. style = style;51 this.o sm = osm;52 this.flags = flags;93 public OffsetIterator(List<Node> nodes, float offset) { 94 this.nodes = nodes; 95 this.offset = offset; 96 idx = 0; 53 97 } 54 98 55 99 @Override 56 public int compareTo(StyleRecord other) { 57 if ((this.flags & FLAG_DISABLED) != 0 && (other.flags & FLAG_DISABLED) == 0) 58 return -1; 59 if ((this.flags & FLAG_DISABLED) == 0 && (other.flags & FLAG_DISABLED) != 0) 60 return 1; 100 public boolean hasNext() { 101 return idx < nodes.size(); 102 } 61 103 62 int d0 = Float.compare(this.style.major_z_index, other.style.major_z_index);63 if (d0 != 0)64 return d0;104 @Override 105 public Point next() { 106 if (Math.abs(offset) < 0.1f) return nc.getPoint(nodes.get(idx++)); 65 107 66 // selected on top of member of selected on top of unselected 67 // FLAG_DISABLED bit is the same at this point 68 if (this.flags > other.flags) 69 return 1; 70 if (this.flags < other.flags) 71 return -1; 108 Point current = nc.getPoint(nodes.get(idx)); 72 109 73 int dz = Float.compare(this.style.z_index, other.style.z_index); 74 if (dz != 0) 75 return dz; 110 if (idx == nodes.size() - 1) { 111 ++idx; 112 return new Point(x_prev0 + current.x - prev.x, y_prev0 + current.y - prev.y); 113 } 76 114 77 // simple node on top of icons and shapes 78 if (this.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE) 79 return 1; 80 if (this.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE) 81 return -1; 115 Point next = nc.getPoint(nodes.get(idx+1)); 82 116 83 // newer primitives to the front 84 long id = this.osm.getUniqueId() - other.osm.getUniqueId(); 85 if (id > 0) 86 return 1; 87 if (id < 0) 88 return -1; 117 int dx_next = next.x - current.x; 118 int dy_next = next.y - current.y; 119 double len_next = Math.sqrt(dx_next*dx_next + dy_next*dy_next); 89 120 90 return Float.compare(this.style.object_z_index, other.style.object_z_index); 121 if (len_next == 0) { 122 len_next = 1; // value does not matter, because dy_next and dx_next is 0 123 } 124 125 int x_current0 = current.x + (int) Math.round(offset * dy_next / len_next); 126 int y_current0 = current.y - (int) Math.round(offset * dx_next / len_next); 127 128 if (idx==0) { 129 ++idx; 130 prev = current; 131 x_prev0 = x_current0; 132 y_prev0 = y_current0; 133 return new Point(x_current0, y_current0); 134 } else { 135 int dx_prev = current.x - prev.x; 136 int dy_prev = current.y - prev.y; 137 138 // determine intersection of the lines parallel to the two 139 // segments 140 int det = dx_next*dy_prev - dx_prev*dy_next; 141 142 if (det == 0) { 143 ++idx; 144 prev = current; 145 x_prev0 = x_current0; 146 y_prev0 = y_current0; 147 return new Point(x_current0, y_current0); 148 } 149 150 int m = dx_next*(y_current0 - y_prev0) - dy_next*(x_current0 - x_prev0); 151 152 int cx_ = x_prev0 + Math.round((float)m * dx_prev / det); 153 int cy_ = y_prev0 + Math.round((float)m * dy_prev / det); 154 ++idx; 155 prev = current; 156 x_prev0 = x_current0; 157 y_prev0 = y_current0; 158 return new Point(cx_, cy_); 159 } 160 } 161 162 @Override 163 public void remove() { 164 throw new UnsupportedOperationException(); 91 165 } 92 166 } 93 167 … … 112 186 } 113 187 } 114 188 115 public void add( Wayosm, int flags) {189 public void add(Relation osm, int flags) { 116 190 StyleList sl = styles.get(osm, circum, nc); 117 191 for (ElemStyle s : sl) { 118 if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) { 119 continue; 192 if (drawMultipolygon && drawArea && s instanceof AreaElemStyle && (flags & FLAG_DISABLED) == 0) { 193 styleElems.add(new StyleRecord(s, osm, flags)); 194 } else if (drawRestriction && s instanceof NodeElemStyle) { 195 styleElems.add(new StyleRecord(s, osm, flags)); 120 196 } 121 styleElems.add(new StyleRecord(s, osm, flags));122 197 } 123 198 } 124 199 125 public void add( Relationosm, int flags) {200 public void add(Way osm, int flags) { 126 201 StyleList sl = styles.get(osm, circum, nc); 127 202 for (ElemStyle s : sl) { 128 if (drawMultipolygon && drawArea && s instanceof AreaElemStyle && (flags & FLAG_DISABLED) == 0) { 129 styleElems.add(new StyleRecord(s, osm, flags)); 130 } else if (drawRestriction && s instanceof NodeElemStyle) { 131 styleElems.add(new StyleRecord(s, osm, flags)); 203 if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) { 204 continue; 132 205 } 206 styleElems.add(new StyleRecord(s, osm, flags)); 133 207 } 134 208 } 135 209 … … 139 213 r.style.paintPrimitive( 140 214 r.osm, 141 215 paintSettings, 142 painter,216 StyledMapRenderer.this, 143 217 (r.flags & FLAG_SELECTED) != 0, 144 218 (r.flags & FLAG_MEMBER_OF_SELECTED) != 0 145 219 ); 146 220 } 147 221 } 148 222 } 223 224 private static class StyleRecord implements Comparable<StyleRecord> { 225 final ElemStyle style; 226 final OsmPrimitive osm; 227 final int flags; 228 229 public StyleRecord(ElemStyle style, OsmPrimitive osm, int flags) { 230 this.style = style; 231 this.osm = osm; 232 this.flags = flags; 233 } 234 235 @Override 236 public int compareTo(StyleRecord other) { 237 if ((this.flags & FLAG_DISABLED) != 0 && (other.flags & FLAG_DISABLED) == 0) 238 return -1; 239 if ((this.flags & FLAG_DISABLED) == 0 && (other.flags & FLAG_DISABLED) != 0) 240 return 1; 241 242 int d0 = Float.compare(this.style.major_z_index, other.style.major_z_index); 243 if (d0 != 0) 244 return d0; 245 246 // selected on top of member of selected on top of unselected 247 // FLAG_DISABLED bit is the same at this point 248 if (this.flags > other.flags) 249 return 1; 250 if (this.flags < other.flags) 251 return -1; 252 253 int dz = Float.compare(this.style.z_index, other.style.z_index); 254 if (dz != 0) 255 return dz; 256 257 // simple node on top of icons and shapes 258 if (this.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE) 259 return 1; 260 if (this.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE) 261 return -1; 262 263 // newer primitives to the front 264 long id = this.osm.getUniqueId() - other.osm.getUniqueId(); 265 if (id > 0) 266 return 1; 267 if (id < 0) 268 return -1; 269 270 return Float.compare(this.style.object_z_index, other.style.object_z_index); 271 } 272 } 273 274 private static Boolean IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG = null; 275 276 /** 277 * Check, if this System has the GlyphVector double translation bug. 278 * 279 * With this bug, <code>gv.setGlyphTransform(i, trfm)</code> has a different 280 * effect than on most other systems, namely the translation components 281 * ("m02" & "m12", {@link AffineTransform}) appear to be twice as large, as 282 * they actually are. The rotation is unaffected (scale & shear not tested 283 * so far). 284 * 285 * This bug has only been observed on Mac OS X, see #7841. 286 * 287 * @return true, if the GlyphVector double translation bug is present on 288 * this System 289 */ 290 public static boolean isGlyphVectorDoubleTranslationBug() { 291 if (IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG != null) 292 return IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG; 293 FontRenderContext frc = new FontRenderContext(null, false, false); 294 Font font = new Font("Dialog", Font.PLAIN, 12); 295 GlyphVector gv = font.createGlyphVector(frc, "x"); 296 gv.setGlyphTransform(0, AffineTransform.getTranslateInstance(1000, 1000)); 297 Shape shape = gv.getGlyphOutline(0); 298 // x is about 1000 on normal stystems and about 2000 when the bug occurs 299 int x = shape.getBounds().x; 300 IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG = x > 1500; 301 return IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG; 302 } 303 304 private ElemStyles styles; 305 private double circum; 306 307 private MapPaintSettings paintSettings; 308 309 private Color relationSelectedColor; 310 private Color highlightColorTransparent; 311 312 private static int FLAG_NORMAL = 0; 313 private static int FLAG_DISABLED = 1; 314 private static int FLAG_MEMBER_OF_SELECTED = 2; 315 private static int FLAG_SELECTED = 4; 316 317 private static final double PHI = Math.toRadians(20); 318 private static final double cosPHI = Math.cos(PHI); 319 private static final double sinPHI = Math.sin(PHI); 320 321 private Collection<WaySegment> highlightWaySegments; 322 323 private boolean useStrokes; 324 private boolean showNames; 325 private boolean showIcons; 326 private boolean isOutlineOnly; 327 328 private Font orderFont; 329 330 private boolean leftHandTraffic; 149 331 150 332 /** 151 333 * {@inheritDoc} … … 154 336 super(g, nc, isInactiveMode); 155 337 } 156 338 339 private Polygon buildPolygon(Point center, int radius, int sides) { 340 return buildPolygon(center, radius, sides, 0.0); 341 } 342 343 private Polygon buildPolygon(Point center, int radius, int sides, double rotation) { 344 Polygon polygon = new Polygon(); 345 for (int i = 0; i < sides; i++) { 346 double angle = ((2 * Math.PI / sides) * i) - rotation; 347 int x = (int) Math.round(center.x + radius * Math.cos(angle)); 348 int y = (int) Math.round(center.y + radius * Math.sin(angle)); 349 polygon.addPoint(x, y); 350 } 351 return polygon; 352 } 353 157 354 private void collectNodeStyles(DataSet data, StyleCollector sc, BBox bbox) { 158 355 for (final Node n: data.searchNodes(bbox)) { 159 356 if (n.isDrawable()) { … … 170 367 } 171 368 } 172 369 370 private void collectRelationStyles(DataSet data, StyleCollector sc, BBox bbox) { 371 for (Relation r: data.searchRelations(bbox)) { 372 if (r.isDrawable()) { 373 if (r.isDisabled()) { 374 sc.add(r, FLAG_DISABLED); 375 } else if (data.isSelected(r)) { 376 sc.add(r, FLAG_SELECTED); 377 } else { 378 sc.add(r, FLAG_NORMAL); 379 } 380 } 381 } 382 } 383 173 384 private void collectWayStyles(DataSet data, StyleCollector sc, BBox bbox) { 174 385 for (final Way w : data.searchWays(bbox)) { 175 386 if (w.isDrawable()) { … … 186 397 } 187 398 } 188 399 189 private void collectRelationStyles(DataSet data, StyleCollector sc, BBox bbox) { 190 for (Relation r: data.searchRelations(bbox)) { 191 if (r.isDrawable()) { 192 if (r.isDisabled()) { 193 sc.add(r, FLAG_DISABLED); 194 } else if (data.isSelected(r)) { 195 sc.add(r, FLAG_SELECTED); 400 private void displaySegments(GeneralPath path, GeneralPath orientationArrows, GeneralPath onewayArrows, GeneralPath onewayArrowsCasing, 401 Color color, BasicStroke line, BasicStroke dashes, Color dashedColor) { 402 g.setColor(isInactiveMode ? inactiveColor : color); 403 if (useStrokes) { 404 g.setStroke(line); 405 } 406 g.draw(path); 407 408 if(!isInactiveMode && useStrokes && dashes != null) { 409 g.setColor(dashedColor); 410 g.setStroke(dashes); 411 g.draw(path); 412 } 413 414 if (orientationArrows != null) { 415 g.setColor(isInactiveMode ? inactiveColor : color); 416 g.setStroke(new BasicStroke(line.getLineWidth(), line.getEndCap(), BasicStroke.JOIN_MITER, line.getMiterLimit())); 417 g.draw(orientationArrows); 418 } 419 420 if (onewayArrows != null) { 421 g.setStroke(new BasicStroke(1, line.getEndCap(), BasicStroke.JOIN_MITER, line.getMiterLimit())); 422 g.fill(onewayArrowsCasing); 423 g.setColor(isInactiveMode ? inactiveColor : backgroundColor); 424 g.fill(onewayArrows); 425 } 426 427 if (useStrokes) { 428 g.setStroke(new BasicStroke()); 429 } 430 } 431 432 protected void drawArea(OsmPrimitive osm, Path2D.Double path, Color color, MapImage fillImage, TextElement text) { 433 434 Shape area = path.createTransformedShape(nc.getAffineTransform()); 435 436 if (!isOutlineOnly) { 437 if (fillImage == null) { 438 if (isInactiveMode) { 439 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.33f)); 440 } 441 g.setColor(color); 442 g.fill(area); 443 } else { 444 TexturePaint texture = new TexturePaint(fillImage.getImage(), 445 // new Rectangle(polygon.xpoints[0], polygon.ypoints[0], fillImage.getWidth(), fillImage.getHeight())); 446 new Rectangle(0, 0, fillImage.getWidth(), fillImage.getHeight())); 447 g.setPaint(texture); 448 Float alpha = Utils.color_int2float(fillImage.alpha); 449 if (alpha != 1f) { 450 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 451 } 452 g.fill(area); 453 g.setPaintMode(); 454 } 455 } 456 457 if (text != null && isShowNames()) { 458 /* 459 * abort if we can't compose the label to be rendered 460 */ 461 if (text.labelCompositionStrategy == null) return; 462 String name = text.labelCompositionStrategy.compose(osm); 463 if (name == null) return; 464 465 Rectangle pb = area.getBounds(); 466 FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache 467 Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font) 468 469 // Point2D c = getCentroid(polygon); 470 // Using the Centroid is Nicer for buildings like: +--------+ 471 // but this needs to be fast. As most houses are | 42 | 472 // boxes anyway, the center of the bounding box +---++---+ 473 // will have to do. ++ 474 // Centroids are not optimal either, just imagine a U-shaped house. 475 // Point2D c = new Point2D.Double(pb.x + pb.width / 2.0, pb.y + pb.height / 2.0); 476 // Rectangle2D.Double centeredNBounds = 477 // new Rectangle2D.Double(c.getX() - nb.getWidth()/2, 478 // c.getY() - nb.getHeight()/2, 479 // nb.getWidth(), 480 // nb.getHeight()); 481 482 Rectangle centeredNBounds = new Rectangle(pb.x + (int)((pb.width - nb.getWidth())/2.0), 483 pb.y + (int)((pb.height - nb.getHeight())/2.0), 484 (int)nb.getWidth(), 485 (int)nb.getHeight()); 486 487 if ((pb.width >= nb.getWidth() && pb.height >= nb.getHeight()) && // quick check 488 area.contains(centeredNBounds) // slow but nice 489 ) { 490 if (isInactiveMode || osm.isDisabled()) { 491 g.setColor(inactiveColor); 196 492 } else { 197 sc.add(r, FLAG_NORMAL);493 g.setColor(text.color); 198 494 } 495 Font defaultFont = g.getFont(); 496 g.setFont (text.font); 497 g.drawString (name, 498 (int)(centeredNBounds.getMinX() - nb.getMinX()), 499 (int)(centeredNBounds.getMinY() - nb.getMinY())); 500 g.setFont(defaultFont); 199 501 } 200 502 } 201 503 } 504 505 public void drawArea(Relation r, Color color, MapImage fillImage, TextElement text) { 506 Multipolygon multipolygon = MultipolygonCache.getInstance().get(nc, r); 507 if (!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) { 508 for (PolyData pd : multipolygon.getCombinedPolygons()) { 509 Path2D.Double p = pd.get(); 510 if (!isAreaVisible(p)) { 511 continue; 512 } 513 drawArea(r, p, 514 pd.selected ? paintSettings.getRelationSelectedColor(color.getAlpha()) : color, 515 fillImage, text); 516 } 517 } 518 } 519 520 public void drawArea(Way w, Color color, MapImage fillImage, TextElement text) { 521 drawArea(w, getPath(w), color, fillImage, text); 522 } 523 524 public void drawBoxText(Node n, BoxTextElemStyle bs) { 525 if (!isShowNames() || bs == null) 526 return; 527 528 Point p = nc.getPoint(n); 529 TextElement text = bs.text; 530 String s = text.labelCompositionStrategy.compose(n); 531 if (s == null) return; 532 533 Font defaultFont = g.getFont(); 534 g.setFont(text.font); 535 536 int x = p.x + text.xOffset; 537 int y = p.y + text.yOffset; 538 /** 539 * 540 * left-above __center-above___ right-above 541 * left-top| |right-top 542 * | | 543 * left-center| center-center |right-center 544 * | | 545 * left-bottom|_________________|right-bottom 546 * left-below center-below right-below 547 * 548 */ 549 Rectangle box = bs.getBox(); 550 if (bs.hAlign == HorizontalTextAlignment.RIGHT) { 551 x += box.x + box.width + 2; 552 } else { 553 FontRenderContext frc = g.getFontRenderContext(); 554 Rectangle2D bounds = text.font.getStringBounds(s, frc); 555 int textWidth = (int) bounds.getWidth(); 556 if (bs.hAlign == HorizontalTextAlignment.CENTER) { 557 x -= textWidth / 2; 558 } else if (bs.hAlign == HorizontalTextAlignment.LEFT) { 559 x -= - box.x + 4 + textWidth; 560 } else throw new AssertionError(); 561 } 562 563 if (bs.vAlign == VerticalTextAlignment.BOTTOM) { 564 y += box.y + box.height; 565 } else { 566 FontRenderContext frc = g.getFontRenderContext(); 567 LineMetrics metrics = text.font.getLineMetrics(s, frc); 568 if (bs.vAlign == VerticalTextAlignment.ABOVE) { 569 y -= - box.y + metrics.getDescent(); 570 } else if (bs.vAlign == VerticalTextAlignment.TOP) { 571 y -= - box.y - metrics.getAscent(); 572 } else if (bs.vAlign == VerticalTextAlignment.CENTER) { 573 y += (metrics.getAscent() - metrics.getDescent()) / 2; 574 } else if (bs.vAlign == VerticalTextAlignment.BELOW) { 575 y += box.y + box.height + metrics.getAscent() + 2; 576 } else throw new AssertionError(); 577 } 578 if (isInactiveMode || n.isDisabled()) { 579 g.setColor(inactiveColor); 580 } else { 581 g.setColor(text.color); 582 } 583 if (text.haloRadius != null) { 584 g.setStroke(new BasicStroke(2*text.haloRadius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); 585 g.setColor(text.haloColor); 586 FontRenderContext frc = g.getFontRenderContext(); 587 GlyphVector gv = text.font.createGlyphVector(frc, s); 588 Shape textOutline = gv.getOutline(x, y); 589 g.draw(textOutline); 590 g.setStroke(new BasicStroke()); 591 g.setColor(text.color); 592 g.fill(textOutline); 593 } else { 594 g.drawString(s, x, y); 595 } 596 g.setFont(defaultFont); 597 } 598 599 public void drawLinePattern(Way way, Image pattern) { 600 final int width = pattern.getWidth(null); 601 final int height = pattern.getHeight(null); 602 603 Point lastP = null; 604 double wayLength = 0; 605 606 Iterator<Node> it = way.getNodes().iterator(); 607 while (it.hasNext()) { 608 Node n = it.next(); 609 Point thisP = nc.getPoint(n); 610 611 if (lastP != null) { 612 final double segmentLength = thisP.distance(lastP); 613 614 final double dx = thisP.x - lastP.x; 615 final double dy = thisP.y - lastP.y; 616 617 double dist = wayLength == 0 ? 0 : width - (wayLength % width); 618 619 AffineTransform saveTransform = g.getTransform(); 620 g.translate(lastP.x, lastP.y); 621 g.rotate(Math.atan2(dy, dx)); 622 623 if (dist > 0) { 624 g.drawImage(pattern, 0, 0, (int) dist, height, 625 width - (int) dist, 0, width, height, null); 626 } 627 while (dist < segmentLength) { 628 if (dist + width > segmentLength) { 629 g.drawImage(pattern, (int) dist, 0, (int) segmentLength, height, 630 0, 0, (int) segmentLength - (int) dist, height, null); 631 } else { 632 g.drawImage(pattern, (int) dist, 0, nc); 633 } 634 dist += width; 635 } 636 g.setTransform(saveTransform); 202 637 638 wayLength += segmentLength; 639 } 640 lastP = thisP; 641 } 642 } 643 203 644 @Override 204 public void render(final DataSet data, boolean renderVirtualNodes, Bounds bounds) {205 //long start = System.currentTimeMillis();206 BBox bbox = new BBox(bounds);645 public void drawNode(Node n, Color color, int size, boolean fill) { 646 if(size <= 0 && !n.isHighlighted()) 647 return; 207 648 208 styles = MapPaintStyles.getStyles(); 649 Point p = nc.getPoint(n); 650 651 if(n.isHighlighted()) { 652 drawPointHighlight(p, size); 653 } 209 654 210 this.paintSettings = MapPaintSettings.INSTANCE; 655 if (size > 1) { 656 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return; 657 int radius = size / 2; 658 659 if (isInactiveMode || n.isDisabled()) { 660 g.setColor(inactiveColor); 661 } else { 662 g.setColor(color); 663 } 664 if (fill) { 665 g.fillRect(p.x-radius-1, p.y-radius-1, size + 1, size + 1); 666 } else { 667 g.drawRect(p.x-radius-1, p.y-radius-1, size, size); 668 } 669 } 670 } 671 672 public void drawNodeIcon(Node n, Image img, float alpha, boolean selected, boolean member) { 673 Point p = nc.getPoint(n); 674 675 final int w = img.getWidth(null), h=img.getHeight(null); 676 if(n.isHighlighted()) { 677 drawPointHighlight(p, Math.max(w, h)); 678 } 679 680 if (alpha != 1f) { 681 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 682 } 683 g.drawImage(img, p.x-w/2, p.y-h/2, nc); 684 g.setPaintMode(); 685 if (selected || member) 686 { 687 Color color = null; 688 if (isInactiveMode || n.isDisabled()) { 689 color = inactiveColor; 690 } else if (selected) { 691 color = selectedColor; 692 } else { 693 color = relationSelectedColor; 694 } 695 g.setColor(color); 696 g.drawRect(p.x-w/2-2, p.y-h/2-2, w+4, h+4); 697 } 698 } 699 700 public void drawNodeSymbol(Node n, Symbol s, Color fillColor, Color strokeColor) { 701 Point p = nc.getPoint(n); 702 int radius = s.size / 2; 703 704 if(n.isHighlighted()) { 705 drawPointHighlight(p, s.size); 706 } 707 708 if (fillColor != null) { 709 g.setColor(fillColor); 710 switch (s.symbol) { 711 case SQUARE: 712 g.fillRect(p.x - radius, p.y - radius, s.size, s.size); 713 break; 714 case CIRCLE: 715 g.fillOval(p.x - radius, p.y - radius, s.size, s.size); 716 break; 717 case TRIANGLE: 718 g.fillPolygon(buildPolygon(p, radius, 3, Math.PI / 2)); 719 break; 720 case PENTAGON: 721 g.fillPolygon(buildPolygon(p, radius, 5, Math.PI / 2)); 722 break; 723 case HEXAGON: 724 g.fillPolygon(buildPolygon(p, radius, 6)); 725 break; 726 case HEPTAGON: 727 g.fillPolygon(buildPolygon(p, radius, 7, Math.PI / 2)); 728 break; 729 case OCTAGON: 730 g.fillPolygon(buildPolygon(p, radius, 8, Math.PI / 8)); 731 break; 732 case NONAGON: 733 g.fillPolygon(buildPolygon(p, radius, 9, Math.PI / 2)); 734 break; 735 case DECAGON: 736 g.fillPolygon(buildPolygon(p, radius, 10)); 737 break; 738 default: 739 throw new AssertionError(); 740 } 741 } 742 if (s.stroke != null) { 743 g.setStroke(s.stroke); 744 g.setColor(strokeColor); 745 switch (s.symbol) { 746 case SQUARE: 747 g.drawRect(p.x - radius, p.y - radius, s.size - 1, s.size - 1); 748 break; 749 case CIRCLE: 750 g.drawOval(p.x - radius, p.y - radius, s.size - 1, s.size - 1); 751 break; 752 case TRIANGLE: 753 g.drawPolygon(buildPolygon(p, radius, 3, Math.PI / 2)); 754 break; 755 case PENTAGON: 756 g.drawPolygon(buildPolygon(p, radius, 5, Math.PI / 2)); 757 break; 758 case HEXAGON: 759 g.drawPolygon(buildPolygon(p, radius, 6)); 760 break; 761 case HEPTAGON: 762 g.drawPolygon(buildPolygon(p, radius, 7, Math.PI / 2)); 763 break; 764 case OCTAGON: 765 g.drawPolygon(buildPolygon(p, radius, 8, Math.PI / 8)); 766 break; 767 case NONAGON: 768 g.drawPolygon(buildPolygon(p, radius, 9, Math.PI / 2)); 769 break; 770 case DECAGON: 771 g.drawPolygon(buildPolygon(p, radius, 10)); 772 break; 773 default: 774 throw new AssertionError(); 775 } 776 g.setStroke(new BasicStroke()); 777 } 778 } 779 780 /** 781 * Draw a number of the order of the two consecutive nodes within the 782 * parents way 783 */ 784 public void drawOrderNumber(Node n1, Node n2, int orderNumber, Color clr) { 785 Point p1 = nc.getPoint(n1); 786 Point p2 = nc.getPoint(n2); 787 StyledMapRenderer.this.drawOrderNumber(p1, p2, orderNumber, clr); 788 } 789 790 /** 791 * highlights a given GeneralPath using the settings from BasicStroke to match the line's 792 * style. Width of the highlight is hard coded. 793 * @param path 794 * @param line 795 */ 796 private void drawPathHighlight(GeneralPath path, BasicStroke line) { 797 if(path == null) 798 return; 799 g.setColor(highlightColorTransparent); 800 float w = (line.getLineWidth() + 4); 801 while(w >= line.getLineWidth()) { 802 g.setStroke(new BasicStroke(w, line.getEndCap(), line.getLineJoin(), line.getMiterLimit())); 803 g.draw(path); 804 w -= 4; 805 } 806 } 807 /** 808 * highlights a given point by drawing a rounded rectangle around it. Give the 809 * size of the object you want to be highlighted, width is added automatically. 810 */ 811 private void drawPointHighlight(Point p, int size) { 812 g.setColor(highlightColorTransparent); 813 int s = size + 7; 814 while(s >= size) { 815 int r = (int) Math.floor(s/2); 816 g.fillRoundRect(p.x-r, p.y-r, s, s, r, r); 817 s -= 4; 818 } 819 } 820 821 public void drawRestriction(Image img, Point pVia, double vx, double vx2, double vy, double vy2, double angle, boolean selected) { 822 /* rotate image with direction last node in from to */ 823 Image rotatedImg = ImageProvider.createRotatedImage(null , img, angle); 824 825 /* scale down image to 16*16 pixels */ 826 Image smallImg = new ImageIcon(rotatedImg.getScaledInstance(16 , 16, Image.SCALE_SMOOTH)).getImage(); 827 int w = smallImg.getWidth(null), h=smallImg.getHeight(null); 828 g.drawImage(smallImg, (int)(pVia.x+vx+vx2)-w/2, (int)(pVia.y+vy+vy2)-h/2, nc); 829 830 if (selected) { 831 g.setColor(isInactiveMode ? inactiveColor : relationSelectedColor); 832 g.drawRect((int)(pVia.x+vx+vx2)-w/2-2,(int)(pVia.y+vy+vy2)-h/2-2, w+4, h+4); 833 } 834 } 835 836 public void drawRestriction(Relation r, MapImage icon) { 837 Way fromWay = null; 838 Way toWay = null; 839 OsmPrimitive via = null; 840 841 /* find the "from", "via" and "to" elements */ 842 for (RelationMember m : r.getMembers()) 843 { 844 if(m.getMember().isIncomplete()) 845 return; 846 else 847 { 848 if(m.isWay()) 849 { 850 Way w = m.getWay(); 851 if(w.getNodesCount() < 2) { 852 continue; 853 } 854 855 if("from".equals(m.getRole())) { 856 if(fromWay == null) { 857 fromWay = w; 858 } 859 } else if("to".equals(m.getRole())) { 860 if(toWay == null) { 861 toWay = w; 862 } 863 } else if("via".equals(m.getRole())) { 864 if(via == null) { 865 via = w; 866 } 867 } 868 } 869 else if(m.isNode()) 870 { 871 Node n = m.getNode(); 872 if("via".equals(m.getRole()) && via == null) { 873 via = n; 874 } 875 } 876 } 877 } 878 879 if (fromWay == null || toWay == null || via == null) 880 return; 881 882 Node viaNode; 883 if(via instanceof Node) 884 { 885 viaNode = (Node) via; 886 if(!fromWay.isFirstLastNode(viaNode)) 887 return; 888 } 889 else 890 { 891 Way viaWay = (Way) via; 892 Node firstNode = viaWay.firstNode(); 893 Node lastNode = viaWay.lastNode(); 894 Boolean onewayvia = false; 895 896 String onewayviastr = viaWay.get("oneway"); 897 if(onewayviastr != null) 898 { 899 if("-1".equals(onewayviastr)) { 900 onewayvia = true; 901 Node tmp = firstNode; 902 firstNode = lastNode; 903 lastNode = tmp; 904 } else { 905 onewayvia = OsmUtils.getOsmBoolean(onewayviastr); 906 if (onewayvia == null) { 907 onewayvia = false; 908 } 909 } 910 } 911 912 if(fromWay.isFirstLastNode(firstNode)) { 913 viaNode = firstNode; 914 } else if (!onewayvia && fromWay.isFirstLastNode(lastNode)) { 915 viaNode = lastNode; 916 } else 917 return; 918 } 919 920 /* find the "direct" nodes before the via node */ 921 Node fromNode = null; 922 if(fromWay.firstNode() == via) { 923 fromNode = fromWay.getNode(1); 924 } else { 925 fromNode = fromWay.getNode(fromWay.getNodesCount()-2); 926 } 927 928 Point pFrom = nc.getPoint(fromNode); 929 Point pVia = nc.getPoint(viaNode); 930 931 /* starting from via, go back the "from" way a few pixels 932 (calculate the vector vx/vy with the specified length and the direction 933 away from the "via" node along the first segment of the "from" way) 934 */ 935 double distanceFromVia=14; 936 double dx = (pFrom.x >= pVia.x) ? (pFrom.x - pVia.x) : (pVia.x - pFrom.x); 937 double dy = (pFrom.y >= pVia.y) ? (pFrom.y - pVia.y) : (pVia.y - pFrom.y); 938 939 double fromAngle; 940 if(dx == 0.0) { 941 fromAngle = Math.PI/2; 942 } else { 943 fromAngle = Math.atan(dy / dx); 944 } 945 double fromAngleDeg = Math.toDegrees(fromAngle); 946 947 double vx = distanceFromVia * Math.cos(fromAngle); 948 double vy = distanceFromVia * Math.sin(fromAngle); 949 950 if(pFrom.x < pVia.x) { 951 vx = -vx; 952 } 953 if(pFrom.y < pVia.y) { 954 vy = -vy; 955 } 956 957 /* go a few pixels away from the way (in a right angle) 958 (calculate the vx2/vy2 vector with the specified length and the direction 959 90degrees away from the first segment of the "from" way) 960 */ 961 double distanceFromWay=10; 962 double vx2 = 0; 963 double vy2 = 0; 964 double iconAngle = 0; 965 966 if(pFrom.x >= pVia.x && pFrom.y >= pVia.y) { 967 if(!leftHandTraffic) { 968 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90)); 969 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90)); 970 } else { 971 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90)); 972 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90)); 973 } 974 iconAngle = 270+fromAngleDeg; 975 } 976 if(pFrom.x < pVia.x && pFrom.y >= pVia.y) { 977 if(!leftHandTraffic) { 978 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg)); 979 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg)); 980 } else { 981 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180)); 982 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180)); 983 } 984 iconAngle = 90-fromAngleDeg; 985 } 986 if(pFrom.x < pVia.x && pFrom.y < pVia.y) { 987 if(!leftHandTraffic) { 988 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 90)); 989 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 90)); 990 } else { 991 vx2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg - 90)); 992 vy2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg - 90)); 993 } 994 iconAngle = 90+fromAngleDeg; 995 } 996 if(pFrom.x >= pVia.x && pFrom.y < pVia.y) { 997 if(!leftHandTraffic) { 998 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg + 180)); 999 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg + 180)); 1000 } else { 1001 vx2 = distanceFromWay * Math.sin(Math.toRadians(fromAngleDeg)); 1002 vy2 = distanceFromWay * Math.cos(Math.toRadians(fromAngleDeg)); 1003 } 1004 iconAngle = 270-fromAngleDeg; 1005 } 1006 1007 drawRestriction(isInactiveMode || r.isDisabled() ? icon.getDisabled() : icon.getImage(), 1008 pVia, vx, vx2, vy, vy2, iconAngle, r.isSelected()); 1009 } 1010 1011 public void drawTextOnPath(Way way, TextElement text) { 1012 if (way == null || text == null) 1013 return; 1014 String name = text.getString(way); 1015 if (name == null || name.isEmpty()) 1016 return; 1017 1018 Polygon poly = new Polygon(); 1019 Point lastPoint = null; 1020 Iterator<Node> it = way.getNodes().iterator(); 1021 double pathLength = 0; 1022 long dx, dy; 1023 while (it.hasNext()) { 1024 Node n = it.next(); 1025 Point p = nc.getPoint(n); 1026 poly.addPoint(p.x, p.y); 1027 1028 if(lastPoint != null) { 1029 dx = p.x - lastPoint.x; 1030 dy = p.y - lastPoint.y; 1031 pathLength += Math.sqrt(dx*dx + dy*dy); 1032 } 1033 lastPoint = p; 1034 } 1035 1036 FontMetrics fontMetrics = g.getFontMetrics(text.font); // if slow, use cache 1037 Rectangle2D rec = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font) 1038 1039 if (rec.getWidth() > pathLength) 1040 return; 1041 1042 double t1 = (pathLength/2 - rec.getWidth()/2) / pathLength; 1043 double t2 = (pathLength/2 + rec.getWidth()/2) / pathLength; 1044 1045 double[] p1 = pointAt(t1, poly, pathLength); 1046 double[] p2 = pointAt(t2, poly, pathLength); 1047 1048 if (p1 == null || p2 == null) 1049 return; 1050 1051 double angleOffset; 1052 double offsetSign; 1053 double tStart; 1054 1055 if (p1[0] < p2[0] && 1056 p1[2] < Math.PI/2 && 1057 p1[2] > -Math.PI/2) { 1058 angleOffset = 0; 1059 offsetSign = 1; 1060 tStart = t1; 1061 } else { 1062 angleOffset = Math.PI; 1063 offsetSign = -1; 1064 tStart = t2; 1065 } 1066 1067 FontRenderContext frc = g.getFontRenderContext(); 1068 GlyphVector gv = text.font.createGlyphVector(frc, name); 1069 1070 for (int i=0; i<gv.getNumGlyphs(); ++i) { 1071 Rectangle2D rect = gv.getGlyphLogicalBounds(i).getBounds2D(); 1072 double t = tStart + offsetSign * (rect.getX() + rect.getWidth()/2) / pathLength; 1073 double[] p = pointAt(t, poly, pathLength); 1074 if (p != null) { 1075 AffineTransform trfm = AffineTransform.getTranslateInstance(p[0] - rect.getX(), p[1]); 1076 trfm.rotate(p[2]+angleOffset); 1077 double off = -rect.getY() - rect.getHeight()/2 + text.yOffset; 1078 trfm.translate(-rect.getWidth()/2, off); 1079 if (isGlyphVectorDoubleTranslationBug()) { 1080 // scale the translation components by one half 1081 AffineTransform tmp = AffineTransform.getTranslateInstance(-0.5 * trfm.getTranslateX(), -0.5 * trfm.getTranslateY()); 1082 tmp.concatenate(trfm); 1083 trfm = tmp; 1084 } 1085 gv.setGlyphTransform(i, trfm); 1086 } 1087 } 1088 if (text.haloRadius != null) { 1089 Shape textOutline = gv.getOutline(); 1090 g.setStroke(new BasicStroke(2*text.haloRadius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); 1091 g.setColor(text.haloColor); 1092 g.draw(textOutline); 1093 g.setStroke(new BasicStroke()); 1094 g.setColor(text.color); 1095 g.fill(textOutline); 1096 } else { 1097 g.setColor(text.color); 1098 g.drawGlyphVector(gv, 0, 0); 1099 } 1100 } 1101 1102 /** 1103 * draw way 1104 * @param showOrientation show arrows that indicate the technical orientation of 1105 * the way (defined by order of nodes) 1106 * @param showOneway show symbols that indicate the direction of the feature, 1107 * e.g. oneway street or waterway 1108 * @param onewayReversed for oneway=-1 and similar 1109 */ 1110 public void drawWay(Way way, Color color, BasicStroke line, BasicStroke dashes, Color dashedColor, float offset, 1111 boolean showOrientation, boolean showHeadArrowOnly, 1112 boolean showOneway, boolean onewayReversed) { 1113 1114 GeneralPath path = new GeneralPath(); 1115 GeneralPath orientationArrows = showOrientation ? new GeneralPath() : null; 1116 GeneralPath onewayArrows = showOneway ? new GeneralPath() : null; 1117 GeneralPath onewayArrowsCasing = showOneway ? new GeneralPath() : null; 1118 Rectangle bounds = g.getClipBounds(); 1119 bounds.grow(100, 100); // avoid arrow heads at the border 1120 1121 double wayLength = 0; 1122 Point lastPoint = null; 1123 boolean initialMoveToNeeded = true; 1124 List<Node> wayNodes = way.getNodes(); 1125 if (wayNodes.size() < 2) return; 1126 1127 // only highlight the segment if the way itself is not highlighted 1128 if (!way.isHighlighted()) { 1129 GeneralPath highlightSegs = null; 1130 for (WaySegment ws : highlightWaySegments) { 1131 if (ws.way != way || ws.lowerIndex < offset) { 1132 continue; 1133 } 1134 if(highlightSegs == null) { 1135 highlightSegs = new GeneralPath(); 1136 } 1137 1138 Point p1 = nc.getPoint(ws.getFirstNode()); 1139 Point p2 = nc.getPoint(ws.getSecondNode()); 1140 highlightSegs.moveTo(p1.x, p1.y); 1141 highlightSegs.lineTo(p2.x, p2.y); 1142 } 1143 1144 drawPathHighlight(highlightSegs, line); 1145 } 1146 1147 Iterator<Point> it = new OffsetIterator(wayNodes, offset); 1148 while (it.hasNext()) { 1149 Point p = it.next(); 1150 if (lastPoint != null) { 1151 Point p1 = lastPoint; 1152 Point p2 = p; 1153 1154 /** 1155 * Do custom clipping to work around openjdk bug. It leads to 1156 * drawing artefacts when zooming in a lot. (#4289, #4424) 1157 * (Looks like int overflow.) 1158 */ 1159 LineClip clip = new LineClip(p1, p2, bounds); 1160 if (clip.execute()) { 1161 if (!p1.equals(clip.getP1())) { 1162 p1 = clip.getP1(); 1163 path.moveTo(p1.x, p1.y); 1164 } else if (initialMoveToNeeded) { 1165 initialMoveToNeeded = false; 1166 path.moveTo(p1.x, p1.y); 1167 } 1168 p2 = clip.getP2(); 1169 path.lineTo(p2.x, p2.y); 1170 1171 /* draw arrow */ 1172 if (showHeadArrowOnly ? !it.hasNext() : showOrientation) { 1173 final double segmentLength = p1.distance(p2); 1174 if (segmentLength != 0.0) { 1175 final double l = (10. + line.getLineWidth()) / segmentLength; 1176 1177 final double sx = l * (p1.x - p2.x); 1178 final double sy = l * (p1.y - p2.y); 1179 1180 orientationArrows.moveTo (p2.x + cosPHI * sx - sinPHI * sy, p2.y + sinPHI * sx + cosPHI * sy); 1181 orientationArrows.lineTo(p2.x, p2.y); 1182 orientationArrows.lineTo (p2.x + cosPHI * sx + sinPHI * sy, p2.y - sinPHI * sx + cosPHI * sy); 1183 } 1184 } 1185 if (showOneway) { 1186 final double segmentLength = p1.distance(p2); 1187 if (segmentLength != 0.0) { 1188 final double nx = (p2.x - p1.x) / segmentLength; 1189 final double ny = (p2.y - p1.y) / segmentLength; 1190 1191 final double interval = 60; 1192 // distance from p1 1193 double dist = interval - (wayLength % interval); 1194 1195 while (dist < segmentLength) { 1196 for (Pair<Float, GeneralPath> sizeAndPath : Arrays.asList(new Pair[] { 1197 new Pair<Float, GeneralPath>(3f, onewayArrowsCasing), 1198 new Pair<Float, GeneralPath>(2f, onewayArrows)})) { 1199 1200 // scale such that border is 1 px 1201 final double fac = - (onewayReversed ? -1 : 1) * sizeAndPath.a * (1 + sinPHI) / (sinPHI * cosPHI); 1202 final double sx = nx * fac; 1203 final double sy = ny * fac; 1204 1205 // Attach the triangle at the incenter and not at the tip. 1206 // Makes the border even at all sides. 1207 final double x = p1.x + nx * (dist + (onewayReversed ? -1 : 1) * (sizeAndPath.a / sinPHI)); 1208 final double y = p1.y + ny * (dist + (onewayReversed ? -1 : 1) * (sizeAndPath.a / sinPHI)); 1209 1210 sizeAndPath.b.moveTo(x, y); 1211 sizeAndPath.b.lineTo (x + cosPHI * sx - sinPHI * sy, y + sinPHI * sx + cosPHI * sy); 1212 sizeAndPath.b.lineTo (x + cosPHI * sx + sinPHI * sy, y - sinPHI * sx + cosPHI * sy); 1213 sizeAndPath.b.lineTo(x, y); 1214 } 1215 dist += interval; 1216 } 1217 } 1218 wayLength += segmentLength; 1219 } 1220 } 1221 } 1222 lastPoint = p; 1223 } 1224 if(way.isHighlighted()) { 1225 drawPathHighlight(path, line); 1226 } 1227 displaySegments(path, orientationArrows, onewayArrows, onewayArrowsCasing, color, line, dashes, dashedColor); 1228 } 1229 1230 public double getCircum() { 1231 return circum; 1232 } 1233 1234 @Override 1235 public void getColors() { 1236 super.getColors(); 1237 this.relationSelectedColor = PaintColors.RELATIONSELECTED.get(); 1238 this.highlightColorTransparent = new Color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 100); 1239 this.backgroundColor = PaintColors.getBackgroundColor(); 1240 } 1241 1242 @Override 1243 protected void getSettings(boolean virtual) { 1244 super.getSettings(virtual); 1245 paintSettings = MapPaintSettings.INSTANCE; 211 1246 212 1247 circum = nc.getDist100Pixel(); 213 boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000); 214 boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true); 215 styles.setDrawMultipolygon(drawMultipolygon); 216 boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true); 217 boolean leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false); 1248 1249 leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false); 1250 1251 useStrokes = paintSettings.getUseStrokesDistance() > circum; 1252 showNames = paintSettings.getShowNamesDistance() > circum; 1253 showIcons = paintSettings.getShowIconsDistance() > circum; 1254 isOutlineOnly = paintSettings.isOutlineOnly(); 1255 orderFont = new Font(Main.pref.get("mappaint.font", "Helvetica"), Font.PLAIN, Main.pref.getInteger("mappaint.fontsize", 8)); 218 1256 219 1257 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 220 1258 Main.pref.getBoolean("mappaint.use-antialiasing", true) ? 221 1259 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); 1260 } 222 1261 223 Collection<WaySegment> hws = data.getHighlightedWaySegments(); 1262 private Path2D.Double getPath(Way w) { 1263 Path2D.Double path = new Path2D.Double(); 1264 boolean initial = true; 1265 for (Node n : w.getNodes()) { 1266 Point2D p = n.getEastNorth(); 1267 if (p != null) { 1268 if (initial) { 1269 path.moveTo(p.getX(), p.getY()); 1270 initial = false; 1271 } else { 1272 path.lineTo(p.getX(), p.getY()); 1273 } 1274 } 1275 } 1276 return path; 1277 } 224 1278 225 this.painter = new MapPainter(paintSettings, g, isInactiveMode, nc, renderVirtualNodes, circum, leftHandTraffic, hws); 1279 private boolean isAreaVisible(Path2D.Double area) { 1280 Rectangle2D bounds = area.getBounds2D(); 1281 if (bounds.isEmpty()) return false; 1282 Point2D p = nc.getPoint2D(new EastNorth(bounds.getX(), bounds.getY())); 1283 if (p.getX() > nc.getWidth()) return false; 1284 if (p.getY() < 0) return false; 1285 p = nc.getPoint2D(new EastNorth(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight())); 1286 if (p.getX() < 0) return false; 1287 if (p.getY() > nc.getHeight()) return false; 1288 return true; 1289 } 1290 1291 public boolean isInactiveMode() { 1292 return isInactiveMode; 1293 } 1294 1295 public boolean isShowIcons() { 1296 return showIcons; 1297 } 1298 1299 public boolean isShowNames() { 1300 return showNames; 1301 } 1302 1303 private double[] pointAt(double t, Polygon poly, double pathLength) { 1304 double totalLen = t * pathLength; 1305 double curLen = 0; 1306 long dx, dy; 1307 double segLen; 1308 1309 // Yes, it is inefficient to iterate from the beginning for each glyph. 1310 // Can be optimized if it turns out to be slow. 1311 for (int i = 1; i < poly.npoints; ++i) { 1312 dx = poly.xpoints[i] - poly.xpoints[i-1]; 1313 dy = poly.ypoints[i] - poly.ypoints[i-1]; 1314 segLen = Math.sqrt(dx*dx + dy*dy); 1315 if (totalLen > curLen + segLen) { 1316 curLen += segLen; 1317 continue; 1318 } 1319 return new double[] { 1320 poly.xpoints[i-1]+(totalLen - curLen)/segLen*dx, 1321 poly.ypoints[i-1]+(totalLen - curLen)/segLen*dy, 1322 Math.atan2(dy, dx)}; 1323 } 1324 return null; 1325 } 1326 1327 @Override 1328 public void render(final DataSet data, boolean renderVirtualNodes, Bounds bounds) { 1329 //long start = System.currentTimeMillis(); 1330 BBox bbox = new BBox(bounds); 1331 getSettings(renderVirtualNodes); 1332 1333 boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000); 1334 boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true); 1335 boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true); 1336 1337 styles = MapPaintStyles.getStyles(); 1338 styles.setDrawMultipolygon(drawMultipolygon); 1339 1340 highlightWaySegments = data.getHighlightedWaySegments(); 226 1341 227 1342 StyleCollector sc = new StyleCollector(drawArea, drawMultipolygon, drawRestriction); 228 1343 collectNodeStyles(data, sc, bbox); … … 231 1346 //long phase1 = System.currentTimeMillis(); 232 1347 sc.drawAll(); 233 1348 sc = null; 234 painter.drawVirtualNodes(data.searchWays(bbox), data.getHighlightedVirtualNodes());1349 drawVirtualNodes(data, bbox); 235 1350 236 1351 //long now = System.currentTimeMillis(); 237 1352 //System.err.println(String.format("PAINTING TOOK %d [PHASE1 took %d] (at scale %s)", now - start, phase1 - start, circum)); -
core/src/org/openstreetmap/josm/data/osm/visitor/paint/WireframeMapRenderer.java
10 10 import java.awt.RenderingHints; 11 11 import java.awt.Stroke; 12 12 import java.awt.geom.GeneralPath; 13 import java.awt.geom.Point2D;14 import java.util.Collection;15 13 import java.util.Iterator; 16 14 17 15 import org.openstreetmap.josm.Main; … … 34 32 */ 35 33 public class WireframeMapRenderer extends AbstractMapRenderer implements Visitor { 36 34 37 /** Color Preference for inactive objects */38 protected Color inactiveColor;39 /** Color Preference for selected objects */40 protected Color selectedColor;41 /** Color Preference for nodes */42 protected Color nodeColor;43 35 /** Color Preference for ways not matching any other group */ 44 36 protected Color dfltWayColor; 45 37 /** Color Preference for relations */ 46 38 protected Color relationColor; 47 39 /** Color Preference for untagged ways */ 48 40 protected Color untaggedWayColor; 49 /** Color Preference for background */50 protected Color backgroundColor;51 /** Color Preference for hightlighted objects */52 protected Color highlightColor;53 41 /** Color Preference for tagged nodes */ 54 42 protected Color taggedColor; 55 43 /** Color Preference for multiply connected nodes */ … … 80 68 protected int connectionNodeSize; 81 69 /** Preference: size of tagged nodes */ 82 70 protected int taggedNodeSize; 83 /** Preference: size of virtual nodes (0 displayes display) */84 protected int virtualNodeSize;85 /** Preference: minimum space (displayed way length) to display virtual nodes */86 protected int virtualNodeSpace;87 /** Preference: minimum space (displayed way length) to display segment numbers */88 protected int segmentNumberSpace;89 71 90 72 /** Color cache to draw subsequent segments of same color as one <code>Path</code>. */ 91 73 protected Color currentColor = null; … … 122 104 super(g, nc, isInactiveMode); 123 105 } 124 106 125 /** 126 * Reads the color definitions from preferences. This function is <code>public</code>, so that 127 * color names in preferences can be displayed even without calling the wireframe display before. 128 */ 129 public void getColors() 130 { 131 inactiveColor = PaintColors.INACTIVE.get(); 132 selectedColor = PaintColors.SELECTED.get(); 133 nodeColor = PaintColors.NODE.get(); 107 @Override 108 public void getColors() { 109 super.getColors(); 134 110 dfltWayColor = PaintColors.DEFAULT_WAY.get(); 135 111 relationColor = PaintColors.RELATION.get(); 136 112 untaggedWayColor = PaintColors.UNTAGGED_WAY.get(); 137 backgroundColor = PaintColors.BACKGROUND.get();138 113 highlightColor = PaintColors.HIGHLIGHT_WIREFRAME.get(); 139 114 taggedColor = PaintColors.TAGGED.get(); 140 115 connectionColor = PaintColors.CONNECTION.get(); … … 146 121 } 147 122 } 148 123 149 /** 150 * Reads all the settings from preferences. Calls the @{link #getColors} 151 * function. 152 * 153 * @param virtual <code>true</code> if virtual nodes are used 154 */ 124 @Override 155 125 protected void getSettings(boolean virtual) { 126 super.getSettings(virtual); 156 127 MapPaintSettings settings = MapPaintSettings.INSTANCE; 157 128 showDirectionArrow = settings.isShowDirectionArrow(); 158 129 showOnewayArrow = settings.isShowOnewayArrow(); … … 166 137 fillUnselectedNode = settings.isFillUnselectedNode(); 167 138 fillConnectionNode = settings.isFillConnectionNode(); 168 139 fillTaggedNode = settings.isFillTaggedNode(); 169 virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;170 virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);171 segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);172 getColors();173 140 174 141 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 175 142 Main.pref.getBoolean("mappaint.wireframe.use-antialiasing", false) ? … … 223 190 osm.visit(this); 224 191 } 225 192 } 226 drawVirtualNodes(data .searchWays(bbox), data.getHighlightedVirtualNodes());193 drawVirtualNodes(data, bbox); 227 194 228 195 // draw highlighted way segments over the already drawn ways. Otherwise each 229 196 // way would have to be checked if it contains a way segment to highlight when 230 197 // in most of the cases there won't be more than one segment. Since the wireframe 231 198 // renderer does not feature any transparency there should be no visual difference. 232 for (final WaySegment wseg : data.getHighlightedWaySegments()) {199 for (final WaySegment wseg : data.getHighlightedWaySegments()) { 233 200 drawSegment(nc.getPoint(wseg.getFirstNode()), nc.getPoint(wseg.getSecondNode()), highlightColor, false); 234 201 } 235 202 displaySegments(); … … 295 262 } 296 263 297 264 /** 298 * Checks if a way segemnt is large enough for additional information display.299 *300 * @param p1 First point of the way segment.301 * @param p2 Second point of the way segment.302 * @param space The free space to check against.303 * @return <code>true</code> if segment is larger than required space304 */305 public static boolean isLargeSegment(Point2D p1, Point2D p2, int space)306 {307 double xd = Math.abs(p1.getX()-p2.getX());308 double yd = Math.abs(p1.getY()-p2.getY());309 return (xd+yd > space);310 }311 312 /**313 * Draws virtual nodes.314 *315 * @param ways The ways to draw nodes for.316 * @param highlightVirtualNodes Way segements, where nodesshould be highlighted.317 */318 public void drawVirtualNodes(Collection<Way> ways, Collection<WaySegment> highlightVirtualNodes) {319 if (virtualNodeSize == 0)320 return;321 // print normal virtual nodes322 GeneralPath path = new GeneralPath();323 for (Way osm : ways) {324 if (osm.isUsable() && !osm.isDisabledAndHidden() && !osm.isDisabled()) {325 visitVirtual(path, osm);326 }327 }328 g.setColor(nodeColor);329 g.draw(path);330 // print highlighted virtual nodes. Since only the color changes, simply331 // drawing them over the existing ones works fine (at least in their current332 // simple style)333 path = new GeneralPath();334 for (WaySegment wseg: highlightVirtualNodes){335 if (wseg.way.isUsable() && !wseg.way.isDisabled()) {336 visitVirtual(path, wseg.toWay());337 }338 }339 g.setColor(highlightColor);340 g.draw(path);341 }342 343 /**344 * Creates path for drawing virtual nodes for one way.345 *346 * @param path The path to append drawing to.347 * @param w The ways to draw node for.348 */349 public void visitVirtual(GeneralPath path, Way w) {350 Iterator<Node> it = w.getNodes().iterator();351 if (it.hasNext()) {352 Point lastP = nc.getPoint(it.next());353 while(it.hasNext())354 {355 Point p = nc.getPoint(it.next());356 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))357 {358 int x = (p.x+lastP.x)/2;359 int y = (p.y+lastP.y)/2;360 path.moveTo(x-virtualNodeSize, y);361 path.lineTo(x+virtualNodeSize, y);362 path.moveTo(x, y-virtualNodeSize);363 path.lineTo(x, y+virtualNodeSize);364 }365 lastP = p;366 }367 }368 }369 370 /**371 265 * Draw a line for all way segments. 372 266 * @param w The way to draw. 373 267 */ … … 405 299 drawSegment(lastP, p, wayColor, 406 300 showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow); 407 301 if (showOrderNumber && !isInactiveMode) { 408 drawOrderNumber(lastP, p, orderNumber );302 drawOrderNumber(lastP, p, orderNumber, g.getColor()); 409 303 } 410 304 lastP = p; 411 305 } … … 472 366 @Override 473 367 public void visit(Changeset cs) {/* ignore */} 474 368 475 /** 476 * Draw an number of the order of the two consecutive nodes within the 477 * parents way 478 * 479 * @param p1 First point of the way segment. 480 * @param p2 Second point of the way segment. 481 * @param orderNumber The number of the segment in the way. 482 */ 483 protected void drawOrderNumber(Point p1, Point p2, int orderNumber) { 484 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) { 485 String on = Integer.toString(orderNumber); 486 int strlen = on.length(); 487 int x = (p1.x+p2.x)/2 - 4*strlen; 488 int y = (p1.y+p2.y)/2 + 4; 489 490 if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace)) 491 { 492 y = (p1.y+p2.y)/2 - virtualNodeSize - 3; 493 } 494 495 displaySegments(); /* draw nodes on top! */ 496 Color c = g.getColor(); 497 g.setColor(backgroundColor); 498 g.fillRect(x-1, y-12, 8*strlen+1, 14); 499 g.setColor(c); 500 g.drawString(on, x, y); 501 } 502 } 503 504 /** 505 * Draw the node as small rectangle with the given color. 506 * 507 * @param n The node to draw. 508 * @param color The color of the node. 509 */ 369 @Override 510 370 public void drawNode(Node n, Color color, int size, boolean fill) { 511 371 if (size > 1) { 512 372 int radius = size / 2; … … 571 431 } 572 432 573 433 /** 574 * Checks if segment is visible in display.575 *576 * @param p1 First point of the way segment.577 * @param p2 Second point of the way segment.578 * @return <code>true</code> if segment is visible.579 */580 protected boolean isSegmentVisible(Point p1, Point p2) {581 if ((p1.x < 0) && (p2.x < 0)) return false;582 if ((p1.y < 0) && (p2.y < 0)) return false;583 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;584 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;585 return true;586 }587 588 /**589 434 * Checks if a polygon is visible in display. 590 435 * 591 436 * @param polygon The polygon to check. -
core/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java
10 10 import org.openstreetmap.josm.data.osm.Relation; 11 11 import org.openstreetmap.josm.data.osm.Way; 12 12 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 13 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;13 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 14 14 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 15 15 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference; 16 16 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 77 77 } 78 78 79 79 @Override 80 public void paintPrimitive(OsmPrimitive osm, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member) {80 public void paintPrimitive(OsmPrimitive osm, MapPaintSettings paintSettings, StyledMapRenderer painter, boolean selected, boolean member) { 81 81 if (osm instanceof Way) 82 82 { 83 83 Color myColor = color; -
core/src/org/openstreetmap/josm/gui/mappaint/BoxTextElemStyle.java
9 9 import org.openstreetmap.josm.data.osm.Node; 10 10 import org.openstreetmap.josm.data.osm.OsmPrimitive; 11 11 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 12 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;12 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 13 13 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 14 14 import org.openstreetmap.josm.tools.CheckParameterUtil; 15 15 … … 180 180 } 181 181 182 182 @Override 183 public void paintPrimitive(OsmPrimitive osm, MapPaintSettings settings, MapPainter painter, boolean selected, boolean member) {183 public void paintPrimitive(OsmPrimitive osm, MapPaintSettings settings, StyledMapRenderer painter, boolean selected, boolean member) { 184 184 if (osm instanceof Node) { 185 185 painter.drawBoxText((Node) osm, this); 186 186 } -
core/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
10 10 import org.openstreetmap.josm.Main; 11 11 import org.openstreetmap.josm.data.osm.OsmPrimitive; 12 12 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 13 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;13 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 14 14 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat; 15 15 16 16 abstract public class ElemStyle implements StyleKeys { … … 43 43 * @param selected true, if primitive is selected 44 44 * @param member true, if primitive is not selected and member of a selected relation 45 45 */ 46 public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member);46 public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter, boolean selected, boolean member); 47 47 48 48 public boolean isProperLineStyle() { 49 49 return false; -
core/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java
11 11 import org.openstreetmap.josm.data.osm.OsmPrimitive; 12 12 import org.openstreetmap.josm.data.osm.Way; 13 13 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 14 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;14 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 15 15 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 16 16 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat; 17 17 import org.openstreetmap.josm.tools.Utils; … … 265 265 } 266 266 267 267 @Override 268 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member) {268 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter, boolean selected, boolean member) { 269 269 Way w = (Way)primitive; 270 270 /* show direction arrows, if draw.segment.relevant_directions_only is not set, 271 271 the way is tagged with a direction key -
core/src/org/openstreetmap/josm/gui/mappaint/LinePatternElemStyle.java
4 4 import org.openstreetmap.josm.data.osm.OsmPrimitive; 5 5 import org.openstreetmap.josm.data.osm.Way; 6 6 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 7 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;7 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 8 8 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference; 9 9 10 10 /** … … 30 30 } 31 31 32 32 @Override 33 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member) {33 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter, boolean selected, boolean member) { 34 34 Way w = (Way)primitive; 35 35 painter.drawLinePattern(w, pattern.getImage()); 36 36 } -
core/src/org/openstreetmap/josm/gui/mappaint/LineTextElemStyle.java
4 4 import org.openstreetmap.josm.data.osm.OsmPrimitive; 5 5 import org.openstreetmap.josm.data.osm.Way; 6 6 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 7 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;7 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 8 8 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 9 9 import org.openstreetmap.josm.tools.Utils; 10 10 … … 30 30 } 31 31 32 32 @Override 33 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member) {33 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter, boolean selected, boolean member) { 34 34 Way w = (Way)primitive; 35 35 painter.drawTextOnPath(w, text); 36 36 } -
core/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java
13 13 import org.openstreetmap.josm.data.osm.OsmPrimitive; 14 14 import org.openstreetmap.josm.data.osm.Relation; 15 15 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 16 import org.openstreetmap.josm.data.osm.visitor.paint. MapPainter;16 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer; 17 17 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProvider; 18 18 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.SimpleBoxProvider; 19 19 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference; … … 229 229 } 230 230 231 231 @Override 232 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, MapPainter painter, boolean selected, boolean member) {232 public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, StyledMapRenderer painter, boolean selected, boolean member) { 233 233 if (primitive instanceof Node) { 234 234 Node n = (Node) primitive; 235 235 if (mapImage != null && painter.isShowIcons()) {
