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

Last change on this file was 18871, checked in by taylor.smock, 6 months ago

See #23218: Use newer error_prone versions when compiling on Java 11+

error_prone 2.11 dropped support for compiling with Java 8, although it still
supports compiling for Java 8. The "major" new check for us is NotJavadoc since
we used /** in quite a few places which were not javadoc.

Other "new" checks that are of interest:

  • AlreadyChecked: if (foo) { doFoo(); } else if (!foo) { doBar(); }
  • UnnecessaryStringBuilder: Avoid StringBuilder (Java converts + to StringBuilder behind-the-scenes, but may also do something else if it performs better)
  • NonApiType: Avoid specific interface types in function definitions
  • NamedLikeContextualKeyword: Avoid using restricted names for classes and methods
  • UnusedMethod: Unused private methods should be removed

This fixes most of the new error_prone issues and some SonarLint issues.

  • Property svn:eol-style set to native
File size: 29.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.Point2D;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.EnumSet;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17
18import org.openstreetmap.josm.data.coor.ILatLon;
19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.Relation;
22import org.openstreetmap.josm.data.osm.RelationMember;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.data.osm.WaySegment;
25import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
26import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
27import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
28import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
29import org.openstreetmap.josm.data.validation.Severity;
30import org.openstreetmap.josm.data.validation.Test;
31import org.openstreetmap.josm.data.validation.TestError;
32import org.openstreetmap.josm.gui.progress.ProgressMonitor;
33import org.openstreetmap.josm.spi.preferences.Config;
34import org.openstreetmap.josm.tools.Geometry;
35import org.openstreetmap.josm.tools.Logging;
36import org.openstreetmap.josm.tools.Utils;
37
38/**
39 * Checks for
40 * <ul>
41 * <li>nodes in power lines/minor_lines that do not have a power=tower/pole/portal tag
42 * <li>nodes where the reference numbering not consistent
43 * <li>ways where are unusually long segments without line support feature
44 * <li>ways where the line type is possibly misused
45 * </ul>
46 * See #7812 and #20716 for discussions about this test.
47 */
48public class PowerLines extends Test {
49 // Common strings
50 private static final String MINOR_LINE = "minor_line";
51 private static final String BUILDING = "building";
52 private static final String POWER = "power";
53
54 // Test identifiers
55 protected static final int POWER_SUPPORT = 2501;
56 protected static final int POWER_CONNECTION = 2502;
57 protected static final int POWER_SEGMENT_LENGTH = 2503;
58 protected static final int POWER_LOCAL_REF_CONTINUITY = 2504;
59 protected static final int POWER_WAY_REF_CONTINUITY = 2505;
60 protected static final int POWER_LINE_TYPE = 2506;
61
62 protected static final String PREFIX = ValidatorPrefHelper.PREFIX + "." + PowerLines.class.getSimpleName();
63
64 /** Values for {@code power} key interpreted as power lines */
65 static final Collection<String> POWER_LINE_TAGS = Arrays.asList("line", MINOR_LINE);
66 /** Values for {@code power} key interpreted as power towers */
67 static final Collection<String> POWER_TOWER_TAGS = Arrays.asList("catenary_mast", "pole", "portal", "tower");
68 /** Values for {@code power} key interpreted as power stations */
69 static final Collection<String> POWER_STATION_TAGS = Arrays.asList("generator", "plant", "substation");
70 /** Values for {@code building} key interpreted as power stations */
71 static final Collection<String> BUILDING_STATION_TAGS = Collections.singletonList("transformer_tower");
72 /** Values for {@code power} key interpreted as allowed power items */
73 static final Collection<String> POWER_INFRASTRUCTURE_TAGS = Arrays.asList("compensator", "connection", "converter",
74 "generator", "insulator", "switch", "switchgear", "terminal", "transformer");
75
76 private double hillyCompensation;
77 private double hillyThreshold;
78 private final Set<Node> badConnections = new HashSet<>();
79 private final Set<Node> missingTags = new HashSet<>();
80 private final Set<Way> wrongLineType = new HashSet<>();
81 private final Set<WaySegment> missingNodes = new HashSet<>();
82 private final Set<OsmPrimitive> refDiscontinuities = new HashSet<>();
83
84 private final List<Set<Node>> segmentRefDiscontinuities = new ArrayList<>();
85 private final List<OsmPrimitive> powerStations = new ArrayList<>();
86
87 private final Collection<Way> foundPowerLines = new HashSet<>();
88 /** All waterway segments, grouped by cells */
89 private final Map<Point2D, List<WaySegment>> cellSegmentsWater = new HashMap<>(32);
90
91 /**
92 * Constructs a new {@code PowerLines} test.
93 */
94 public PowerLines() {
95 super(tr("Power lines"), tr("Checks if power line missing a support node and " +
96 "for nodes in power lines that do not have a power=tower/pole tag"));
97 }
98
99 @Override
100 public void visit(Node n) {
101 boolean nodeInLineOrCable = false;
102 boolean connectedToUnrelated = false;
103 for (Way parent : n.getParentWays()) {
104 if (parent.hasTag(POWER, "line", MINOR_LINE, "cable"))
105 nodeInLineOrCable = true;
106 else if (!isRelatedToPower(parent))
107 connectedToUnrelated = true;
108 }
109 if (nodeInLineOrCable && connectedToUnrelated)
110 badConnections.add(n);
111 }
112
113 @Override
114 public void visit(Way w) {
115 if (!isPrimitiveUsable(w)) return;
116
117 if (isPowerLine(w) && !w.hasKey("line") && !w.hasTag("location", "underground") && w.isUsable()) {
118 foundPowerLines.add(w);
119 } else if (w.isClosed() && isPowerStation(w)) {
120 powerStations.add(w);
121 } else if (concernsWaterArea(w)) {
122 this.addWaterWaySegments(w);
123 }
124 }
125
126 /**
127 * Add segments to the appropriate cells
128 * @param w The way to add segments from
129 */
130 private void addWaterWaySegments(Way w) {
131 for (int i = 0; i < w.getNodesCount() - 1; i++) {
132 final WaySegment es1 = new WaySegment(w, i);
133 final Node first = es1.getFirstNode();
134 final Node second = es1.getSecondNode();
135
136 if (first.isLatLonKnown() && second.isLatLonKnown()) {
137 CrossingWays.getSegments(this.cellSegmentsWater, first, second).forEach(list -> list.add(es1));
138 }
139 }
140 }
141
142 @Override
143 public void visit(Relation r) {
144 if (r.isMultipolygon() && isPowerStation(r)) {
145 powerStations.add(r);
146 } else if (concernsWaterArea(r)) {
147 r.getMemberPrimitives(Way.class).forEach(this::addWaterWaySegments);
148 }
149 }
150
151 @Override
152 public void startTest(ProgressMonitor progressMonitor) {
153 super.startTest(progressMonitor);
154 hillyCompensation = Config.getPref().getDouble(PREFIX + ".hilly_compensation", 0.2);
155 hillyThreshold = Config.getPref().getDouble(PREFIX + ".hilly_threshold", 4.0);
156 }
157
158 @Override
159 public void endTest() {
160 // Do the actual checks
161 for (Way w : this.foundPowerLines) {
162 powerlineChecks(w);
163 }
164 // Then return the errors
165 for (Node n : missingTags) {
166 if (!isInPowerStation(n)) {
167 errors.add(TestError.builder(this, Severity.WARNING, POWER_SUPPORT)
168 // the "missing tag" grouping can become broken if the MapCSS message get reworded
169 .message(tr("missing tag"), tr("node without power=*"))
170 .primitives(n)
171 .build());
172 }
173 }
174
175 for (Node n : badConnections) {
176 errors.add(TestError.builder(this, Severity.WARNING, POWER_CONNECTION)
177 .message(tr("Node connects a power line or cable with an object "
178 + "which is not related to the power infrastructure"))
179 .primitives(n)
180 .build());
181 }
182
183 for (WaySegment s : missingNodes) {
184 errors.add(TestError.builder(this, Severity.WARNING, POWER_SEGMENT_LENGTH)
185 .message(tr("Possibly missing line support node within power line"))
186 .primitives(s.getFirstNode(), s.getSecondNode())
187 .highlightWaySegments(new HashSet<>(Collections.singleton(s)))
188 .build());
189 }
190
191 for (OsmPrimitive p : refDiscontinuities) {
192 if (p instanceof Way)
193 errors.add(TestError.builder(this, Severity.WARNING, POWER_WAY_REF_CONTINUITY)
194 .message(tr("Mixed reference numbering"))
195 .primitives(p)
196 .build());
197 }
198
199 final String discontinuityMsg = tr("Reference numbering don''t match majority of way''s nodes");
200
201 for (OsmPrimitive p : refDiscontinuities) {
202 if (p instanceof Node)
203 errors.add(TestError.builder(this, Severity.WARNING, POWER_LOCAL_REF_CONTINUITY)
204 .message(discontinuityMsg)
205 .primitives(p)
206 .build());
207 }
208
209 for (Set<Node> nodes : segmentRefDiscontinuities) {
210 errors.add(TestError.builder(this, Severity.WARNING, POWER_LOCAL_REF_CONTINUITY)
211 .message(discontinuityMsg)
212 .primitives(nodes)
213 .build());
214 }
215
216 for (Way w : wrongLineType) {
217 errors.add(TestError.builder(this, Severity.WARNING, POWER_LINE_TYPE)
218 .message(tr("Possibly wrong power line type used"))
219 .primitives(w)
220 .build());
221 }
222
223 super.endTest();
224 }
225
226 /**
227 * The base powerline checks
228 * @param w The powerline to check
229 */
230 private void powerlineChecks(Way w) {
231 final int segmentCount = w.getNodesCount() - 1;
232 final double mean = w.getLength() / segmentCount;
233 final double stdDev = Utils.getStandardDeviation(w.getSegmentLengths(), mean);
234 final boolean isContinuesAsMinorLine = isContinuesAsMinorLine(w);
235 boolean isCrossingWater = false;
236 int poleCount = 0;
237 int towerCount = 0;
238 Node prevNode = w.firstNode();
239
240 double baseThreshold = w.hasTag(POWER, "line") ? 1.6 : 1.8;
241 if (mean / stdDev < hillyThreshold) {
242 //compensate for possibly hilly areas where towers can't be put anywhere
243 baseThreshold += hillyCompensation;
244 }
245
246 for (Node n : w.getNodes()) {
247
248 /// handle power station line connections (e.g. power=line + line=*)
249 if (isConnectedToStationLine(n, w) || n.hasTag(POWER, "connection")) {
250 prevNode = n;
251 continue; // skip, it would be false positive
252 }
253
254 /// handle missing power line support tags (e.g. tower)
255 if (!isPowerTower(n) && !isPowerInfrastructure(n) && IN_DOWNLOADED_AREA.test(n)
256 && (!w.isFirstLastNode(n) || !isPowerStation(n)))
257 missingTags.add(n);
258
259 /// handle missing nodes
260 double segmentLen = n.greatCircleDistance(prevNode);
261 final Set<Way> crossingWaterWays = new HashSet<>(8);
262 final Set<ILatLon> crossingPositions = new HashSet<>(8);
263 findCrossings(this.cellSegmentsWater, w, crossingWaterWays, crossingPositions);
264
265 if (!crossingWaterWays.isEmpty()) {
266 double compensation = calculateIntersectingLen(prevNode, crossingPositions);
267 segmentLen -= compensation;
268 }
269
270 if (segmentCount > 4
271 && segmentLen > mean * baseThreshold
272 && !isPowerInfrastructure(n)
273 && IN_DOWNLOADED_AREA.test(n))
274 missingNodes.add(WaySegment.forNodePair(w, prevNode, n));
275
276 /// handle wrong line types
277 if (!crossingWaterWays.isEmpty())
278 isCrossingWater = true;
279
280 if (n.hasTag(POWER, "pole"))
281 poleCount++;
282 else if (n.hasTag(POWER, "tower", "portal"))
283 towerCount++;
284
285 prevNode = n;
286 }
287
288 /// handle ref=* numbering discontinuities
289 if (detectDiscontinuity(w, refDiscontinuities, segmentRefDiscontinuities))
290 refDiscontinuities.add(w);
291
292 /// handle wrong line types
293 if (((poleCount > towerCount && w.hasTag(POWER, "line"))
294 || (poleCount < towerCount && w.hasTag(POWER, MINOR_LINE)
295 && !isCrossingWater
296 && !isContinuesAsMinorLine))
297 && IN_DOWNLOADED_AREA.test(w))
298 wrongLineType.add(w);
299
300 }
301
302 /**
303 * The summarized length (in metres) of a way where a power line hangs over a water area.
304 * @param ref Reference point
305 * @param crossingNodes Crossing nodes, unordered
306 * @return The summarized length (in metres) of a way where a power line hangs over a water area
307 */
308 private static double calculateIntersectingLen(Node ref, Set<ILatLon> crossingNodes) {
309 double min = Double.POSITIVE_INFINITY;
310 double max = Double.NEGATIVE_INFINITY;
311
312 for (ILatLon coor : crossingNodes) {
313
314 if (ref != null && coor != null) {
315 double dist = ref.greatCircleDistance(coor);
316
317 if (dist < min)
318 min = dist;
319 if (dist > max)
320 max = dist;
321 }
322 }
323 return max - min;
324 }
325
326 /**
327 * Searches for way intersections, which intersect the {@code pair} attribute.
328 * @param ways collection of ways to search for crossings
329 * @param parent parent powerline way to find crossings for
330 * @param crossingWays found crossing ways
331 * @param crossingPositions collection of the crossing positions
332 * @implNote Inspired by {@code utilsplugin2/selection/NodeWayUtils.java#addWaysIntersectingWay()}
333 */
334 private static void findCrossings(Map<Point2D, List<WaySegment>> ways, Way parent, Set<Way> crossingWays,
335 Set<ILatLon> crossingPositions) {
336 int nodesSize = parent.getNodesCount();
337 for (int i = 0; i < nodesSize - 1; i++) {
338 final WaySegment es1 = new WaySegment(parent, i);
339 if (!es1.getFirstNode().isLatLonKnown() || !es1.getSecondNode().isLatLonKnown()) {
340 Logging.warn("PowerLines crossing ways test section skipped " + es1);
341 continue;
342 }
343 for (List<WaySegment> segments : CrossingWays.getSegments(ways, es1.getFirstNode(), es1.getSecondNode())) {
344 for (WaySegment segment : segments) {
345 if (es1.intersects(segment)) {
346 final ILatLon ll = Geometry.getSegmentSegmentIntersection(es1.getFirstNode(), es1.getSecondNode(),
347 segment.getFirstNode(), segment.getSecondNode());
348 if (ll != null) {
349 crossingWays.add(es1.getWay());
350 crossingPositions.add(ll);
351 }
352 }
353 }
354 }
355 }
356 }
357
358 /** Power line support features ref=* numbering direction. */
359 private enum NumberingDirection {
360 /** No direction */
361 NONE,
362 /** Numbering follows way direction */
363 SAME,
364 /** Numbering goes opposite way direction */
365 OPPOSITE
366 }
367
368 /** Helper class for reference numbering test. Used for storing continuous reference segment info. */
369 private static class SegmentInfo {
370 /** Node index, follows way direction */
371 private final int startIndex;
372 /** ref=* value at {@link SegmentInfo#startIndex} */
373 private final int startRef;
374 /** Segment length */
375 private final int length;
376 /** Segment direction */
377 private final NumberingDirection direction;
378
379 SegmentInfo(int startIndex, int length, int ref, NumberingDirection direction) {
380 this.startIndex = startIndex;
381 this.length = length;
382 this.direction = direction;
383
384 if (direction == NumberingDirection.SAME)
385 this.startRef = ref - length;
386 else
387 this.startRef = ref + length;
388
389 if (length == 0 && direction != NumberingDirection.NONE) {
390 throw new IllegalArgumentException("When the segment length is zero, the direction should be NONE");
391 }
392 }
393
394 @Override
395 public String toString() {
396 return String.format("SegmentInfo{startIndex=%d, startRef=%d, length=%d, direction=%s}",
397 startIndex, startRef, length, direction);
398 }
399 }
400
401 /**
402 * Detects ref=* numbering discontinuities in the given way.
403 * @param way checked way
404 * @param nRefDiscontinuities single node ref=* discontinuities
405 * @param sRefDiscontinuities continuous node ref=* discontinuities
406 * @return {@code true} if warning needs to be issued for the whole way
407 */
408 static boolean detectDiscontinuity(Way way, Set<OsmPrimitive> nRefDiscontinuities, List<Set<Node>> sRefDiscontinuities) {
409 final RefChecker checker = new RefChecker(way);
410 final List<SegmentInfo> segments = checker.getSegments();
411 final SegmentInfo referenceSegment = checker.getLongestSegment();
412
413 if (referenceSegment == null)
414 return !segments.isEmpty();
415
416 // collect disconnected ref segments which are not align up to the reference
417 for (SegmentInfo segment : segments) {
418 if (!isSegmentAlign(referenceSegment, segment)) {
419 if (referenceSegment.length == 0)
420 return true;
421
422 if (segment.length == 0) {
423 nRefDiscontinuities.add(way.getNode(segment.startIndex));
424 } else {
425 Set<Node> nodeGroup = new HashSet<>();
426
427 for (int i = segment.startIndex; i <= segment.startIndex + segment.length; i++) {
428 nodeGroup.add(way.getNode(i));
429 }
430 sRefDiscontinuities.add(nodeGroup);
431 }
432 }
433 }
434
435 return false;
436 }
437
438 /**
439 * Checks if parameter segments align. The {@code reference} is expected to be at least as long as the {@code candidate}.
440 * @param reference Reference segment to check against
441 * @param candidate Candidate segment
442 * @return {@code true} if the two segments ref=* numbering align
443 */
444 private static boolean isSegmentAlign(SegmentInfo reference, SegmentInfo candidate) {
445 if (reference.direction == NumberingDirection.NONE
446 || reference.direction == candidate.direction
447 || candidate.direction == NumberingDirection.NONE)
448 return Math.abs(candidate.startIndex - reference.startIndex) == Math.abs(candidate.startRef - reference.startRef);
449 return false;
450 }
451
452 /**
453 * Detects continuous reference numbering sequences. Ignores the first and last node because
454 * ways can be connected, and the connection nodes can have different numbering.
455 * <p>
456 * If the numbering switches in the middle of the way, this can also be seen as error,
457 * because line relations would require split ways.
458 */
459 static class RefChecker {
460 private final List<SegmentInfo> segments = new ArrayList<>();
461 private NumberingDirection direction = NumberingDirection.NONE;
462 private Integer startIndex;
463 private Integer previousRef;
464
465 RefChecker(final Way way) {
466 run(way);
467 }
468
469 private void run(Way way) {
470 final int wayLength = way.getNodesCount();
471
472 // first and last node skipped
473 for (int i = 1; i < wayLength - 1; i++) {
474 Node n = way.getNode(i);
475 if (!isPowerTower(n)) {
476 continue;
477 }
478 maintain(parseRef(n.get("ref")), i);
479 }
480
481 // needed for creation of the last segment
482 maintain(null, wayLength - 1);
483 }
484
485 /**
486 * Maintains class variables and constructs a new segment when necessary.
487 * @param ref recognised ref=* number
488 * @param index node index in a {@link Way}
489 */
490 private void maintain(Integer ref, int index) {
491 if (previousRef == null && ref != null) {
492 // ref change: null -> number
493 startIndex = index;
494 } else if (previousRef != null && ref == null) {
495 // ref change: number -> null
496 segments.add(new SegmentInfo(startIndex, index - 1 - startIndex, previousRef, direction));
497 direction = NumberingDirection.NONE; // to fix directionality
498 } else if (previousRef != null) {
499 // ref change: number -> number
500 if (Math.abs(ref - previousRef) != 1) {
501 segments.add(new SegmentInfo(startIndex, index - 1 - startIndex, previousRef, direction));
502 startIndex = index;
503 previousRef = ref; // to fix directionality
504 }
505 direction = detectDirection(ref, previousRef);
506 }
507 previousRef = ref;
508 }
509
510 /**
511 * Parses integer tag values. Later can be relatively easily extended or rewritten to handle
512 * complex references like 25/A, 25/B etc.
513 * @param value the value to be parsed
514 * @return parsed int or {@code null} in case of {@link NumberFormatException}
515 */
516 private static Integer parseRef(String value) {
517 try {
518 return Integer.parseInt(value);
519 } catch (NumberFormatException ignore) {
520 Logging.trace("The " + RefChecker.class + " couldn't parse ref=" + value + ", consider rewriting the parser");
521 return null;
522 }
523 }
524
525 /**
526 * Detects numbering direction. The parameters should follow way direction.
527 * @param ref last known reference value
528 * @param previousRef reference value before {@code ref}
529 * @return recognised direction
530 */
531 private static NumberingDirection detectDirection(int ref, int previousRef) {
532 if (ref > previousRef)
533 return NumberingDirection.SAME;
534 else if (ref < previousRef)
535 return NumberingDirection.OPPOSITE;
536 return NumberingDirection.NONE;
537 }
538
539 /**
540 * Calculates the longest segment.
541 * @return the longest segment, or the lowest index if there are more than one with same length and direction,
542 * or {@code null} if there are more than one with same length and different direction
543 */
544 SegmentInfo getLongestSegment() {
545 final Set<NumberingDirection> directions = EnumSet.noneOf(NumberingDirection.class);
546 int longestLength = -1;
547 int counter = 0;
548 SegmentInfo longest = null;
549
550 for (SegmentInfo segment : segments) {
551 if (segment.length > longestLength) {
552 longestLength = segment.length;
553 longest = segment;
554 counter = 0;
555 directions.clear();
556 directions.add(segment.direction);
557 } else if (segment.length == longestLength) {
558 counter++;
559 directions.add(segment.direction);
560 }
561 }
562
563 // there are multiple segments with the same longest length and their directions don't match
564 if (counter > 0 && directions.size() > 1)
565 return null;
566
567 return longest;
568 }
569
570 /**
571 * Get the detected segments
572 * @return the detected segments
573 */
574 List<SegmentInfo> getSegments() {
575 return segments;
576 }
577 }
578
579 private static boolean isRelatedToPower(Way way) {
580 if (way.hasTag(POWER) || way.hasTag(BUILDING))
581 return true;
582 for (OsmPrimitive ref : way.getReferrers()) {
583 if (ref instanceof Relation && ref.isMultipolygon() && (ref.hasTag(POWER) || ref.hasTag(BUILDING))) {
584 for (RelationMember rm : ((Relation) ref).getMembers()) {
585 if (way == rm.getMember())
586 return true;
587 }
588 }
589 }
590 return false;
591 }
592
593 /**
594 * Determines if the current node connected to a line which usually used inside power stations.
595 * @param n node to check
596 * @param w parent way of {@code n}
597 * @return {@code true} if {@code n} connected to power=line + line=*
598 */
599 private static boolean isConnectedToStationLine(Node n, Way w) {
600 for (OsmPrimitive p : n.getReferrers()) {
601 if (p instanceof Way && !p.equals(w) && isPowerLine((Way) p) && p.hasKey("line"))
602 return true;
603 }
604 return false;
605 }
606
607 /**
608 * Checks if the way continues as a power=minor_line.
609 * @param way Way to be checked
610 * @return {@code true} if the way continues as a power=minor_line
611 */
612 private static boolean isContinuesAsMinorLine(Way way) {
613 return way.firstNode().referrers(Way.class).filter(referrer -> !way.equals(referrer)).anyMatch(PowerLines::isMinorLine) ||
614 way.lastNode().referrers(Way.class).filter(referrer -> !way.equals(referrer)).anyMatch(PowerLines::isMinorLine);
615 }
616
617 /**
618 * Checks if the given primitive denotes a power=minor_line.
619 * @param p primitive to be checked
620 * @return {@code true} if the given primitive denotes a power=minor_line
621 */
622 private static boolean isMinorLine(OsmPrimitive p) {
623 return p.hasTag(POWER, MINOR_LINE);
624 }
625
626 /**
627 * Check if primitive has a tag that marks it as a water area or boundary of a water area.
628 * @param p the primitive
629 * @return {@code true} if primitive has a tag that marks it as a water area or boundary of a water area
630 */
631 private static boolean concernsWaterArea(OsmPrimitive p) {
632 return p.hasTag("water", "river", "lake") || p.hasKey("waterway") || p.hasTag("natural", "coastline");
633 }
634
635 /**
636 * Checks if the given node is inside a power station.
637 * @param n Node to be checked
638 * @return true if the given node is inside a power station
639 */
640 protected final boolean isInPowerStation(Node n) {
641 for (OsmPrimitive station : powerStations) {
642 List<List<Node>> nodesLists = new ArrayList<>();
643 if (station instanceof Way) {
644 nodesLists.add(((Way) station).getNodes());
645 } else if (station instanceof Relation) {
646 Multipolygon polygon = MultipolygonCache.getInstance().get((Relation) station);
647 if (polygon != null) {
648 for (JoinedWay outer : Multipolygon.joinWays(polygon.getOuterWays())) {
649 nodesLists.add(outer.getNodes());
650 }
651 }
652 }
653 for (List<Node> nodes : nodesLists) {
654 if (Geometry.nodeInsidePolygon(n, nodes)) {
655 return true;
656 }
657 }
658 }
659 return false;
660 }
661
662 /**
663 * Determines if the specified way denotes a power line.
664 * @param w The way to be tested
665 * @return {@code true} if power key is set and equal to line/minor_line
666 */
667 protected static boolean isPowerLine(Way w) {
668 return isPowerIn(w, POWER_LINE_TAGS);
669 }
670
671 /**
672 * Determines if the specified primitive denotes a power station.
673 * @param p The primitive to be tested
674 * @return {@code true} if power key is set and equal to generator/substation/plant
675 */
676 protected static boolean isPowerStation(OsmPrimitive p) {
677 return isPowerIn(p, POWER_STATION_TAGS) || isBuildingIn(p, BUILDING_STATION_TAGS);
678 }
679
680 /**
681 * Determines if the specified node denotes a power support feature.
682 * @param n The node to be tested
683 * @return {@code true} if power key is set and equal to pole/tower/portal/catenary_mast
684 */
685 protected static boolean isPowerTower(Node n) {
686 return isPowerIn(n, POWER_TOWER_TAGS);
687 }
688
689 /**
690 * Determines if the specified node denotes a power infrastructure allowed on a power line.
691 * @param n The node to be tested
692 * @return {@code true} if power key is set and equal to compensator/converter/generator/insulator
693 * /switch/switchgear/terminal/transformer
694 */
695 protected static boolean isPowerInfrastructure(Node n) {
696 return isPowerIn(n, POWER_INFRASTRUCTURE_TAGS);
697 }
698
699 /**
700 * Helper function to check if power tag is a certain value.
701 * @param p The primitive to be tested
702 * @param values List of possible values
703 * @return {@code true} if power key is set and equal to possible values
704 */
705 private static boolean isPowerIn(OsmPrimitive p, Collection<String> values) {
706 return p.hasTag(POWER, values);
707 }
708
709 /**
710 * Helper function to check if building tag is a certain value.
711 * @param p The primitive to be tested
712 * @param values List of possible values
713 * @return {@code true} if power key is set and equal to possible values
714 */
715 private static boolean isBuildingIn(OsmPrimitive p, Collection<String> values) {
716 return p.hasTag(BUILDING, values);
717 }
718
719 @Override
720 public void clear() {
721 super.clear();
722 badConnections.clear();
723 cellSegmentsWater.clear();
724 foundPowerLines.clear();
725 missingNodes.clear();
726 missingTags.clear();
727 powerStations.clear();
728 refDiscontinuities.clear();
729 segmentRefDiscontinuities.clear();
730 wrongLineType.clear();
731 }
732}
Note: See TracBrowser for help on using the repository browser.