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

mappaint restructuring aimed at mapcss support:

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

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.