Changeset 4627 in josm for trunk/src


Ignore:
Timestamp:
2011-12-03T00:14:54+01:00 (12 years ago)
Author:
Don-vip
Message:

Major performance improvements in multipolygons rendering

Location:
trunk/src/org/openstreetmap/josm
Files:
9 edited

Legend:

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

    • Property svn:mime-type deleted
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java

    • Property svn:mime-type deleted
    r4623 r4627  
    1919import java.awt.geom.AffineTransform;
    2020import java.awt.geom.GeneralPath;
     21import java.awt.geom.Path2D;
     22import java.awt.geom.Point2D;
    2123import java.awt.geom.Rectangle2D;
    2224import java.awt.image.BufferedImage;
     
    2931
    3032import org.openstreetmap.josm.Main;
     33import org.openstreetmap.josm.data.coor.EastNorth;
    3134import org.openstreetmap.josm.data.osm.Node;
    3235import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    4144import org.openstreetmap.josm.gui.NavigatableComponent;
    4245import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle;
    43 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
    44 import org.openstreetmap.josm.gui.mappaint.TextElement;
    4546import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.HorizontalTextAlignment;
    4647import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.VerticalTextAlignment;
     48import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
    4749import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
     50import org.openstreetmap.josm.gui.mappaint.TextElement;
    4851import org.openstreetmap.josm.tools.ImageProvider;
    4952import org.openstreetmap.josm.tools.Pair;
     
    783786    }
    784787
    785     private Polygon getPolygon(Way w) {
    786         Polygon polygon = new Polygon();
    787 
    788         for (Node n : w.getNodes()) {
    789             Point p = nc.getPoint(n);
    790             polygon.addPoint(p.x,p.y);
    791         }
    792         return polygon;
     788    private Path2D.Double getPath(Way w) {
     789        Path2D.Double path = new Path2D.Double();
     790        boolean initial = true;
     791        for (Node n : w.getNodes())
     792        {
     793            Point2D p = n.getEastNorth();
     794            if (initial) {
     795                path.moveTo(p.getX(), p.getY());
     796                initial = false;
     797            } else {
     798                path.lineTo(p.getX(), p.getY());
     799            }
     800        }
     801        return path;
    793802    }
    794803
    795804    public void drawArea(Way w, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
    796         Polygon polygon = getPolygon(w);
    797         drawArea(w, polygon, color, fillImage, fillImageAlpha, text);
    798     }
    799 
    800     protected void drawArea(OsmPrimitive osm, Polygon polygon, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
    801 
     805        drawArea(w, getPath(w), color, fillImage, fillImageAlpha, text);
     806    }
     807
     808    protected void drawArea(OsmPrimitive osm, Path2D.Double path, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
     809
     810        Shape area = path.createTransformedShape(nc.getAffineTransform());
     811       
    802812        if (!isOutlineOnly) {
    803813            if (fillImage == null) {
    804814                g.setColor(color);
    805                 g.fillPolygon(polygon);
     815                g.fill(area);
    806816            } else {
    807817                TexturePaint texture = new TexturePaint(fillImage,
    808                         new Rectangle(polygon.xpoints[0], polygon.ypoints[0], fillImage.getWidth(), fillImage.getHeight()));
     818//                        new Rectangle(polygon.xpoints[0], polygon.ypoints[0], fillImage.getWidth(), fillImage.getHeight()));
     819                      new Rectangle(0, 0, fillImage.getWidth(), fillImage.getHeight()));
    809820                g.setPaint(texture);
    810821                if (fillImageAlpha != 1f) {
    811822                    g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fillImageAlpha));
    812823                }
    813                 g.fill(polygon);
     824                g.fill(area);
    814825                g.setPaintMode();
    815826            }
     
    824835            if (name == null) return;
    825836
    826             Rectangle pb = polygon.getBounds();
     837            Rectangle pb = area.getBounds();
    827838            FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache
    828839            Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font)
     
    847858
    848859            if ((pb.width >= nb.getWidth() && pb.height >= nb.getHeight()) && // quick check
    849                     polygon.contains(centeredNBounds) // slow but nice
     860                  area.contains(centeredNBounds) // slow but nice
    850861            ) {
    851862                g.setColor(text.color);
     
    862873    public void drawArea(Relation r, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
    863874        Multipolygon multipolygon = MultipolygonCache.getInstance().get(nc, r);
    864         if(!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
     875        if (!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
    865876            for (PolyData pd : multipolygon.getCombinedPolygons()) {
    866                 Polygon p = pd.get();
    867                 if(!isPolygonVisible(p)) {
     877                Path2D.Double p = pd.get();
     878                if (!isAreaVisible(p)) {
    868879                    continue;
    869880                }
     
    875886    }
    876887
    877     private boolean isPolygonVisible(Polygon polygon) {
    878         Rectangle bounds = polygon.getBounds();
    879         if (bounds.width == 0 && bounds.height == 0) return false;
    880         if (bounds.x > nc.getWidth()) return false;
    881         if (bounds.y > nc.getHeight()) return false;
    882         if (bounds.x + bounds.width < 0) return false;
    883         if (bounds.y + bounds.height < 0) return false;
     888    private boolean isAreaVisible(Path2D.Double area) {
     889        Rectangle2D bounds = area.getBounds2D();
     890        if (bounds.isEmpty()) return false;
     891        Point2D p = nc.getPoint2D(new EastNorth(bounds.getX(), bounds.getY()));
     892        if (p.getX() > nc.getWidth()) return false;
     893        if (p.getY() < 0) return false;
     894        p = nc.getPoint2D(new EastNorth(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()));
     895        if (p.getX() < 0) return false;
     896        if (p.getY() > nc.getHeight()) return false;
    884897        return true;
    885898    }
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/PaintColors.java

    • Property svn:mime-type deleted
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/Multipolygon.java

    • Property svn:mime-type deleted
    r4623 r4627  
    22package org.openstreetmap.josm.data.osm.visitor.paint.relations;
    33
    4 import java.awt.Point;
    5 import java.awt.Polygon;
    6 import java.awt.Rectangle;
     4import java.awt.geom.Path2D;
     5import java.awt.geom.Path2D.Double;
     6import java.awt.geom.PathIterator;
     7import java.awt.geom.Point2D;
     8import java.awt.geom.Rectangle2D;
    79import java.util.ArrayList;
    810import java.util.Collection;
     
    1719import org.openstreetmap.josm.data.osm.Way;
    1820import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData.Intersection;
    19 import org.openstreetmap.josm.gui.NavigatableComponent;
    2021
    2122public class Multipolygon {
     
    170171        public enum Intersection {INSIDE, OUTSIDE, CROSSING}
    171172
    172         public Polygon poly = new Polygon();
     173        public final Path2D.Double poly;
    173174        public final boolean selected;
    174         private Point lastP;
    175         private Rectangle bounds;
    176 
    177         public PolyData(NavigatableComponent nc, JoinedWay joinedWay) {
    178             this(nc, joinedWay.getNodes(), joinedWay.isSelected());
    179         }
    180 
    181         public PolyData(NavigatableComponent nc, List<Node> nodes, boolean selected) {
     175        private Rectangle2D bounds;
     176
     177        public PolyData(JoinedWay joinedWay) {
     178            this(joinedWay.getNodes(), joinedWay.isSelected());
     179        }
     180
     181        public PolyData(List<Node> nodes, boolean selected) {
    182182            this.selected = selected;
    183             Point p = null;
     183            boolean initial = true;
     184            this.poly = new Path2D.Double();
     185            this.poly.setWindingRule(Path2D.WIND_EVEN_ODD);
    184186            for (Node n : nodes)
    185187            {
    186                 p = nc.getPoint(n);
    187                 poly.addPoint(p.x,p.y);
    188             }
    189             if (!nodes.get(0).equals(nodes.get(nodes.size() - 1))) {
    190                 p = nc.getPoint(nodes.get(0));
    191                 poly.addPoint(p.x, p.y);
    192             }
    193             lastP = p;
     188                Point2D p = n.getEastNorth();
     189                if (initial) {
     190                    poly.moveTo(p.getX(), p.getY());
     191                    initial = false;
     192                } else {
     193                    poly.lineTo(p.getX(), p.getY());
     194                }
     195            }
     196            poly.closePath();
    194197        }
    195198
    196199        public PolyData(PolyData copy) {
    197             poly = new Polygon(copy.poly.xpoints, copy.poly.ypoints, copy.poly.npoints);
    198200            this.selected = copy.selected;
    199             lastP = copy.lastP;
    200         }
    201 
    202         public Intersection contains(Polygon p) {
    203             int contains = p.npoints;
    204             for(int i = 0; i < p.npoints; ++i)
    205             {
    206                 if(poly.contains(p.xpoints[i],p.ypoints[i])) {
    207                     --contains;
    208                 }
    209             }
    210             if(contains == 0) return Intersection.INSIDE;
    211             if(contains == p.npoints) return Intersection.OUTSIDE;
     201            this.poly = (Double) copy.poly.clone();
     202        }
     203       
     204        public Intersection contains(Path2D.Double p) {
     205            int contains = 0;
     206            int total = 0;
     207            double[] coords = new double[6];
     208            for (PathIterator it = p.getPathIterator(null); !it.isDone(); it.next()) {
     209                switch (it.currentSegment(coords)) {
     210                    case PathIterator.SEG_MOVETO:
     211                    case PathIterator.SEG_LINETO:
     212                        if (poly.contains(coords[0], coords[1])) {
     213                            contains++;
     214                        }
     215                        total++;
     216                }
     217            }
     218            if (contains == total) return Intersection.INSIDE;
     219            if (contains == 0) return Intersection.OUTSIDE;
    212220            return Intersection.CROSSING;
    213221        }
    214222
    215         public void addInner(Polygon p) {
    216             for(int i = 0; i < p.npoints; ++i) {
    217                 poly.addPoint(p.xpoints[i],p.ypoints[i]);
    218             }
    219             poly.addPoint(lastP.x, lastP.y);
    220         }
    221 
    222         public Polygon get() {
     223        public void addInner(Path2D.Double inner) {
     224            poly.append(inner.getPathIterator(null), false);
     225        }
     226
     227        public Path2D.Double get() {
    223228            return poly;
    224229        }
    225230
    226         public Rectangle getBounds() {
     231        public Rectangle2D getBounds() {
    227232            if (bounds == null) {
    228                 bounds = poly.getBounds();
     233                bounds = poly.getBounds2D();
    229234            }
    230235            return bounds;
    231         }
    232 
    233         @Override
    234         public String toString() {
    235             return "Points: " + poly.npoints + " Selected: " + selected;
    236236        }
    237237    }
     
    243243    private final List<PolyData> combinedPolygons = new ArrayList<PolyData>();
    244244
    245     public Multipolygon(NavigatableComponent nc, Relation r) {
    246         load(r, nc);
    247     }
    248 
    249     private void load(Relation r, NavigatableComponent nc) {
     245    public Multipolygon(Relation r) {
     246        load(r);
     247    }
     248
     249    private void load(Relation r) {
    250250        MultipolygonRoleMatcher matcher = getMultipolygonRoleMatcher();
    251251
     
    253253        for (RelationMember m : r.getMembers()) {
    254254            if (m.getMember().isDrawable()) {
    255                 if(m.isWay()) {
     255                if (m.isWay()) {
    256256                    Way w = m.getWay();
    257257
    258                     if(w.getNodesCount() < 2) {
     258                    if (w.getNodesCount() < 2) {
    259259                        continue;
    260260                    }
    261261
    262                     if(matcher.isInnerRole(m.getRole())) {
     262                    if (matcher.isInnerRole(m.getRole())) {
    263263                        innerWays.add(w);
    264                     } else if(matcher.isOuterRole(m.getRole())) {
     264                    } else if (matcher.isOuterRole(m.getRole())) {
    265265                        outerWays.add(w);
    266266                    } else if (!m.hasRole()) {
     
    271271        }
    272272
    273         createPolygons(nc, innerWays, innerPolygons);
    274         createPolygons(nc, outerWays, outerPolygons);
     273        createPolygons(innerWays, innerPolygons);
     274        createPolygons(outerWays, outerPolygons);
    275275        if (!outerPolygons.isEmpty()) {
    276276            addInnerToOuters();
     
    278278    }
    279279
    280     private void createPolygons(NavigatableComponent nc, List<Way> ways, List<PolyData> result) {
     280    private void createPolygons(List<Way> ways, List<PolyData> result) {
    281281        List<Way> waysToJoin = new ArrayList<Way>();
    282282        for (Way way: ways) {
    283283            if (way.isClosed()) {
    284                 result.add(new PolyData(nc, way.getNodes(), way.isSelected()));
     284                result.add(new PolyData(way.getNodes(), way.isSelected()));
    285285            } else {
    286286                waysToJoin.add(way);
     
    289289
    290290        for (JoinedWay jw: joinWays(waysToJoin)) {
    291             result.add(new PolyData(nc, jw));
     291            result.add(new PolyData(jw));
    292292        }
    293293    }
     
    390390
    391391    public PolyData findOuterPolygon(PolyData inner, List<PolyData> outerPolygons) {
     392
     393        // First try to test only bbox, use precise testing only if we don't get unique result
     394        Rectangle2D innerBox = inner.getBounds();
     395        PolyData insidePolygon = null;
     396        PolyData intersectingPolygon = null;
     397        int insideCount = 0;
     398        int intersectingCount = 0;
     399
     400        for (PolyData outer: outerPolygons) {
     401            if (outer.getBounds().contains(innerBox)) {
     402                insidePolygon = outer;
     403                insideCount++;
     404            } else if (outer.getBounds().intersects(innerBox)) {
     405                intersectingPolygon = outer;
     406                intersectingCount++;
     407            }
     408        }
     409       
     410        if (insideCount == 1)
     411            return insidePolygon;
     412        else if (intersectingCount == 1)
     413            return intersectingPolygon;
     414
    392415        PolyData result = null;
    393 
    394         {// First try to test only bbox, use precise testing only if we don't get unique result
    395             Rectangle innerBox = inner.getBounds();
    396             PolyData insidePolygon = null;
    397             PolyData intersectingPolygon = null;
    398             int insideCount = 0;
    399             int intersectingCount = 0;
    400 
    401             for (PolyData outer: outerPolygons) {
    402                 if (outer.getBounds().contains(innerBox)) {
    403                     insidePolygon = outer;
    404                     insideCount++;
    405                 } else if (outer.getBounds().intersects(innerBox)) {
    406                     intersectingPolygon = outer;
    407                     intersectingCount++;
    408                 }
    409             }
    410 
    411             if (insideCount == 1)
    412                 return insidePolygon;
    413             else if (intersectingCount == 1)
    414                 return intersectingPolygon;
    415         }
    416 
    417416        for (PolyData combined : outerPolygons) {
    418417            Intersection c = combined.contains(inner.poly);
    419             if(c != Intersection.OUTSIDE)
     418            if (c != Intersection.OUTSIDE)
    420419            {
    421                 if(result == null || result.contains(combined.poly) != Intersection.INSIDE) {
     420                if (result == null || result.contains(combined.poly) != Intersection.INSIDE) {
    422421                    result = combined;
    423422                }
     
    444443            for (PolyData pdInner: innerPolygons) {
    445444                PolyData o = findOuterPolygon(pdInner, combinedPolygons);
    446                 if(o == null) {
     445                if (o == null) {
    447446                    o = outerPolygons.get(0);
    448447                }
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java

    r4626 r4627  
    2626import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
    2727import org.openstreetmap.josm.gui.NavigatableComponent;
    28 import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
    2928import org.openstreetmap.josm.gui.layer.Layer;
    3029import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     
    3433 *
    3534 */
    36 public class MultipolygonCache implements DataSetListener, LayerChangeListener, ZoomChangeListener, ProjectionChangeListener {
     35public class MultipolygonCache implements DataSetListener, LayerChangeListener, ProjectionChangeListener {
    3736
    3837    private static final MultipolygonCache instance = new MultipolygonCache();
     
    4140   
    4241    private MultipolygonCache() {
    43         this.cache = new HashMap<NavigatableComponent, Map<DataSet, Map<Relation,Multipolygon>>>();
     42        this.cache = new HashMap<NavigatableComponent, Map<DataSet, Map<Relation, Multipolygon>>>();
    4443        Main.addProjectionChangeListener(this);
    4544    }
     
    6665            multipolygon = map2.get(r);
    6766            if (multipolygon == null || forceRefresh) {
    68                 map2.put(r, multipolygon = new Multipolygon(nc, r));
     67                map2.put(r, multipolygon = new Multipolygon(r));
    6968            }
    7069        }
     
    209208
    210209    @Override
    211     public void zoomChanged(/*NavigatableComponent source*/) {
    212         // TODO Change zoomChanged() method to add a "NavigatableComponent source" argument ? (this method is however used at least by one plugin)
    213         //clear(source);
    214         clear();
    215     }
    216 
    217     @Override
    218210    public void projectionChanged(Projection oldValue, Projection newValue) {
    219211        clear();
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java

    r4623 r4627  
    2121import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
    2222import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData.Intersection;
     23import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
    2324import org.openstreetmap.josm.data.validation.Severity;
    2425import org.openstreetmap.josm.data.validation.Test;
     
    134135            checkMembersAndRoles(r);
    135136
    136             Multipolygon polygon = new Multipolygon(Main.map.mapView, r);
     137            Multipolygon polygon = MultipolygonCache.getInstance().get(Main.map.mapView, r);
    137138
    138139            boolean hasOuterWay = false;
  • trunk/src/org/openstreetmap/josm/gui/MapView.java

    r4623 r4627  
    239239        });
    240240       
    241         // Add Multipolygon cache to layer and zoom listeners
     241        // Add Multipolygon cache to layer listeners
    242242        addLayerChangeListener(MultipolygonCache.getInstance());
    243         addZoomChangeListener(MultipolygonCache.getInstance());
    244243    }
    245244
  • trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java

    r4126 r4627  
    77import java.awt.Point;
    88import java.awt.Rectangle;
     9import java.awt.geom.AffineTransform;
    910import java.awt.geom.Point2D;
    1011import java.util.ArrayList;
     
    6162    public static final IntegerProperty PROP_SNAP_DISTANCE = new IntegerProperty("mappaint.node.snap-distance", 10);
    6263
     64    public static final String PROPNAME_CENTER = "center";
     65    public static final String PROPNAME_SCALE  = "scale";
     66   
    6367    /**
    6468     * the zoom listeners
     
    229233    }
    230234
     235    public AffineTransform getAffineTransform() {
     236        return new AffineTransform(
     237                1.0/scale, 0.0, 0.0, -1.0/scale, getWidth()/2.0 - center.east()/scale, getHeight()/2.0 + center.north()/scale);
     238    }
     239   
    231240    /**
    232241     * Return the point on the screen where this Coordinate would be.
     
    335344            EastNorth oldCenter = center;
    336345            center = newCenter;
    337             firePropertyChange("center", oldCenter, newCenter);
     346            firePropertyChange(PROPNAME_CENTER, oldCenter, newCenter);
    338347        }
    339348        if (scale != newScale) {
    340349            double oldScale = scale;
    341350            scale = newScale;
    342             firePropertyChange("scale", oldScale, newScale);
     351            firePropertyChange(PROPNAME_SCALE, oldScale, newScale);
    343352        }
    344353
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    r4310 r4627  
    5050import org.openstreetmap.josm.gui.MapView;
    5151import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
     52import org.openstreetmap.josm.gui.NavigatableComponent;
    5253import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    5354import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
     
    790791
    791792    public void propertyChange(PropertyChangeEvent evt) {
    792         if ("center".equals(evt.getPropertyName()) || "scale".equals(evt.getPropertyName())) {
     793        if (NavigatableComponent.PROPNAME_CENTER.equals(evt.getPropertyName()) || NavigatableComponent.PROPNAME_SCALE.equals(evt.getPropertyName())) {
    793794            updateOffscreenBuffer = true;
    794795        }
Note: See TracChangeset for help on using the changeset viewer.