Ticket #3781: rework-multipolygons-0.patch

File rework-multipolygons-0.patch, 22.2 KB (added by hansendc, 17 years ago)
  • src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java

     
    2626import java.util.Iterator;
    2727import java.util.LinkedList;
    2828import java.util.List;
     29import java.util.TreeSet;
     30import java.util.Comparator;
    2931
    3032import javax.swing.ImageIcon;
    3133
     
    5254public class MapPaintVisitor extends SimplePaintVisitor {
    5355    protected boolean useRealWidth;
    5456    protected boolean zoomLevelDisplay;
    55     protected int fillAreas;
    5657    protected boolean drawMultipolygon;
    5758    protected boolean drawRestriction;
    5859    protected boolean leftHandTraffic;
     
    7374    protected double circum;
    7475    protected double dist;
    7576    protected Collection<String> regionalNameOrder;
    76     protected Boolean selectedCall;
    7777    protected Boolean useStyleCache;
    7878    private static int paintid = 0;
    7979    private static int viewid = 0;
     
    139139     * @param n The node to draw.
    140140     */
    141141    @Override
    142     public void visit(Node n) {
     142    public void visit(Node n) {}
     143    public void drawNode(Node n) {
    143144        /* check, if the node is visible at all */
    144145        if((n.getEastNorth().east()  > maxEN.east() ) ||
    145146                (n.getEastNorth().north() > maxEN.north()) ||
     
    175176     * Draw a line for all segments, according to tags.
    176177     * @param w The way to draw.
    177178     */
    178     @Override
    179     public void visit(Way w) {
     179    @Override
     180    public void visit(Way w) {}
     181    public void drawWay(Way w, int fillAreas) {
    180182        if(w.getNodesCount() < 2)
    181183        {
    182184            w.mappaintVisibleCode = viewid;
     
    390392        displaySegments();
    391393    }
    392394
    393     public Collection<Way> joinWays(Collection<Way> join, OsmPrimitive errs)
     395    public Collection<PolyData> joinWays(Collection<Way> join, OsmPrimitive errs)
    394396    {
    395         Collection<Way> res = new LinkedList<Way>();
     397        Collection<PolyData> res = new LinkedList<PolyData>();
    396398        Object[] joinArray = join.toArray();
    397399        int left = join.size();
    398400        while(left != 0)
     
    479481            {
    480482                w = new Way(w);
    481483                w.setNodes(n);
    482                 if (selected) {
    483                     data.addSelected(Collections.singleton(w),false /* don't notify listeners */);
    484                 } else {
    485                     data.clearSelection(w);
    486                 }
     484                // Do not mess with the DataSet's contents here.
    487485            }
    488486            if(!w.isClosed())
    489487            {
     
    493491                            w.getDisplayName(DefaultNameFormatter.getInstance())), true);
    494492                }
    495493            }
    496             res.add(w);
     494            PolyData pd = new PolyData(w);
     495            pd.selected = selected;
     496            res.add(pd);
    497497        } /* while(left != 0) */
    498498
    499499        return res;
     
    530530        osm.mappaintDrawnCode = paintid;
    531531    }
    532532
    533     @Override
    534     public void visit(Relation r) {
    535 
     533    public void visit(Relation r) {};
     534    public void paintUnselectedRelation(Relation r) {
    536535        r.mappaintVisibleCode = 0;
    537536
    538         /* TODO: is it possible to do this like the nodes/ways code? */
    539         //if(profilerOmitDraw)
    540         //    return;
    541 
    542         if(selectedCall)
     537        if (drawMultipolygon && "multipolygon".equals(r.get("type")))
    543538        {
    544             for (RelationMember m : r.getMembers())
    545             {
    546                 if (m.isNode() && !m.getMember().incomplete && !m.getMember().isDeleted() && !m.getMember().isFiltered())
    547                 {
    548                     drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
    549                 }
    550             }
    551             return;
    552         }
    553         else if (drawMultipolygon && "multipolygon".equals(r.get("type")))
    554         {
    555539            if(drawMultipolygon(r))
    556540                return;
    557541        }
     
    564548        {
    565549            for (RelationMember m : r.getMembers())
    566550            {
    567                 if (m.isWay() && !m.getMember().incomplete && !m.getMember().isDeleted()) /* nodes drawn on second call */
     551                if (m.isWay() && drawable(m.getMember()))
    568552                {
    569553                    drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember())
    570554                            : null, true, true);
     
    844828        }
    845829    }
    846830
     831    class PolyData {
     832        public Polygon poly = new Polygon();
     833        public Way way;
     834        public boolean selected = false;
     835        private Point p = null;
     836        private Collection<Polygon> inner = null;
     837        PolyData(Way w)
     838        {
     839            way = w;
     840            for (Node n : w.getNodes())
     841            {
     842                p = nc.getPoint(n);
     843                poly.addPoint(p.x,p.y);
     844            }
     845        }
     846        public int contains(Polygon p)
     847        {
     848            int contains = p.npoints;
     849            for(int i = 0; i < p.npoints; ++i)
     850            {
     851                if(poly.contains(p.xpoints[i],p.ypoints[i])) {
     852                    --contains;
     853                }
     854            }
     855            if(contains == 0) return 1;
     856            if(contains == p.npoints) return 0;
     857            return 2;
     858        }
     859        public void addInner(Polygon p)
     860        {
     861            if(inner == null) {
     862                inner = new ArrayList<Polygon>();
     863            }
     864            inner.add(p);
     865        }
     866        public boolean isClosed()
     867        {
     868            return (poly.npoints >= 3
     869                    && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
     870                                                       && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
     871        }
     872        public Polygon get()
     873        {
     874            if(inner != null)
     875            {
     876                for (Polygon pp : inner)
     877                {
     878                    for(int i = 0; i < pp.npoints; ++i) {
     879                        poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
     880                    }
     881                    poly.addPoint(p.x,p.y);
     882                }
     883                inner = null;
     884            }
     885            return poly;
     886        }
     887    }
     888    void addInnerToOuters(Relation r, boolean incomplete, PolyData pdInner, LinkedList<PolyData> outerPolygons)
     889    {
     890        Way wInner = pdInner.way;
     891        if(wInner != null && !wInner.isClosed())
     892        {
     893            Point pInner = nc.getPoint(wInner.getNode(0));
     894            pdInner.poly.addPoint(pInner.x,pInner.y);
     895        }
     896        PolyData o = null;
     897        for (PolyData pdOuter : outerPolygons)
     898        {
     899            Integer c = pdOuter.contains(pdInner.poly);
     900            if(c >= 1)
     901            {
     902                if(c > 1 && pdOuter.way != null && pdOuter.way.isClosed())
     903                {
     904                    r.putError(tr("Intersection between ways ''{0}'' and ''{1}''.",
     905                            pdOuter.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
     906                }
     907                if(o == null || o.contains(pdOuter.poly) > 0) {
     908                    o = pdOuter;
     909                }
     910            }
     911        }
     912        if(o == null)
     913        {
     914            if(!incomplete)
     915            {
     916                r.putError(tr("Inner way ''{0}'' is outside.",
     917                        wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
     918            }
     919            o = outerPolygons.get(0);
     920        }
     921        o.addInner(pdInner.poly);
     922    }
     923
    847924    public Boolean drawMultipolygon(Relation r) {
    848925        Collection<Way> inner = new LinkedList<Way>();
    849926        Collection<Way> outer = new LinkedList<Way>();
     
    861938                        m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
    862939            } else if(m.getMember().incomplete) {
    863940                incomplete = true;
    864             } else
    865             {
    866                 if(m.isWay())
    867                 {
     941            } else {
     942                if(m.isWay()) {
    868943                    Way w = m.getWay();
    869                     if(w.getNodesCount() < 2)
    870                     {
     944                    if(w.getNodesCount() < 2) {
    871945                        r.putError(tr("Way ''{0}'' with less than two points.",
    872946                                w.getDisplayName(DefaultNameFormatter.getInstance())), true);
    873947                    }
     
    875949                        inner.add(w);
    876950                    } else if("outer".equals(m.getRole())) {
    877951                        outer.add(w);
    878                     } else
    879                     {
     952                    } else {
    880953                        r.putError(tr("No useful role ''{0}'' for Way ''{1}''.",
    881954                                m.getRole(), w.getDisplayName(DefaultNameFormatter.getInstance())), true);
    882955                        if(!m.hasRole()) {
     
    911984        {
    912985            Boolean zoomok = isZoomOk(wayStyle);
    913986            Boolean visible = false;
    914             Collection<Way> join = new LinkedList<Way>();
     987            Collection<Way> outerjoin = new LinkedList<Way>();
     988            Collection<Way> innerjoin = new LinkedList<Way>();
    915989
    916990            drawn = true;
    917991            for (Way w : outer)
     
    919993                if(w.isClosed()) {
    920994                    outerclosed.add(w);
    921995                } else {
    922                     join.add(w);
     996                    outerjoin.add(w);
    923997                }
    924998            }
    925             if(join.size() != 0)
    926             {
    927                 for(Way w : joinWays(join, incomplete ? null : r)) {
    928                     outerclosed.add(w);
    929                 }
    930             }
    931 
    932             join.clear();
    933999            for (Way w : inner)
    9341000            {
    9351001                if(w.isClosed()) {
    9361002                    innerclosed.add(w);
    9371003                } else {
    938                     join.add(w);
     1004                    innerjoin.add(w);
    9391005                }
    9401006            }
    941             if(join.size() != 0)
     1007            if(outerclosed.size() == 0 && outerjoin.size() == 0)
    9421008            {
    943                 for(Way w : joinWays(join, incomplete ? null : r)) {
    944                     innerclosed.add(w);
    945                 }
    946             }
    947 
    948             if(outerclosed.size() == 0)
    949             {
    9501009                r.putError(tr("No outer way for multipolygon ''{0}''.",
    9511010                        r.getDisplayName(DefaultNameFormatter.getInstance())), true);
    9521011                visible = true; /* prevent killing remaining ways */
    9531012            }
    9541013            else if(zoomok)
    9551014            {
    956                 class PolyData {
    957                     public Polygon poly = new Polygon();
    958                     public Way way;
    959                     private Point p = null;
    960                     private Collection<Polygon> inner = null;
    961                     PolyData(Way w)
    962                     {
    963                         way = w;
    964                         for (Node n : w.getNodes())
    965                         {
    966                             p = nc.getPoint(n);
    967                             poly.addPoint(p.x,p.y);
    968                         }
    969                     }
    970                     public int contains(Polygon p)
    971                     {
    972                         int contains = p.npoints;
    973                         for(int i = 0; i < p.npoints; ++i)
    974                         {
    975                             if(poly.contains(p.xpoints[i],p.ypoints[i])) {
    976                                 --contains;
    977                             }
    978                         }
    979                         if(contains == 0) return 1;
    980                         if(contains == p.npoints) return 0;
    981                         return 2;
    982                     }
    983                     public void addInner(Polygon p)
    984                     {
    985                         if(inner == null) {
    986                             inner = new ArrayList<Polygon>();
    987                         }
    988                         inner.add(p);
    989                     }
    990                     public boolean isClosed()
    991                     {
    992                         return (poly.npoints >= 3
    993                                 && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
    994                                                                    && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
    995                     }
    996                     public Polygon get()
    997                     {
    998                         if(inner != null)
    999                         {
    1000                             for (Polygon pp : inner)
    1001                             {
    1002                                 for(int i = 0; i < pp.npoints; ++i) {
    1003                                     poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
    1004                                 }
    1005                                 poly.addPoint(p.x,p.y);
    1006                             }
    1007                             inner = null;
    1008                         }
    1009                         return poly;
    1010                     }
     1015                LinkedList<PolyData> outerPoly = new LinkedList<PolyData>();
     1016                for (Way w : outerclosed) {
     1017                    outerPoly.add(new PolyData(w));
    10111018                }
    1012                 LinkedList<PolyData> poly = new LinkedList<PolyData>();
    1013                 for (Way w : outerclosed)
    1014                 {
    1015                     poly.add(new PolyData(w));
    1016                 }
     1019                outerPoly.addAll(joinWays(outerjoin, incomplete ? null : r));
    10171020                for (Way wInner : innerclosed)
    10181021                {
    1019                     Polygon polygon = new Polygon();
    1020 
    1021                     for (Node n : wInner.getNodes())
    1022                     {
    1023                         Point pInner = nc.getPoint(n);
    1024                         polygon.addPoint(pInner.x,pInner.y);
    1025                     }
    1026                     if(!wInner.isClosed())
    1027                     {
    1028                         Point pInner = nc.getPoint(wInner.getNode(0));
    1029                         polygon.addPoint(pInner.x,pInner.y);
    1030                     }
    1031                     PolyData o = null;
    1032                     for (PolyData pd : poly)
    1033                     {
    1034                         Integer c = pd.contains(polygon);
    1035                         if(c >= 1)
    1036                         {
    1037                             if(c > 1 && pd.way.isClosed())
    1038                             {
    1039                                 r.putError(tr("Intersection between ways ''{0}'' and ''{1}''.",
    1040                                         pd.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
    1041                             }
    1042                             if(o == null || o.contains(pd.poly) > 0) {
    1043                                 o = pd;
    1044                             }
    1045                         }
    1046                     }
    1047                     if(o == null)
    1048                     {
    1049                         if(!incomplete)
    1050                         {
    1051                             r.putError(tr("Inner way ''{0}'' is outside.",
    1052                                     wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
    1053                         }
    1054                         o = poly.get(0);
    1055                     }
    1056                     o.addInner(polygon);
     1022                    PolyData pdInner = new PolyData(wInner);
     1023                    // incomplete is probably redundant
     1024                    addInnerToOuters(r, incomplete, pdInner, outerPoly);
    10571025                }
     1026                for (PolyData pdInner : joinWays(innerjoin, incomplete ? null : r)) {
     1027                    addInnerToOuters(r, incomplete, pdInner, outerPoly);
     1028                }
    10581029                AreaElemStyle areaStyle = (AreaElemStyle)wayStyle;
    1059                 for (PolyData pd : poly)
    1060                 {
     1030                for (PolyData pd : outerPoly) {
    10611031                    Polygon p = pd.get();
    1062                     if(isPolygonVisible(p))
    1063                     {
    1064                         drawAreaPolygon(p, (data.isSelected(pd.way) || data.isSelected(r)) ? selectedColor
    1065                                 : areaStyle.color);
    1066                         visible = true;
    1067                     }
     1032                    if(!isPolygonVisible(p))
     1033                        continue;
     1034
     1035                    boolean selected = pd.selected || data.isSelected(pd.way) || data.isSelected(r);
     1036                    drawAreaPolygon(p, selected ? selectedColor : areaStyle.color);
     1037                    visible = true;
    10681038                }
    10691039            }
    10701040            if(!visible) /* nothing visible, so disable relation and all its ways */
     
    14141384        }
    14151385    }
    14161386
     1387    boolean drawable(OsmPrimitive osm)
     1388    {
     1389        return !osm.isDeleted() && !osm.isFiltered() && !osm.incomplete;
     1390    }
     1391
    14171392    @Override
    14181393    public void getColors()
    14191394    {
     
    14251400
    14261401    DataSet data;
    14271402
     1403    <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
     1404        ArrayList<T> sorted = new ArrayList<T>(prims);
     1405        Collections.sort(sorted,
     1406            new Comparator<T>() {
     1407                public int compare(T o1, T o2) {
     1408                    boolean s1 = data.isSelected(o1);
     1409                    boolean s2 = data.isSelected(o2);
     1410                    if (s1 && !s2)
     1411                        return 1;
     1412                    if (!s1 && s2)
     1413                        return -1;
     1414                    return o1.compareTo(o2);
     1415                }
     1416            });
     1417        return sorted;
     1418    }
     1419
    14281420    /* Shows areas before non-areas */
    14291421    @Override
    14301422    public void visitAll(DataSet data, Boolean virtual) {
     
    14331425        //profilerOmitDraw = Main.pref.getBoolean("mappaint.profiler.omitdraw",false);
    14341426
    14351427        useStyleCache = Main.pref.getBoolean("mappaint.cache",true);
    1436         fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
     1428        int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
    14371429        fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
    14381430        showNames = Main.pref.getInteger("mappaint.shownames", 10000000);
    14391431        showIcons = Main.pref.getInteger("mappaint.showicons", 10000000);
     
    14681460        maxEN = nc.getEastNorth(nc.getWidth()-1,0);
    14691461
    14701462
    1471         selectedCall = false;
    14721463        ++paintid;
    14731464        viewid = nc.getViewID();
    14741465
     
    14911482            //    profilerN = 0;
    14921483            for (final Relation osm : data.relations)
    14931484            {
    1494                 if(!osm.isDeleted() && !osm.isFiltered() && !osm.incomplete && osm.mappaintVisibleCode != viewid)
     1485                if(drawable(osm) && osm.mappaintVisibleCode != viewid)
    14951486                {
    1496                     osm.visit(this);
     1487                    paintUnselectedRelation(osm);
    14971488                    //            profilerN++;
    14981489                }
    14991490            }
     
    15061497
    15071498            /*** AREAS ***/
    15081499            //    profilerN = 0;
    1509             for (final Way osm : data.ways)
    1510             {
    1511                 if (!osm.incomplete && !osm.isDeleted() && !osm.isFiltered()
     1500            for (final Way osm : selectedLast(data, data.ways)) {
     1501                if (drawable(osm)
    15121502                        && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
    15131503                {
    15141504                    if(isPrimitiveArea(osm) && osm.mappaintDrawnAreaCode != paintid)
    15151505                    {
    1516                         osm.visit(this);
     1506                        drawWay(osm, fillAreas);
    15171507                        //                profilerN++;
    15181508                    } else {
    15191509                        noAreaWays.add(osm);
     
    15301520
    15311521            /*** WAYS ***/
    15321522            //    profilerN = 0;
    1533             fillAreas = 0;
    1534             for (final OsmPrimitive osm : noAreaWays)
     1523            for (final Way osm : noAreaWays)
    15351524            {
    1536                 osm.visit(this);
     1525                drawWay(osm, 0);
    15371526                //        profilerN++;
    15381527            }
    15391528
     
    15481537        {
    15491538            /*** WAYS (filling disabled)  ***/
    15501539            //    profilerN = 0;
    1551             for (final OsmPrimitive osm : data.ways)
    1552                 if (!osm.incomplete && !osm.isDeleted() && !osm.isFiltered() && !data.isSelected(osm)
    1553                         && osm.mappaintVisibleCode != viewid )
     1540            for (final Way way : data.ways)
     1541                if (drawable(way) && !data.isSelected(way)
     1542                        && way.mappaintVisibleCode != viewid )
    15541543                {
    1555                     osm.visit(this);
     1544                    drawWay(way, 0);
    15561545                    //            profilerN++;
    15571546                }
    15581547
     
    15651554        }
    15661555
    15671556        /*** SELECTED  ***/
    1568         selectedCall = true;
    15691557        //profilerN = 0;
    15701558        for (final OsmPrimitive osm : data.getSelected()) {
    15711559            if (!osm.incomplete && !osm.isDeleted() && !(osm instanceof Node)
    15721560                    && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
    15731561            {
    1574                 osm.visit(this);
     1562                osm.visit(new AbstractVisitor() {
     1563                    public void visit(Way w) {
     1564                        drawWay(w, 0);
     1565                    }
     1566                    public void visit(Node n) {
     1567                        drawNode(n);
     1568                    }
     1569                    public void visit(Relation r) {
     1570                        /* TODO: is it possible to do this like the nodes/ways code? */
     1571                        //if(profilerOmitDraw)
     1572                        //    return;
     1573                        r.mappaintVisibleCode = 0;
     1574                        for (RelationMember m : r.getMembers()) {
     1575                            if (m.isNode() && drawable(m.getMember())) {
     1576                                drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
     1577                            }
     1578                        }
     1579                    }
     1580                });
    15751581                //        profilerN++;
    15761582            }
    15771583        }
     
    15871593
    15881594        /*** NODES ***/
    15891595        //profilerN = 0;
    1590         for (final OsmPrimitive osm : data.nodes)
     1596        for (final Node osm : data.nodes)
    15911597            if (!osm.incomplete && !osm.isDeleted() && (data.isSelected(osm) || !osm.isFiltered())
    15921598                    && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
    15931599            {
    1594                 osm.visit(this);
     1600                drawNode(osm);
    15951601                //        profilerN++;
    15961602            }
    15971603