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