Changeset 4627 in josm


Ignore:
Timestamp:
03.12.2011 00:14:54 (6 months 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.