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 }