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