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