Changeset 3836 in josm for trunk


Ignore:
Timestamp:
2011-01-31T14:18:47+01:00 (13 years ago)
Author:
bastiK
Message:

mappaint restructuring aimed at mapcss support:

  • area- and line style of multipolygon outer & inner ways are no longer determined by MapPaintVisitor, but the information is calculated in advance and cached
  • cache is aware of zoom level
  • z_index property to allow more fancy styles in future

Performance: when the style cache is filled, painting is the same or ~5% faster. The first painting, which includes style generation, takes ~30% longer than before. (There is potential for optimization, though.) Memory usage unchanged.

Location:
trunk/src/org/openstreetmap/josm
Files:
3 added
2 deleted
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r3824 r3836  
    300300     *--------*/
    301301    public StyleCache mappaintStyle = null;
    302     public int mappaintDrawnCode = 0;
    303302
    304303    /* This should not be called from outside. Fixing the UI to add relevant
     
    307306    public void clearCached()
    308307    {
    309         mappaintDrawnCode = 0;
    310308        mappaintStyle = null;
    311309    }
     
    808806    public boolean isSelected() {
    809807        return dataSet != null && dataSet.isSelected(this);
     808    }
     809
     810    public boolean isMemberOfSelected() {
     811        if (referrers == null)
     812            return false;
     813        if (referrers instanceof OsmPrimitive)
     814            return referrers instanceof Relation && ((OsmPrimitive) referrers).isSelected();
     815        for (OsmPrimitive ref : (OsmPrimitive[]) referrers) {
     816            if (ref instanceof Relation && ref.isSelected())
     817                return true;
     818        }
     819        return false;
    810820    }
    811821
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r3630 r3836  
    110110        }
    111111        return false;
    112     }
    113 
    114     /* mappaint data */
    115     public boolean isMappaintArea = false;
    116     public Integer mappaintDrawnAreaCode = 0;
    117     /* end of mappaint data */
    118     @Override public void clearCached() {
    119         super.clearCached();
    120         isMappaintArea = false;
    121         mappaintDrawnAreaCode = 0;
    122112    }
    123113
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPaintVisitor.java

    r3831 r3836  
    33
    44import java.awt.Graphics2D;
    5 import java.awt.Polygon;
    6 import java.awt.Rectangle;
    75import java.awt.RenderingHints;
    8 import java.awt.geom.Point2D;
    96import java.util.ArrayList;
    10 import java.util.Collection;
    117import java.util.Collections;
    128import java.util.Comparator;
    13 import java.util.LinkedList;
     9import java.util.List;
    1410
    1511import org.openstreetmap.josm.Main;
    1612import org.openstreetmap.josm.data.Bounds;
    17 import org.openstreetmap.josm.data.coor.EastNorth;
    18 import org.openstreetmap.josm.data.coor.LatLon;
    1913import org.openstreetmap.josm.data.osm.BBox;
    2014import org.openstreetmap.josm.data.osm.DataSet;
     
    2216import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2317import org.openstreetmap.josm.data.osm.Relation;
    24 import org.openstreetmap.josm.data.osm.RelationMember;
    2518import org.openstreetmap.josm.data.osm.Way;
    26 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
    27 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
    28 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
    2919import org.openstreetmap.josm.gui.NavigatableComponent;
    3020import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
    3121import org.openstreetmap.josm.gui.mappaint.ElemStyle;
    3222import org.openstreetmap.josm.gui.mappaint.ElemStyles;
    33 import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
    3423import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
    3524import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
    36 import org.openstreetmap.josm.gui.mappaint.StyleCache;
     25import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
     26import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
     27import org.openstreetmap.josm.tools.Pair;
    3728
    3829public class MapPaintVisitor implements PaintVisitor {
    3930
    4031    private Graphics2D g;
     32    private boolean inactive;
    4133    private NavigatableComponent nc;
    4234
    43     private boolean zoomLevelDisplay;
    44     private boolean drawMultipolygon;
    45     private boolean drawRestriction;
    46     private boolean leftHandTraffic;
    4735    private ElemStyles styles;
    4836    private double circum;
    49     private double dist;
    50     private static int paintid = 0;
    51     private EastNorth minEN;
    52     private EastNorth maxEN;
    5337    private MapPainter painter;
    5438    private MapPaintSettings paintSettings;
    55 
    56     private boolean inactive;
    57 
    58     protected boolean isZoomOk(ElemStyle e) {
    59         if (!zoomLevelDisplay) /* show everything if the user wishes so */
    60             return true;
    61 
    62         if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */
    63             return (circum < 1500);
    64 
    65         return !(circum >= e.maxScale || circum < e.minScale);
    66     }
    67 
    68     public StyleCache getPrimitiveStyle(OsmPrimitive osm, boolean nodefault) {
    69         if(osm.mappaintStyle == null)
    70         {
    71             if(styles != null) {
    72                 osm.mappaintStyle = styles.get(osm);
    73                 if(osm instanceof Way) {
    74                     ((Way)osm).isMappaintArea = styles.isArea(osm);
    75                 }
    76             }
    77             if (osm.mappaintStyle.equals(StyleCache.EMPTY_STYLECACHE)) {
    78                 if(osm instanceof Node)
    79                     osm.mappaintStyle = StyleCache.SIMPLE_NODE_STYLECACHE;
    80                 else if (osm instanceof Way)
    81                     osm.mappaintStyle = StyleCache.UNTAGGED_WAY_STYLECACHE;
    82             }
    83         }
    84         if (nodefault && osm.mappaintStyle.equals(StyleCache.UNTAGGED_WAY_STYLECACHE))
    85             return StyleCache.EMPTY_STYLECACHE;
    86         return osm.mappaintStyle;
    87     }
    88 
    89     public IconElemStyle getPrimitiveNodeStyle(OsmPrimitive osm) {
    90         if(osm.mappaintStyle == null && styles != null) {
    91             IconElemStyle icon = styles.getIcon(osm);
    92             osm.mappaintStyle = StyleCache.create(icon);
    93             return icon;
    94         }
    95         for (ElemStyle s : osm.mappaintStyle.getStyles()) {
    96             if (s instanceof IconElemStyle)
    97                 return (IconElemStyle) s;
    98         }
    99         return null;
    100     }
    101 
    102     public boolean isPrimitiveArea(Way osm) {
    103         if(osm.mappaintStyle == null && styles != null) {
    104             osm.mappaintStyle = styles.get(osm);
    105             osm.isMappaintArea = styles.isArea(osm);
    106         }
    107         return osm.isMappaintArea;
    108     }
    109 
    110     public void drawNode(Node n) {
    111         /* check, if the node is visible at all */
    112         EastNorth en = n.getEastNorth();
    113         if((en.east()  > maxEN.east() ) ||
    114                 (en.north() > maxEN.north()) ||
    115                 (en.east()  < minEN.east() ) ||
    116                 (en.north() < minEN.north()))
    117             return;
    118 
    119         StyleCache sc = getPrimitiveStyle(n, false);
    120 
    121         for (ElemStyle s : sc.getStyles()) {
    122             if (isZoomOk(s)) {
    123                 s.paintPrimitive(n, paintSettings, painter, data.isSelected(n), false);
    124             }
    125 
    126         }
    127     }
    128 
    129     public void drawWay(Way w, int fillAreas) {
    130         if(w.getNodesCount() < 2)
    131             return;
    132 
    133         if (w.hasIncompleteNodes())
    134             return;
    135 
    136         /* check, if the way is visible at all */
    137         double minx = 10000;
    138         double maxx = -10000;
    139         double miny = 10000;
    140         double maxy = -10000;
    141 
    142         for (Node n : w.getNodes())
    143         {
    144             if(n.getEastNorth().east() > maxx) {
    145                 maxx = n.getEastNorth().east();
    146             }
    147             if(n.getEastNorth().north() > maxy) {
    148                 maxy = n.getEastNorth().north();
    149             }
    150             if(n.getEastNorth().east() < minx) {
    151                 minx = n.getEastNorth().east();
    152             }
    153             if(n.getEastNorth().north() < miny) {
    154                 miny = n.getEastNorth().north();
    155             }
    156         }
    157 
    158         if ((minx > maxEN.east()) ||
    159                 (miny > maxEN.north()) ||
    160                 (maxx < minEN.east()) ||
    161                 (maxy < minEN.north()))
    162             return;
    163 
    164         StyleCache sc = getPrimitiveStyle(w, false);
    165         for (ElemStyle s : sc.getStyles()) {
    166             if(!isZoomOk(s))
    167                 return;
    168             if (fillAreas > dist || !(s instanceof AreaElemStyle)) {
    169                 s.paintPrimitive(w, paintSettings, painter, data.isSelected(w), false);
    170             }
    171         }
    172     }
    173 
    174     public void paintUnselectedRelation(Relation r) {
    175         if (drawMultipolygon && "multipolygon".equals(r.get("type")))
    176             drawMultipolygon(r);
    177         else if (drawRestriction && "restriction".equals(r.get("type"))) {
    178             IconElemStyle nodeStyle = getPrimitiveNodeStyle(r);
    179             if (nodeStyle != null) {
    180                 painter.drawRestriction(r, leftHandTraffic, nodeStyle);
    181             }
    182         }
    183     }
    184 
    185     public boolean drawMultipolygon(Relation r) {
    186         boolean drawn = false;
    187 
    188         Multipolygon multipolygon = new Multipolygon(nc);
    189         multipolygon.load(r);
    190 
    191         AreaElemStyle areaStyle = null;
    192         LineElemStyle lineStyle = null;
    193         for (ElemStyle s : getPrimitiveStyle(r, false).getStyles()) {
    194             if (s instanceof AreaElemStyle) {
    195                 areaStyle = (AreaElemStyle) s;
    196             } else if (s instanceof LineElemStyle) {
    197                 lineStyle = (LineElemStyle) s;
    198             }
    199         }
    200 
    201         boolean disabled = r.isDisabled();
    202         // If area style was not found for relation then use style of ways
    203         if(styles != null && areaStyle == null) {
    204             for (Way w : multipolygon.getOuterWays()) {
    205                 for (ElemStyle s : styles.getArea(w).getStyles()) {
    206                     if (s instanceof AreaElemStyle) {
    207                         areaStyle = (AreaElemStyle) s;
    208                     } else if (s instanceof LineElemStyle) {
    209                         lineStyle = (LineElemStyle) s;
    210                     }
    211                 }
    212                 disabled = disabled || w.isDisabled();
    213                 if(areaStyle != null) {
    214                     break;
    215                 }
    216             }
    217         }
    218 
    219         if (areaStyle != null) {
    220             boolean zoomok = isZoomOk(areaStyle);
    221             boolean visible = false;
    222 
    223             drawn = true;
    224 
    225             if(zoomok && !disabled && !multipolygon.getOuterWays().isEmpty()) {
    226                 for (PolyData pd : multipolygon.getCombinedPolygons()) {
    227                     Polygon p = pd.get();
    228                     if(!isPolygonVisible(p)) {
    229                         continue;
    230                     }
    231 
    232                     boolean selected = pd.selected || data.isSelected(r);
    233                     painter.drawArea(p, selected ? paintSettings.getRelationSelectedColor()
    234                                 : areaStyle.color, painter.getAreaName(r));
    235                     visible = true;
    236                 }
    237             }
    238 
    239             if(!visible)
    240                 return drawn;
    241             for (Way wInner : multipolygon.getInnerWays()) {
    242                 StyleCache inner = getPrimitiveStyle(wInner, true);
    243                 AreaElemStyle innerArea = null;
    244                 for (ElemStyle s : inner.getStyles()) {
    245                     if (s instanceof AreaElemStyle) {
    246                         innerArea = (AreaElemStyle) s;
    247                         break;
    248                     }
    249                 }
    250 
    251                 if(inner.getStyles().isEmpty()) {
    252                     if (data.isSelected(wInner) || disabled)
    253                         continue;
    254                     if(zoomok && (wInner.mappaintDrawnCode != paintid || multipolygon.getOuterWays().isEmpty())) {
    255                         lineStyle.paintPrimitive(wInner, paintSettings,
    256                                 painter, (data.isSelected(wInner) || data.isSelected(r)), false);
    257                     }
    258                     wInner.mappaintDrawnCode = paintid;
    259                 }
    260                 else {
    261                     if(areaStyle.equals(innerArea)) {
    262                         wInner.mappaintDrawnAreaCode = paintid;
    263                        
    264                         if(!data.isSelected(wInner)) {
    265                             wInner.mappaintDrawnCode = paintid;
    266                             drawWay(wInner, 0);
    267                         }
    268                     }
    269                 }
    270             }
    271             for (Way wOuter : multipolygon.getOuterWays()) {
    272                 StyleCache outer = getPrimitiveStyle(wOuter, true);
    273                 boolean hasOuterArea = false;
    274                 for (ElemStyle s : outer.getStyles()) {
    275                     if (s instanceof AreaElemStyle) {
    276                         hasOuterArea = true;
    277                         break;
    278                     }
    279                 }
    280 
    281                 if (outer.getStyles().isEmpty()) {
    282                     // Selected ways are drawn at the very end
    283                     if (data.isSelected(wOuter))
    284                         continue;
    285                     if(zoomok) {
    286                         lineStyle.paintPrimitive(wOuter, paintSettings, painter,
    287                             (data.isSelected(wOuter) || data.isSelected(r)), r.isSelected());
    288                     }
    289                     wOuter.mappaintDrawnCode = paintid;
    290                 } else if (hasOuterArea) {
    291                     wOuter.mappaintDrawnAreaCode = paintid;
    292                     if(!data.isSelected(wOuter)) {
    293                         wOuter.mappaintDrawnCode = paintid;
    294                         drawWay(wOuter, 0);
    295                     }
    296                 }
    297             }
    298         }
    299         return drawn;
    300     }
    301 
    302     protected boolean isPolygonVisible(Polygon polygon) {
    303         Rectangle bounds = polygon.getBounds();
    304         if (bounds.width == 0 && bounds.height == 0) return false;
    305         if (bounds.x > nc.getWidth()) return false;
    306         if (bounds.y > nc.getHeight()) return false;
    307         if (bounds.x + bounds.width < 0) return false;
    308         if (bounds.y + bounds.height < 0) return false;
    309         return true;
    310     }
    311 
    312     protected Point2D getCentroid(Polygon p)
    313     {
    314         double cx = 0.0, cy = 0.0, a = 0.0;
    315 
    316         // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
    317         // Faked it slowly using j.  If this is really gets used, this should be fixed.
    318         for (int i = 0;  i < p.npoints;  i++) {
    319             int j = i+1 == p.npoints ? 0 : i+1;
    320             a += (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
    321             cx += (p.xpoints[i] + p.xpoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
    322             cy += (p.ypoints[i] + p.ypoints[j]) * (p.xpoints[i] * p.ypoints[j] - p.ypoints[i] * p.xpoints[j]);
    323         }
    324         return new Point2D.Double(cx / (3.0*a), cy / (3.0*a));
    325     }
    326 
    327     protected double getArea(Polygon p)
    328     {
    329         double sum = 0.0;
    330 
    331         // usually requires points[0] == points[npoints] and can then use i+1 instead of j.
    332         // Faked it slowly using j.  If this is really gets used, this should be fixed.
    333         for (int i = 0;  i < p.npoints;  i++) {
    334             int j = i+1 == p.npoints ? 0 : i+1;
    335             sum = sum + (p.xpoints[i] * p.ypoints[j]) - (p.ypoints[i] * p.xpoints[j]);
    336         }
    337         return Math.abs(sum/2.0);
    338     }
    339 
    340     DataSet data;
    341 
    342     <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
    343         ArrayList<T> sorted = new ArrayList<T>(prims);
    344         Collections.sort(sorted,
    345                 new Comparator<T>() {
    346             public int compare(T o1, T o2) {
    347                 boolean s1 = data.isSelected(o1);
    348                 boolean s2 = data.isSelected(o2);
    349                 if (s1 && !s2)
    350                     return 1;
    351                 if (!s1 && s2)
    352                     return -1;
    353                 return o1.compareTo(o2);
    354             }
    355         });
    356         return sorted;
    357     }
    358 
    359     /* Shows areas before non-areas */
     39    private DataSet data;
     40
     41    private class StyleCollector {
     42        private List<Pair<ElemStyle, OsmPrimitive>> styleElems;
     43        protected boolean memberSelected = false;
     44        private Class klass;
     45
     46        public StyleCollector(Class<?> klass) {
     47            styleElems = new ArrayList<Pair<ElemStyle, OsmPrimitive>>();
     48            this.klass = klass;
     49        }
     50
     51        public void add(OsmPrimitive osm) {
     52            StyleList sl = styles.get(osm, circum, nc);
     53            for (ElemStyle s : sl) {
     54                if (klass.isInstance(s)) {
     55                    styleElems.add(new Pair<ElemStyle, OsmPrimitive>(s, osm));
     56                }
     57            }
     58        }
     59
     60        public void drawAll() {
     61            Collections.sort(styleElems, STYLE_COMPARATOR);
     62            for (Pair<ElemStyle, OsmPrimitive> p : styleElems) {
     63                p.a.paintPrimitive(p.b, paintSettings, painter, data.isSelected(p.b), memberSelected);
     64            }
     65        }
     66
     67        public boolean isMemberSelected() {
     68            return memberSelected;
     69        }
     70
     71        public void setMemberSelected(boolean memberSelected) {
     72            this.memberSelected = memberSelected;
     73        }
     74    }
     75
     76    private final static Comparator<Pair<ElemStyle, OsmPrimitive>> STYLE_COMPARATOR = new Comparator<Pair<ElemStyle, OsmPrimitive>>() {
     77        @Override
     78        public int compare(Pair<ElemStyle, OsmPrimitive> p1, Pair<ElemStyle, OsmPrimitive> p2) {
     79            int d1 = Float.compare(p1.a.z_index, p2.a.z_index);
     80            if (d1 != 0)
     81                return d1;
     82            if (p1.a == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && p2.a != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE)
     83                return 1;
     84            if (p1.a != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && p2.a == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE)
     85                return -1;
     86            // newer primitives to the front
     87            long id = p1.b.getUniqueId() - p2.b.getUniqueId();
     88            if (id > 0)
     89                return 1;
     90            if (id < 0)
     91                return -1;
     92            return Float.compare(p1.a.object_z_index, p2.a.object_z_index);
     93        }
     94    };
     95
    36096    public void visitAll(final DataSet data, boolean virtual, Bounds bounds) {
    36197        //long start = System.currentTimeMillis();
    36298        BBox bbox = new BBox(bounds);
    36399        this.data = data;
    364         ++paintid;
    365 
    366         int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
    367         LatLon ll1 = nc.getLatLon(0, 0);
    368         LatLon ll2 = nc.getLatLon(100, 0);
    369         dist = ll1.greatCircleDistance(ll2);
    370 
    371         zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
     100
     101        styles = MapPaintStyles.getStyles();
     102
    372103        circum = nc.getDist100Pixel();
    373         styles = MapPaintStyles.getStyles();
    374         drawMultipolygon = Main.pref.getBoolean("mappaint.multipolygon", true);
    375         drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
    376         leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
    377         minEN = nc.getEastNorth(0, nc.getHeight() - 1);
    378         maxEN = nc.getEastNorth(nc.getWidth() - 1, 0);
     104        boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000);
     105        boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true);
     106        styles.setDrawMultipolygon(drawMultipolygon);
     107        boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
     108        boolean leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false);
    379109
    380110        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     
    383113
    384114        this.paintSettings = MapPaintSettings.INSTANCE;
    385         this.painter = new MapPainter(paintSettings, g, inactive, nc, virtual, dist, circum);
    386 
    387         if (fillAreas > dist && styles != null && styles.hasAreas()) {
    388             Collection<Way> noAreaWays = new LinkedList<Way>();
    389             final Collection<Way> ways = data.searchWays(bbox);
    390 
    391             /*** disabled ***/
    392             for (final Way osm : ways) {
    393                 if (osm.isDisabled() && osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
    394                     drawWay(osm, 0);
    395                     osm.mappaintDrawnCode = paintid;
    396                 }
    397             }
    398 
    399             /*** RELATIONS ***/
    400             for (final Relation osm: data.searchRelations(bbox)) {
    401                 if (osm.isDrawable()) {
    402                     paintUnselectedRelation(osm);
    403                 }
    404             }
    405 
    406             /*** AREAS ***/
    407             for (final Way osm : selectedLast(data, ways)) {
    408                 if (osm.isDrawable() && osm.mappaintDrawnCode != paintid) {
    409                     if (isPrimitiveArea(osm)) {
    410                         if(osm.mappaintDrawnAreaCode != paintid)
    411                             drawWay(osm, fillAreas);
    412                     } else if(!data.isSelected(osm)) {
    413                         noAreaWays.add(osm);
    414                     }
    415                 }
    416             }
    417 
    418             /*** WAYS ***/
    419             for (final Way osm : noAreaWays) {
    420                 drawWay(osm, 0);
    421                 osm.mappaintDrawnCode = paintid;
    422             }
    423         } else {
    424             drawMultipolygon = false;
    425             final Collection<Way> ways = data.searchWays(bbox);
    426 
    427             /*** WAYS (disabled)  ***/
    428             for (final Way way: ways) {
    429                 if (way.isDisabled() && way.isDrawable() && !data.isSelected(way)) {
    430                     drawWay(way, 0);
    431                     way.mappaintDrawnCode = paintid;
    432                 }
    433             }
    434 
    435             /*** RELATIONS ***/
    436             for (final Relation osm: data.searchRelations(bbox)) {
    437                 if (osm.isDrawable()) {
    438                     paintUnselectedRelation(osm);
    439                 }
    440             }
    441 
    442             /*** WAYS (filling disabled)  ***/
    443             for (final Way way: ways) {
    444                 if (way.isDrawable() && !data.isSelected(way)) {
    445                     drawWay(way, 0);
    446                 }
    447             }
    448         }
    449 
    450         /*** SELECTED  ***/
    451         for (final OsmPrimitive osm : data.getSelected()) {
    452             if (osm.isUsable() && !(osm instanceof Node) && (osm instanceof Relation || osm.mappaintDrawnCode != paintid)) {
    453                 osm.visit(new AbstractVisitor() {
    454                     public void visit(Way w) {
    455                         drawWay(w, 0);
    456                     }
    457 
    458                     public void visit(Node n) {
    459                         // Selected nodes are painted in following part
    460                     }
    461 
    462                     public void visit(Relation r) {
    463                         for (RelationMember m : r.getMembers()) {
    464                             OsmPrimitive osm = m.getMember();
    465                             if(osm.isDrawable()) {
    466                                 StyleCache sc = getPrimitiveStyle(m.getMember(), false);
    467                                 if(osm instanceof Way)
    468                                 {
    469                                     for (ElemStyle s : sc.getStyles()) {
    470                                         if (!(s instanceof AreaElemStyle)) {
    471                                             s.paintPrimitive(osm, paintSettings, painter, data.isSelected(osm), true);
    472                                         }
    473                                     }
    474                                 }
    475                                 else if(osm instanceof Node)
    476                                 {
    477                                     for (ElemStyle s : sc.getStyles()) {
    478                                         if (isZoomOk(s)) {
    479                                             s.paintPrimitive(osm, paintSettings, painter, data.isSelected(osm), true);
    480                                         }
    481                                     }
    482                                 }
    483                                 osm.mappaintDrawnCode = paintid;
    484                             }
    485                         }
    486                     }
    487                 });
    488             }
    489         }
    490 
    491         /*** NODES ***/
    492         for (final Node osm: data.searchNodes(bbox)) {
    493             if (!osm.isIncomplete() && !osm.isDeleted() && (data.isSelected(osm) || !osm.isDisabledAndHidden())
    494                     && osm.mappaintDrawnCode != paintid) {
    495                 drawNode(osm);
    496             }
    497         }
     115        this.painter = new MapPainter(paintSettings, g, inactive, nc, virtual, circum, leftHandTraffic);
     116
     117        StyleCollector scDisabledLines = new StyleCollector(LineElemStyle.class);
     118        StyleCollector scSelectedLines = new StyleCollector(LineElemStyle.class);
     119        StyleCollector scSelectedAreas = new StyleCollector(AreaElemStyle.class);
     120        StyleCollector scMemberLines = new StyleCollector(LineElemStyle.class);
     121        scMemberLines.setMemberSelected(true);
     122        StyleCollector scNormalAreas = new StyleCollector(AreaElemStyle.class);
     123        StyleCollector scNormalLines = new StyleCollector(LineElemStyle.class);
     124        for (final Way w : data.searchWays(bbox)) {
     125            if (w.isDrawable()) {
     126                if (w.isDisabled()) {
     127                    scDisabledLines.add(w);
     128                } else if (w.isSelected()) {
     129                    scSelectedLines.add(w);
     130                    if (drawArea) {
     131                        scSelectedAreas.add(w);
     132                    }
     133                } else if (w.isMemberOfSelected()) {
     134                    scMemberLines.add(w);
     135                    if (drawArea) {
     136                        scNormalAreas.add(w);
     137                    }
     138                } else {
     139                    scNormalLines.add(w);
     140                    if (drawArea) {
     141                        scNormalAreas.add(w);
     142                    }
     143                }
     144            }
     145        }
     146        scDisabledLines.drawAll();
     147        scDisabledLines = null;
     148
     149        StyleCollector scDisabledNodes = new StyleCollector(NodeElemStyle.class);
     150        StyleCollector scSelectedNodes = new StyleCollector(NodeElemStyle.class);
     151        StyleCollector scMemberNodes = new StyleCollector(NodeElemStyle.class);
     152        scMemberNodes.setMemberSelected(true);
     153        StyleCollector scNormalNodes = new StyleCollector(NodeElemStyle.class);
     154        for (final Node n: data.searchNodes(bbox)) {
     155            if (n.isDrawable()) {
     156                if (n.isDisabled()) {
     157                    scDisabledNodes.add(n);
     158                } else if (n.isSelected()) {
     159                    scSelectedNodes.add(n);
     160                } else if (n.isMemberOfSelected()) {
     161                    scMemberNodes.add(n);
     162                } else {
     163                    scNormalNodes.add(n);
     164                }
     165            }
     166        }
     167        scDisabledNodes.drawAll();
     168        scDisabledNodes = null;
     169
     170        StyleCollector scDisabledRestrictions = new StyleCollector(NodeElemStyle.class);
     171        StyleCollector scNormalRestrictions = new StyleCollector(NodeElemStyle.class);
     172        StyleCollector scSelectedRestrictions = new StyleCollector(NodeElemStyle.class);
     173        for (Relation r: data.searchRelations(bbox)) {
     174            if (r.isDrawable()) {
     175                if (r.isDisabled()) {
     176                    if (drawRestriction) {
     177                        scDisabledRestrictions.add(r);
     178                    }
     179                } else if (r.isSelected()) {
     180                    if (drawMultipolygon) {
     181                        scSelectedAreas.add(r);
     182                    }
     183                    if (drawRestriction) {
     184                        scSelectedRestrictions.add(r);
     185                    }
     186                } else {
     187                    if (drawMultipolygon) {
     188                        scNormalAreas.add(r);
     189                    }
     190                    if (drawRestriction) {
     191                        scNormalRestrictions.add(r);
     192                    }
     193                }
     194            }
     195        }
     196        scDisabledRestrictions.drawAll();
     197        scDisabledRestrictions = null;
     198
     199        scNormalAreas.drawAll();
     200        scSelectedAreas.drawAll();
     201        scNormalLines.drawAll();
     202        scMemberLines.drawAll();
     203        scSelectedLines.drawAll();
     204        scNormalNodes.drawAll();
     205        scNormalRestrictions.drawAll();
     206        scMemberNodes.drawAll();
     207        scSelectedRestrictions.drawAll();
     208        scSelectedNodes.drawAll();
    498209
    499210        painter.drawVirtualNodes(data.searchWays(bbox));
    500         //System.err.println("PAINTING TOOK "+(System.currentTimeMillis() - start));
     211        //System.err.println("PAINTING TOOK "+(System.currentTimeMillis() - start)+ " (at scale "+circum+")");
    501212    }
    502213
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java

    r3831 r3836  
    2626import org.openstreetmap.josm.data.osm.RelationMember;
    2727import org.openstreetmap.josm.data.osm.Way;
     28import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
     29import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
    2830import org.openstreetmap.josm.gui.NavigatableComponent;
    29 import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
     31import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
    3032import org.openstreetmap.josm.tools.ImageProvider;
    3133import org.openstreetmap.josm.tools.LanguageInfo;
     
    5860    private final double circum;
    5961
     62    private final boolean leftHandTraffic;
     63
    6064    private final Collection<String> regionalNameOrder;
    6165
     
    6670    public MapPainter(MapPaintSettings settings, Graphics2D g,
    6771        boolean inactive, NavigatableComponent nc, boolean virtual,
    68         double dist, double circum) {
    69 
     72        double circum, boolean leftHandTraffic)
     73    {
    7074        this.g = g;
    7175        this.inactive = inactive;
    7276        this.nc = nc;
    73         this.useStrokes = settings.getUseStrokesDistance() > dist;
    74         this.showNames = settings.getShowNamesDistance() > dist;
    75         this.showIcons = settings.getShowIconsDistance() > dist;
     77        this.useStrokes = settings.getUseStrokesDistance() > circum;
     78        this.showNames = settings.getShowNamesDistance() > circum;
     79        this.showIcons = settings.getShowIconsDistance() > circum;
    7680        this.outlineOnly = settings.isOutlineOnly();
    7781
     
    9397        this.regionalNameOrder = Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(names));
    9498        this.circum = circum;
    95     }
    96 
    97     public void drawWay(Way way, Color color, int width, float dashed[], Color dashedColor, boolean showDirection,
     99        this.leftHandTraffic = leftHandTraffic;
     100    }
     101
     102    public void drawWay(Way way, Color color, float width, float dashed[], Color dashedColor, boolean showDirection,
    98103            boolean reversedDirection, boolean showHeadArrowOnly) {
    99104
     
    154159    }
    155160
    156     private void displaySegments(GeneralPath path, GeneralPath arrows, Color color, int width, float dashed[], Color dashedColor) {
     161    private void displaySegments(GeneralPath path, GeneralPath arrows, Color color, float width, float dashed[], Color dashedColor) {
    157162        g.setColor(inactive ? inactiveColor : color);
    158163        if (useStrokes) {
    159             if (dashed.length > 0) {
     164            if (dashed == null || dashed.length > 0) {
    160165                g.setStroke(new BasicStroke(width,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0, dashed,0));
    161166            } else {
     
    168173        if(!inactive && useStrokes && dashedColor != null) {
    169174            g.setColor(dashedColor);
    170             if (dashed.length > 0) {
     175            if (dashed == null || dashed.length > 0) {
    171176                float[] dashedOffset = new float[dashed.length];
    172177                System.arraycopy(dashed, 1, dashedOffset, 0, dashed.length - 1);
     
    319324    }
    320325
     326    public void drawArea(Relation r, Color color, String name) {
     327        Multipolygon multipolygon = new Multipolygon(nc);
     328        multipolygon.load(r);
     329        if(!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
     330            for (PolyData pd : multipolygon.getCombinedPolygons()) {
     331                Polygon p = pd.get();
     332                if(!isPolygonVisible(p)) {
     333                    continue;
     334                }
     335                drawArea(p, color, getAreaName(r));
     336            }
     337        }
     338    }
     339
     340    private boolean isPolygonVisible(Polygon polygon) {
     341        Rectangle bounds = polygon.getBounds();
     342        if (bounds.width == 0 && bounds.height == 0) return false;
     343        if (bounds.x > nc.getWidth()) return false;
     344        if (bounds.y > nc.getHeight()) return false;
     345        if (bounds.x + bounds.width < 0) return false;
     346        if (bounds.y + bounds.height < 0) return false;
     347        return true;
     348    }
     349
    321350    public void drawRestriction(ImageIcon icon, Point pVia, double vx, double vx2, double vy, double vy2, double iconAngle, boolean selected) {
    322351        /* rotate icon with direction last node in from to */
     
    334363    }
    335364
    336     public void drawRestriction(Relation r, boolean leftHandTraffic, IconElemStyle icon) {
     365    public void drawRestriction(Relation r, NodeElemStyle icon) {
    337366
    338367        Way fromWay = null;
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java

    r3825 r3836  
    2626import org.openstreetmap.josm.gui.mappaint.ElemStyles;
    2727import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
    28 import org.openstreetmap.josm.gui.mappaint.StyleCache;
    2928import org.openstreetmap.josm.gui.mappaint.xml.AreaPrototype;
    3029
     
    4746    private final List<List<Node>> nonClosedWays = new ArrayList<List<Node>>();
    4847
     48    private final double SCALE = 1.0; // arbitrary scale - we could test every possible scale, but this should suffice
     49
    4950    public MultipolygonTest() {
    5051        super(tr("Multipolygon"),
     
    115116    public void visit(Way w) {
    116117        if (styles != null && !w.isClosed()) {
    117             AreaPrototype e = styles.getAreaProto(w);
    118             if (e != null && ! e.closed) {
    119                 errors.add( new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED,  w));
     118            for (ElemStyle s : styles.generateStyles(w, SCALE, null, false).a) {
     119                if (s instanceof AreaElemStyle) {
     120                    errors.add( new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED,  w));
     121                    break;
     122                }
    120123            }
    121124        }
     
    143146            List<List<Node>> outerWays = joinWays(polygon.getOuterWays());
    144147            if (styles != null) {
    145                 StyleCache sc = styles.get(r);
    146148
    147149                AreaElemStyle area = null;
    148                 for (ElemStyle s : sc.getStyles()) {
     150                for (ElemStyle s : styles.generateStyles(r, SCALE, null, false).a) {
    149151                    if (s instanceof AreaElemStyle) {
    150152                        area = (AreaElemStyle) s;
     
    155157                if (area == null) {
    156158                    errors.add( new TestError(this, Severity.OTHER, tr("No style in multipolygon relation"),
    157                     NO_STYLE_POLYGON, r));
     159                            NO_STYLE_POLYGON, r));
    158160                    for (Way w : polygon.getOuterWays()) {
    159161
    160                         for (ElemStyle s : styles.getArea(w).getStyles()) {
     162                        for (ElemStyle s : styles.generateStyles(r, SCALE, null, true).a) {
    161163                            if (s instanceof AreaElemStyle) {
    162164                                area = (AreaElemStyle) s;
     
    173175                    for (Way wInner : polygon.getInnerWays()) {
    174176                        AreaElemStyle areaInner = null;
    175                         for (ElemStyle s : styles.get(wInner).getStyles()) {
     177                        for (ElemStyle s : styles.generateStyles(wInner, SCALE, null, false).a) {
    176178                            if (s instanceof AreaElemStyle) {
    177179                                areaInner = (AreaElemStyle) s;
     
    185187                            l.add(wInner);
    186188                            errors.add( new TestError(this, Severity.WARNING, tr("Style for inner way equals multipolygon"),
    187                             INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
     189                                    INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
    188190                        }
    189191                    }
    190192                    for (Way wOuter : polygon.getOuterWays()) {
    191193                        AreaElemStyle areaOuter = null;
    192                         for (ElemStyle s : styles.get(wOuter).getStyles()) {
     194                        for (ElemStyle s : styles.generateStyles(wOuter, SCALE, null, false).a) {
    193195                            if (s instanceof AreaElemStyle) {
    194196                                areaOuter = (AreaElemStyle) s;
  • trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java

    r3824 r3836  
    55
    66import org.openstreetmap.josm.data.osm.OsmPrimitive;
     7import org.openstreetmap.josm.data.osm.Relation;
    78import org.openstreetmap.josm.data.osm.Way;
    89import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
     
    1415    public Color color;
    1516
    16     public AreaElemStyle(long minScale, long maxScale, Color color) {
    17         super(minScale, maxScale);
     17    protected AreaElemStyle(Cascade c, Color color) {
     18        super(c);
    1819        this.color = color;
     20    }
     21
     22    public static AreaElemStyle create(Cascade c) {
     23        Color color = c.get("fill-color", null, Color.class);
     24        if (color == null)
     25            return null;
     26        return new AreaElemStyle(c, color);
    1927    }
    2028
     
    2533            String name = painter.isShowNames() ? painter.getAreaName(w) : null;
    2634            painter.drawArea(w, w.isSelected() ? paintSettings.getSelectedColor() : color, name);
    27             // line.paintPrimitive(way, paintSettings, painter, selected);
     35        } else if (primitive instanceof Relation) {
     36            painter.drawArea((Relation) primitive, selected ? paintSettings.getRelationSelectedColor() : color, painter.getAreaName(primitive));
    2837        }
    2938    }
     
    4049    @Override
    4150    public int hashCode() {
    42         return color.hashCode();
     51        return 11 * super.hashCode() + color.hashCode();
    4352    }
    4453
    4554    @Override
    4655    public String toString() {
    47         return "AreaElemStyle{" + "color=" + color + '}';
     56        return "AreaElemStyle{" + super.toString() + "color=" + color + '}';
    4857    }
    4958}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java

    r3824 r3836  
    77
    88abstract public class ElemStyle {
    9     // zoom range to display the feature
    10     public long minScale;
    11     public long maxScale;
     9   
     10    public float z_index;
     11    public float object_z_index;
    1212
    13     public ElemStyle(long minScale, long maxScale) {
    14         this.minScale = minScale;
    15         this.maxScale = maxScale;
     13    public ElemStyle(float z_index, float object_z_index) {
     14        this.z_index = z_index;
     15        this.object_z_index = object_z_index;
    1616    }
     17
     18    protected ElemStyle(Cascade c) {
     19        z_index = c.get("z-index", 0f, Float.class);
     20        object_z_index = c.get("object-z-index", 0f, Float.class);
     21    }
     22
     23    public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member);
    1724
    1825    @Override
     
    2128            return false;
    2229        ElemStyle s = (ElemStyle) o;
    23         return minScale == s.minScale && maxScale == s.maxScale;
     30        return z_index == s.z_index && object_z_index == s.object_z_index;
    2431    }
    2532
    2633    @Override
    2734    public int hashCode() {
    28         return getClass().hashCode();
     35        int hash = 5;
     36        hash = 41 * hash + Float.floatToIntBits(this.z_index);
     37        hash = 41 * hash + Float.floatToIntBits(this.object_z_index);
     38        return hash;
    2939    }
    3040
    31     public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member);
     41    @Override
     42    public String toString() {
     43        if (z_index != 0f || object_z_index != 0f)
     44            return String.format("z_idx=%s/%s ", z_index, object_z_index);
     45        return "";
     46    }
    3247}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java

    r3825 r3836  
    22package org.openstreetmap.josm.gui.mappaint;
    33
    4 import static org.openstreetmap.josm.tools.Utils.equal;
    5 
     4import java.awt.Color;
    65import java.util.ArrayList;
    76import java.util.Collection;
    87import java.util.HashSet;
     8import java.util.Iterator;
    99import java.util.List;
     10import java.util.Map.Entry;
    1011import java.util.Set;
    1112
     
    1314import org.openstreetmap.josm.data.osm.Node;
    1415import org.openstreetmap.josm.data.osm.OsmPrimitive;
     16import org.openstreetmap.josm.data.osm.Relation;
    1517import org.openstreetmap.josm.data.osm.Way;
    16 import org.openstreetmap.josm.gui.mappaint.xml.AreaPrototype;
    17 import org.openstreetmap.josm.gui.mappaint.xml.IconPrototype;
    18 import org.openstreetmap.josm.gui.mappaint.xml.LinePrototype;
    19 import org.openstreetmap.josm.gui.mappaint.xml.LinemodPrototype;
     18import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
     19import org.openstreetmap.josm.gui.NavigatableComponent;
     20import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
    2021import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
    2122import org.openstreetmap.josm.tools.FilteredCollection;
     23import org.openstreetmap.josm.tools.Pair;
    2224import org.openstreetmap.josm.tools.Predicate;
     25import org.openstreetmap.josm.tools.Utils;
    2326
    2427public class ElemStyles {
    2528    private List<XmlStyleSource> styleSources;
     29    private boolean drawMultipolygon;
    2630
    2731    public ElemStyles()
     
    4145            @Override
    4246            public boolean evaluate(XmlStyleSource s) {
    43                 return equal(s.getPrefName(), name);
     47                return Utils.equal(s.getPrefName(), name);
    4448            }
    4549
    4650        });
    47     }
    48 
    49     public static class WayPrototypesRecord {
    50         public LinePrototype line;
    51         public List<LinemodPrototype> linemods;
    52         public AreaPrototype area;
    53 
    54         public List<ElemStyle> createStyles() {
    55             List<ElemStyle> ret = new ArrayList<ElemStyle>();
    56             if (area != null) {
    57                 ret.add(area.createStyle());
    58             }
    59             if (line != null) {
    60                 ret.add(line.createStyle());
    61             } else {
    62                 if (area != null) {
    63                     ret.add(LineElemStyle.createSimpleLineStyle(area.color));
    64                 } else {
    65                     ret.add(LineElemStyle.UNTAGGED_WAY);
    66                 }
    67             }
    68 
    69             if (linemods != null) {
    70                 for (LinemodPrototype p : linemods) {
    71                     LineElemStyle s = p.createStyle(line.getWidth());
    72                     if (p.over) {
    73                         ret.add(s);
    74                     } else {
    75                         ret.add(0, s);
    76                     }
    77                 }
    78             }
    79             return ret;
    80         }
    81     }
    82 
    83     public StyleCache get(OsmPrimitive osm) {
    84         if (osm instanceof Node) {
    85             IconPrototype icon = getNode(osm);
    86             if (icon == null)
    87                 return StyleCache.EMPTY_STYLECACHE;
    88             return StyleCache.create(icon.createStyle());
    89         } else {
    90             WayPrototypesRecord p = get(osm, false);
    91             return StyleCache.create(p.createStyles());
    92         }
    93     }
    94 
    95     public IconPrototype getNode(OsmPrimitive osm) {
    96         IconPrototype icon = null;
    97         for (XmlStyleSource s : getStyleSources()) {
    98             icon = s.getNode(osm, icon);
    99         }
    100         return icon;
    101     }
    102 
    103     private WayPrototypesRecord get(OsmPrimitive osm, boolean forceArea) {
    104         WayPrototypesRecord p = new WayPrototypesRecord();
    105         for (XmlStyleSource s : getStyleSources()) {
    106             s.get(osm, forceArea || !(osm instanceof Way) || ((Way) osm).isClosed(), p);
    107         }
    108         return p;
    109     }
    110 
    111     public boolean hasAreas() {
    112         for (XmlStyleSource s : getStyleSources()) {
    113             if (s.hasAreas())
    114                 return true;
    115         }
    116         return false;
    117     }
    118 
    119     public boolean isArea(OsmPrimitive osm) {
    120         for (XmlStyleSource s : getStyleSources()) {
    121             if (s.isArea(osm))
    122                 return true;
    123         }
    124         return false;
    125     }
    126 
    127     public StyleCache getArea(Way osm) {
    128         if (osm.hasKeys()) {
    129             /* force area mode also for unclosed ways */
    130             WayPrototypesRecord p = get(osm, true);
    131             if (p.area != null)
    132                 return StyleCache.create(p.createStyles());
    133         }
    134         return StyleCache.EMPTY_STYLECACHE;
    135     }
    136 
    137     public AreaPrototype getAreaProto(Way osm) {
    138         if (osm.hasKeys()) {
    139             /* force area mode also for unclosed ways */
    140             WayPrototypesRecord p = get(osm, true);
    141             if (p.area != null)
    142                 return p.area;
    143         }
    144         return null;
    145     }
    146 
    147     public IconElemStyle getIcon(OsmPrimitive osm) {
    148         if (!osm.hasKeys())
    149             return null;
    150         NodeElemStyle icon = getNode(osm).createStyle();
    151         if (icon instanceof IconElemStyle) {
    152             return (IconElemStyle) icon;
    153         }
    154         return null;
    15551    }
    15652
     
    16561        return names;
    16662    }
     63
     64    public StyleList get(OsmPrimitive osm, double scale, NavigatableComponent nc) {
     65        return getStyleCacheWithRange(osm, scale, nc).a;
     66    }
     67
     68    public Pair<StyleList, Range> getStyleCacheWithRange(OsmPrimitive osm, double scale, NavigatableComponent nc) {
     69        if (osm.mappaintStyle == null) {
     70            osm.mappaintStyle = StyleCache.EMPTY_STYLECACHE;
     71        } else {
     72            Pair<StyleList, Range> lst = osm.mappaintStyle.getWithRange(scale);
     73            if (lst.a != null)
     74                return lst;
     75        }
     76        Pair<StyleList, Range> p = getImpl(osm, scale, nc);
     77        if (osm instanceof Node && p.a.isEmpty()) {
     78            p.a = StyleList.SIMPLE_NODE;
     79        } else if (osm instanceof Way && !Utils.exists(p.a, LineElemStyle.class)) {
     80            AreaElemStyle area = Utils.find(p.a, AreaElemStyle.class);
     81            LineElemStyle line = (area == null ? LineElemStyle.UNTAGGED_WAY : LineElemStyle.createSimpleLineStyle(area.color));
     82            p.a = new StyleList(p.a, line);
     83        }
     84        osm.mappaintStyle = osm.mappaintStyle.put(p.a, p.b);
     85        return p;
     86    }
     87
     88    private Pair<StyleList, Range> getImpl(OsmPrimitive osm, double scale, NavigatableComponent nc) {
     89        if (osm instanceof Node)
     90        {
     91            return generateStyles(osm, scale, null, false);
     92        }
     93        else if (osm instanceof Way)
     94        {
     95            Pair<StyleList, Range> p = generateStyles(osm, scale, null, false);
     96
     97            boolean isOuterWayOfSomeMP = false;
     98            boolean hasIndependentLineElemStyle = false;
     99            Color wayColor = null;
     100
     101            for (OsmPrimitive referrer : osm.getReferrers()) {
     102                Relation r = (Relation) referrer;
     103                if (!drawMultipolygon || !"multipolygon".equals(r.get("type"))  || !r.isUsable()) {
     104                    continue;
     105                }
     106                Multipolygon multipolygon = new Multipolygon(nc);
     107                multipolygon.load(r);
     108
     109                if (multipolygon.getOuterWays().contains(osm)) {
     110                    if (!isOuterWayOfSomeMP) { // do this only one time
     111                        List<ElemStyle> tmp = new ArrayList<ElemStyle>(p.a.size());
     112                        for (ElemStyle s : p.a) {
     113                            if (s instanceof AreaElemStyle) {
     114                                wayColor = ((AreaElemStyle) s).color;
     115                            } else {
     116                                tmp.add(s);
     117                            }
     118                        }
     119                        p.a = new StyleList(tmp);
     120                        isOuterWayOfSomeMP = true;
     121                        hasIndependentLineElemStyle = Utils.exists(p.a, LineElemStyle.class);
     122                    }
     123
     124                    if (!hasIndependentLineElemStyle) {
     125                        Pair<StyleList, Range> mpElemStyles = getStyleCacheWithRange(r, scale, nc);
     126                        LineElemStyle mpLine = Utils.find(mpElemStyles.a, LineElemStyle.class);
     127                        if (mpLine != null) {
     128                                p.a = new StyleList(p.a, mpLine);
     129                                p.b = Range.cut(p.b, mpElemStyles.b);
     130                                break;
     131                        } else if (wayColor == null) {
     132                            AreaElemStyle mpArea = Utils.find(mpElemStyles.a, AreaElemStyle.class);
     133                            if (mpArea != null) {
     134                                p.b = Range.cut(p.b, mpElemStyles.b);
     135                                wayColor = mpArea.color;
     136                            }
     137                        }
     138                    }
     139                }
     140            }
     141            if (isOuterWayOfSomeMP) {
     142                if (!Utils.exists(p.a, LineElemStyle.class)) {
     143                    p.a = new StyleList(p.a, LineElemStyle.createSimpleLineStyle(wayColor));
     144                }
     145                return p;
     146            }
     147
     148            for (OsmPrimitive referrer : osm.getReferrers()) {
     149                Relation ref = (Relation) referrer;
     150                if (!drawMultipolygon || !"multipolygon".equals(ref.get("type"))  || !ref.isUsable()) {
     151                    continue;
     152                }
     153                Multipolygon multipolygon = new Multipolygon(nc);
     154                multipolygon.load(ref);
     155
     156                if (multipolygon.getInnerWays().contains(osm)) {
     157                    Iterator<Way> it = multipolygon.getOuterWays().iterator();
     158                    p = generateStyles(osm, scale, it.hasNext() ? it.next() : null, false);
     159                    boolean hasIndependentElemStyle = false;
     160                    for (ElemStyle s : p.a) {
     161                        if (s instanceof LineElemStyle || s instanceof AreaElemStyle) {
     162                            hasIndependentElemStyle = true;
     163                        }
     164                    }
     165                    if (!hasIndependentElemStyle && !multipolygon.getOuterWays().isEmpty()) {
     166                        StyleList mpElemStyles = get(ref, scale, nc);
     167                        Color mpColor = null;
     168                        for (ElemStyle mpS : mpElemStyles) {
     169                            if (mpS instanceof AreaElemStyle) {
     170                                mpColor = ((AreaElemStyle) mpS).color;
     171                                break;
     172                            }
     173                        }
     174                        p.a = new StyleList(p.a, LineElemStyle.createSimpleLineStyle(mpColor));
     175                    }
     176                    return p;
     177                }
     178            }
     179            return p;
     180        }
     181        else if (osm instanceof Relation)
     182        {
     183            Pair<StyleList, Range> p = generateStyles(osm, scale, null, true);
     184            if (drawMultipolygon && "multipolygon".equals(osm.get("type"))) {
     185                if (!Utils.exists(p.a, AreaElemStyle.class)) {
     186                    // look at outer ways to find area style
     187                    Multipolygon multipolygon = new Multipolygon(nc);
     188                    multipolygon.load((Relation) osm);
     189                    for (Way w : multipolygon.getOuterWays()) {
     190                        Pair<StyleList, Range> wayStyles = generateStyles(w, scale, null, false);
     191                        ElemStyle area = Utils.find(wayStyles.a, AreaElemStyle.class);
     192                        if (area != null) {
     193                            p.a = new StyleList(p.a, area);
     194                            p.b = Range.cut(p.b, wayStyles.b);
     195                            break;
     196                        }
     197                    }
     198                }
     199            }
     200            return p;
     201        }
     202        return null;
     203    }
     204
     205    /**
     206     * @param multipolyOuterWay support for a very old multipolygon tagging style
     207     * where you add the tags both to the outer and the inner way.
     208     * However, independent inner way style is also possible.
     209     * @param pretendWayIsClosed For styles that require the way to be closed,
     210     * we pretend it is. This is useful for generating area styles from the (segmented)
     211     * outer ways of a multipolygon.
     212     */
     213    public Pair<StyleList, Range> generateStyles(OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed) {
     214
     215        List<ElemStyle> sl = new ArrayList<ElemStyle>();
     216        MultiCascade mc = new MultiCascade();
     217
     218        for (XmlStyleSource s : styleSources) {
     219            s.apply(mc, osm, scale, multipolyOuterWay, pretendWayIsClosed);
     220        }
     221
     222        for (Entry<String, Cascade> e : mc.entrySet()) {
     223            if ("*".equals(e.getKey()))
     224                continue;
     225            Cascade c = e.getValue();
     226            if (osm instanceof Way) {
     227                addIfNotNull(sl, AreaElemStyle.create(c));
     228                addIfNotNull(sl, LineElemStyle.createLine(c));
     229                addIfNotNull(sl, LineElemStyle.createCasing(c));
     230            } else if (osm instanceof Node) {
     231                addIfNotNull(sl, NodeElemStyle.create(c));
     232            } else if (osm instanceof Relation) {
     233                if ("multipolygon".equals(osm.get("type"))) {
     234                    addIfNotNull(sl, AreaElemStyle.create(c));
     235                    addIfNotNull(sl, LineElemStyle.createLine(c));
     236                    addIfNotNull(sl, LineElemStyle.createCasing(c));
     237                } else if ("restriction".equals(osm.get("type"))) {
     238                    addIfNotNull(sl, NodeElemStyle.create(c));
     239                }
     240            }
     241        }
     242
     243        return new Pair<StyleList, Range>(new StyleList(sl), mc.range);
     244    }
     245
     246    private static <T> void addIfNotNull(List<T> list, T obj) {
     247        if (obj != null) {
     248            list.add(obj);
     249        }
     250    }
     251
     252    public boolean isDrawMultipolygon() {
     253        return drawMultipolygon;
     254    }
     255
     256    public void setDrawMultipolygon(boolean drawMultipolygon) {
     257        this.drawMultipolygon = drawMultipolygon;
     258    }
    167259}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java

    r3824 r3836  
    1515public class LineElemStyle extends ElemStyle {
    1616
    17     public static final LineElemStyle UNTAGGED_WAY;
     17    public static LineElemStyle createSimpleLineStyle(Color color) {
     18        return new LineElemStyle(Cascade.EMPTY_CASCADE, -1f, 0f, color != null ? color : PaintColors.UNTAGGED.get(), null, null);
     19    }
     20    public static final LineElemStyle UNTAGGED_WAY = createSimpleLineStyle(null);
    1821
    19     static {
    20         UNTAGGED_WAY = new LineElemStyle(0, Long.MAX_VALUE, -1, 0, PaintColors.UNTAGGED.get(), new float[0], null);
    21     }
    22 
    23     public static LineElemStyle createSimpleLineStyle(Color color) {
    24         return new LineElemStyle(0, Long.MAX_VALUE, -1, 0, color, new float[0], null);
    25     }
    26 
    27     private int width;
    28     public int realWidth; //the real width of this line in meter
     22    private float width;
     23    public float realWidth; // the real width of this line in meter
    2924    public Color color;
    3025    private float[] dashed;
    3126    public Color dashedColor;
    3227
    33     public LineElemStyle(long minScale, long maxScale, int width, int realWidth, Color color, float[] dashed, Color dashedColor) {
    34         super(minScale, maxScale);
     28    protected LineElemStyle(Cascade c, float width, float realWidth, Color color, float[] dashed, Color dashedColor) {
     29        super(c);
    3530        setWidth(width);
    3631        this.realWidth = realWidth;
     
    3833        this.dashed = dashed;
    3934        this.dashedColor = dashedColor;
     35    }
     36
     37    public static LineElemStyle createLine(Cascade c) {
     38        return createImpl(c, "");
     39    }
     40
     41    public static LineElemStyle createCasing(Cascade c) {
     42        return createImpl(c, "casing-");
     43    }
     44
     45    private static LineElemStyle createImpl(Cascade c, String prefix) {
     46        Float width = c.get(prefix + "width", null, Float.class);
     47        if (width == null)
     48            return null;
     49
     50        float realWidth = c.get(prefix + "real-width", 0f, Float.class);
     51        Color color = c.get(prefix + "color", null, Color.class);
     52        if (color == null) {
     53            color = c.get(prefix + "fill-color", null, Color.class);
     54        }
     55        if (color == null) {
     56            color = PaintColors.UNTAGGED_WAY.get();
     57        }
     58        float[] dashes = c.get(prefix + "dashes", null, float[].class);
     59        Color dashesBackground = c.get(prefix + "dashes-background-color", null, Color.class);
     60
     61        return new LineElemStyle(c, width, realWidth, color, dashes, dashesBackground);
    4062    }
    4163
     
    5577
    5678        Color myDashedColor = dashedColor;
    57         int myWidth = getWidth();
     79        float myWidth = getWidth();
    5880
    5981        if (realWidth > 0 && paintSettings.isUseRealWidth() && !showDirection) {
     
    6789            if(widthTag != null) {
    6890                try {
    69                     realWidth = Integer.parseInt(widthTag);
     91                    realWidth = new Float(Integer.parseInt(widthTag));
    7092                }
    7193                catch(NumberFormatException nfe) {
     
    107129    }
    108130
    109     public int getWidth() {
    110         if (width == -1)
     131    public float getWidth() {
     132        if (width == -1f)
    111133            return MapPaintSettings.INSTANCE.getDefaultSegmentWidth();
    112134        return width;
    113135    }
    114136
    115     public void setWidth(int width) {
     137    public void setWidth(float width) {
    116138        this.width = width;
    117139    }
     
    133155    @Override
    134156    public int hashCode() {
    135         int hash = 3;
    136         hash = 29 * hash + this.width;
    137         hash = 29 * hash + this.realWidth;
    138         hash = 29 * hash + this.color.hashCode();
    139         hash = 29 * hash + Arrays.hashCode(this.dashed);
    140         hash = 29 * hash + (this.dashedColor != null ? this.dashedColor.hashCode() : 0);
     157        int hash = super.hashCode();
     158        hash = 29 * hash + Float.floatToIntBits(width);
     159        hash = 29 * hash + Float.floatToIntBits(realWidth);
     160        hash = 29 * hash + color.hashCode();
     161        hash = 29 * hash + Arrays.hashCode(dashed);
     162        hash = 29 * hash + (dashedColor != null ? dashedColor.hashCode() : 0);
    141163        return hash;
    142164    }
     
    144166    @Override
    145167    public String toString() {
    146         return "LineElemStyle{" + "width=" + width + " realWidth=" + realWidth + " color=" + color + " dashed=" + Arrays.toString(dashed) + " dashedColor=" + dashedColor + '}';
     168        return "LineElemStyle{" + super.toString() + "width=" + width + " realWidth=" + realWidth + " color=" + color + " dashed=" + Arrays.toString(dashed) + " dashedColor=" + dashedColor + '}';
    147169    }
    148170}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/NodeElemStyle.java

    r3824 r3836  
    22package org.openstreetmap.josm.gui.mappaint;
    33
     4import java.awt.Color;
     5
     6import javax.swing.GrayFilter;
     7import javax.swing.ImageIcon;
     8
    49import org.openstreetmap.josm.data.osm.Node;
     10import org.openstreetmap.josm.data.osm.OsmPrimitive;
     11import org.openstreetmap.josm.data.osm.Relation;
     12import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    513import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
     14import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
     15import org.openstreetmap.josm.tools.Utils;
    616
    7 abstract public class NodeElemStyle extends ElemStyle {
     17/**
     18 * applies for Nodes and turn restriction relations
     19 */
     20public class NodeElemStyle extends ElemStyle {
    821    public boolean annotate;
    922    public String annotation_key;
     23    public ImageIcon icon;
     24    private ImageIcon disabledIcon;
    1025
    11     public NodeElemStyle(long minScale, long maxScale) {
    12         super(minScale, maxScale);
     26    public static final NodeElemStyle SIMPLE_NODE_ELEMSTYLE = new NodeElemStyle(Cascade.EMPTY_CASCADE, true, null, null);
     27
     28    protected NodeElemStyle(Cascade c, boolean annotate, String annotation_key, ImageIcon icon) {
     29        super(c);
     30        this.annotate = annotate;
     31        this.annotation_key = annotation_key;
     32        this.icon = icon;
     33    }
     34
     35    public static NodeElemStyle create(Cascade c) {
     36        IconReference iconRef = c.get("icon-image", null, IconReference.class);
     37        if (iconRef == null)
     38            return null;
     39
     40        ImageIcon icon = MapPaintStyles.getIcon(iconRef);
     41        String text = c.get("text", null, String.class);
     42
     43        boolean annotate = text != null;
     44        String annotation_key = null;
     45
     46        if (annotate && !"yes".equalsIgnoreCase(text)) {
     47            annotation_key = text;
     48        }
     49        return new NodeElemStyle(c, annotate, annotation_key, icon);
     50    }
     51
     52    @Override
     53    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings settings, MapPainter painter, boolean selected, boolean member) {
     54        if (primitive instanceof Node) {
     55            Node n = (Node) primitive;
     56            if (icon != null && painter.isShowIcons()) {
     57                painter.drawNodeIcon(n, (painter.isInactive() || n.isDisabled()) ? getDisabledIcon() : icon,
     58                        selected, member, getName(n, painter));
     59            } else {
     60                if (n.isHighlighted()) {
     61                    painter.drawNode(n, settings.getHighlightColor(), settings.getSelectedNodeSize(), settings.isFillSelectedNode(), getName(n, painter));
     62                } else {
     63                    Color color;
     64                    boolean isConnection = n.isConnectionNode();
     65
     66                    if (painter.isInactive() || n.isDisabled()) {
     67                        color = settings.getInactiveColor();
     68                    } else if (selected) {
     69                        color = settings.getSelectedColor();
     70                    } else if (member) {
     71                        color = settings.getRelationSelectedColor();
     72                    } else if (isConnection) {
     73                        if (n.isTagged()) {
     74                            color = settings.getTaggedConnectionColor();
     75                        } else {
     76                            color = settings.getConnectionColor();
     77                        }
     78                    } else {
     79                        if (n.isTagged()) {
     80                            color = settings.getTaggedColor();
     81                        } else {
     82                            color = settings.getNodeColor();
     83                        }
     84                    }
     85
     86                    final int size = Utils.max((selected ? settings.getSelectedNodeSize() : 0),
     87                                            (n.isTagged() ? settings.getTaggedNodeSize() : 0),
     88                                            (isConnection ? settings.getConnectionNodeSize() : 0),
     89                                            settings.getUnselectedNodeSize());
     90
     91                    final boolean fill = (selected && settings.isFillSelectedNode()) ||
     92                                            (n.isTagged() && settings.isFillTaggedNode()) ||
     93                                            (isConnection && settings.isFillConnectionNode()) ||
     94                                            settings.isFillUnselectedNode();
     95
     96                    painter.drawNode(n, color, size, fill, getName(n, painter));
     97                }
     98            }
     99        } else if (primitive instanceof Relation) {
     100            painter.drawRestriction((Relation) primitive, this);
     101        }
     102    }
     103
     104    public ImageIcon getDisabledIcon() {
     105        if (disabledIcon != null)
     106            return disabledIcon;
     107        if (icon == null)
     108            return null;
     109        return disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(icon.getImage()));
    13110    }
    14111
     
    23120        return null;
    24121    }
     122
     123    @Override
     124    public int hashCode() {
     125        int hash = super.hashCode();
     126        hash = 17 * hash + (annotate ? 1 : 0);
     127        hash = 17 * hash + (annotation_key != null ? annotation_key.hashCode() : 0);
     128        hash = 17 * hash + (icon != null ? icon.getImage().hashCode() : 0);
     129        return hash;
     130    }
     131
     132    @Override
     133    public boolean equals(Object obj) {
     134        if (obj == null || getClass() != obj.getClass())
     135            return false;
     136        if (!super.equals(obj))
     137            return false;
     138
     139        final NodeElemStyle other = (NodeElemStyle) obj;
     140        // we should get the same image object due to caching
     141        if (icon != other.icon && (icon == null || other.icon == null || icon.getImage() != other.icon.getImage()))
     142            return false;
     143        if (annotate != other.annotate)
     144            return false;
     145        if (!Utils.equal(annotation_key, annotation_key))
     146            return false;
     147        return true;
     148    }
     149
     150    @Override
     151    public String toString() {
     152        return "NodeElemStyle{" + super.toString() + "annotate=" + annotate + " annotation_key=" + annotation_key + " icon=" + icon + '}';
     153    }
     154
    25155}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java

    r3824 r3836  
    55import java.util.Arrays;
    66import java.util.Collection;
    7 import java.util.Collections;
     7import java.util.Iterator;
    88import java.util.List;
    99
    10 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1110import org.openstreetmap.josm.data.osm.Storage;
    12 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    13 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter;
    14 
     11import org.openstreetmap.josm.tools.Pair;
     12import org.openstreetmap.josm.tools.Utils;
     13
     14/**
     15 * Caches styles for a single primitive.
     16 * Splits the range of possible scale values (0 < scale < +Infinity) into multiple
     17 * subranges, for each scale range it keeps a list of styles.
     18 * Immutable class, equals & hashCode is required (the same for StyleList, ElemStyle
     19 * and its subclasses).
     20 */
    1521public class StyleCache {
    16 
    17     private List<ElemStyle> styles;
     22    /* list of boundaries for the scale ranges */
     23    ArrayList<Double> bd;
     24    /* styles for each scale range */
     25    ArrayList<StyleList> data;
    1826
    1927    private final static Storage<StyleCache> internPool = new Storage<StyleCache>(); // TODO: clean up the intern pool from time to time (after purge or layer removal)
    2028
    21     public final static StyleCache EMPTY_STYLECACHE = create();
    22     public final static StyleCache SIMPLE_NODE_STYLECACHE = create(SimpleNodeElemStyle.INSTANCE);
    23     public final static StyleCache UNTAGGED_WAY_STYLECACHE = create(LineElemStyle.UNTAGGED_WAY);
    24 
     29    public final static StyleCache EMPTY_STYLECACHE = (new StyleCache()).intern();
    2530   
    2631    private StyleCache() {
    27         styles = new ArrayList<ElemStyle>();
    28     }
    29 
    30     public static StyleCache create() {
    31         StyleCache sc = new StyleCache();
    32         sc.styles = new ArrayList<ElemStyle>();
    33         return sc.intern();
    34     }
    35 
    36     public static StyleCache create(ElemStyle... styles) {
    37         StyleCache sc = new StyleCache();
    38         sc.styles = Arrays.asList(styles);
    39         return sc.intern();
    40     }
    41 
    42     public static StyleCache create(Collection<ElemStyle> styles) {
    43         StyleCache sc = new StyleCache();
    44         sc.styles = new ArrayList<ElemStyle>(styles);
    45         return sc.intern();
    46     }
    47 
    48     public Collection<ElemStyle> getStyles() {
    49         return Collections.unmodifiableList(styles);
    50     }
    51 
    52     /**
    53      * like String.intern() (reduce memory consumption)
     32        bd = new ArrayList<Double>();
     33        bd.add(0.0);
     34        bd.add(Double.POSITIVE_INFINITY);
     35        data = new ArrayList<StyleList>();
     36        data.add(null);
     37    }
     38
     39    private StyleCache(StyleCache s) {
     40        bd = new ArrayList<Double>(s.bd);
     41        data = new ArrayList<StyleList>(s.data);
     42    }
     43
     44    /**
     45     * List of Styles, immutable
     46     */
     47    public static class StyleList implements Iterable<ElemStyle>
     48    {
     49        private List<ElemStyle> lst;
     50
     51        public static final StyleList SIMPLE_NODE = new StyleList(NodeElemStyle.SIMPLE_NODE_ELEMSTYLE);
     52
     53        public StyleList() {
     54            lst = new ArrayList<ElemStyle>();
     55        }
     56
     57        public StyleList(ElemStyle... init) {
     58            lst = new ArrayList<ElemStyle>(Arrays.asList(init));
     59        }
     60
     61        public StyleList(Collection<ElemStyle> sl) {
     62            lst = new ArrayList<ElemStyle>(sl);
     63        }
     64
     65        public StyleList(StyleList sl, ElemStyle s) {
     66            lst = new ArrayList<ElemStyle>(sl.lst);
     67            lst.add(s);
     68        }
     69
     70        @Override
     71        public Iterator<ElemStyle> iterator() {
     72            return lst.iterator();
     73        }
     74
     75        public boolean isEmpty() {
     76            return lst.isEmpty();
     77        }
     78
     79        public int size() {
     80            return lst.size();
     81        }
     82
     83        @Override
     84        public String toString() {
     85            return lst.toString();
     86        }
     87
     88        @Override
     89        public boolean equals(Object obj) {
     90            if (obj == null || getClass() != obj.getClass())
     91                return false;
     92            final StyleList other = (StyleList) obj;
     93            return Utils.equal(lst, other.lst);
     94        }
     95
     96        @Override
     97        public int hashCode() {
     98            return lst.hashCode();
     99        }
     100    }
     101
     102    /**
     103     * looks up styles for a certain scale value
     104     */
     105    public StyleList get(double scale) {
     106        if (scale <= 0)
     107            throw new IllegalArgumentException();
     108        for (int i=0; i<data.size(); ++i) {
     109            if (bd.get(i) < scale && scale <= bd.get(i+1)) {
     110                return data.get(i);
     111            }
     112        }
     113        throw new AssertionError();
     114    }
     115
     116    /**
     117     * looks up styles for a certain scale value and additionally returns
     118     * the scale range for the returned styles
     119     */
     120    public Pair<StyleList, Range> getWithRange(double scale) {
     121        if (scale <= 0)
     122            throw new IllegalArgumentException();
     123        for (int i=0; i<data.size(); ++i) {
     124            if (bd.get(i) < scale && scale <= bd.get(i+1)) {
     125                return new Pair<StyleList, Range>(data.get(i), new Range(bd.get(i), bd.get(i+1)));
     126            }
     127        }
     128        throw new AssertionError();
     129    }
     130
     131    public StyleCache put(StyleList sl, Range r) {
     132        return put(sl, r.getLower(), r.getUpper());
     133    }
     134
     135    /**
     136     * add a new styles to the cache. this is only possible, if
     137     * for this scale range, there is nothing in the cache yet.
     138     */
     139    public StyleCache put(StyleList sl, double lower, double upper) {
     140        StyleCache s = new StyleCache(this);
     141        s.putImpl(sl, lower, upper);
     142        s.consistencyTest();
     143        return s.intern();
     144    }
     145
     146    /**
     147     * ASCII-art explanation:
     148     *
     149     *              data[i]
     150     *  --|-------|---------|--
     151     * bd[i-1]  bd[i]    bd[i+1]
     152     *
     153     *         (--------]
     154     *       lower     upper
     155     */
     156    private void putImpl(StyleList sl, double lower, double upper) {
     157        int i=0;
     158        while (bd.get(i) < lower) {
     159            ++i;
     160        }
     161        if (bd.get(i) == lower) {
     162            if (upper > bd.get(i+1))
     163                throw new AssertionError("the new range must be within a single subrange");
     164            if (data.get(i) != null)
     165                throw new AssertionError("the new range must be within a subrange that has no data");
     166
     167            //  --|-------|--------|--
     168            //   i-1      i       i+1
     169            //            (--------]
     170            if (bd.get(i+1) == upper) {
     171                data.set(i, sl);
     172            }
     173            //  --|-------|--------|--
     174            //   i-1      i       i+1
     175            //            (-----]
     176            else {
     177                bd.add(i+1, upper);
     178                data.add(i, sl);
     179            }
     180            return;
     181        } else {
     182            if (bd.get(i) < upper)
     183                throw new AssertionError("the new range must be within a single subrange");
     184            if (data.get(i-1) != null)
     185                throw new AssertionError();
     186
     187            //  --|-------|--------|--
     188            //   i-1      i       i+1
     189            //       (--]   or
     190            //       (----]
     191            bd.add(i, lower);
     192            data.add(i, sl);
     193           
     194            //  --|--|----|--------|--
     195            //   i-1 i   i+1      i+2
     196            //       (--]
     197            if (bd.get(i+1) > upper) {
     198                bd.add(i+1, upper);
     199                data.add(i+1, null);
     200            }
     201            return;
     202        }
     203    }
     204   
     205    public void consistencyTest() {
     206        if (bd.size() < 2) throw new AssertionError();
     207        if (data.size() < 1) throw new AssertionError();
     208        if (bd.size() != data.size() + 1) throw new AssertionError();
     209        if (bd.get(0) != 0) throw new AssertionError();
     210        if (bd.get(bd.size() - 1) != Double.POSITIVE_INFINITY) throw new AssertionError();
     211        for (int i=0; i<data.size() - 1; ++i) {
     212            if (bd.get(i) >= bd.get(i + 1)) throw new AssertionError();
     213        }
     214    }
     215
     216    /**
     217     * Like String.intern() (reduce memory consumption).
     218     * StyleCache must not be changed after it has
     219     * been added to the intern pool.
    54220     */
    55221    public StyleCache intern() {
    56222        return internPool.putUnique(this);
    57     }
    58 
    59     public void paint(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member) {
    60         for (ElemStyle s : styles) {
    61             s.paintPrimitive(primitive, paintSettings, painter, selected, member);
    62         }
    63223    }
    64224
     
    67227        if (obj == null || getClass() != obj.getClass())
    68228            return false;
    69         return styles.equals(((StyleCache) obj).styles);
     229        final StyleCache other = (StyleCache) obj;
     230        return bd.equals(other.bd) && data.equals(other.data);
    70231    }
    71232
    72233    @Override
    73234    public int hashCode() {
    74         return styles.hashCode();
     235        int hash = 7;
     236        hash = 23 * hash + bd.hashCode();
     237        hash = 23 * hash + data.hashCode();
     238        return hash;
    75239    }
    76240
    77241    @Override
    78242    public String toString() {
    79         return "SC{" + styles + '}';
     243        return "SC{" + bd + ' ' + data + '}';
    80244    }
    81245}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/AreaPrototype.java

    r3824 r3836  
    44import java.awt.Color;
    55
    6 import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
     6import org.openstreetmap.josm.gui.mappaint.Range;
    77
    88public class AreaPrototype extends Prototype {
    99    public Color color;
    10     public boolean closed;
     10    public boolean closed; // if true, it does not apply to unclosed ways
    1111
    12     public AreaPrototype (AreaPrototype a, long maxScale, long minScale) {
    13         super(maxScale, minScale);
     12    public AreaPrototype (AreaPrototype a, Range range) {
     13        super(range);
    1414        this.color = a.color;
    1515        this.closed = a.closed;
     
    2222    public void init()
    2323    {
     24        priority = 0;
     25        range = new Range();
    2426        closed = false;
    2527        color = null;
    26         priority = 0;
    27     }
    28 
    29     public AreaElemStyle createStyle() {
    30         return new AreaElemStyle(minScale, maxScale, color);
    3128    }
    3229}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/IconPrototype.java

    r3827 r3836  
    22package org.openstreetmap.josm.gui.mappaint.xml;
    33
    4 import org.openstreetmap.josm.gui.mappaint.IconElemStyle;
    5 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
    64import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
    7 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
    8 import org.openstreetmap.josm.gui.mappaint.SimpleNodeElemStyle;
     5import org.openstreetmap.josm.gui.mappaint.Range;
    96
    107public class IconPrototype extends Prototype {
    118   
    129    public IconReference icon;
    13     public boolean annotate;
     10    public Boolean annotate;
    1411
    15     public IconPrototype (IconPrototype i, long maxScale, long minScale) {
    16         super(maxScale, minScale);
     12    public IconPrototype (IconPrototype i, Range range) {
     13        super(range);
    1714        this.icon = i.icon;
    1815        this.annotate = i.annotate;
     
    2421
    2522    public void init() {
     23        priority = 0;
     24        range = new Range();
    2625        icon = null;
    27         priority = 0;
    28         annotate = true;
    29     }
    30 
    31     public NodeElemStyle createStyle() {
    32         if (icon == null) {
    33             return SimpleNodeElemStyle.INSTANCE;
    34         } else {
    35             IconElemStyle i = new IconElemStyle(minScale, maxScale, MapPaintStyles.getIcon(icon));
    36             i.annotate = annotate;
    37             return i;
    38         }
     26        annotate = null;
    3927    }
    4028}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinePrototype.java

    r3824 r3836  
    66import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
    77import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    8 import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
     8import org.openstreetmap.josm.gui.mappaint.Range;
    99import org.openstreetmap.josm.tools.I18n;
    1010
     
    1212
    1313    protected int width;
    14     public int realWidth; //the real width of this line in meter
     14    public Integer realWidth; // the real width of this line in meter
    1515    public Color color;
    1616    protected float[] dashed;
    1717    public Color dashedColor;
    1818
    19     public LinePrototype(LinePrototype s, long maxScale, long minScale) {
    20         super(maxScale, minScale);
     19    public LinePrototype(LinePrototype s, Range range) {
     20        super(range);
    2121        this.width = s.width;
    2222        this.realWidth = s.realWidth;
     
    3232    public void init()
    3333    {
     34        priority = 0;
     35        range = new Range();
    3436        width = -1;
    35         realWidth = 0;
    36         dashed = new float[0];
     37        realWidth = null;
     38        dashed = null;
    3739        dashedColor = null;
    38         priority = 0;
    3940        color = PaintColors.UNTAGGED.get();
    4041    }
     
    4546
    4647    public void setDashed(float[] dashed) {
    47         if (dashed.length == 0) {
     48        if (dashed == null || dashed.length == 0) {
    4849            this.dashed = dashed;
    4950            return;
     
    7576        this.width = width;
    7677    }
    77 
    78     public LineElemStyle createStyle() {
    79         return new LineElemStyle(minScale, maxScale, width, realWidth, color, dashed, dashedColor);
    80     }
    8178}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/LinemodPrototype.java

    r3824 r3836  
    22package org.openstreetmap.josm.gui.mappaint.xml;
    33
    4 import org.openstreetmap.josm.gui.mappaint.LineElemStyle;
     4import org.openstreetmap.josm.gui.mappaint.Range;
    55
    66public class LinemodPrototype extends LinePrototype implements Comparable<LinemodPrototype> {
     
    1111    public WidthMode widthMode;
    1212
    13     public LinemodPrototype(LinemodPrototype s, long maxScale, long minScale) {
    14         super(s, maxScale, minScale);
     13    public LinemodPrototype(LinemodPrototype s, Range range) {
     14        super(s, range);
    1515        this.over = s.over;
    1616        this.widthMode = s.widthMode;
     
    2828
    2929    // get width for overlays
    30     public int getWidth(int ref)
     30    public float getWidth(float ref)
    3131    {
    32         int res;
     32        float res;
    3333        if(widthMode == WidthMode.ABSOLUTE) {
    3434            res = width;
     
    6060            return 0;
    6161    }
    62 
    63     /**
    64      * this method cannot be used for LinemodPrototypes
    65      *  - use createStyle(int) instead
    66      */
    67     @Override
    68     public LineElemStyle createStyle() {
    69         throw new UnsupportedOperationException();
    70     }
    71 
    72     public LineElemStyle createStyle(int refWidth) {
    73         return new LineElemStyle(minScale, maxScale, getWidth(refWidth), realWidth, color, dashed, dashedColor);
    74     }
    7562}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/Prototype.java

    r3824 r3836  
    66import org.openstreetmap.josm.data.osm.OsmPrimitive;
    77import org.openstreetmap.josm.data.osm.OsmUtils;
     8import org.openstreetmap.josm.gui.mappaint.Range;
    89
    910abstract public class Prototype {
    1011    // zoom range to display the feature
    11     public long minScale;
    12     public long maxScale;
     12    public Range range;
    1313
    1414    public int priority;
     
    1616    public Collection<XmlCondition> conditions = null;
    1717
    18     public Prototype(long maxScale, long minScale) {
    19         this.maxScale = maxScale;
    20         this.minScale = minScale;
     18    public Prototype(Range range) {
     19        this.range = range;
    2120    }
    2221
    2322    public Prototype() {
    24     }
    25 
    26     @Override
    27     public boolean equals(Object o) {
    28         return (o instanceof Prototype) && (((Prototype) o).getCode().equals(getCode()));
    29     }
    30 
    31     @Override
    32     public int hashCode() {
    33         return getClass().hashCode();
    3423    }
    3524
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSource.java

    r3827 r3836  
    1010import java.util.List;
    1111
     12import org.openstreetmap.josm.Main;
    1213import org.openstreetmap.josm.data.osm.Node;
    1314import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1415import org.openstreetmap.josm.data.osm.OsmUtils;
     16import org.openstreetmap.josm.data.osm.Relation;
    1517import org.openstreetmap.josm.data.osm.Way;
    16 import org.openstreetmap.josm.gui.mappaint.ElemStyles.WayPrototypesRecord;
     18import org.openstreetmap.josm.gui.mappaint.Cascade;
     19import org.openstreetmap.josm.gui.mappaint.MultiCascade;
     20import org.openstreetmap.josm.gui.mappaint.Range;
    1721import org.openstreetmap.josm.gui.preferences.SourceEntry;
     22import org.openstreetmap.josm.tools.Utils;
    1823
    1924public class XmlStyleSource extends SourceEntry {
     
    3944    }
    4045
    41     public IconPrototype getNode(OsmPrimitive primitive, IconPrototype icon) {
     46    private static class WayPrototypesRecord {
     47        public LinePrototype line;
     48        public List<LinemodPrototype> linemods;
     49        public AreaPrototype area;
     50    }
     51
     52    private <T extends Prototype> T update(T current, T candidate, Double scale, MultiCascade mc) {
     53        return requiresUpdate(current, candidate, scale, mc) ? candidate : current;
     54    }
     55
     56    /**
     57     * checks whether a certain match is better than the current match
     58     * @param current can be null
     59     * @param candidate the new Prototype that could be used instead
     60     * @param scale ignored if null, otherwise checks if scale is within the range of candidate
     61     * @param mc side effect: update the valid region for the current MultiCascade
     62     */
     63    private boolean requiresUpdate(Prototype current, Prototype candidate, Double scale, MultiCascade mc) {
     64        if (current == null || candidate.priority >= current.priority) {
     65            if (scale == null)
     66                return true;
     67
     68            if (candidate.range.contains(scale)) {
     69                mc.range = Range.cut(mc.range, candidate.range);
     70                return true;
     71            } else {
     72                mc.range = mc.range.reduceAround(scale, candidate.range);
     73                return false;
     74            }
     75        }
     76        return false;
     77    }
     78
     79    private IconPrototype getNode(OsmPrimitive primitive, Double scale, MultiCascade mc) {
     80        IconPrototype icon = null;
    4281        for (String key : primitive.keySet()) {
    4382            String val = primitive.get(key);
    44             IconPrototype style;
    45             if ((style = icons.get("n" + key + "=" + val)) != null) {
    46                 if (icon == null || style.priority >= icon.priority) {
    47                     icon = style;
    48                 }
    49             }
    50             if ((style = icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null) {
    51                 if (icon == null || style.priority >= icon.priority) {
    52                     icon = style;
    53                 }
    54             }
    55             if ((style = icons.get("x" + key)) != null) {
    56                 if (icon == null || style.priority >= icon.priority) {
    57                     icon = style;
    58                 }
     83            IconPrototype p;
     84            if ((p = icons.get("n" + key + "=" + val)) != null) {
     85                icon = update(icon, p, scale, mc);
     86            }
     87            if ((p = icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null) {
     88                icon = update(icon, p, scale, mc);
     89            }
     90            if ((p = icons.get("x" + key)) != null) {
     91                icon = update(icon, p, scale, mc);
    5992            }
    6093        }
    6194        for (IconPrototype s : iconsList) {
    62             if ((icon == null || s.priority >= icon.priority) && s.check(primitive)) {
    63                 icon = s;
     95            if (s.check(primitive))
     96            {
     97                icon = update(icon, s, scale, mc);
    6498            }
    6599        }
     
    72106     *  multipolygon relations.
    73107     */
    74     public void get(OsmPrimitive primitive, boolean closed, WayPrototypesRecord p) {
     108    private void get(OsmPrimitive primitive, boolean closed, WayPrototypesRecord p, Double scale, MultiCascade mc) {
    75109        String lineIdx = null;
    76110        HashMap<String, LinemodPrototype> overlayMap = new HashMap<String, LinemodPrototype>();
     
    81115            LinemodPrototype styleLinemod;
    82116            String idx = "n" + key + "=" + val;
    83             if ((styleArea = areas.get(idx)) != null && (p.area == null || styleArea.priority >= p.area.priority) && (closed || !styleArea.closed)) {
    84                 p.area = styleArea;
    85             }
    86             if ((styleLine = lines.get(idx)) != null && (p.line == null || styleLine.priority >= p.line.priority)) {
    87                 p.line = styleLine;
    88                 lineIdx = idx;
     117            if ((styleArea = areas.get(idx)) != null && (closed || !styleArea.closed)) {
     118                p.area = update(p.area, styleArea, scale, mc);
     119            }
     120            if ((styleLine = lines.get(idx)) != null) {
     121                if (requiresUpdate(p.line, styleLine, scale, mc)) {
     122                    p.line = styleLine;
     123                    lineIdx = idx;
     124                }
    89125            }
    90126            if ((styleLinemod = modifiers.get(idx)) != null) {
    91                 overlayMap.put(idx, styleLinemod);
     127                if (requiresUpdate(null, styleLinemod, scale, mc)) {
     128                    overlayMap.put(idx, styleLinemod);
     129                }
    92130            }
    93131            idx = "b" + key + "=" + OsmUtils.getNamedOsmBoolean(val);
    94             if ((styleArea = areas.get(idx)) != null && (p.area == null || styleArea.priority >= p.area.priority) && (closed || !styleArea.closed)) {
    95                 p.area = styleArea;
    96             }
    97             if ((styleLine = lines.get(idx)) != null && (p.line == null || styleLine.priority >= p.line.priority)) {
    98                 p.line = styleLine;
    99                 lineIdx = idx;
     132            if ((styleArea = areas.get(idx)) != null && (closed || !styleArea.closed)) {
     133                p.area = update(p.area, styleArea, scale, mc);
     134            }
     135            if ((styleLine = lines.get(idx)) != null) {
     136                if (requiresUpdate(p.line, styleLine, scale, mc)) {
     137                    p.line = styleLine;
     138                    lineIdx = idx;
     139                }
    100140            }
    101141            if ((styleLinemod = modifiers.get(idx)) != null) {
    102                 overlayMap.put(idx, styleLinemod);
     142                if (requiresUpdate(null, styleLinemod, scale, mc)) {
     143                    overlayMap.put(idx, styleLinemod);
     144                }
    103145            }
    104146            idx = "x" + key;
    105             if ((styleArea = areas.get(idx)) != null && (p.area == null || styleArea.priority >= p.area.priority) && (closed || !styleArea.closed)) {
    106                 p.area = styleArea;
    107             }
    108             if ((styleLine = lines.get(idx)) != null && (p.line == null || styleLine.priority >= p.line.priority)) {
    109                 p.line = styleLine;
    110                 lineIdx = idx;
     147            if ((styleArea = areas.get(idx)) != null && (closed || !styleArea.closed)) {
     148                p.area = update(p.area, styleArea, scale, mc);
     149            }
     150            if ((styleLine = lines.get(idx)) != null) {
     151                if (requiresUpdate(p.line, styleLine, scale, mc)) {
     152                    p.line = styleLine;
     153                    lineIdx = idx;
     154                }
    111155            }
    112156            if ((styleLinemod = modifiers.get(idx)) != null) {
    113                 overlayMap.put(idx, styleLinemod);
     157                if (requiresUpdate(null, styleLinemod, scale, mc)) {
     158                    overlayMap.put(idx, styleLinemod);
     159                }
    114160            }
    115161        }
    116162        for (AreaPrototype s : areasList) {
    117             if ((p.area == null || s.priority >= p.area.priority) && (closed || !s.closed) && s.check(primitive)) {
    118                 p.area = s;
     163            if ((closed || !s.closed) && s.check(primitive)) {
     164                p.area = update(p.area, s, scale, mc);
    119165            }
    120166        }
    121167        for (LinePrototype s : linesList) {
    122             if ((p.line == null || s.priority >= p.line.priority) && s.check(primitive)) {
    123                 p.line = s;
     168            if (s.check(primitive)) {
     169                p.line = update(p.line, s, scale, mc);
    124170            }
    125171        }
    126172        for (LinemodPrototype s : modifiersList) {
    127173            if (s.check(primitive)) {
    128                 overlayMap.put(s.getCode(), s);
     174                if (requiresUpdate(null, s, scale, mc)) {
     175                    overlayMap.put(s.getCode(), s);
     176                }
    129177            }
    130178        }
    131179        overlayMap.remove(lineIdx); // do not use overlay if linestyle is from the same rule (example: railway=tram)
    132         if (!overlayMap.isEmpty() && p.line != null) {
     180        if (!overlayMap.isEmpty()) {
    133181            List<LinemodPrototype> tmp = new LinkedList<LinemodPrototype>();
    134182            if (p.linemods != null) {
     
    139187            p.linemods = tmp;
    140188        }
    141     }
    142 
    143     public boolean isArea(OsmPrimitive o) {
    144         if (o.hasKeys() && !(o instanceof Node)) {
    145             boolean noclosed = o instanceof Way && !((Way) o).isClosed();
    146             Iterator<String> iterator = o.keySet().iterator();
    147             while (iterator.hasNext()) {
    148                 String key = iterator.next();
    149                 String val = o.get(key);
    150                 AreaPrototype s = areas.get("n" + key + "=" + val);
    151                 if (s == null || (s.closed && noclosed)) {
    152                     s = areas.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val));
    153                 }
    154                 if (s == null || (s.closed && noclosed)) {
    155                     s = areas.get("x" + key);
    156                 }
    157                 if (s != null && !(s.closed && noclosed)) {
    158                     return true;
    159                 }
    160             }
    161             for (AreaPrototype s : areasList) {
    162                 if (!(s.closed && noclosed) && s.check(o)) {
    163                     return true;
    164                 }
    165             }
    166         }
    167         return false;
    168     }
    169 
    170     public boolean hasAreas() {
    171         return areas.size() > 0;
    172189    }
    173190
     
    202219         }
    203220     }
     221
     222    public void apply(MultiCascade mc, OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed) {
     223        Cascade def = mc.getCascade("default");
     224        boolean useMinMaxScale = Main.pref.getBoolean("mappaint.zoomLevelDisplay", false);
     225
     226        if (osm instanceof Node || (osm instanceof Relation && "restriction".equals(osm.get("type")))) {
     227            IconPrototype icon = getNode(osm, (useMinMaxScale ? scale : null), mc);
     228            if (icon != null) {
     229                def.put("icon-image", icon.icon);
     230                if (osm instanceof Node) {
     231                    if (icon.annotate != null) {
     232                        if (icon.annotate) {
     233                            def.put("text", "yes");
     234                        } else {
     235                            def.remove("text");
     236                        }
     237                    }
     238                }
     239            }
     240        } else if (osm instanceof Way || (osm instanceof Relation && "multipolygon".equals(osm.get("type")))) {
     241            WayPrototypesRecord p = new WayPrototypesRecord();
     242            get(osm, pretendWayIsClosed || !(osm instanceof Way) || ((Way) osm).isClosed(), p, (useMinMaxScale ? scale : null), mc);
     243            if (p.line != null) {
     244                def.put("width", new Float(p.line.getWidth()));
     245                def.putOrClear("real-width", p.line.realWidth != null ? new Float(p.line.realWidth) : null);
     246                def.putOrClear("color", p.line.color);
     247                def.putOrClear("dashes", p.line.getDashed());
     248                def.putOrClear("dashes-background-color", p.line.dashedColor);
     249            }
     250            Float refWidth = def.get("width", null, Float.class);
     251            if (refWidth != null && p.linemods != null) {
     252                int numOver = 0, numUnder = 0;
     253
     254                while (mc.containsKey(String.format("over_%d", ++numOver))) {}
     255                while (mc.containsKey(String.format("under_%d", ++numUnder))) {}
     256
     257                for (LinemodPrototype mod : p.linemods) {
     258                    Cascade c;
     259                    if (mod.over) {
     260                        c = mc.getCascade(String.format("over_%d", numOver));
     261                        c.put("object-z-index", new Float(numOver));
     262                        ++numOver;
     263                    } else {
     264                        c = mc.getCascade(String.format("under_%d", numUnder));
     265                        c.put("object-z-index", new Float(-numUnder));
     266                        ++numUnder;
     267                    }
     268                    c.put("width", new Float(mod.getWidth(refWidth)));
     269                    c.putOrClear("color", mod.color);
     270                    c.putOrClear("dashes", mod.getDashed());
     271                    c.putOrClear("dashes-background-color", mod.dashedColor);
     272                }
     273            }
     274            if (multipolyOuterWay != null) {
     275                WayPrototypesRecord p2 = new WayPrototypesRecord();
     276                get(multipolyOuterWay, true, p2, (useMinMaxScale ? scale : null), mc);
     277                if (Utils.equal(p.area, p2.area)) {
     278                    p.area = null;
     279                }
     280            }
     281            if (p.area != null) {
     282                def.putOrClear("fill-color", p.area.color);
     283                def.remove("fill-image");
     284            }
     285        }
     286    }
     287
    204288}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/xml/XmlStyleSourceHandler.java

    r3827 r3836  
    88import org.openstreetmap.josm.Main;
    99import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
     10import org.openstreetmap.josm.gui.mappaint.Range;
    1011import org.openstreetmap.josm.tools.ColorHelper;
    1112import org.xml.sax.Attributes;
     
    2324        XmlCondition cond = new XmlCondition();
    2425        Collection<XmlCondition> conditions;
    25         long scaleMax;
    26         long scaleMin;
     26        double scaleMax;
     27        double scaleMin;
    2728        LinePrototype line = new LinePrototype();
    2829        LinemodPrototype linemod = new LinemodPrototype();
     
    3233        {
    3334            conditions = null;
    34             scaleMax = 1000000000;
     35            scaleMax = Double.POSITIVE_INFINITY;
    3536            scaleMin = 0;
    3637            line.init();
     
    102103                        dashed = new float[]{9};
    103104                    } else {
    104                         dashed = new float[0];
     105                        dashed = null;
    105106                    }
    106107                }
     
    249250            {
    250251                style.add(rule.cond, rule.conditions,
    251                         new LinePrototype(rule.line, rule.scaleMax, rule.scaleMin));
     252                        new LinePrototype(rule.line, new Range(rule.scaleMin, rule.scaleMax)));
    252253            }
    253254            if(hadLineMod)
    254255            {
    255256                style.add(rule.cond, rule.conditions,
    256                         new LinemodPrototype(rule.linemod, rule.scaleMax, rule.scaleMin));
     257                        new LinemodPrototype(rule.linemod, new Range(rule.scaleMin, rule.scaleMax)));
    257258            }
    258259            if(hadIcon)
    259260            {
    260261                style.add(rule.cond, rule.conditions,
    261                         new IconPrototype(rule.icon, rule.scaleMax, rule.scaleMin));
     262                        new IconPrototype(rule.icon, new Range(rule.scaleMin, rule.scaleMax)));
    262263            }
    263264            if(hadArea)
    264265            {
    265266                style.add(rule.cond, rule.conditions,
    266                         new AreaPrototype(rule.area, rule.scaleMax, rule.scaleMin));
     267                        new AreaPrototype(rule.area, new Range(rule.scaleMin, rule.scaleMax)));
    267268            }
    268269            inRule = false;
  • trunk/src/org/openstreetmap/josm/tools/SubclassFilteredCollection.java

    r3801 r3836  
    1313 * @param <S> element type of the underlying collection
    1414 * @param <T> element type of filtered collection (and subclass of S). The predicate
    15  *      must except only objects of type T.
     15 *      must accept only objects of type T.
    1616 */
    1717public class SubclassFilteredCollection<S, T extends S> extends AbstractCollection<T> {
     
    5050            S old = current;
    5151            current = null;
    52             return (T) old;
     52            // we are save because predicate only accepts objects of type T
     53            @SuppressWarnings("unchecked") T res = (T) old;
     54            return res;
    5355        }
    5456
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r3796 r3836  
    44public class Utils {
    55
    6     public static <T> boolean exists(Iterable<? extends T> coll, Predicate<? super T> pred) {
    7         for (T el : coll) {
    8             if (pred.evaluate(el))
     6    public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) {
     7        for (T item : collection) {
     8            if (predicate.evaluate(item))
    99                return true;
    1010        }
    1111        return false;
     12    }
     13
     14    public static <T> boolean exists(Iterable collection, Class<? extends T> klass) {
     15        for (Object item : collection) {
     16            if (klass.isInstance(item))
     17                return true;
     18        }
     19        return false;
     20    }
     21
     22    public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) {
     23        for (T item : collection) {
     24            if (predicate.evaluate(item))
     25                return item;
     26        }
     27        return null;
     28    }
     29
     30    public static <T> T find(Iterable collection, Class<? extends T> klass) {
     31        for (Object item : collection) {
     32            if (klass.isInstance(item)) {
     33                @SuppressWarnings("unchecked") T res = (T) item;
     34                return res;
     35            }
     36        }
     37        return null;
    1238    }
    1339
     
    2854    }
    2955
     56    public static int max(int a, int b, int c, int d) {
     57        return Math.max(Math.max(a, b), Math.max(c, d));
     58    }
     59
    3060    /**
    3161     * for convenience: test whether 2 objects are either both null or a.equals(b)
    3262     */
    3363    public static <T> boolean equal(T a, T b) {
    34         if (a == null && b == null)
     64        if (a == b)
    3565            return true;
    3666        return (a != null && a.equals(b));
Note: See TracChangeset for help on using the changeset viewer.