Changeset 5571 in josm


Ignore:
Timestamp:
2012-11-07T19:32:09+01:00 (11 years ago)
Author:
Don-vip
Message:

fix #7979 - fix ArrayIndexOutOfBoundsException when rendering ways with the "quick and dirty" approach, and a TODO note to code a better approach on the next development cycle

Location:
trunk/src/org/openstreetmap/josm
Files:
1 deleted
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/AbstractMapRenderer.java

    r4087 r5571  
    22package org.openstreetmap.josm.data.osm.visitor.paint;
    33
     4import java.awt.Color;
    45import java.awt.Graphics2D;
    5 
     6import java.awt.Point;
     7import java.awt.geom.GeneralPath;
     8import java.awt.geom.Point2D;
     9import java.util.Iterator;
     10
     11import org.openstreetmap.josm.Main;
     12import org.openstreetmap.josm.data.osm.BBox;
     13import org.openstreetmap.josm.data.osm.DataSet;
     14import org.openstreetmap.josm.data.osm.Node;
     15import org.openstreetmap.josm.data.osm.Way;
     16import org.openstreetmap.josm.data.osm.WaySegment;
    617import org.openstreetmap.josm.gui.NavigatableComponent;
    718import org.openstreetmap.josm.tools.CheckParameterUtil;
     
    1728    /** the map viewport - provides projection and hit detection functionality */
    1829    protected NavigatableComponent nc;
     30   
    1931    /** 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. */
    2233    protected boolean isInactiveMode;
    23 
     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;
     49
     50    /** Preference: minimum space (displayed way length) to display segment numbers */
     51    protected int segmentNumberSpace;
     52   
    2453    /**
    2554     * <p>Creates an abstract paint visitor</p>
     
    3968        this.isInactiveMode = isInactiveMode;
    4069    }
     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            // TODO: proper solution ?
     139            // Idea from bastiK: avoid the WaySegment class and add another data class with { Way way; Node firstNode, secondNode; int firstIdx; }.
     140            // On read, it would first check, if the way still has firstIdx+2 nodes, then check if the corresponding way nodes are still the same
     141            // and report changes in a more controlled manner.
     142        }
     143    }
     144   
     145    /**
     146     * Reads the color definitions from preferences. This function is <code>public</code>, so that
     147     * color names in preferences can be displayed even without calling the wireframe display before.
     148     */
     149    public void getColors() {
     150        this.backgroundColor = PaintColors.BACKGROUND.get();
     151        this.inactiveColor = PaintColors.INACTIVE.get();
     152        this.selectedColor = PaintColors.SELECTED.get();
     153        this.nodeColor = PaintColors.NODE.get();
     154        this.highlightColor = PaintColors.HIGHLIGHT.get();
     155    }
     156   
     157    /**
     158     * Reads all the settings from preferences. Calls the @{link #getColors}
     159     * function.
     160     *
     161     * @param virtual <code>true</code> if virtual nodes are used
     162     */
     163    protected void getSettings(boolean virtual) {
     164        this.virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0;
     165        this.virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
     166        this.segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40);
     167        getColors();
     168    }
     169   
     170    /**
     171     * Checks if a way segemnt is large enough for additional information display.
     172     *
     173     * @param p1 First point of the way segment.
     174     * @param p2 Second point of the way segment.
     175     * @param space The free space to check against.
     176     * @return <code>true</code> if segment is larger than required space
     177     */
     178    public static boolean isLargeSegment(Point2D p1, Point2D p2, int space) {
     179        double xd = Math.abs(p1.getX()-p2.getX());
     180        double yd = Math.abs(p1.getY()-p2.getY());
     181        return (xd+yd > space);
     182    }
     183   
     184    /**
     185     * Checks if segment is visible in display.
     186     *
     187     * @param p1 First point of the way segment.
     188     * @param p2 Second point of the way segment.
     189     * @return <code>true</code> if segment is visible.
     190     */
     191    protected boolean isSegmentVisible(Point p1, Point p2) {
     192        if ((p1.x < 0) && (p2.x < 0)) return false;
     193        if ((p1.y < 0) && (p2.y < 0)) return false;
     194        if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false;
     195        if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false;
     196        return true;
     197    }
     198   
     199    /**
     200     * Creates path for drawing virtual nodes for one way.
     201     *
     202     * @param path The path to append drawing to.
     203     * @param w The ways to draw node for.
     204     */
     205    public void visitVirtual(GeneralPath path, Way w) {
     206        Iterator<Node> it = w.getNodes().iterator();
     207        if (it.hasNext()) {
     208            Point lastP = nc.getPoint(it.next());
     209            while (it.hasNext())
     210            {
     211                Point p = nc.getPoint(it.next());
     212                if (isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace))
     213                {
     214                    int x = (p.x+lastP.x)/2;
     215                    int y = (p.y+lastP.y)/2;
     216                    path.moveTo(x-virtualNodeSize, y);
     217                    path.lineTo(x+virtualNodeSize, y);
     218                    path.moveTo(x, y-virtualNodeSize);
     219                    path.lineTo(x, y+virtualNodeSize);
     220                }
     221                lastP = p;
     222            }
     223        }
     224    }
    41225}
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java

    r5217 r5571  
    22package org.openstreetmap.josm.data.osm.visitor.paint;
    33
     4import java.awt.AlphaComposite;
     5import java.awt.BasicStroke;
     6import java.awt.Color;
     7import java.awt.Font;
     8import java.awt.FontMetrics;
    49import java.awt.Graphics2D;
     10import java.awt.Image;
     11import java.awt.Point;
     12import java.awt.Polygon;
     13import java.awt.Rectangle;
    514import java.awt.RenderingHints;
     15import java.awt.Shape;
     16import java.awt.TexturePaint;
     17import java.awt.font.FontRenderContext;
     18import java.awt.font.GlyphVector;
     19import java.awt.font.LineMetrics;
     20import java.awt.geom.AffineTransform;
     21import java.awt.geom.GeneralPath;
     22import java.awt.geom.Path2D;
     23import java.awt.geom.Point2D;
     24import java.awt.geom.Rectangle2D;
    625import java.util.ArrayList;
     26import java.util.Arrays;
    727import java.util.Collection;
    828import java.util.Collections;
     29import java.util.Iterator;
    930import java.util.List;
     31
     32import javax.swing.ImageIcon;
    1033
    1134import org.openstreetmap.josm.Main;
    1235import org.openstreetmap.josm.data.Bounds;
     36import org.openstreetmap.josm.data.coor.EastNorth;
    1337import org.openstreetmap.josm.data.osm.BBox;
    1438import org.openstreetmap.josm.data.osm.DataSet;
    1539import org.openstreetmap.josm.data.osm.Node;
    1640import org.openstreetmap.josm.data.osm.OsmPrimitive;
     41import org.openstreetmap.josm.data.osm.OsmUtils;
    1742import org.openstreetmap.josm.data.osm.Relation;
     43import org.openstreetmap.josm.data.osm.RelationMember;
    1844import org.openstreetmap.josm.data.osm.Way;
    1945import org.openstreetmap.josm.data.osm.WaySegment;
     46import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
     47import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
     48import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
    2049import org.openstreetmap.josm.gui.NavigatableComponent;
    2150import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
     51import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle;
     52import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.HorizontalTextAlignment;
     53import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.VerticalTextAlignment;
    2254import org.openstreetmap.josm.gui.mappaint.ElemStyle;
    2355import org.openstreetmap.josm.gui.mappaint.ElemStyles;
     56import org.openstreetmap.josm.gui.mappaint.MapImage;
    2457import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
    2558import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
     59import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
    2660import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
     61import org.openstreetmap.josm.gui.mappaint.TextElement;
     62import org.openstreetmap.josm.tools.ImageProvider;
     63import org.openstreetmap.josm.tools.Pair;
     64import org.openstreetmap.josm.tools.Utils;
    2765
    2866/**
     
    3068 *
    3169 */
    32 public class StyledMapRenderer extends AbstractMapRenderer{
    33 
    34     private ElemStyles styles;
    35     private double circum;
    36     private MapPainter painter;
    37     private MapPaintSettings paintSettings;
    38 
    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;
    43 
    44     private static class StyleRecord implements Comparable<StyleRecord> {
    45         final ElemStyle style;
    46         final OsmPrimitive osm;
    47         final int flags;
    48 
    49         public StyleRecord(ElemStyle style, OsmPrimitive osm, int flags) {
    50             this.style = style;
    51             this.osm = osm;
    52             this.flags = flags;
     70public class StyledMapRenderer extends AbstractMapRenderer {
     71
     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> {
     81
     82        private List<Node> nodes;
     83        private float offset;
     84        private int idx;
     85
     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;
     92
     93        public OffsetIterator(List<Node> nodes, float offset) {
     94            this.nodes = nodes;
     95            this.offset = offset;
     96            idx = 0;
    5397        }
    5498
    5599        @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;
    61 
    62             int d0 = Float.compare(this.style.major_z_index, other.style.major_z_index);
    63             if (d0 != 0)
    64                 return d0;
    65 
    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;
    72 
    73             int dz = Float.compare(this.style.z_index, other.style.z_index);
    74             if (dz != 0)
    75                 return dz;
    76 
    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;
    82 
    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;
    89 
    90             return Float.compare(this.style.object_z_index, other.style.object_z_index);
     100        public boolean hasNext() {
     101            return idx < nodes.size();
     102        }
     103
     104        @Override
     105        public Point next() {
     106            if (Math.abs(offset) < 0.1f) return nc.getPoint(nodes.get(idx++));
     107
     108            Point current = nc.getPoint(nodes.get(idx));
     109
     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            }
     114
     115            Point next = nc.getPoint(nodes.get(idx+1));
     116
     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);
     120
     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();
    91165        }
    92166    }
     
    109183            StyleList sl = styles.get(osm, circum, nc);
    110184            for (ElemStyle s : sl) {
    111                 styleElems.add(new StyleRecord(s, osm, flags));
    112             }
    113         }
    114 
    115         public void add(Way osm, int flags) {
    116             StyleList sl = styles.get(osm, circum, nc);
    117             for (ElemStyle s : sl) {
    118                 if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) {
    119                     continue;
    120                 }
    121185                styleElems.add(new StyleRecord(s, osm, flags));
    122186            }
     
    134198        }
    135199
     200        public void add(Way osm, int flags) {
     201            StyleList sl = styles.get(osm, circum, nc);
     202            for (ElemStyle s : sl) {
     203                if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) {
     204                    continue;
     205                }
     206                styleElems.add(new StyleRecord(s, osm, flags));
     207            }
     208        }
     209
    136210        public void drawAll() {
    137211            Collections.sort(styleElems);
     
    140214                        r.osm,
    141215                        paintSettings,
    142                         painter,
     216                        StyledMapRenderer.this,
    143217                        (r.flags & FLAG_SELECTED) != 0,
    144218                        (r.flags & FLAG_MEMBER_OF_SELECTED) != 0
     
    147221        }
    148222    }
     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;
    149331
    150332    /**
     
    153335    public StyledMapRenderer(Graphics2D g, NavigatableComponent nc, boolean isInactiveMode) {
    154336        super(g, nc, isInactiveMode);
     337    }
     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;
    155352    }
    156353
     
    171368    }
    172369
     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
    173384    private void collectWayStyles(DataSet data, StyleCollector sc, BBox bbox) {
    174385        for (final Way w : data.searchWays(bbox)) {
     
    187398    }
    188399
    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);
    196492                } else {
    197                     sc.add(r, FLAG_NORMAL);
    198                 }
    199             }
    200         }
     493                    g.setColor(text.color);
     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);
     501            }
     502        }
     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);
     637
     638                wayLength += segmentLength;
     639            }
     640            lastP = thisP;
     641        }
     642    }
     643   
     644    @Override
     645    public void drawNode(Node n, Color color, int size, boolean fill) {
     646        if(size <= 0 && !n.isHighlighted())
     647            return;
     648
     649        Point p = nc.getPoint(n);
     650
     651        if(n.isHighlighted()) {
     652            drawPointHighlight(p, size);
     653        }
     654
     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;
     1246
     1247        circum = nc.getDist100Pixel();
     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));
     1256
     1257        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     1258                Main.pref.getBoolean("mappaint.use-antialiasing", true) ?
     1259                        RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
     1260    }
     1261
     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    }
     1278
     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;
    2011325    }
    2021326
     
    2051329        //long start = System.currentTimeMillis();
    2061330        BBox bbox = new BBox(bounds);
    207 
    208         styles = MapPaintStyles.getStyles();
    209 
    210         this.paintSettings = MapPaintSettings.INSTANCE;
    211 
    212         circum = nc.getDist100Pixel();
     1331        getSettings(renderVirtualNodes);
     1332
    2131333        boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000);
    2141334        boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true);
     1335        boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
     1336
     1337        styles = MapPaintStyles.getStyles();
    2151338        styles.setDrawMultipolygon(drawMultipolygon);
    216         boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
    217         boolean leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
    218 
    219         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    220                 Main.pref.getBoolean("mappaint.use-antialiasing", true) ?
    221                         RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
    222 
    223         Collection<WaySegment> hws = data.getHighlightedWaySegments();
    224 
    225         this.painter = new MapPainter(paintSettings, g, isInactiveMode, nc, renderVirtualNodes, circum, leftHandTraffic, hws);
     1339
     1340        highlightWaySegments = data.getHighlightedWaySegments();
    2261341
    2271342        StyleCollector sc = new StyleCollector(drawArea, drawMultipolygon, drawRestriction);
     
    2321347        sc.drawAll();
    2331348        sc = null;
    234         painter.drawVirtualNodes(data.searchWays(bbox), data.getHighlightedVirtualNodes());
     1349        drawVirtualNodes(data, bbox);
    2351350
    2361351        //long now = System.currentTimeMillis();
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/WireframeMapRenderer.java

    r5421 r5571  
    1111import java.awt.Stroke;
    1212import java.awt.geom.GeneralPath;
    13 import java.awt.geom.Point2D;
    14 import java.util.Collection;
    1513import java.util.Iterator;
    1614
     
    3533public class WireframeMapRenderer extends AbstractMapRenderer implements Visitor {
    3634
    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;
    4335    /** Color Preference for ways not matching any other group */
    4436    protected Color dfltWayColor;
     
    4739    /** Color Preference for untagged ways */
    4840    protected Color untaggedWayColor;
    49     /** Color Preference for background */
    50     protected Color backgroundColor;
    51     /** Color Preference for hightlighted objects */
    52     protected Color highlightColor;
    5341    /** Color Preference for tagged nodes */
    5442    protected Color taggedColor;
     
    8169    /** Preference: size of tagged nodes */
    8270    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;
    8971
    9072    /** Color cache to draw subsequent segments of same color as one <code>Path</code>. */
     
    123105    }
    124106
    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();
    134110        dfltWayColor = PaintColors.DEFAULT_WAY.get();
    135111        relationColor = PaintColors.RELATION.get();
    136112        untaggedWayColor = PaintColors.UNTAGGED_WAY.get();
    137         backgroundColor = PaintColors.BACKGROUND.get();
    138113        highlightColor = PaintColors.HIGHLIGHT_WIREFRAME.get();
    139114        taggedColor = PaintColors.TAGGED.get();
     
    147122    }
    148123
    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
    155125    protected void getSettings(boolean virtual) {
     126        super.getSettings(virtual);
    156127        MapPaintSettings settings = MapPaintSettings.INSTANCE;
    157128        showDirectionArrow = settings.isShowDirectionArrow();
     
    167138        fillConnectionNode = settings.isFillConnectionNode();
    168139        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();
    173140
    174141        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     
    224191            }
    225192        }
    226         drawVirtualNodes(data.searchWays(bbox), data.getHighlightedVirtualNodes());
     193        drawVirtualNodes(data, bbox);
    227194
    228195        // draw highlighted way segments over the already drawn ways. Otherwise each
     
    230197        // in most of the cases there won't be more than one segment. Since the wireframe
    231198        // 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()) {
    233200            drawSegment(nc.getPoint(wseg.getFirstNode()), nc.getPoint(wseg.getSecondNode()), highlightColor, false);
    234201        }
     
    296263
    297264    /**
    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 space
    304      */
    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 nodes
    322         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, simply
    331         // drawing them over the existing ones works fine (at least in their current
    332         // 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     /**
    371265     * Draw a line for all way segments.
    372266     * @param w The way to draw.
     
    406300                        showOnlyHeadArrowOnly ? !it.hasNext() : showThisDirectionArrow);
    407301                if (showOrderNumber && !isInactiveMode) {
    408                     drawOrderNumber(lastP, p, orderNumber);
     302                    drawOrderNumber(lastP, p, orderNumber, g.getColor());
    409303                }
    410304                lastP = p;
     
    473367    public void visit(Changeset cs) {/* ignore */}
    474368
    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
    510370    public void drawNode(Node n, Color color, int size, boolean fill) {
    511371        if (size > 1) {
     
    572432
    573433    /**
    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     /**
    589434     * Checks if a polygon is visible in display.
    590435     *
  • trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java

    r5376 r5571  
    1111import org.openstreetmap.josm.data.osm.Way;
    1212import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    13 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     13import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    1414import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    1515import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
     
    7878
    7979    @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) {
    8181        if (osm instanceof Way)
    8282        {
  • trunk/src/org/openstreetmap/josm/gui/mappaint/BoxTextElemStyle.java

    r5342 r5571  
    1010import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1111import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    12 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     12import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    1313import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    1414import org.openstreetmap.josm.tools.CheckParameterUtil;
     
    181181
    182182    @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) {
    184184        if (osm instanceof Node) {
    185185            painter.drawBoxText((Node) osm, this);
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java

    r5342 r5571  
    1111import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1212import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    13 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     13import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    1414import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
    1515
     
    4444     * @param member true, if primitive is not selected and member of a selected relation
    4545     */
    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);
    4747
    4848    public boolean isProperLineStyle() {
  • trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java

    r5342 r5571  
    1212import org.openstreetmap.josm.data.osm.Way;
    1313import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    14 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     14import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    1515import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    1616import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
     
    266266
    267267    @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) {
    269269        Way w = (Way)primitive;
    270270        /* show direction arrows, if draw.segment.relevant_directions_only is not set,
  • trunk/src/org/openstreetmap/josm/gui/mappaint/LinePatternElemStyle.java

    r5217 r5571  
    55import org.openstreetmap.josm.data.osm.Way;
    66import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    7 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     7import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    88import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
    99
     
    3131
    3232    @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) {
    3434        Way w = (Way)primitive;
    3535        painter.drawLinePattern(w, pattern.getImage());
  • trunk/src/org/openstreetmap/josm/gui/mappaint/LineTextElemStyle.java

    r5342 r5571  
    55import org.openstreetmap.josm.data.osm.Way;
    66import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    7 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     7import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    88import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    99import org.openstreetmap.josm.tools.Utils;
     
    3131
    3232    @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) {
    3434        Way w = (Way)primitive;
    3535        painter.drawTextOnPath(w, text);
  • trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java

    r5342 r5571  
    1414import org.openstreetmap.josm.data.osm.Relation;
    1515import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    16 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     16import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
    1717import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProvider;
    1818import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.SimpleBoxProvider;
     
    230230
    231231    @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) {
    233233        if (primitive instanceof Node) {
    234234            Node n = (Node) primitive;
Note: See TracChangeset for help on using the changeset viewer.