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}