1 | // License: GPL. See LICENSE file for details.
|
---|
2 | package org.openstreetmap.josm.data.validation.tests;
|
---|
3 |
|
---|
4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
5 |
|
---|
6 | import java.awt.geom.Line2D;
|
---|
7 | import java.awt.geom.Point2D;
|
---|
8 | import java.util.ArrayList;
|
---|
9 | import java.util.Arrays;
|
---|
10 | import java.util.HashMap;
|
---|
11 | import java.util.HashSet;
|
---|
12 | import java.util.List;
|
---|
13 | import java.util.Map;
|
---|
14 | import java.util.Set;
|
---|
15 |
|
---|
16 | import org.openstreetmap.josm.data.osm.Node;
|
---|
17 | import org.openstreetmap.josm.data.osm.Way;
|
---|
18 | import org.openstreetmap.josm.data.osm.WaySegment;
|
---|
19 | import org.openstreetmap.josm.data.validation.OsmValidator;
|
---|
20 | import org.openstreetmap.josm.data.validation.Severity;
|
---|
21 | import org.openstreetmap.josm.data.validation.Test;
|
---|
22 | import org.openstreetmap.josm.data.validation.TestError;
|
---|
23 | import org.openstreetmap.josm.data.validation.util.ValUtil;
|
---|
24 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
25 |
|
---|
26 | /**
|
---|
27 | * Tests if there are segments that crosses in the same layer
|
---|
28 | *
|
---|
29 | * @author frsantos
|
---|
30 | */
|
---|
31 | public class CrossingWays extends Test {
|
---|
32 | protected static final int CROSSING_WAYS = 601;
|
---|
33 |
|
---|
34 | /** All way segments, grouped by cells */
|
---|
35 | private Map<Point2D,List<ExtendedSegment>> cellSegments;
|
---|
36 | /** The already detected errors */
|
---|
37 | private Set<WaySegment> errorSegments;
|
---|
38 | /** The already detected ways in error */
|
---|
39 | private Map<List<Way>, List<WaySegment>> seenWays;
|
---|
40 |
|
---|
41 | /**
|
---|
42 | * Constructor
|
---|
43 | */
|
---|
44 | public CrossingWays() {
|
---|
45 | super(tr("Crossing ways."),
|
---|
46 | tr("This test checks if two roads, railways, waterways or buildings crosses in the same layer, but are not connected by a node."));
|
---|
47 | }
|
---|
48 |
|
---|
49 | @Override
|
---|
50 | public void startTest(ProgressMonitor monitor) {
|
---|
51 | super.startTest(monitor);
|
---|
52 | cellSegments = new HashMap<Point2D,List<ExtendedSegment>>(1000);
|
---|
53 | errorSegments = new HashSet<WaySegment>();
|
---|
54 | seenWays = new HashMap<List<Way>, List<WaySegment>>(50);
|
---|
55 | }
|
---|
56 |
|
---|
57 | @Override
|
---|
58 | public void endTest() {
|
---|
59 | super.endTest();
|
---|
60 | cellSegments = null;
|
---|
61 | errorSegments = null;
|
---|
62 | seenWays = null;
|
---|
63 | }
|
---|
64 |
|
---|
65 | @Override
|
---|
66 | public void visit(Way w) {
|
---|
67 | if(!w.isUsable())
|
---|
68 | return;
|
---|
69 |
|
---|
70 | String natural1 = w.get("natural");
|
---|
71 | String landuse1 = w.get("landuse");
|
---|
72 | boolean isCoastline1 = "water".equals(natural1) || "coastline".equals(natural1) || "reservoir".equals(landuse1);
|
---|
73 | String highway1 = w.get("highway");
|
---|
74 | String railway1 = w.get("railway");
|
---|
75 | boolean isSubway1 = "subway".equals(railway1);
|
---|
76 | boolean isTram1 = "tram".equals(railway1);
|
---|
77 | boolean isBuilding = isBuilding(w);
|
---|
78 | String waterway1 = w.get("waterway");
|
---|
79 |
|
---|
80 | if (w.get("highway") == null && w.get("waterway") == null
|
---|
81 | && (railway1 == null || isSubway1 || isTram1)
|
---|
82 | && !isCoastline1 && !isBuilding)
|
---|
83 | return;
|
---|
84 |
|
---|
85 | String layer1 = w.get("layer");
|
---|
86 | if ("0".equals(layer1)) {
|
---|
87 | layer1 = null; //0 is default value
|
---|
88 | }
|
---|
89 |
|
---|
90 | int nodesSize = w.getNodesCount();
|
---|
91 | for (int i = 0; i < nodesSize - 1; i++) {
|
---|
92 | WaySegment ws = new WaySegment(w, i);
|
---|
93 | ExtendedSegment es1 = new ExtendedSegment(ws, layer1, highway1, railway1, isCoastline1, waterway1);
|
---|
94 | for (List<ExtendedSegment> segments : getSegments(es1.n1, es1.n2)) {
|
---|
95 | for (ExtendedSegment es2 : segments) {
|
---|
96 | List<Way> prims;
|
---|
97 | List<WaySegment> highlight;
|
---|
98 |
|
---|
99 | if (errorSegments.contains(ws) && errorSegments.contains(es2.ws)) {
|
---|
100 | continue;
|
---|
101 | }
|
---|
102 |
|
---|
103 | String layer2 = es2.layer;
|
---|
104 | String highway2 = es2.highway;
|
---|
105 | String railway2 = es2.railway;
|
---|
106 | boolean isCoastline2 = es2.coastline;
|
---|
107 | if (layer1 == null ? layer2 != null : !layer1.equals(layer2)) {
|
---|
108 | continue;
|
---|
109 | }
|
---|
110 |
|
---|
111 | if (!es1.intersects(es2) ) {
|
---|
112 | continue;
|
---|
113 | }
|
---|
114 | if (isSubway1 && "subway".equals(railway2)) {
|
---|
115 | continue;
|
---|
116 | }
|
---|
117 | if (isTram1 && "tram".equals(railway2)) {
|
---|
118 | continue;
|
---|
119 | }
|
---|
120 |
|
---|
121 | if (isCoastline1 != isCoastline2) {
|
---|
122 | continue;
|
---|
123 | }
|
---|
124 | if (("river".equals(waterway1) && "riverbank".equals(es2.waterway))
|
---|
125 | || ("riverbank".equals(waterway1) && "river".equals(es2.waterway))) {
|
---|
126 | continue;
|
---|
127 | }
|
---|
128 |
|
---|
129 | if ("proposed".equals(es1.highway) || "proposed".equals(highway2)
|
---|
130 | || "proposed".equals(es1.railway) || "proposed".equals(railway2)
|
---|
131 | || "abandoned".equals(es1.railway) || "abandoned".equals(railway2)) {
|
---|
132 | continue;
|
---|
133 | }
|
---|
134 |
|
---|
135 | prims = Arrays.asList(es1.ws.way, es2.ws.way);
|
---|
136 | if ((highlight = seenWays.get(prims)) == null) {
|
---|
137 | highlight = new ArrayList<WaySegment>();
|
---|
138 | highlight.add(es1.ws);
|
---|
139 | highlight.add(es2.ws);
|
---|
140 |
|
---|
141 | String message;
|
---|
142 | if (isBuilding) {
|
---|
143 | message = tr("Crossing buildings");
|
---|
144 | } else if ((es1.waterway != null && es2.waterway != null)) {
|
---|
145 | message = tr("Crossing waterways");
|
---|
146 | } else if ((es1.waterway != null && es2.highway != null)
|
---|
147 | || (es2.waterway != null && es1.highway != null)) {
|
---|
148 | message = tr("Crossing waterway/highway");
|
---|
149 | } else {
|
---|
150 | message = tr("Crossing ways");
|
---|
151 | }
|
---|
152 |
|
---|
153 | errors.add(new TestError(this, Severity.WARNING,
|
---|
154 | message,
|
---|
155 | CROSSING_WAYS,
|
---|
156 | prims,
|
---|
157 | highlight));
|
---|
158 | seenWays.put(prims, highlight);
|
---|
159 | } else {
|
---|
160 | highlight.add(es1.ws);
|
---|
161 | highlight.add(es2.ws);
|
---|
162 | }
|
---|
163 | }
|
---|
164 | segments.add(es1);
|
---|
165 | }
|
---|
166 | }
|
---|
167 | }
|
---|
168 |
|
---|
169 | /**
|
---|
170 | * Returns all the cells this segment crosses. Each cell contains the list
|
---|
171 | * of segments already processed
|
---|
172 | *
|
---|
173 | * @param n1 The first node
|
---|
174 | * @param n2 The second node
|
---|
175 | * @return A list with all the cells the segment crosses
|
---|
176 | */
|
---|
177 | public List<List<ExtendedSegment>> getSegments(Node n1, Node n2) {
|
---|
178 |
|
---|
179 | List<List<ExtendedSegment>> cells = new ArrayList<List<ExtendedSegment>>();
|
---|
180 | for(Point2D cell : ValUtil.getSegmentCells(n1, n2, OsmValidator.griddetail)) {
|
---|
181 | List<ExtendedSegment> segments = cellSegments.get(cell);
|
---|
182 | if (segments == null) {
|
---|
183 | segments = new ArrayList<ExtendedSegment>();
|
---|
184 | cellSegments.put(cell, segments);
|
---|
185 | }
|
---|
186 | cells.add(segments);
|
---|
187 | }
|
---|
188 | return cells;
|
---|
189 | }
|
---|
190 |
|
---|
191 | /**
|
---|
192 | * A way segment with some additional information
|
---|
193 | */
|
---|
194 | public static class ExtendedSegment {
|
---|
195 | private final Node n1, n2;
|
---|
196 |
|
---|
197 | private final WaySegment ws;
|
---|
198 |
|
---|
199 | /** The layer */
|
---|
200 | private final String layer;
|
---|
201 |
|
---|
202 | /** The highway type */
|
---|
203 | private final String highway;
|
---|
204 |
|
---|
205 | /** The railway type */
|
---|
206 | private final String railway;
|
---|
207 |
|
---|
208 | /** The waterway type */
|
---|
209 | private final String waterway;
|
---|
210 |
|
---|
211 | /** The coastline type */
|
---|
212 | private final boolean coastline;
|
---|
213 |
|
---|
214 | /**
|
---|
215 | * Constructor
|
---|
216 | * @param ws The way segment
|
---|
217 | * @param layer The layer of the way this segment is in
|
---|
218 | * @param highway The highway type of the way this segment is in
|
---|
219 | * @param railway The railway type of the way this segment is in
|
---|
220 | * @param coastline The coastline flag of the way the segment is in
|
---|
221 | * @param waterway The waterway type of the way this segment is in
|
---|
222 | */
|
---|
223 | public ExtendedSegment(WaySegment ws, String layer, String highway, String railway, boolean coastline, String waterway) {
|
---|
224 | this.ws = ws;
|
---|
225 | this.n1 = ws.way.getNodes().get(ws.lowerIndex);
|
---|
226 | this.n2 = ws.way.getNodes().get(ws.lowerIndex + 1);
|
---|
227 | this.layer = layer;
|
---|
228 | this.highway = highway;
|
---|
229 | this.railway = railway;
|
---|
230 | this.coastline = coastline;
|
---|
231 | this.waterway = waterway;
|
---|
232 | }
|
---|
233 |
|
---|
234 | /**
|
---|
235 | * Checks whether this segment crosses other segment
|
---|
236 | * @param s2 The other segment
|
---|
237 | * @return true if both segments crosses
|
---|
238 | */
|
---|
239 | public boolean intersects(ExtendedSegment s2) {
|
---|
240 | if (n1.equals(s2.n1) || n2.equals(s2.n2) ||
|
---|
241 | n1.equals(s2.n2) || n2.equals(s2.n1))
|
---|
242 | return false;
|
---|
243 |
|
---|
244 | return Line2D.linesIntersect(
|
---|
245 | n1.getEastNorth().east(), n1.getEastNorth().north(),
|
---|
246 | n2.getEastNorth().east(), n2.getEastNorth().north(),
|
---|
247 | s2.n1.getEastNorth().east(), s2.n1.getEastNorth().north(),
|
---|
248 | s2.n2.getEastNorth().east(), s2.n2.getEastNorth().north());
|
---|
249 | }
|
---|
250 | }
|
---|
251 | }
|
---|