source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java@ 7308

Last change on this file since 7308 was 7062, checked in by Don-vip, 10 years ago

fix #9976 - false positive: missing power=tower/pole

File size: 8.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.Collection;
9import java.util.HashMap;
10import java.util.Iterator;
11import java.util.List;
12import java.util.Map;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.command.ChangePropertyCommand;
16import org.openstreetmap.josm.command.Command;
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.Relation;
20import org.openstreetmap.josm.data.osm.Way;
21import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
22import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
23import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
24import org.openstreetmap.josm.data.validation.Severity;
25import org.openstreetmap.josm.data.validation.Test;
26import org.openstreetmap.josm.data.validation.TestError;
27import org.openstreetmap.josm.gui.progress.ProgressMonitor;
28import org.openstreetmap.josm.tools.Geometry;
29
30/**
31 * Checks for nodes in power lines/minor_lines that do not have a power=tower/pole tag.<br>
32 * See #7812 for discussions about this test.
33 */
34public class PowerLines extends Test {
35
36 protected static final int POWER_LINES = 2501;
37
38 /** Values for {@code power} key interpreted as power lines */
39 public static final Collection<String> POWER_LINE_TAGS = Arrays.asList("line", "minor_line");
40 /** Values for {@code power} key interpreted as power towers */
41 public static final Collection<String> POWER_TOWER_TAGS = Arrays.asList("tower", "pole");
42 /** Values for {@code power} key interpreted as power stations */
43 public static final Collection<String> POWER_STATION_TAGS = Arrays.asList("station", "sub_station", "substation", "plant", "generator");
44 /** Values for {@code power} key interpreted as allowed power items */
45 public static final Collection<String> POWER_ALLOWED_TAGS = Arrays.asList("switch", "transformer", "busbar", "generator", "switchgear");
46
47 private final Map<Way, String> towerPoleTagMap = new HashMap<>();
48
49 private final List<PowerLineError> potentialErrors = new ArrayList<>();
50
51 private final List<OsmPrimitive> powerStations = new ArrayList<>();
52
53 /**
54 * Constructs a new {@code PowerLines} test.
55 */
56 public PowerLines() {
57 super(tr("Power lines"), tr("Checks for nodes in power lines that do not have a power=tower/pole tag."));
58 }
59
60 @Override
61 public void visit(Way w) {
62 if (w.isUsable()) {
63 if (isPowerLine(w) && !w.hasTag("location", "underground")) {
64 String fixValue = null;
65 boolean erroneous = false;
66 boolean canFix = false;
67 for (Node n : w.getNodes()) {
68 if (!isPowerTower(n)) {
69 if (!isPowerAllowed(n)) {
70 if (!w.isFirstLastNode(n) || !isPowerStation(n)) {
71 potentialErrors.add(new PowerLineError(n, w));
72 erroneous = true;
73 }
74 }
75 } else if (fixValue == null) {
76 // First tower/pole tag found, remember it
77 fixValue = n.get("power");
78 canFix = true;
79 } else if (!fixValue.equals(n.get("power"))) {
80 // The power line contains both "tower" and "pole" -> cannot fix this error
81 canFix = false;
82 }
83 }
84 if (erroneous && canFix) {
85 towerPoleTagMap.put(w, fixValue);
86 }
87 } else if (w.isClosed() && isPowerStation(w)) {
88 powerStations.add(w);
89 }
90 }
91 }
92
93 @Override
94 public void visit(Relation r) {
95 if (r.isMultipolygon() && isPowerStation(r)) {
96 powerStations.add(r);
97 }
98 }
99
100 @Override
101 public void startTest(ProgressMonitor progressMonitor) {
102 super.startTest(progressMonitor);
103 towerPoleTagMap.clear();
104 powerStations.clear();
105 potentialErrors.clear();
106 }
107
108 @Override
109 public void endTest() {
110 for (PowerLineError e : potentialErrors) {
111 Node n = e.getNode();
112 if (n != null && !isInPowerStation(n)) {
113 errors.add(e);
114 }
115 }
116 potentialErrors.clear();
117 super.endTest();
118 }
119
120 protected final boolean isInPowerStation(Node n) {
121 for (OsmPrimitive station : powerStations) {
122 List<List<Node>> nodesLists = new ArrayList<>();
123 if (station instanceof Way) {
124 nodesLists.add(((Way)station).getNodes());
125 } else if (station instanceof Relation) {
126 Multipolygon polygon = MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) station);
127 if (polygon != null) {
128 for (JoinedWay outer : Multipolygon.joinWays(polygon.getOuterWays())) {
129 nodesLists.add(outer.getNodes());
130 }
131 }
132 }
133 for (List<Node> nodes : nodesLists) {
134 if (Geometry.nodeInsidePolygon(n, nodes)) {
135 return true;
136 }
137 }
138 }
139 return false;
140 }
141
142 @Override
143 public Command fixError(TestError testError) {
144 if (testError instanceof PowerLineError && isFixable(testError)) {
145 // primitives list can be empty if all primitives have been purged
146 Iterator<? extends OsmPrimitive> it = testError.getPrimitives().iterator();
147 if (it.hasNext()) {
148 return new ChangePropertyCommand(it.next(),
149 "power", towerPoleTagMap.get(((PowerLineError)testError).line));
150 }
151 }
152 return null;
153 }
154
155 @Override
156 public boolean isFixable(TestError testError) {
157 return testError instanceof PowerLineError && towerPoleTagMap.containsKey(((PowerLineError)testError).line);
158 }
159
160 /**
161 * Determines if the specified way denotes a power line.
162 * @param w The way to be tested
163 * @return {@code true} if power key is set and equal to line/minor_line
164 */
165 protected static final boolean isPowerLine(Way w) {
166 return isPowerIn(w, POWER_LINE_TAGS);
167 }
168
169 /**
170 * Determines if the specified primitive denotes a power station.
171 * @param p The primitive to be tested
172 * @return {@code true} if power key is set and equal to station/sub_station/plant
173 */
174 protected static final boolean isPowerStation(OsmPrimitive p) {
175 return isPowerIn(p, POWER_STATION_TAGS);
176 }
177
178 /**
179 * Determines if the specified node denotes a power tower/pole.
180 * @param n The node to be tested
181 * @return {@code true} if power key is set and equal to tower/pole
182 */
183 protected static final boolean isPowerTower(Node n) {
184 return isPowerIn(n, POWER_TOWER_TAGS);
185 }
186
187 /**
188 * Determines if the specified node denotes a power infrastructure allowed on a power line.
189 * @param n The node to be tested
190 * @return True if power key is set and equal to switch/tranformer/busbar/generator
191 */
192 protected static final boolean isPowerAllowed(Node n) {
193 return isPowerIn(n, POWER_ALLOWED_TAGS);
194 }
195
196 /**
197 * Helper function to check if power tags is a certain value.
198 * @param p The primitive to be tested
199 * @param values List of possible values
200 * @return {@code true} if power key is set and equal to possible values
201 */
202 private static final boolean isPowerIn(OsmPrimitive p, Collection<String> values) {
203 String v = p.get("power");
204 return v != null && values != null && values.contains(v);
205 }
206
207 protected class PowerLineError extends TestError {
208 private final Way line;
209 public PowerLineError(Node n, Way line) {
210 super(PowerLines.this, Severity.WARNING,
211 tr("Missing power tower/pole within power line"), POWER_LINES, n);
212 this.line = line;
213 }
214 public final Node getNode() {
215 // primitives list can be empty if all primitives have been purged
216 Iterator<? extends OsmPrimitive> it = getPrimitives().iterator();
217 return it.hasNext() ? (Node) it.next() : null;
218 }
219 }
220}
Note: See TracBrowser for help on using the repository browser.