Changeset 3836 in josm


Ignore:
Timestamp:
Jan 31, 2011 2:18:47 PM (2 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.