001    // License: GPL. See LICENSE file for details.
002    package org.openstreetmap.josm.data.validation.tests;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.util.ArrayList;
007    import java.util.Collection;
008    import java.util.HashMap;
009    import java.util.LinkedHashSet;
010    import java.util.List;
011    import java.util.Map;
012    
013    import org.openstreetmap.josm.data.osm.Node;
014    import org.openstreetmap.josm.data.osm.OsmPrimitive;
015    import org.openstreetmap.josm.data.osm.OsmUtils;
016    import org.openstreetmap.josm.data.osm.Way;
017    import org.openstreetmap.josm.data.osm.WaySegment;
018    import org.openstreetmap.josm.data.validation.Severity;
019    import org.openstreetmap.josm.data.validation.Test;
020    import org.openstreetmap.josm.data.validation.TestError;
021    import org.openstreetmap.josm.gui.progress.ProgressMonitor;
022    import org.openstreetmap.josm.tools.MultiMap;
023    import org.openstreetmap.josm.tools.Pair;
024    
025    /**
026     * Tests if there are overlapping ways
027     *
028     * @author frsantos
029     */
030    public class OverlappingWays extends Test {
031    
032        /** Bag of all way segments */
033        MultiMap<Pair<Node,Node>, WaySegment> nodePairs;
034    
035        protected static final int OVERLAPPING_HIGHWAY = 101;
036        protected static final int OVERLAPPING_RAILWAY = 102;
037        protected static final int OVERLAPPING_WAY = 103;
038        protected static final int OVERLAPPING_HIGHWAY_AREA = 111;
039        protected static final int OVERLAPPING_RAILWAY_AREA = 112;
040        protected static final int OVERLAPPING_WAY_AREA = 113;
041        protected static final int OVERLAPPING_AREA = 120;
042    
043        /** Constructor */
044        public OverlappingWays() {
045            super(tr("Overlapping ways"),
046                    tr("This test checks that a connection between two nodes "
047                            + "is not used by more than one way."));
048        }
049    
050        @Override
051        public void startTest(ProgressMonitor monitor)  {
052            super.startTest(monitor);
053            nodePairs = new MultiMap<Pair<Node,Node>, WaySegment>(1000);
054        }
055    
056        @Override
057        public void endTest() {
058            Map<List<Way>, LinkedHashSet<WaySegment>> ways_seen = new HashMap<List<Way>, LinkedHashSet<WaySegment>>(500);
059    
060            for (LinkedHashSet<WaySegment> duplicated : nodePairs.values()) {
061                int ways = duplicated.size();
062    
063                if (ways > 1) {
064                    List<OsmPrimitive> prims = new ArrayList<OsmPrimitive>();
065                    List<Way> current_ways = new ArrayList<Way>();
066                    Collection<WaySegment> highlight;
067                    int highway = 0;
068                    int railway = 0;
069                    int area = 0;
070    
071                    for (WaySegment ws : duplicated) {
072                        if (ws.way.get("highway") != null) {
073                            highway++;
074                        } else if (ws.way.get("railway") != null) {
075                            railway++;
076                        }
077                        Boolean ar = OsmUtils.getOsmBoolean(ws.way.get("area"));
078                        if (ar != null && ar) {
079                            area++;
080                        }
081                        if (ws.way.get("landuse") != null
082                                || ws.way.get("natural") != null
083                                || ws.way.get("amenity") != null
084                                || ws.way.get("leisure") != null
085                                || ws.way.get("building") != null) {
086                            area++;
087                            ways--;
088                        }
089    
090                        prims.add(ws.way);
091                        current_ways.add(ws.way);
092                    }
093                    /* These ways not seen before
094                     * If two or more of the overlapping ways are
095                     * highways or railways mark a separate error
096                     */
097                    if ((highlight = ways_seen.get(current_ways)) == null) {
098                        String errortype;
099                        int type;
100    
101                        if (area > 0) {
102                            if (ways == 0 || duplicated.size() == area) {
103                                errortype = tr("Areas share segment");
104                                type = OVERLAPPING_AREA;
105                            } else if (highway == ways) {
106                                errortype = tr("Highways share segment with area");
107                                type = OVERLAPPING_HIGHWAY_AREA;
108                            } else if (railway == ways) {
109                                errortype = tr("Railways share segment with area");
110                                type = OVERLAPPING_RAILWAY_AREA;
111                            } else {
112                                errortype = tr("Ways share segment with area");
113                                type = OVERLAPPING_WAY_AREA;
114                            }
115                        }
116                        else if (highway == ways) {
117                            errortype = tr("Overlapping highways");
118                            type = OVERLAPPING_HIGHWAY;
119                        } else if (railway == ways) {
120                            errortype = tr("Overlapping railways");
121                            type = OVERLAPPING_RAILWAY;
122                        } else {
123                            errortype = tr("Overlapping ways");
124                            type = OVERLAPPING_WAY;
125                        }
126    
127                        errors.add(new TestError(this,
128                                type < OVERLAPPING_HIGHWAY_AREA ? Severity.WARNING : Severity.OTHER,
129                                        errortype, type, prims, duplicated));
130                        ways_seen.put(current_ways, duplicated);
131                    } else { /* way seen, mark highlight layer only */
132                        for (WaySegment ws : duplicated) {
133                            highlight.add(ws);
134                        }
135                    }
136                }
137            }
138            super.endTest();
139            nodePairs = null;
140        }
141    
142        @Override
143        public void visit(Way w) {
144            Node lastN = null;
145            int i = -2;
146            for (Node n : w.getNodes()) {
147                i++;
148                if (lastN == null) {
149                    lastN = n;
150                    continue;
151                }
152                nodePairs.put(Pair.sort(new Pair<Node,Node>(lastN, n)),
153                        new WaySegment(w, i));
154                lastN = n;
155            }
156        }
157    }