Ticket #3781: rework-multipolygons-1.patch

File rework-multipolygons-1.patch, 23.4 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;
     
    504504    {
    505505        if(osm instanceof Way)
    506506        {
     507            Way w = (Way)osm;
    507508            if(style instanceof AreaElemStyle)
    508509            {
    509510                Way way = (Way)osm;
     
    530531        osm.mappaintDrawnCode = paintid;
    531532    }
    532533
    533     @Override
    534     public void visit(Relation r) {
    535 
     534    public void visit(Relation r) {};
     535    public void paintUnselectedRelation(Relation r) {
    536536        r.mappaintVisibleCode = 0;
    537537
    538         /* TODO: is it possible to do this like the nodes/ways code? */
    539         //if(profilerOmitDraw)
    540         //    return;
    541 
    542         if(selectedCall)
     538        if (drawMultipolygon && "multipolygon".equals(r.get("type")))
    543539        {
    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         {
    555540            if(drawMultipolygon(r))
    556541                return;
    557542        }
     
    564549        {
    565550            for (RelationMember m : r.getMembers())
    566551            {
    567                 if (m.isWay() && !m.getMember().incomplete && !m.getMember().isDeleted()) /* nodes drawn on second call */
     552                if (m.isWay() && drawable(m.getMember()))
    568553                {
    569554                    drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember())
    570555                            : null, true, true);
     
    844829        }
    845830    }
    846831
     832    class PolyData {
     833        public Polygon poly = new Polygon();
     834        public Way way;
     835        public boolean selected = false;
     836        private Point p = null;
     837        private Collection<Polygon> inner = null;
     838        PolyData(Way w)
     839        {
     840            way = w;
     841            for (Node n : w.getNodes())
     842            {
     843                p = nc.getPoint(n);
     844                poly.addPoint(p.x,p.y);
     845            }
     846        }
     847        public int contains(Polygon p)
     848        {
     849            int contains = p.npoints;
     850            for(int i = 0; i < p.npoints; ++i)
     851            {
     852                if(poly.contains(p.xpoints[i],p.ypoints[i])) {
     853                    --contains;
     854                }
     855            }
     856            if(contains == 0) return 1;
     857            if(contains == p.npoints) return 0;
     858            return 2;
     859        }
     860        public void addInner(Polygon p)
     861        {
     862            if(inner == null) {
     863                inner = new ArrayList<Polygon>();
     864            }
     865            inner.add(p);
     866        }
     867        public boolean isClosed()
     868        {
     869            return (poly.npoints >= 3
     870                    && poly.xpoints[0] == poly.xpoints[poly.npoints-1]
     871                                                       && poly.ypoints[0] == poly.ypoints[poly.npoints-1]);
     872        }
     873        public Polygon get()
     874        {
     875            if(inner != null)
     876            {
     877                for (Polygon pp : inner)
     878                {
     879                    for(int i = 0; i < pp.npoints; ++i) {
     880                        poly.addPoint(pp.xpoints[i],pp.ypoints[i]);
     881                    }
     882                    poly.addPoint(p.x,p.y);
     883                }
     884                inner = null;
     885            }
     886            return poly;
     887        }
     888    }
     889    void addInnerToOuters(Relation r, boolean incomplete, PolyData pdInner, LinkedList<PolyData> outerPolygons)
     890    {
     891        Way wInner = pdInner.way;
     892        if(wInner != null && !wInner.isClosed())
     893        {
     894            Point pInner = nc.getPoint(wInner.getNode(0));
     895            pdInner.poly.addPoint(pInner.x,pInner.y);
     896        }
     897        PolyData o = null;
     898        for (PolyData pdOuter : outerPolygons)
     899        {
     900            Integer c = pdOuter.contains(pdInner.poly);
     901            if(c >= 1)
     902            {
     903                if(c > 1 && pdOuter.way != null && pdOuter.way.isClosed())
     904                {
     905                    r.putError(tr("Intersection between ways ''{0}'' and ''{1}''.",
     906                            pdOuter.way.getDisplayName(DefaultNameFormatter.getInstance()), wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
     907                }
     908                if(o == null || o.contains(pdOuter.poly) > 0) {
     909                    o = pdOuter;
     910                }
     911            }
     912        }
     913        if(o == null)
     914        {
     915            if(!incomplete)
     916            {
     917                r.putError(tr("Inner way ''{0}'' is outside.",
     918                        wInner.getDisplayName(DefaultNameFormatter.getInstance())), true);
     919            }
     920            o = outerPolygons.get(0);
     921        }
     922        o.addInner(pdInner.poly);
     923    }
     924
    847925    public Boolean drawMultipolygon(Relation r) {
    848926        Collection<Way> inner = new LinkedList<Way>();
    849927        Collection<Way> outer = new LinkedList<Way>();
     
    861939                        m.getMember().getDisplayName(DefaultNameFormatter.getInstance())), true);
    862940            } else if(m.getMember().incomplete) {
    863941                incomplete = true;
    864             } else
    865             {
    866                 if(m.isWay())
    867                 {
     942            } else {
     943                if(m.isWay()) {
    868944                    Way w = m.getWay();
    869                     if(w.getNodesCount() < 2)
    870                     {
     945                    if(w.getNodesCount() < 2) {
    871946                        r.putError(tr("Way ''{0}'' with less than two points.",
    872947                                w.getDisplayName(DefaultNameFormatter.getInstance())), true);
    873948                    }
     
    875950                        inner.add(w);
    876951                    } else if("outer".equals(m.getRole())) {
    877952                        outer.add(w);
    878                     } else
    879                     {
     953                    } else {
    880954                        r.putError(tr("No useful role ''{0}'' for Way ''{1}''.",
    881955                                m.getRole(), w.getDisplayName(DefaultNameFormatter.getInstance())), true);
    882956                        if(!m.hasRole()) {
     
    911985        {
    912986            Boolean zoomok = isZoomOk(wayStyle);
    913987            Boolean visible = false;
    914             Collection<Way> join = new LinkedList<Way>();
     988            Collection<Way> outerjoin = new LinkedList<Way>();
     989            Collection<Way> innerjoin = new LinkedList<Way>();
    915990
    916991            drawn = true;
    917992            for (Way w : outer)
     
    919994                if(w.isClosed()) {
    920995                    outerclosed.add(w);
    921996                } else {
    922                     join.add(w);
     997                    outerjoin.add(w);
    923998                }
    924999            }
    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();
    9331000            for (Way w : inner)
    9341001            {
    9351002                if(w.isClosed()) {
    9361003                    innerclosed.add(w);
    9371004                } else {
    938                     join.add(w);
     1005                    innerjoin.add(w);
    9391006                }
    9401007            }
    941             if(join.size() != 0)
     1008            if(outerclosed.size() == 0 && outerjoin.size() == 0)
    9421009            {
    943                 for(Way w : joinWays(join, incomplete ? null : r)) {
    944                     innerclosed.add(w);
    945                 }
    946             }
    947 
    948             if(outerclosed.size() == 0)
    949             {
    9501010                r.putError(tr("No outer way for multipolygon ''{0}''.",
    9511011                        r.getDisplayName(DefaultNameFormatter.getInstance())), true);
    9521012                visible = true; /* prevent killing remaining ways */
    9531013            }
    9541014            else if(zoomok)
    9551015            {
    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                     }
     1016                LinkedList<PolyData> outerPoly = new LinkedList<PolyData>();
     1017                for (Way w : outerclosed) {
     1018                    outerPoly.add(new PolyData(w));
    10111019                }
    1012                 LinkedList<PolyData> poly = new LinkedList<PolyData>();
    1013                 for (Way w : outerclosed)
    1014                 {
    1015                     poly.add(new PolyData(w));
    1016                 }
     1020                outerPoly.addAll(joinWays(outerjoin, incomplete ? null : r));
    10171021                for (Way wInner : innerclosed)
    10181022                {
    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);
     1023                    PolyData pdInner = new PolyData(wInner);
     1024                    // incomplete is probably redundant
     1025                    addInnerToOuters(r, incomplete, pdInner, outerPoly);
    10571026                }
     1027                for (PolyData pdInner : joinWays(innerjoin, incomplete ? null : r)) {
     1028                    addInnerToOuters(r, incomplete, pdInner, outerPoly);
     1029                }
    10581030                AreaElemStyle areaStyle = (AreaElemStyle)wayStyle;
    1059                 for (PolyData pd : poly)
    1060                 {
     1031                for (PolyData pd : outerPoly) {
    10611032                    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                     }
     1033                    if(!isPolygonVisible(p))
     1034                        continue;
     1035
     1036                    boolean selected = pd.selected || data.isSelected(pd.way) || data.isSelected(r);
     1037                    drawAreaPolygon(p, selected ? selectedColor : areaStyle.color);
     1038                    visible = true;
    10681039                }
    10691040            }
    10701041            if(!visible) /* nothing visible, so disable relation and all its ways */
     
    10831054                ElemStyle innerStyle = getPrimitiveStyle(wInner);
    10841055                if(innerStyle == null)
    10851056                {
     1057                    if (data.isSelected(wInner))
     1058                        continue;
    10861059                    if(zoomok && (wInner.mappaintDrawnCode != paintid
    10871060                            || outer.size() == 0))
    10881061                    {
     
    11141087                ElemStyle outerStyle = getPrimitiveStyle(wOuter);
    11151088                if(outerStyle == null)
    11161089                {
     1090                    // Selected ways are drawn at the very end
     1091                    if (data.isSelected(wOuter))
     1092                        continue;
    11171093                    if(zoomok)
    11181094                    {
    11191095                        drawWay(wOuter, ((AreaElemStyle)wayStyle).line,
     
    14141390        }
    14151391    }
    14161392
     1393    boolean drawable(OsmPrimitive osm)
     1394    {
     1395        return !osm.isDeleted() && !osm.isFiltered() && !osm.incomplete;
     1396    }
     1397
    14171398    @Override
    14181399    public void getColors()
    14191400    {
     
    14251406
    14261407    DataSet data;
    14271408
     1409    <T extends OsmPrimitive> Collection<T> selectedLast(final DataSet data, Collection <T> prims) {
     1410        ArrayList<T> sorted = new ArrayList<T>(prims);
     1411        Collections.sort(sorted,
     1412            new Comparator<T>() {
     1413                public int compare(T o1, T o2) {
     1414                    boolean s1 = data.isSelected(o1);
     1415                    boolean s2 = data.isSelected(o2);
     1416                    if (s1 && !s2)
     1417                        return 1;
     1418                    if (!s1 && s2)
     1419                        return -1;
     1420                    return o1.compareTo(o2);
     1421                }
     1422            });
     1423        return sorted;
     1424    }
     1425
    14281426    /* Shows areas before non-areas */
    14291427    @Override
    14301428    public void visitAll(DataSet data, Boolean virtual) {
     
    14331431        //profilerOmitDraw = Main.pref.getBoolean("mappaint.profiler.omitdraw",false);
    14341432
    14351433        useStyleCache = Main.pref.getBoolean("mappaint.cache",true);
    1436         fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
     1434        int fillAreas = Main.pref.getInteger("mappaint.fillareas", 10000000);
    14371435        fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50))));
    14381436        showNames = Main.pref.getInteger("mappaint.shownames", 10000000);
    14391437        showIcons = Main.pref.getInteger("mappaint.showicons", 10000000);
     
    14681466        maxEN = nc.getEastNorth(nc.getWidth()-1,0);
    14691467
    14701468
    1471         selectedCall = false;
    14721469        ++paintid;
    14731470        viewid = nc.getViewID();
    14741471
     
    14911488            //    profilerN = 0;
    14921489            for (final Relation osm : data.relations)
    14931490            {
    1494                 if(!osm.isDeleted() && !osm.isFiltered() && !osm.incomplete && osm.mappaintVisibleCode != viewid)
     1491                if(drawable(osm) && osm.mappaintVisibleCode != viewid)
    14951492                {
    1496                     osm.visit(this);
     1493                    paintUnselectedRelation(osm);
    14971494                    //            profilerN++;
    14981495                }
    14991496            }
     
    15061503
    15071504            /*** AREAS ***/
    15081505            //    profilerN = 0;
    1509             for (final Way osm : data.ways)
    1510             {
    1511                 if (!osm.incomplete && !osm.isDeleted() && !osm.isFiltered()
     1506            for (final Way osm : selectedLast(data, data.ways)) {
     1507                if (drawable(osm)
    15121508                        && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
    15131509                {
    15141510                    if(isPrimitiveArea(osm) && osm.mappaintDrawnAreaCode != paintid)
    15151511                    {
    1516                         osm.visit(this);
     1512                        drawWay(osm, fillAreas);
    15171513                        //                profilerN++;
    1518                     } else {
     1514                    }// else {
    15191515                        noAreaWays.add(osm);
    1520                     }
     1516                    //}
    15211517                }
    15221518            }
    15231519
     
    15301526
    15311527            /*** WAYS ***/
    15321528            //    profilerN = 0;
    1533             fillAreas = 0;
    1534             for (final OsmPrimitive osm : noAreaWays)
     1529            for (final Way osm : noAreaWays)
    15351530            {
    1536                 osm.visit(this);
     1531                drawWay(osm, 0);
    15371532                //        profilerN++;
    15381533            }
    15391534
     
    15481543        {
    15491544            /*** WAYS (filling disabled)  ***/
    15501545            //    profilerN = 0;
    1551             for (final OsmPrimitive osm : data.ways)
    1552                 if (!osm.incomplete && !osm.isDeleted() && !osm.isFiltered() && !data.isSelected(osm)
    1553                         && osm.mappaintVisibleCode != viewid )
     1546            for (final Way way : data.ways)
     1547                if (drawable(way) && !data.isSelected(way)
     1548                        && way.mappaintVisibleCode != viewid )
    15541549                {
    1555                     osm.visit(this);
     1550                    drawWay(way, 0);
    15561551                    //            profilerN++;
    15571552                }
    15581553
     
    15651560        }
    15661561
    15671562        /*** SELECTED  ***/
    1568         selectedCall = true;
    15691563        //profilerN = 0;
    15701564        for (final OsmPrimitive osm : data.getSelected()) {
    15711565            if (!osm.incomplete && !osm.isDeleted() && !(osm instanceof Node)
    1572                     && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
     1566                    && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid
     1567                    )
    15731568            {
    1574                 osm.visit(this);
     1569                osm.visit(new AbstractVisitor() {
     1570                    public void visit(Way w) {
     1571                        drawWay(w, 0);
     1572                    }
     1573                    public void visit(Node n) {
     1574                        drawNode(n);
     1575                    }
     1576                    public void visit(Relation r) {
     1577                        /* TODO: is it possible to do this like the nodes/ways code? */
     1578                        //if(profilerOmitDraw)
     1579                        //    return;
     1580                        r.mappaintVisibleCode = 0;
     1581                        for (RelationMember m : r.getMembers()) {
     1582                            if (m.isNode() && drawable(m.getMember())) {
     1583                                drawSelectedMember(m.getMember(), styles != null ? getPrimitiveStyle(m.getMember()) : null, true, true);
     1584                            }
     1585                        }
     1586                    }
     1587                });
    15751588                //        profilerN++;
    15761589            }
    15771590        }
     
    15871600
    15881601        /*** NODES ***/
    15891602        //profilerN = 0;
    1590         for (final OsmPrimitive osm : data.nodes)
     1603        for (final Node osm : data.nodes)
    15911604            if (!osm.incomplete && !osm.isDeleted() && (data.isSelected(osm) || !osm.isFiltered())
    15921605                    && osm.mappaintVisibleCode != viewid && osm.mappaintDrawnCode != paintid)
    15931606            {
    1594                 osm.visit(this);
     1607                drawNode(osm);
    15951608                //        profilerN++;
    15961609            }
    15971610