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