source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java@ 6303

Last change on this file since 6303 was 6303, checked in by Don-vip, 11 years ago

fix #9141 - new validator test: detect nodes with missing highway=crossing

File size: 6.6 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.HashMap;
9import java.util.HashSet;
10import java.util.List;
11import java.util.Map;
12
13import org.openstreetmap.josm.command.ChangePropertyCommand;
14import org.openstreetmap.josm.command.Command;
15import org.openstreetmap.josm.data.osm.Node;
16import org.openstreetmap.josm.data.osm.OsmPrimitive;
17import org.openstreetmap.josm.data.osm.OsmUtils;
18import org.openstreetmap.josm.data.osm.Way;
19import org.openstreetmap.josm.data.validation.Severity;
20import org.openstreetmap.josm.data.validation.Test;
21import org.openstreetmap.josm.data.validation.TestError;
22import org.openstreetmap.josm.tools.Utils;
23
24/**
25 * Test that performs semantic checks on highways.
26 * @since 5902
27 */
28public class Highways extends Test {
29
30 protected static final int WRONG_ROUNDABOUT_HIGHWAY = 2701;
31 protected static final int MISSING_PEDESTRIAN_CROSSING = 2702;
32
33 protected static final List<String> CLASSIFIED_HIGHWAYS = Arrays.asList(
34 "motorway", "trunk", "primary", "secondary", "tertiary", "living_street", "residential", "unclassified");
35
36 boolean leftByPedestrians = false;
37 boolean leftByCyclists = false;
38 boolean leftByCars = false;
39 int pedestrianWays = 0;
40 int cyclistWays = 0;
41 int carsWays = 0;
42
43 /**
44 * Constructs a new {@code Highways} test.
45 */
46 public Highways() {
47 super(tr("Highways"), tr("Performs semantic checks on highways."));
48 }
49
50 protected class WrongRoundaboutHighway extends TestError {
51
52 public final String correctValue;
53
54 public WrongRoundaboutHighway(Way w, String key) {
55 super(Highways.this, Severity.WARNING,
56 tr("Incorrect roundabout (highway: {0} instead of {1})", w.get("highway"), key),
57 WRONG_ROUNDABOUT_HIGHWAY, w);
58 this.correctValue = key;
59 }
60 }
61
62 @Override
63 public void visit(Node n) {
64 if (n.isUsable() && !n.hasTag("highway", "crossing") && !n.hasTag("crossing", "no") && n.isReferredByWays(2)) {
65 testMissingPedestrianCrossing(n);
66 }
67 }
68
69 @Override
70 public void visit(Way w) {
71 if (w.isUsable() && w.hasKey("highway") && w.hasKey("junction") && w.get("junction").equals("roundabout")) {
72 testWrongRoundabout(w);
73 }
74 }
75
76 private void testWrongRoundabout(Way w) {
77 Map<String, List<Way>> map = new HashMap<String, List<Way>>();
78 // Count all highways (per type) connected to this roundabout
79 // As roundabouts are closed ways, take care of not processing the first/last node twice
80 for (Node n : new HashSet<Node>(w.getNodes())) {
81 for (Way h : Utils.filteredCollection(n.getReferrers(), Way.class)) {
82 if (h != w && h.hasKey("highway")) {
83 List<Way> list = map.get(h.get("highway"));
84 if (list == null) {
85 map.put(h.get("highway"), list = new ArrayList<Way>());
86 }
87 list.add(h);
88 }
89 }
90 }
91 // The roundabout should carry the highway tag of its two biggest highways
92 for (String s : CLASSIFIED_HIGHWAYS) {
93 List<Way> list = map.get(s);
94 if (list != null && list.size() >= 2) {
95 // Except when a single road is connected, but with two oneway segments
96 Boolean oneway1 = OsmUtils.getOsmBoolean(list.get(0).get("oneway"));
97 Boolean oneway2 = OsmUtils.getOsmBoolean(list.get(1).get("oneway"));
98 if (list.size() > 2 || oneway1 == null || oneway2 == null || !oneway1 || !oneway2) {
99 // Error when the highway tags do not match
100 if (!w.get("highway").equals(s)) {
101 errors.add(new WrongRoundaboutHighway(w, tr("Incorrect roundabout (highway: {0} instead of {1})", w.get("highway"), s)));
102 }
103 break;
104 }
105 }
106 }
107 }
108
109 private void testMissingPedestrianCrossing(Node n) {
110 leftByPedestrians = false;
111 leftByCyclists = false;
112 leftByCars = false;
113 pedestrianWays = 0;
114 cyclistWays = 0;
115 carsWays = 0;
116
117 for (Way w : OsmPrimitive.getFilteredList(n.getReferrers(), Way.class)) {
118 String highway = w.get("highway");
119 if (highway != null) {
120 if (highway.equals("footway") || highway.equals("path")) {
121 handlePedestrianWay(n, w);
122 if (w.hasTag("bicycle", "yes", "designated")) {
123 handleCyclistWay(n, w);
124 }
125 } else if (highway.equals("cycleway")) {
126 handleCyclistWay(n, w);
127 if (w.hasTag("foot", "yes", "designated")) {
128 handlePedestrianWay(n, w);
129 }
130 } else if (CLASSIFIED_HIGHWAYS.contains(highway) || highway.equals("service") || highway.equals("road")) {
131 handleCarWay(n, w);
132 }
133 if ((leftByPedestrians || leftByCyclists) && leftByCars) {
134 errors.add(new TestError(this, Severity.WARNING, tr("Missing crossing information"), MISSING_PEDESTRIAN_CROSSING, n));
135 return;
136 }
137 }
138 }
139 }
140
141 private void handleCarWay(Node n, Way w) {
142 carsWays++;
143 if (!w.isFirstLastNode(n) || carsWays > 1) {
144 leftByCars = true;
145 }
146 }
147
148 private void handleCyclistWay(Node n, Way w) {
149 cyclistWays++;
150 if (!w.isFirstLastNode(n) || cyclistWays > 1) {
151 leftByCyclists = true;
152 }
153 }
154
155 private void handlePedestrianWay(Node n, Way w) {
156 pedestrianWays++;
157 if (!w.isFirstLastNode(n) || pedestrianWays > 1) {
158 leftByPedestrians = true;
159 }
160 }
161
162 @Override
163 public boolean isFixable(TestError testError) {
164 return testError instanceof WrongRoundaboutHighway;
165 }
166
167 @Override
168 public Command fixError(TestError testError) {
169 if (testError instanceof WrongRoundaboutHighway) {
170 return new ChangePropertyCommand(testError.getPrimitives().iterator().next(),
171 "highway", ((WrongRoundaboutHighway) testError).correctValue);
172 }
173 return null;
174 }
175}
Note: See TracBrowser for help on using the repository browser.