Changeset 33091 in osm
- Timestamp:
- 2016-11-30T16:56:32+01:00 (8 years ago)
- Location:
- applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/PTAssistantValidatorTest.java
r33082 r33091 8 8 import java.util.Collection; 9 9 import java.util.List; 10 import java.util.Map.Entry; 10 11 11 12 import javax.swing.JOptionPane; … … 39 40 import org.openstreetmap.josm.plugins.pt_assistant.utils.StopToWayAssigner; 40 41 import org.openstreetmap.josm.plugins.pt_assistant.utils.StopUtils; 42 import org.openstreetmap.josm.tools.Utils; 41 43 42 44 public class PTAssistantValidatorTest extends Test { … … 406 408 } 407 409 } 410 411 /** 412 * Overrides the superclass method 413 */ 414 public void startTest() { 415 super.startTest(progressMonitor); 416 SegmentChecker.reset(); 417 } 418 419 /** 420 * Method is called after all primitives has been visited, overrides the method of the superclass. 421 */ 422 public void endTest() { 423 424 // modify the error messages for the stop-by-stop test: 425 SegmentChecker.modifyStopByStopErrorMessages(); 426 427 // add the stop-by-stop errors with modified messages: 428 for (Entry<Builder, PTRouteSegment> entry: SegmentChecker.wrongSegmentBuilders.entrySet()) { 429 TestError error = entry.getKey().build(); 430 SegmentChecker.wrongSegments.put(error, entry.getValue()); 431 this.errors.add(error); 432 } 433 434 super.endTest(); 435 436 } 408 437 409 438 /** -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/SegmentChecker.java
r33082 r33091 10 10 import java.util.HashMap; 11 11 import java.util.List; 12 import java.util.Map.Entry; 12 13 13 14 import javax.swing.SwingUtilities; … … 48 49 public class SegmentChecker extends Checker { 49 50 50 /* PTRouteSegments that have been validated and are correct */ 51 private static List<PTRouteSegment> correctSegments = new ArrayList<>(); 52 53 /* PTRouteSegments that are wrong, stored in case the user calls the fix */ 54 private static HashMap<TestError, PTRouteSegment> wrongSegments = new HashMap<>(); 55 56 /* Manager of the PTStops and PTWays of the current route */ 57 private PTRouteDataManager manager; 58 59 /* Assigns PTStops to nearest PTWays and stores that correspondence */ 60 private StopToWayAssigner assigner; 61 62 public SegmentChecker(Relation relation, Test test) { 63 64 super(relation, test); 65 66 this.manager = new PTRouteDataManager(relation); 67 68 for (RelationMember rm : manager.getFailedMembers()) { 69 List<Relation> primitives = new ArrayList<>(1); 70 primitives.add(relation); 71 List<OsmPrimitive> highlighted = new ArrayList<>(1); 72 highlighted.add(rm.getMember()); 73 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_RELAITON_MEMBER_ROLES); 74 builder.message(tr("PT: Relation member roles do not match tags")); 75 builder.primitives(primitives); 76 builder.highlight(highlighted); 77 TestError e = builder.build(); 78 this.errors.add(e); 79 } 80 81 this.assigner = new StopToWayAssigner(manager.getPTWays()); 82 83 } 84 85 /** 86 * Returns the number of route segments that have been already successfully 87 * verified 88 * 89 * @return the number of route segments 90 */ 91 public static int getCorrectSegmentCount() { 92 return correctSegments.size(); 93 } 94 95 /** 96 * Adds the given correct segment to the list of correct segments without 97 * checking its correctness 98 * 99 * @param segment 100 * to add to the list of correct segments 101 */ 102 public static synchronized void addCorrectSegment(PTRouteSegment segment) { 103 for (PTRouteSegment correctSegment : correctSegments) { 104 if (correctSegment.equalsRouteSegment(segment)) { 105 return; 106 } 107 } 108 correctSegments.add(segment); 109 } 110 111 /** 112 * Used for unit tests 113 * @param error test error 114 * @return wrong route segment 115 */ 116 protected static PTRouteSegment getWrongSegment(TestError error) { 117 return wrongSegments.get(error); 118 } 119 120 public void performFirstStopTest() { 121 122 performEndStopTest(manager.getFirstStop()); 123 124 } 125 126 public void performLastStopTest() { 127 128 performEndStopTest(manager.getLastStop()); 129 130 } 131 132 private void performEndStopTest(PTStop endStop) { 133 134 if (endStop == null) { 135 return; 136 } 137 138 /* 139 * This test checks: (1) that a stop position exists; (2) that it is the 140 * first or last node of its parent ways which belong to this route. 141 */ 142 143 if (endStop.getStopPosition() == null) { 144 145 List<Node> potentialStopPositionList = endStop.findPotentialStopPositions(); 146 List<Node> stopPositionsOfThisRoute = new ArrayList<>(); 147 boolean containsAtLeastOneStopPositionAsFirstOrLastNode = false; 148 149 for (Node potentialStopPosition : potentialStopPositionList) { 150 151 int belongsToWay = belongsToAWayOfThisRoute(potentialStopPosition); 152 153 if (belongsToWay == 0) { 154 stopPositionsOfThisRoute.add(potentialStopPosition); 155 containsAtLeastOneStopPositionAsFirstOrLastNode = true; 156 } 157 158 if (belongsToWay == 1) { 159 stopPositionsOfThisRoute.add(potentialStopPosition); 160 } 161 } 162 163 if (stopPositionsOfThisRoute.isEmpty()) { 164 List<Relation> primitives = new ArrayList<>(1); 165 primitives.add(relation); 166 List<OsmPrimitive> highlighted = new ArrayList<>(1); 167 highlighted.add(endStop.getPlatform()); 168 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_END_STOP); 169 builder.message(tr("PT: Route should start and end with a stop_position")); 170 builder.primitives(primitives); 171 builder.highlight(highlighted); 172 TestError e = builder.build(); 173 this.errors.add(e); 174 return; 175 } 176 177 if (stopPositionsOfThisRoute.size() == 1) { 178 endStop.setStopPosition(stopPositionsOfThisRoute.get(0)); 179 } 180 181 // At this point, there is at least one stop_position for this 182 // endStop: 183 if (!containsAtLeastOneStopPositionAsFirstOrLastNode) { 184 List<Relation> primitives = new ArrayList<>(1); 185 primitives.add(relation); 186 List<OsmPrimitive> highlighted = new ArrayList<>(); 187 highlighted.addAll(stopPositionsOfThisRoute); 188 189 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_SPLIT_WAY); 190 builder.message(tr("PT: First or last way needs to be split")); 191 builder.primitives(primitives); 192 builder.highlight(highlighted); 193 TestError e = builder.build(); 194 this.errors.add(e); 195 } 196 197 } else { 198 199 // if the stop_position is known: 200 int belongsToWay = this.belongsToAWayOfThisRoute(endStop.getStopPosition()); 201 202 if (belongsToWay == 1) { 203 204 List<Relation> primitives = new ArrayList<>(1); 205 primitives.add(relation); 206 List<OsmPrimitive> highlighted = new ArrayList<>(); 207 highlighted.add(endStop.getStopPosition()); 208 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_SPLIT_WAY); 209 builder.message(tr("PT: First or last way needs to be split")); 210 builder.primitives(primitives); 211 builder.highlight(highlighted); 212 TestError e = builder.build(); 213 this.errors.add(e); 214 } 215 } 216 217 } 218 219 /** 220 * Checks if the given node belongs to the ways of this route. 221 * 222 * @param node 223 * Node to be checked 224 * @return 1 if belongs only as an inner node, 0 if belongs as a first or 225 * last node for at least one way, -1 if does not belong to any way. 226 */ 227 private int belongsToAWayOfThisRoute(Node node) { 228 229 boolean contains = false; 230 231 List<PTWay> ptways = manager.getPTWays(); 232 for (PTWay ptway : ptways) { 233 List<Way> ways = ptway.getWays(); 234 for (Way way : ways) { 235 if (way.containsNode(node)) { 236 237 if (way.firstNode().equals(node) || way.lastNode().equals(node)) { 238 return 0; 239 } 240 241 contains = true; 242 } 243 } 244 } 245 246 if (contains) { 247 return 1; 248 } 249 250 return -1; 251 } 252 253 public void performStopNotServedTest() { 254 for (PTStop stop : manager.getPTStops()) { 255 Way way = assigner.get(stop); 256 if (way == null) { 257 createStopError(stop); 258 } 259 } 260 } 261 262 /** 263 * Performs the stop-by-stop test by visiting each segment between two 264 * consecutive stops and checking if the ways between them are correct 265 */ 266 public void performStopByStopTest() { 267 268 if (manager.getPTStopCount() < 2) { 269 return; 270 } 271 272 // Check each route segment: 273 for (int i = 1; i < manager.getPTStopCount(); i++) { 274 275 PTStop startStop = manager.getPTStops().get(i - 1); 276 PTStop endStop = manager.getPTStops().get(i); 277 278 Way startWay = assigner.get(startStop); 279 Way endWay = assigner.get(endStop); 280 if (startWay == null || endWay == null || (startWay == endWay && startWay == manager.getLastWay())) { 281 continue; 282 } 283 284 List<PTWay> segmentWays = manager.getPTWaysBetween(startWay, endWay); 285 286 Node firstNode = findFirstNodeOfRouteSegmentInDirectionOfTravel(segmentWays.get(0)); 287 if (firstNode == null) { 288 // check if this error has just been reported: 289 if (!this.errors.isEmpty() && this.errors.get(this.errors.size() - 1).getHighlighted().size() == 1 290 && this.errors.get(this.errors.size() - 1).getHighlighted().iterator().next() == startWay) { 291 // do nothing, this error has already been reported in 292 // the previous route segment 293 } else { 294 List<Relation> primitives = new ArrayList<>(1); 295 primitives.add(relation); 296 List<OsmPrimitive> highlighted = new ArrayList<>(); 297 highlighted.add(startWay); 298 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 299 builder.message(tr("PT: Problem in the route segment")); 300 builder.primitives(primitives); 301 builder.highlight(highlighted); 302 TestError e = builder.build(); 303 this.errors.add(e); 304 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 305 wrongSegments.put(e, routeSegment); 306 } 307 continue; 308 } 309 310 boolean sortingCorrect = existingWaySortingIsCorrect(segmentWays.get(0), firstNode, 311 segmentWays.get(segmentWays.size() - 1)); 312 if (sortingCorrect) { 313 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 314 addCorrectSegment(routeSegment); 315 } else { 316 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 317 TestError error = this.errors.get(this.errors.size() - 1); 318 wrongSegments.put(error, routeSegment); 319 } 320 } 321 } 322 323 /** 324 * Creates a TestError and adds it to the list of errors for a stop that is 325 * not served. 326 * 327 * @param stop stop 328 */ 329 private void createStopError(PTStop stop) { 330 List<Relation> primitives = new ArrayList<>(1); 331 primitives.add(relation); 332 List<OsmPrimitive> highlighted = new ArrayList<>(); 333 OsmPrimitive stopPrimitive = stop.getPlatform(); 334 if (stopPrimitive == null) { 335 stopPrimitive = stop.getStopPosition(); 336 } 337 highlighted.add(stopPrimitive); 338 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_STOP_NOT_SERVED); 339 builder.message(tr("PT: Stop not served")); 340 builder.primitives(primitives); 341 builder.highlight(highlighted); 342 TestError e = builder.build(); 343 this.errors.add(e); 344 } 345 346 private Node findFirstNodeOfRouteSegmentInDirectionOfTravel(PTWay startWay) { 347 348 // 1) at first check if one of the first or last node of the first ptway 349 // is a deadend node: 350 Node[] startWayEndnodes = startWay.getEndNodes(); 351 if (isDeadendNode(startWayEndnodes[0])) { 352 return startWayEndnodes[0]; 353 } 354 if (isDeadendNode(startWayEndnodes[1])) { 355 return startWayEndnodes[1]; 356 } 357 358 // 2) failing that, check which node this startWay shares with the 359 // following way: 360 PTWay nextWay = manager.getNextPTWay(startWay); 361 if (nextWay == null) { 362 return null; 363 } 364 PTWay wayAfterNext = manager.getNextPTWay(nextWay); 365 Node[] nextWayEndnodes = nextWay.getEndNodes(); 366 if ((startWayEndnodes[0] == nextWayEndnodes[0] && startWayEndnodes[1] == nextWayEndnodes[1]) 367 || (startWayEndnodes[0] == nextWayEndnodes[1] && startWayEndnodes[1] == nextWayEndnodes[0])) { 368 // if this is a split roundabout: 369 Node[] wayAfterNextEndnodes = wayAfterNext.getEndNodes(); 370 if (startWayEndnodes[0] == wayAfterNextEndnodes[0] || startWayEndnodes[0] == wayAfterNextEndnodes[1]) { 371 return startWayEndnodes[0]; 372 } 373 if (startWayEndnodes[1] == wayAfterNextEndnodes[0] || startWayEndnodes[1] == wayAfterNextEndnodes[1]) { 374 return startWayEndnodes[1]; 375 } 376 } 377 378 if (startWayEndnodes[0] == nextWayEndnodes[0] || startWayEndnodes[0] == nextWayEndnodes[1]) { 379 return startWayEndnodes[1]; 380 } 381 if (startWayEndnodes[1] == nextWayEndnodes[0] || startWayEndnodes[1] == nextWayEndnodes[1]) { 382 return startWayEndnodes[0]; 383 } 384 385 return null; 386 387 } 388 389 private boolean isDeadendNode(Node node) { 390 int count = 0; 391 for (PTWay ptway : manager.getPTWays()) { 392 List<Way> ways = ptway.getWays(); 393 for (Way way : ways) { 394 if (way.firstNode() == node || way.lastNode() == node) { 395 count++; 396 } 397 } 398 } 399 return count == 1; 400 } 401 402 /** 403 * Finds the deadend node closest to the given node represented by its 404 * coordinates 405 * 406 * @param coord 407 * coordinates of the givenn node 408 * @param deadendNodes dead end nodes 409 * @return the closest deadend node 410 */ 411 @SuppressWarnings("unused") 412 private Node findClosestDeadendNode(LatLon coord, List<Node> deadendNodes) { 413 414 Node closestDeadendNode = null; 415 double minSqDistance = Double.MAX_VALUE; 416 for (Node deadendNode : deadendNodes) { 417 double distanceSq = coord.distanceSq(deadendNode.getCoor()); 418 if (distanceSq < minSqDistance) { 419 minSqDistance = distanceSq; 420 closestDeadendNode = deadendNode; 421 } 422 } 423 return closestDeadendNode; 424 425 } 426 427 /** 428 * Checks if the existing sorting of the given route segment is correct 429 * 430 * @param start 431 * PTWay assigned to the first stop of the segment 432 * @param startWayPreviousNodeInDirectionOfTravel 433 * Node if the start way which is furthest away from the rest of 434 * the route 435 * @param end 436 * PTWay assigned to the end stop of the segment 437 * @return true if the sorting is correct, false otherwise. 438 */ 439 private boolean existingWaySortingIsCorrect(PTWay start, Node startWayPreviousNodeInDirectionOfTravel, PTWay end) { 440 441 if (start == end) { 442 // if both PTStops are on the same PTWay 443 return true; 444 } 445 446 PTWay current = start; 447 Node currentNode = startWayPreviousNodeInDirectionOfTravel; 448 449 while (!current.equals(end)) { 450 // "equals" is used here instead of "==" because when the same way 451 // is passed multiple times by the bus, the algorithm should stop no 452 // matter which of the geometrically equal PTWays it finds 453 454 PTWay nextPTWayAccortingToExistingSorting = manager.getNextPTWay(current); 455 456 // if current contains an unsplit roundabout: 457 if (current.containsUnsplitRoundabout()) { 458 currentNode = manager.getCommonNode(current, nextPTWayAccortingToExistingSorting); 459 if (currentNode == null) { 460 List<Relation> primitives = new ArrayList<>(1); 461 primitives.add(relation); 462 List<OsmPrimitive> highlighted = new ArrayList<>(); 463 highlighted.addAll(current.getWays()); 464 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 465 builder.message(tr("PT: Problem in the route segment")); 466 builder.primitives(primitives); 467 builder.highlight(highlighted); 468 TestError e = builder.build(); 469 this.errors.add(e); 470 return false; 471 } 472 } else { 473 // if this is a regular way, not an unsplit roundabout 474 475 // find the next node in direction of travel (which is part of 476 // the PTWay start): 477 currentNode = getOppositeEndNode(current, currentNode); 478 479 List<PTWay> nextWaysInDirectionOfTravel = this.findNextPTWaysInDirectionOfTravel(current, currentNode); 480 481 if (!nextWaysInDirectionOfTravel.contains(nextPTWayAccortingToExistingSorting)) { 482 List<Relation> primitives = new ArrayList<>(1); 483 primitives.add(relation); 484 List<OsmPrimitive> highlighted = new ArrayList<>(); 485 486 highlighted.addAll(current.getWays()); 487 488 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 489 builder.message(tr("PT: Problem in the route segment")); 490 builder.primitives(primitives); 491 builder.highlight(highlighted); 492 TestError e = builder.build(); 493 this.errors.add(e); 494 return false; 495 496 } 497 } 498 499 current = nextPTWayAccortingToExistingSorting; 500 501 } 502 503 return true; 504 } 505 506 /** 507 * Will return the same node if the way is an unsplit roundabout 508 * 509 * @param way way 510 * @param node node 511 * @return the same node if the way is an unsplit roundabout 512 */ 513 private Node getOppositeEndNode(Way way, Node node) { 514 515 if (node == way.firstNode()) { 516 return way.lastNode(); 517 } 518 519 if (node == way.lastNode()) { 520 return way.firstNode(); 521 } 522 523 return null; 524 } 525 526 /** 527 * Does not work correctly for unsplit roundabouts 528 * 529 * @param ptway way 530 * @param node node 531 * @return node 532 */ 533 private Node getOppositeEndNode(PTWay ptway, Node node) { 534 if (ptway.isWay()) { 535 return getOppositeEndNode(ptway.getWays().get(0), node); 536 } 537 538 Way firstWay = ptway.getWays().get(0); 539 Way lastWay = ptway.getWays().get(ptway.getWays().size() - 1); 540 Node oppositeNode = node; 541 if (firstWay.firstNode() == node || firstWay.lastNode() == node) { 542 for (int i = 0; i < ptway.getWays().size(); i++) { 543 oppositeNode = getOppositeEndNode(ptway.getWays().get(i), oppositeNode); 544 } 545 return oppositeNode; 546 } else if (lastWay.firstNode() == node || lastWay.lastNode() == node) { 547 for (int i = ptway.getWays().size() - 1; i >= 0; i--) { 548 oppositeNode = getOppositeEndNode(ptway.getWays().get(i), oppositeNode); 549 } 550 return oppositeNode; 551 } 552 553 return null; 554 555 } 556 557 /** 558 * Finds the next ways for the route stop-by-stop parsing procedure 559 * 560 * @param currentWay current way 561 * @param nextNodeInDirectionOfTravel next node in direction of travel 562 * @return the next ways for the route stop-by-stop parsing procedure 563 */ 564 private List<PTWay> findNextPTWaysInDirectionOfTravel(PTWay currentWay, Node nextNodeInDirectionOfTravel) { 565 566 List<PTWay> nextPtways = new ArrayList<>(); 567 568 List<PTWay> ptways = manager.getPTWays(); 569 570 for (PTWay ptway : ptways) { 571 572 if (ptway != currentWay) { 573 for (Way way : ptway.getWays()) { 574 if (way.containsNode(nextNodeInDirectionOfTravel)) { 575 nextPtways.add(ptway); 576 } 577 } 578 } 579 } 580 581 return nextPtways; 582 583 } 584 585 protected static boolean isFixable(TestError testError) { 586 587 /*- 588 * When is an error fixable (outdated)? 589 * - if there is a correct segment 590 * - if it can be fixed by sorting 591 * - if the route is compete even without some ways 592 * - if simple routing closes the gap 593 */ 594 595 if (testError.getCode() == PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP) { 596 return true; 597 } 598 599 return false; 600 601 } 602 603 @SuppressWarnings("unused") 604 private static boolean isFixableByUsingCorrectSegment(TestError testError) { 605 PTRouteSegment wrongSegment = wrongSegments.get(testError); 606 PTRouteSegment correctSegment = null; 607 for (PTRouteSegment segment : correctSegments) { 608 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 609 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 610 correctSegment = segment; 611 break; 612 } 613 } 614 return correctSegment != null; 615 } 616 617 @SuppressWarnings("unused") 618 private static boolean isFixableBySortingAndRemoval(TestError testError) { 619 PTRouteSegment wrongSegment = wrongSegments.get(testError); 620 List<List<PTWay>> fixVariants = wrongSegment.getFixVariants(); 621 if (!fixVariants.isEmpty()) { 622 return true; 623 } 624 return false; 625 } 626 627 /** 628 * Finds fixes using sorting and removal. Modifies the messages in the test 629 * error according to the availability of automatic fixes. 630 */ 631 protected void findFixes() { 632 633 for (TestError error : wrongSegments.keySet()) { 634 // look for fixes using sorting and removing: 635 findFix(error); 636 637 // change the error code based on the availability of fixes: 638 PTRouteSegment wrongSegment = wrongSegments.get(error); 639 List<PTRouteSegment> correctSegmentsForThisError = new ArrayList<>(); 640 for (PTRouteSegment segment : correctSegments) { 641 if (wrongSegment.getFirstWay().getId() == segment.getFirstWay().getId() 642 && wrongSegment.getLastWay().getId() == segment.getLastWay().getId()) { 643 correctSegmentsForThisError.add(segment); 644 } 645 } 646 647 int numberOfFixes = correctSegmentsForThisError.size(); 648 649 if (numberOfFixes == 0) { 650 numberOfFixes = wrongSegment.getFixVariants().size(); 651 } 652 if (numberOfFixes == 0) { 653 for (PTRouteSegment segment : correctSegments) { 654 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 655 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 656 correctSegmentsForThisError.add(segment); 657 } 658 } 659 numberOfFixes = correctSegmentsForThisError.size(); 660 } 661 662 // change the error code: 663 if (numberOfFixes == 0) { 664 error.setMessage(tr("PT: Problem in the route segment with no automatic fix")); 665 } else if (numberOfFixes == 1) { 666 error.setMessage(tr("PT: Problem in the route segment with one automatic fix")); 667 } else { 668 error.setMessage("PT: Problem in the route segment with several automatic fixes"); 669 } 670 } 671 672 } 673 674 /** 675 * This method assumes that the first and the second ways of the route 676 * segment are correctly connected. If they are not, the error will be 677 * marked as not fixable. 678 * 679 * @param testError test error 680 */ 681 private void findFix(TestError testError) { 682 683 PTRouteSegment wrongSegment = wrongSegments.get(testError); 684 PTWay startPTWay = wrongSegment.getFirstPTWay(); 685 PTWay endPTWay = wrongSegment.getLastPTWay(); 686 687 Node previousNode = findFirstNodeOfRouteSegmentInDirectionOfTravel(startPTWay); 688 if (previousNode == null) { 689 return; 690 } 691 692 List<List<PTWay>> initialFixes = new ArrayList<>(); 693 List<PTWay> initialFix = new ArrayList<>(); 694 initialFix.add(startPTWay); 695 initialFixes.add(initialFix); 696 697 List<List<PTWay>> allFixes = findWaysForFix(initialFixes, initialFix, previousNode, endPTWay); 698 for (List<PTWay> fix : allFixes) { 699 if (!fix.isEmpty() && fix.get(fix.size() - 1).equals(endPTWay)) { 700 wrongSegment.addFixVariant(fix); 701 } 702 } 703 704 } 705 706 /** 707 * Recursive method to parse the route segment 708 * 709 * @param allFixes all fixes 710 * @param currentFix current fix 711 * @param previousNode previous node 712 * @param endWay end way 713 * @return list of list of ways 714 */ 715 private List<List<PTWay>> findWaysForFix(List<List<PTWay>> allFixes, List<PTWay> currentFix, Node previousNode, 716 PTWay endWay) { 717 718 PTWay currentWay = currentFix.get(currentFix.size() - 1); 719 Node nextNode = getOppositeEndNode(currentWay, previousNode); 720 721 List<PTWay> nextWays = this.findNextPTWaysInDirectionOfTravel(currentWay, nextNode); 722 723 if (nextWays.size() > 1) { 724 for (int i = 1; i < nextWays.size(); i++) { 725 List<PTWay> newFix = new ArrayList<>(); 726 newFix.addAll(currentFix); 727 newFix.add(nextWays.get(i)); 728 allFixes.add(newFix); 729 if (!nextWays.get(i).equals(endWay) && !currentFix.contains(nextWays.get(i))) { 730 allFixes = findWaysForFix(allFixes, newFix, nextNode, endWay); 731 } 732 } 733 } 734 735 if (!nextWays.isEmpty()) { 736 boolean contains = currentFix.contains(nextWays.get(0)); 737 currentFix.add(nextWays.get(0)); 738 if (!nextWays.get(0).equals(endWay) && !contains) { 739 allFixes = findWaysForFix(allFixes, currentFix, nextNode, endWay); 740 } 741 } 742 743 return allFixes; 744 } 745 746 /** 747 * Fixes the error by first searching in the list of correct segments and 748 * then trying to sort and remove existing route relation members 749 * 750 * @param testError test error 751 * @return fix command 752 */ 753 protected static Command fixError(TestError testError) { 754 755 // if fix options for another route are displayed in the pt_assistant 756 // layer, clear them: 757 ((PTAssistantValidatorTest) testError.getTester()).clearFixVariants(); 758 759 PTRouteSegment wrongSegment = wrongSegments.get(testError); 760 761 // 1) try to fix by using the correct segment: 762 List<PTRouteSegment> correctSegmentsForThisError = new ArrayList<>(); 763 for (PTRouteSegment segment : correctSegments) { 764 if (wrongSegment.getFirstWay().getId() == segment.getFirstWay().getId() 765 && wrongSegment.getLastWay().getId() == segment.getLastWay().getId()) { 766 correctSegmentsForThisError.add(segment); 767 } 768 } 769 770 // if no correct segment found, apply less strict criteria to look for 771 // one: 772 if (correctSegmentsForThisError.isEmpty() && wrongSegment.getFixVariants().isEmpty()) { 773 for (PTRouteSegment segment : correctSegments) { 774 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 775 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 776 correctSegmentsForThisError.add(segment); 777 } 778 } 779 if (!correctSegmentsForThisError.isEmpty()) { 780 // display the notification: 781 if (SwingUtilities.isEventDispatchThread()) { 782 Notification notification = new Notification( 783 tr("Warning: the diplayed fix variants are based on less strict criteria")); 784 notification.show(); 785 } else { 786 SwingUtilities.invokeLater(new Runnable() { 787 @Override 788 public void run() { 789 Notification notification = new Notification( 790 tr("Warning: the diplayed fix variants are based on less strict criteria")); 791 notification.show(); 792 } 793 }); 794 } 795 } 796 } 797 798 if (!correctSegmentsForThisError.isEmpty()) { 799 800 if (correctSegmentsForThisError.size() > 1) { 801 List<List<PTWay>> fixVariants = new ArrayList<>(); 802 for (PTRouteSegment segment : correctSegmentsForThisError) { 803 fixVariants.add(segment.getPTWays()); 804 } 805 displayFixVariants(fixVariants, testError); 806 return null; 807 } 808 809 PTAssistantPlugin.setLastFix(correctSegmentsForThisError.get(0)); 810 return carryOutSingleFix(testError, correctSegmentsForThisError.get(0).getPTWays()); 811 812 } else if (!wrongSegment.getFixVariants().isEmpty()) { 813 // 2) try to fix using the sorting and removal of existing ways 814 // of the wrong segment: 815 if (wrongSegment.getFixVariants().size() > 1) { 816 displayFixVariants(wrongSegment.getFixVariants(), testError); 817 return null; 818 } 819 820 PTAssistantPlugin.setLastFix(new PTRouteSegment(wrongSegment.getFirstStop(), 821 wrongSegment.getLastStop(), wrongSegment.getFixVariants().get(0), (Relation) testError.getPrimitives().iterator().next())); 822 return carryOutSingleFix(testError, wrongSegment.getFixVariants().get(0)); 823 } 824 825 // if there is no fix: 826 return fixErrorByZooming(testError); 827 828 } 829 830 /** 831 * This is largely a copy of the displayFixVariants() method, adapted for 832 * use with the key listener 833 * 834 * @param fixVariants fix variants 835 * @param testError test error 836 */ 837 private static void displayFixVariants(List<List<PTWay>> fixVariants, TestError testError) { 838 // find the letters of the fix variants: 839 char alphabet = 'A'; 840 final List<Character> allowedCharacters = new ArrayList<>(); 841 for (int i = 0; i < fixVariants.size(); i++) { 842 allowedCharacters.add(alphabet); 843 alphabet++; 844 } 845 846 // zoom to problem: 847 final Collection<OsmPrimitive> waysToZoom = new ArrayList<>(); 848 for (Object highlightedPrimitive : testError.getHighlighted()) { 849 waysToZoom.add((OsmPrimitive) highlightedPrimitive); 850 } 851 if (SwingUtilities.isEventDispatchThread()) { 852 AutoScaleAction.zoomTo(waysToZoom); 853 } else { 854 SwingUtilities.invokeLater(new Runnable() { 855 @Override 856 public void run() { 857 AutoScaleAction.zoomTo(waysToZoom); 858 } 859 }); 860 } 861 862 // display the fix variants: 863 final PTAssistantValidatorTest test = (PTAssistantValidatorTest) testError.getTester(); 864 test.addFixVariants(fixVariants); 865 PTAssistantLayer.getLayer().repaint((Relation) testError.getPrimitives().iterator().next()); 866 867 // prepare the variables for the key listener: 868 final TestError testErrorParameter = testError; 869 870 // // add the key listener: 871 Main.map.mapView.requestFocus(); 872 Main.map.mapView.addKeyListener(new KeyListener() { 873 874 public void keyTyped(KeyEvent e) { 875 //TODO Auto-generated method stub 876 } 877 878 public void keyPressed(KeyEvent e) { 879 Character typedKey = e.getKeyChar(); 880 Character typedKeyUpperCase = typedKey.toString().toUpperCase().toCharArray()[0]; 881 if (allowedCharacters.contains(typedKeyUpperCase)) { 882 Main.map.mapView.removeKeyListener(this); 883 List<PTWay> selectedFix = test.getFixVariant(typedKeyUpperCase); 884 test.clearFixVariants(); 885 carryOutSelectedFix(testErrorParameter, selectedFix); 886 } 887 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 888 Main.map.mapView.removeKeyListener(this); 889 test.clearFixVariants(); 890 } 891 } 892 893 public void keyReleased(KeyEvent e) { 894 // TODO Auto-generated method stub 895 } 896 }); 897 898 // display the notification: 899 if (SwingUtilities.isEventDispatchThread()) { 900 Notification notification = new Notification( 901 tr("Type letter to select the fix variant or press Escape for no fix")); 902 notification.show(); 903 } else { 904 SwingUtilities.invokeLater(new Runnable() { 905 @Override 906 public void run() { 907 Notification notification = new Notification( 908 tr("Type letter to select the fix variant or press Escape for no fix")); 909 notification.show(); 910 } 911 }); 912 } 913 } 914 915 /** 916 * Carries out the fix (i.e. modifies the route) after the user has picked 917 * the fix from several fix variants. 918 * 919 * @param testError 920 * test error to be fixed 921 * @param fix 922 * the fix variant to be adopted 923 */ 924 private static void carryOutSelectedFix(TestError testError, List<PTWay> fix) { 925 // modify the route: 926 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 927 Relation modifiedRelation = new Relation(originalRelation); 928 modifiedRelation.setMembers(getModifiedRelationMembers(testError, fix)); 929 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 930 Main.main.undoRedo.addNoRedraw(changeCommand); 931 Main.main.undoRedo.afterAdd(); 932 PTRouteSegment wrongSegment = wrongSegments.get(testError); 933 wrongSegments.remove(testError); 934 wrongSegment.setPTWays(fix); 935 addCorrectSegment(wrongSegment); 936 PTAssistantPlugin.setLastFixNoGui(wrongSegment); 937 938 // get ways for the fix: 939 List<Way> primitives = new ArrayList<>(); 940 for (PTWay ptway : fix) { 941 primitives.addAll(ptway.getWays()); 942 } 943 944 // get layer: 945 OsmDataLayer layer = null; 946 List<OsmDataLayer> listOfLayers = Main.getLayerManager().getLayersOfType(OsmDataLayer.class); 947 for (OsmDataLayer osmDataLayer : listOfLayers) { 948 if (osmDataLayer.data == originalRelation.getDataSet()) { 949 layer = osmDataLayer; 950 break; 951 } 952 } 953 954 // create editor: 955 GenericRelationEditor editor = (GenericRelationEditor) RelationEditor.getEditor(layer, originalRelation, 956 originalRelation.getMembersFor(primitives)); 957 958 // open editor: 959 editor.setVisible(true); 960 961 } 962 963 /** 964 * Carries out the fix (i.e. modifies the route) when there is only one fix 965 * variant. 966 * 967 * @param testError test error 968 * @param fix fix 969 */ 970 private static Command carryOutSingleFix(TestError testError, List<PTWay> fix) { 971 // Zoom to the problematic ways: 972 final Collection<OsmPrimitive> waysToZoom = new ArrayList<>(); 973 for (Object highlightedPrimitive : testError.getHighlighted()) { 974 waysToZoom.add((OsmPrimitive) highlightedPrimitive); 975 } 976 if (SwingUtilities.isEventDispatchThread()) { 977 AutoScaleAction.zoomTo(waysToZoom); 978 } else { 979 SwingUtilities.invokeLater(new Runnable() { 980 @Override 981 public void run() { 982 AutoScaleAction.zoomTo(waysToZoom); 983 } 984 }); 985 } 986 987 // wait: 988 synchronized (SegmentChecker.class) { 989 try { 990 SegmentChecker.class.wait(1500); 991 } catch (InterruptedException e) { 992 // TODO Auto-generated catch block 993 e.printStackTrace(); 994 } 995 } 996 997 // modify the route: 998 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 999 Relation modifiedRelation = new Relation(originalRelation); 1000 modifiedRelation.setMembers(getModifiedRelationMembers(testError, fix)); 1001 wrongSegments.remove(testError); 1002 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 1003 return changeCommand; 1004 } 1005 1006 /** 1007 * Returns a list of the modified relation members. This list can be used by 1008 * the calling method (relation.setMemers()) to modify the modify the route 1009 * relation. The route relation is not modified by this method. The lists of 1010 * wrong and correct segments are not updated. 1011 * 1012 * @param testError 1013 * test error to be fixed 1014 * @param fix 1015 * the fix variant to be adopted 1016 * @return List of modified relation members to be applied to the route 1017 * relation 1018 */ 1019 private static List<RelationMember> getModifiedRelationMembers(TestError testError, List<PTWay> fix) { 1020 PTRouteSegment wrongSegment = wrongSegments.get(testError); 1021 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 1022 1023 // copy stops first: 1024 List<RelationMember> modifiedRelationMembers = listStopMembers(originalRelation); 1025 1026 // copy PTWays last: 1027 List<RelationMember> waysOfOriginalRelation = listNotStopMembers(originalRelation); 1028 for (int i = 0; i < waysOfOriginalRelation.size(); i++) { 1029 if (waysOfOriginalRelation.get(i).getWay() == wrongSegment.getPTWays().get(0).getWays().get(0)) { 1030 modifiedRelationMembers.addAll(fix); 1031 i = i + wrongSegment.getPTWays().size() - 1; 1032 } else { 1033 modifiedRelationMembers.add(waysOfOriginalRelation.get(i)); 1034 } 1035 } 1036 1037 return modifiedRelationMembers; 1038 } 1039 1040 public static void carryOutRepeatLastFix(PTRouteSegment segment) { 1041 1042 List<TestError> wrongSegmentsToRemove = new ArrayList<>(); 1043 1044 // find all wrong ways that have the same segment: 1045 for (TestError testError: wrongSegments.keySet()) { 1046 PTRouteSegment wrongSegment = wrongSegments.get(testError); 1047 if (wrongSegment.getFirstWay() == segment.getFirstWay() && wrongSegment.getLastWay() == segment.getLastWay()) { 1048 // modify the route: 1049 Relation originalRelation = wrongSegment.getRelation(); 1050 Relation modifiedRelation = new Relation(originalRelation); 1051 modifiedRelation.setMembers(getModifiedRelationMembers(testError, segment.getPTWays())); 1052 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 1053 Main.main.undoRedo.addNoRedraw(changeCommand); 1054 Main.main.undoRedo.afterAdd(); 1055 wrongSegmentsToRemove.add(testError); 1056 } 1057 } 1058 1059 // update the errors displayed in the validator dialog: 1060 List<TestError> modifiedValidatorTestErrors = new ArrayList<>(); 1061 for (TestError validatorTestError: Main.map.validatorDialog.tree.getErrors()) { 1062 if (!wrongSegmentsToRemove.contains(validatorTestError)) { 1063 modifiedValidatorTestErrors.add(validatorTestError); 1064 } 1065 } 1066 Main.map.validatorDialog.tree.setErrors(modifiedValidatorTestErrors); 1067 1068 // update wrong segments: 1069 for (TestError testError: wrongSegmentsToRemove) { 1070 wrongSegments.remove(testError); 1071 } 1072 1073 } 1074 1075 /** 1076 * Resets the static list variables (used for unit testing) 1077 */ 1078 protected static void reset() { 1079 correctSegments.clear(); 1080 wrongSegments.clear(); 1081 } 51 /* PTRouteSegments that have been validated and are correct */ 52 private static List<PTRouteSegment> correctSegments = new ArrayList<>(); 53 54 /* PTRouteSegments that are wrong, stored in case the user calls the fix */ 55 protected static HashMap<TestError, PTRouteSegment> wrongSegments = new HashMap<>(); 56 protected static HashMap<Builder, PTRouteSegment> wrongSegmentBuilders = new HashMap<>(); 57 58 /* Manager of the PTStops and PTWays of the current route */ 59 private PTRouteDataManager manager; 60 61 /* Assigns PTStops to nearest PTWays and stores that correspondence */ 62 private StopToWayAssigner assigner; 63 64 public SegmentChecker(Relation relation, Test test) { 65 66 super(relation, test); 67 68 this.manager = new PTRouteDataManager(relation); 69 70 for (RelationMember rm : manager.getFailedMembers()) { 71 List<Relation> primitives = new ArrayList<>(1); 72 primitives.add(relation); 73 List<OsmPrimitive> highlighted = new ArrayList<>(1); 74 highlighted.add(rm.getMember()); 75 Builder builder = TestError.builder(this.test, Severity.WARNING, 76 PTAssistantValidatorTest.ERROR_CODE_RELAITON_MEMBER_ROLES); 77 builder.message(tr("PT: Relation member roles do not match tags")); 78 builder.primitives(primitives); 79 builder.highlight(highlighted); 80 TestError e = builder.build(); 81 this.errors.add(e); 82 } 83 84 this.assigner = new StopToWayAssigner(manager.getPTWays()); 85 86 } 87 88 /** 89 * Returns the number of route segments that have been already successfully 90 * verified 91 * 92 * @return the number of route segments 93 */ 94 public static int getCorrectSegmentCount() { 95 return correctSegments.size(); 96 } 97 98 /** 99 * Adds the given correct segment to the list of correct segments without 100 * checking its correctness 101 * 102 * @param segment 103 * to add to the list of correct segments 104 */ 105 public static synchronized void addCorrectSegment(PTRouteSegment segment) { 106 for (PTRouteSegment correctSegment : correctSegments) { 107 if (correctSegment.equalsRouteSegment(segment)) { 108 return; 109 } 110 } 111 correctSegments.add(segment); 112 } 113 114 /** 115 * Used for unit tests 116 * 117 * @param error 118 * test error 119 * @return wrong route segment 120 */ 121 protected static PTRouteSegment getWrongSegment(TestError error) { 122 return wrongSegments.get(error); 123 } 124 125 public void performFirstStopTest() { 126 127 performEndStopTest(manager.getFirstStop()); 128 129 } 130 131 public void performLastStopTest() { 132 133 performEndStopTest(manager.getLastStop()); 134 135 } 136 137 private void performEndStopTest(PTStop endStop) { 138 139 if (endStop == null) { 140 return; 141 } 142 143 /* 144 * This test checks: (1) that a stop position exists; (2) that it is the 145 * first or last node of its parent ways which belong to this route. 146 */ 147 148 if (endStop.getStopPosition() == null) { 149 150 List<Node> potentialStopPositionList = endStop.findPotentialStopPositions(); 151 List<Node> stopPositionsOfThisRoute = new ArrayList<>(); 152 boolean containsAtLeastOneStopPositionAsFirstOrLastNode = false; 153 154 for (Node potentialStopPosition : potentialStopPositionList) { 155 156 int belongsToWay = belongsToAWayOfThisRoute(potentialStopPosition); 157 158 if (belongsToWay == 0) { 159 stopPositionsOfThisRoute.add(potentialStopPosition); 160 containsAtLeastOneStopPositionAsFirstOrLastNode = true; 161 } 162 163 if (belongsToWay == 1) { 164 stopPositionsOfThisRoute.add(potentialStopPosition); 165 } 166 } 167 168 if (stopPositionsOfThisRoute.isEmpty()) { 169 List<Relation> primitives = new ArrayList<>(1); 170 primitives.add(relation); 171 List<OsmPrimitive> highlighted = new ArrayList<>(1); 172 highlighted.add(endStop.getPlatform()); 173 Builder builder = TestError.builder(this.test, Severity.WARNING, 174 PTAssistantValidatorTest.ERROR_CODE_END_STOP); 175 builder.message(tr("PT: Route should start and end with a stop_position")); 176 builder.primitives(primitives); 177 builder.highlight(highlighted); 178 TestError e = builder.build(); 179 this.errors.add(e); 180 return; 181 } 182 183 if (stopPositionsOfThisRoute.size() == 1) { 184 endStop.setStopPosition(stopPositionsOfThisRoute.get(0)); 185 } 186 187 // At this point, there is at least one stop_position for this 188 // endStop: 189 if (!containsAtLeastOneStopPositionAsFirstOrLastNode) { 190 List<Relation> primitives = new ArrayList<>(1); 191 primitives.add(relation); 192 List<OsmPrimitive> highlighted = new ArrayList<>(); 193 highlighted.addAll(stopPositionsOfThisRoute); 194 195 Builder builder = TestError.builder(this.test, Severity.WARNING, 196 PTAssistantValidatorTest.ERROR_CODE_SPLIT_WAY); 197 builder.message(tr("PT: First or last way needs to be split")); 198 builder.primitives(primitives); 199 builder.highlight(highlighted); 200 TestError e = builder.build(); 201 this.errors.add(e); 202 } 203 204 } else { 205 206 // if the stop_position is known: 207 int belongsToWay = this.belongsToAWayOfThisRoute(endStop.getStopPosition()); 208 209 if (belongsToWay == 1) { 210 211 List<Relation> primitives = new ArrayList<>(1); 212 primitives.add(relation); 213 List<OsmPrimitive> highlighted = new ArrayList<>(); 214 highlighted.add(endStop.getStopPosition()); 215 Builder builder = TestError.builder(this.test, Severity.WARNING, 216 PTAssistantValidatorTest.ERROR_CODE_SPLIT_WAY); 217 builder.message(tr("PT: First or last way needs to be split")); 218 builder.primitives(primitives); 219 builder.highlight(highlighted); 220 TestError e = builder.build(); 221 this.errors.add(e); 222 } 223 } 224 225 } 226 227 /** 228 * Checks if the given node belongs to the ways of this route. 229 * 230 * @param node 231 * Node to be checked 232 * @return 1 if belongs only as an inner node, 0 if belongs as a first or 233 * last node for at least one way, -1 if does not belong to any way. 234 */ 235 private int belongsToAWayOfThisRoute(Node node) { 236 237 boolean contains = false; 238 239 List<PTWay> ptways = manager.getPTWays(); 240 for (PTWay ptway : ptways) { 241 List<Way> ways = ptway.getWays(); 242 for (Way way : ways) { 243 if (way.containsNode(node)) { 244 245 if (way.firstNode().equals(node) || way.lastNode().equals(node)) { 246 return 0; 247 } 248 249 contains = true; 250 } 251 } 252 } 253 254 if (contains) { 255 return 1; 256 } 257 258 return -1; 259 } 260 261 public void performStopNotServedTest() { 262 for (PTStop stop : manager.getPTStops()) { 263 Way way = assigner.get(stop); 264 if (way == null) { 265 createStopError(stop); 266 } 267 } 268 } 269 270 /** 271 * Performs the stop-by-stop test by visiting each segment between two 272 * consecutive stops and checking if the ways between them are correct 273 */ 274 public void performStopByStopTest() { 275 276 if (manager.getPTStopCount() < 2) { 277 return; 278 } 279 280 List<OsmPrimitive> lastCreatedBuilderHighlighted = null; 281 282 // Check each route segment: 283 for (int i = 1; i < manager.getPTStopCount(); i++) { 284 285 PTStop startStop = manager.getPTStops().get(i - 1); 286 PTStop endStop = manager.getPTStops().get(i); 287 288 Way startWay = assigner.get(startStop); 289 Way endWay = assigner.get(endStop); 290 if (startWay == null || endWay == null || (startWay == endWay && startWay == manager.getLastWay())) { 291 continue; 292 } 293 294 List<PTWay> segmentWays = manager.getPTWaysBetween(startWay, endWay); 295 296 Node firstNode = findFirstNodeOfRouteSegmentInDirectionOfTravel(segmentWays.get(0)); 297 298 // get last added TestErrorBuilder: 299 300 if (firstNode == null) { 301 // check if this error has just been reported: 302 if (wrongSegmentBuilders.isEmpty() && lastCreatedBuilderHighlighted.size() == 1 303 && lastCreatedBuilderHighlighted.get(0) == startWay) { 304 // do nothing, this error has already been reported in 305 // the previous route segment 306 } else { 307 List<Relation> primitives = new ArrayList<>(1); 308 primitives.add(relation); 309 List<OsmPrimitive> highlighted = new ArrayList<>(); 310 highlighted.add(startWay); 311 Builder builder = TestError.builder(this.test, Severity.WARNING, 312 PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 313 builder.primitives(primitives); 314 builder.highlight(highlighted); 315 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 316 wrongSegmentBuilders.put(builder, routeSegment); 317 } 318 continue; 319 } 320 321 PTWay wronglySortedPtway = existingWaySortingIsWrong(segmentWays.get(0), firstNode, 322 segmentWays.get(segmentWays.size() - 1)); 323 if (wronglySortedPtway == null) { // i.e. if the sorting is correct: 324 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 325 addCorrectSegment(routeSegment); 326 } else { // i.e. if the sorting is wrong: 327 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 328 // TestError error = this.errors.get(this.errors.size() - 1); 329 // wrongSegments.put(error, routeSegment); 330 331 List<Relation> primitives = new ArrayList<>(1); 332 primitives.add(relation); 333 List<OsmPrimitive> highlighted = new ArrayList<>(); 334 highlighted.addAll(wronglySortedPtway.getWays()); 335 Builder builder = TestError.builder(this.test, Severity.WARNING, 336 PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 337 builder.primitives(primitives); 338 builder.highlight(highlighted); 339 lastCreatedBuilderHighlighted = highlighted; 340 wrongSegmentBuilders.put(builder, routeSegment); 341 } 342 } 343 } 344 345 /** 346 * Creates a TestError and adds it to the list of errors for a stop that is 347 * not served. 348 * 349 * @param stop 350 * stop 351 */ 352 private void createStopError(PTStop stop) { 353 List<Relation> primitives = new ArrayList<>(1); 354 primitives.add(relation); 355 List<OsmPrimitive> highlighted = new ArrayList<>(); 356 OsmPrimitive stopPrimitive = stop.getPlatform(); 357 if (stopPrimitive == null) { 358 stopPrimitive = stop.getStopPosition(); 359 } 360 highlighted.add(stopPrimitive); 361 Builder builder = TestError.builder(this.test, Severity.WARNING, 362 PTAssistantValidatorTest.ERROR_CODE_STOP_NOT_SERVED); 363 builder.message(tr("PT: Stop not served")); 364 builder.primitives(primitives); 365 builder.highlight(highlighted); 366 TestError e = builder.build(); 367 this.errors.add(e); 368 } 369 370 private Node findFirstNodeOfRouteSegmentInDirectionOfTravel(PTWay startWay) { 371 372 // 1) at first check if one of the first or last node of the first ptway 373 // is a deadend node: 374 Node[] startWayEndnodes = startWay.getEndNodes(); 375 if (isDeadendNode(startWayEndnodes[0])) { 376 return startWayEndnodes[0]; 377 } 378 if (isDeadendNode(startWayEndnodes[1])) { 379 return startWayEndnodes[1]; 380 } 381 382 // 2) failing that, check which node this startWay shares with the 383 // following way: 384 PTWay nextWay = manager.getNextPTWay(startWay); 385 if (nextWay == null) { 386 return null; 387 } 388 PTWay wayAfterNext = manager.getNextPTWay(nextWay); 389 Node[] nextWayEndnodes = nextWay.getEndNodes(); 390 if ((startWayEndnodes[0] == nextWayEndnodes[0] && startWayEndnodes[1] == nextWayEndnodes[1]) 391 || (startWayEndnodes[0] == nextWayEndnodes[1] && startWayEndnodes[1] == nextWayEndnodes[0])) { 392 // if this is a split roundabout: 393 Node[] wayAfterNextEndnodes = wayAfterNext.getEndNodes(); 394 if (startWayEndnodes[0] == wayAfterNextEndnodes[0] || startWayEndnodes[0] == wayAfterNextEndnodes[1]) { 395 return startWayEndnodes[0]; 396 } 397 if (startWayEndnodes[1] == wayAfterNextEndnodes[0] || startWayEndnodes[1] == wayAfterNextEndnodes[1]) { 398 return startWayEndnodes[1]; 399 } 400 } 401 402 if (startWayEndnodes[0] == nextWayEndnodes[0] || startWayEndnodes[0] == nextWayEndnodes[1]) { 403 return startWayEndnodes[1]; 404 } 405 if (startWayEndnodes[1] == nextWayEndnodes[0] || startWayEndnodes[1] == nextWayEndnodes[1]) { 406 return startWayEndnodes[0]; 407 } 408 409 return null; 410 411 } 412 413 private boolean isDeadendNode(Node node) { 414 int count = 0; 415 for (PTWay ptway : manager.getPTWays()) { 416 List<Way> ways = ptway.getWays(); 417 for (Way way : ways) { 418 if (way.firstNode() == node || way.lastNode() == node) { 419 count++; 420 } 421 } 422 } 423 return count == 1; 424 } 425 426 /** 427 * Finds the deadend node closest to the given node represented by its 428 * coordinates 429 * 430 * @param coord 431 * coordinates of the givenn node 432 * @param deadendNodes 433 * dead end nodes 434 * @return the closest deadend node 435 */ 436 @SuppressWarnings("unused") 437 private Node findClosestDeadendNode(LatLon coord, List<Node> deadendNodes) { 438 439 Node closestDeadendNode = null; 440 double minSqDistance = Double.MAX_VALUE; 441 for (Node deadendNode : deadendNodes) { 442 double distanceSq = coord.distanceSq(deadendNode.getCoor()); 443 if (distanceSq < minSqDistance) { 444 minSqDistance = distanceSq; 445 closestDeadendNode = deadendNode; 446 } 447 } 448 return closestDeadendNode; 449 450 } 451 452 /** 453 * Checks if the existing sorting of the given route segment is correct 454 * 455 * @param start 456 * PTWay assigned to the first stop of the segment 457 * @param startWayPreviousNodeInDirectionOfTravel 458 * Node if the start way which is furthest away from the rest of 459 * the route 460 * @param end 461 * PTWay assigned to the end stop of the segment 462 * @return null if the sorting is correct, or the wrongly sorted PTWay 463 * otherwise. 464 */ 465 private PTWay existingWaySortingIsWrong(PTWay start, Node startWayPreviousNodeInDirectionOfTravel, PTWay end) { 466 467 if (start == end) { 468 // if both PTStops are on the same PTWay 469 // return true; 470 return null; 471 } 472 473 PTWay current = start; 474 Node currentNode = startWayPreviousNodeInDirectionOfTravel; 475 476 while (!current.equals(end)) { 477 // "equals" is used here instead of "==" because when the same way 478 // is passed multiple times by the bus, the algorithm should stop no 479 // matter which of the geometrically equal PTWays it finds 480 481 PTWay nextPTWayAccortingToExistingSorting = manager.getNextPTWay(current); 482 483 // if current contains an unsplit roundabout: 484 if (current.containsUnsplitRoundabout()) { 485 currentNode = manager.getCommonNode(current, nextPTWayAccortingToExistingSorting); 486 if (currentNode == null) { 487 488 return current; 489 490 } 491 } else { 492 // if this is a regular way, not an unsplit roundabout 493 494 // find the next node in direction of travel (which is part of 495 // the PTWay start): 496 currentNode = getOppositeEndNode(current, currentNode); 497 498 List<PTWay> nextWaysInDirectionOfTravel = this.findNextPTWaysInDirectionOfTravel(current, currentNode); 499 500 if (!nextWaysInDirectionOfTravel.contains(nextPTWayAccortingToExistingSorting)) { 501 return current; 502 503 } 504 } 505 506 current = nextPTWayAccortingToExistingSorting; 507 508 } 509 510 return null; 511 } 512 513 /** 514 * Will return the same node if the way is an unsplit roundabout 515 * 516 * @param way 517 * way 518 * @param node 519 * node 520 * @return the same node if the way is an unsplit roundabout 521 */ 522 private Node getOppositeEndNode(Way way, Node node) { 523 524 if (node == way.firstNode()) { 525 return way.lastNode(); 526 } 527 528 if (node == way.lastNode()) { 529 return way.firstNode(); 530 } 531 532 return null; 533 } 534 535 /** 536 * Does not work correctly for unsplit roundabouts 537 * 538 * @param ptway 539 * way 540 * @param node 541 * node 542 * @return node 543 */ 544 private Node getOppositeEndNode(PTWay ptway, Node node) { 545 if (ptway.isWay()) { 546 return getOppositeEndNode(ptway.getWays().get(0), node); 547 } 548 549 Way firstWay = ptway.getWays().get(0); 550 Way lastWay = ptway.getWays().get(ptway.getWays().size() - 1); 551 Node oppositeNode = node; 552 if (firstWay.firstNode() == node || firstWay.lastNode() == node) { 553 for (int i = 0; i < ptway.getWays().size(); i++) { 554 oppositeNode = getOppositeEndNode(ptway.getWays().get(i), oppositeNode); 555 } 556 return oppositeNode; 557 } else if (lastWay.firstNode() == node || lastWay.lastNode() == node) { 558 for (int i = ptway.getWays().size() - 1; i >= 0; i--) { 559 oppositeNode = getOppositeEndNode(ptway.getWays().get(i), oppositeNode); 560 } 561 return oppositeNode; 562 } 563 564 return null; 565 566 } 567 568 /** 569 * Finds the next ways for the route stop-by-stop parsing procedure 570 * 571 * @param currentWay 572 * current way 573 * @param nextNodeInDirectionOfTravel 574 * next node in direction of travel 575 * @return the next ways for the route stop-by-stop parsing procedure 576 */ 577 private List<PTWay> findNextPTWaysInDirectionOfTravel(PTWay currentWay, Node nextNodeInDirectionOfTravel) { 578 579 List<PTWay> nextPtways = new ArrayList<>(); 580 581 List<PTWay> ptways = manager.getPTWays(); 582 583 for (PTWay ptway : ptways) { 584 585 if (ptway != currentWay) { 586 for (Way way : ptway.getWays()) { 587 if (way.containsNode(nextNodeInDirectionOfTravel)) { 588 nextPtways.add(ptway); 589 } 590 } 591 } 592 } 593 594 return nextPtways; 595 596 } 597 598 protected static boolean isFixable(TestError testError) { 599 600 /*- 601 * When is an error fixable (outdated)? 602 * - if there is a correct segment 603 * - if it can be fixed by sorting 604 * - if the route is compete even without some ways 605 * - if simple routing closes the gap 606 */ 607 608 if (testError.getCode() == PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP) { 609 return true; 610 } 611 612 return false; 613 614 } 615 616 @SuppressWarnings("unused") 617 private static boolean isFixableByUsingCorrectSegment(TestError testError) { 618 PTRouteSegment wrongSegment = wrongSegments.get(testError); 619 PTRouteSegment correctSegment = null; 620 for (PTRouteSegment segment : correctSegments) { 621 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 622 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 623 correctSegment = segment; 624 break; 625 } 626 } 627 return correctSegment != null; 628 } 629 630 @SuppressWarnings("unused") 631 private static boolean isFixableBySortingAndRemoval(TestError testError) { 632 PTRouteSegment wrongSegment = wrongSegments.get(testError); 633 List<List<PTWay>> fixVariants = wrongSegment.getFixVariants(); 634 if (!fixVariants.isEmpty()) { 635 return true; 636 } 637 return false; 638 } 639 640 /** 641 * Finds fixes using sorting and removal. 642 */ 643 protected void findFixes() { 644 645 for (Builder builder : wrongSegmentBuilders.keySet()) { 646 647 if (wrongSegmentBuilders.get(builder).getRelation() == this.relation) { 648 649 findFix(builder); 650 651 } 652 } 653 654 } 655 656 /** 657 * Modifies the error messages of the stop-by-stop test errors depending on how many fixes each of them has. 658 */ 659 protected static void modifyStopByStopErrorMessages() { 660 661 for (Entry<Builder, PTRouteSegment> entry : SegmentChecker.wrongSegmentBuilders.entrySet()) { 662 663 // change the error code based on the availability of fixes: 664 Builder builder = entry.getKey(); 665 PTRouteSegment wrongSegment = entry.getValue(); 666 List<PTRouteSegment> correctSegmentsForThisError = new ArrayList<>(); 667 for (PTRouteSegment segment : correctSegments) { 668 if (wrongSegment.getFirstWay().getId() == segment.getFirstWay().getId() 669 && wrongSegment.getLastWay().getId() == segment.getLastWay().getId()) { 670 correctSegmentsForThisError.add(segment); 671 } 672 } 673 674 int numberOfFixes = correctSegmentsForThisError.size(); 675 676 if (numberOfFixes == 0) { 677 numberOfFixes = wrongSegment.getFixVariants().size(); 678 } 679 if (numberOfFixes == 0) { 680 for (PTRouteSegment segment : correctSegments) { 681 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 682 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 683 correctSegmentsForThisError.add(segment); 684 } 685 } 686 numberOfFixes = correctSegmentsForThisError.size(); 687 } 688 689 // change the error message: 690 if (numberOfFixes == 0) { 691 builder.message(tr("PT: Problem in the route segment with no automatic fix")); 692 } else if (numberOfFixes == 1) { 693 builder.message(tr("PT: Problem in the route segment with one automatic fix")); 694 } else { 695 builder.message("PT: Problem in the route segment with several automatic fixes"); 696 } 697 698 } 699 700 } 701 702 /** 703 * This method assumes that the first and the second ways of the route 704 * segment are correctly connected. If they are not, the error will be 705 * marked as not fixable. 706 * 707 * @param testError 708 * test error 709 */ 710 private void findFix(Builder builder) { 711 712 PTRouteSegment wrongSegment = wrongSegmentBuilders.get(builder); 713 PTWay startPTWay = wrongSegment.getFirstPTWay(); 714 PTWay endPTWay = wrongSegment.getLastPTWay(); 715 716 Node previousNode = findFirstNodeOfRouteSegmentInDirectionOfTravel(startPTWay); 717 if (previousNode == null) { 718 return; 719 } 720 721 List<List<PTWay>> initialFixes = new ArrayList<>(); 722 List<PTWay> initialFix = new ArrayList<>(); 723 initialFix.add(startPTWay); 724 initialFixes.add(initialFix); 725 726 List<List<PTWay>> allFixes = findWaysForFix(initialFixes, initialFix, previousNode, endPTWay); 727 for (List<PTWay> fix : allFixes) { 728 if (!fix.isEmpty() && fix.get(fix.size() - 1).equals(endPTWay)) { 729 wrongSegment.addFixVariant(fix); 730 } 731 } 732 733 } 734 735 /** 736 * Recursive method to parse the route segment 737 * 738 * @param allFixes 739 * all fixes 740 * @param currentFix 741 * current fix 742 * @param previousNode 743 * previous node 744 * @param endWay 745 * end way 746 * @return list of list of ways 747 */ 748 private List<List<PTWay>> findWaysForFix(List<List<PTWay>> allFixes, List<PTWay> currentFix, Node previousNode, 749 PTWay endWay) { 750 751 PTWay currentWay = currentFix.get(currentFix.size() - 1); 752 Node nextNode = getOppositeEndNode(currentWay, previousNode); 753 754 List<PTWay> nextWays = this.findNextPTWaysInDirectionOfTravel(currentWay, nextNode); 755 756 if (nextWays.size() > 1) { 757 for (int i = 1; i < nextWays.size(); i++) { 758 List<PTWay> newFix = new ArrayList<>(); 759 newFix.addAll(currentFix); 760 newFix.add(nextWays.get(i)); 761 allFixes.add(newFix); 762 if (!nextWays.get(i).equals(endWay) && !currentFix.contains(nextWays.get(i))) { 763 allFixes = findWaysForFix(allFixes, newFix, nextNode, endWay); 764 } 765 } 766 } 767 768 if (!nextWays.isEmpty()) { 769 boolean contains = currentFix.contains(nextWays.get(0)); 770 currentFix.add(nextWays.get(0)); 771 if (!nextWays.get(0).equals(endWay) && !contains) { 772 allFixes = findWaysForFix(allFixes, currentFix, nextNode, endWay); 773 } 774 } 775 776 return allFixes; 777 } 778 779 /** 780 * Fixes the error by first searching in the list of correct segments and 781 * then trying to sort and remove existing route relation members 782 * 783 * @param testError 784 * test error 785 * @return fix command 786 */ 787 protected static Command fixError(TestError testError) { 788 789 // if fix options for another route are displayed in the pt_assistant 790 // layer, clear them: 791 ((PTAssistantValidatorTest) testError.getTester()).clearFixVariants(); 792 793 PTRouteSegment wrongSegment = wrongSegments.get(testError); 794 795 // 1) try to fix by using the correct segment: 796 List<PTRouteSegment> correctSegmentsForThisError = new ArrayList<>(); 797 for (PTRouteSegment segment : correctSegments) { 798 if (wrongSegment.getFirstWay().getId() == segment.getFirstWay().getId() 799 && wrongSegment.getLastWay().getId() == segment.getLastWay().getId()) { 800 correctSegmentsForThisError.add(segment); 801 } 802 } 803 804 // if no correct segment found, apply less strict criteria to look for 805 // one: 806 if (correctSegmentsForThisError.isEmpty() && wrongSegment.getFixVariants().isEmpty()) { 807 for (PTRouteSegment segment : correctSegments) { 808 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 809 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 810 correctSegmentsForThisError.add(segment); 811 } 812 } 813 if (!correctSegmentsForThisError.isEmpty()) { 814 // display the notification: 815 if (SwingUtilities.isEventDispatchThread()) { 816 Notification notification = new Notification( 817 tr("Warning: the diplayed fix variants are based on less strict criteria")); 818 notification.show(); 819 } else { 820 SwingUtilities.invokeLater(new Runnable() { 821 @Override 822 public void run() { 823 Notification notification = new Notification( 824 tr("Warning: the diplayed fix variants are based on less strict criteria")); 825 notification.show(); 826 } 827 }); 828 } 829 } 830 } 831 832 if (!correctSegmentsForThisError.isEmpty()) { 833 834 if (correctSegmentsForThisError.size() > 1) { 835 List<List<PTWay>> fixVariants = new ArrayList<>(); 836 for (PTRouteSegment segment : correctSegmentsForThisError) { 837 fixVariants.add(segment.getPTWays()); 838 } 839 displayFixVariants(fixVariants, testError); 840 return null; 841 } 842 843 PTAssistantPlugin.setLastFix(correctSegmentsForThisError.get(0)); 844 return carryOutSingleFix(testError, correctSegmentsForThisError.get(0).getPTWays()); 845 846 } else if (!wrongSegment.getFixVariants().isEmpty()) { 847 // 2) try to fix using the sorting and removal of existing ways 848 // of the wrong segment: 849 if (wrongSegment.getFixVariants().size() > 1) { 850 displayFixVariants(wrongSegment.getFixVariants(), testError); 851 return null; 852 } 853 854 PTAssistantPlugin.setLastFix(new PTRouteSegment(wrongSegment.getFirstStop(), wrongSegment.getLastStop(), 855 wrongSegment.getFixVariants().get(0), (Relation) testError.getPrimitives().iterator().next())); 856 return carryOutSingleFix(testError, wrongSegment.getFixVariants().get(0)); 857 } 858 859 // if there is no fix: 860 return fixErrorByZooming(testError); 861 862 } 863 864 /** 865 * This is largely a copy of the displayFixVariants() method, adapted for 866 * use with the key listener 867 * 868 * @param fixVariants 869 * fix variants 870 * @param testError 871 * test error 872 */ 873 private static void displayFixVariants(List<List<PTWay>> fixVariants, TestError testError) { 874 // find the letters of the fix variants: 875 char alphabet = 'A'; 876 final List<Character> allowedCharacters = new ArrayList<>(); 877 for (int i = 0; i < fixVariants.size(); i++) { 878 allowedCharacters.add(alphabet); 879 alphabet++; 880 } 881 882 // zoom to problem: 883 final Collection<OsmPrimitive> waysToZoom = new ArrayList<>(); 884 for (Object highlightedPrimitive : testError.getHighlighted()) { 885 waysToZoom.add((OsmPrimitive) highlightedPrimitive); 886 } 887 if (SwingUtilities.isEventDispatchThread()) { 888 AutoScaleAction.zoomTo(waysToZoom); 889 } else { 890 SwingUtilities.invokeLater(new Runnable() { 891 @Override 892 public void run() { 893 AutoScaleAction.zoomTo(waysToZoom); 894 } 895 }); 896 } 897 898 // display the fix variants: 899 final PTAssistantValidatorTest test = (PTAssistantValidatorTest) testError.getTester(); 900 test.addFixVariants(fixVariants); 901 PTAssistantLayer.getLayer().repaint((Relation) testError.getPrimitives().iterator().next()); 902 903 // prepare the variables for the key listener: 904 final TestError testErrorParameter = testError; 905 906 // // add the key listener: 907 Main.map.mapView.requestFocus(); 908 Main.map.mapView.addKeyListener(new KeyListener() { 909 910 public void keyTyped(KeyEvent e) { 911 // TODO Auto-generated method stub 912 } 913 914 public void keyPressed(KeyEvent e) { 915 Character typedKey = e.getKeyChar(); 916 Character typedKeyUpperCase = typedKey.toString().toUpperCase().toCharArray()[0]; 917 if (allowedCharacters.contains(typedKeyUpperCase)) { 918 Main.map.mapView.removeKeyListener(this); 919 List<PTWay> selectedFix = test.getFixVariant(typedKeyUpperCase); 920 test.clearFixVariants(); 921 carryOutSelectedFix(testErrorParameter, selectedFix); 922 } 923 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 924 Main.map.mapView.removeKeyListener(this); 925 test.clearFixVariants(); 926 } 927 } 928 929 public void keyReleased(KeyEvent e) { 930 // TODO Auto-generated method stub 931 } 932 }); 933 934 // display the notification: 935 if (SwingUtilities.isEventDispatchThread()) { 936 Notification notification = new Notification( 937 tr("Type letter to select the fix variant or press Escape for no fix")); 938 notification.show(); 939 } else { 940 SwingUtilities.invokeLater(new Runnable() { 941 @Override 942 public void run() { 943 Notification notification = new Notification( 944 tr("Type letter to select the fix variant or press Escape for no fix")); 945 notification.show(); 946 } 947 }); 948 } 949 } 950 951 /** 952 * Carries out the fix (i.e. modifies the route) after the user has picked 953 * the fix from several fix variants. 954 * 955 * @param testError 956 * test error to be fixed 957 * @param fix 958 * the fix variant to be adopted 959 */ 960 private static void carryOutSelectedFix(TestError testError, List<PTWay> fix) { 961 // modify the route: 962 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 963 Relation modifiedRelation = new Relation(originalRelation); 964 modifiedRelation.setMembers(getModifiedRelationMembers(testError, fix)); 965 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 966 Main.main.undoRedo.addNoRedraw(changeCommand); 967 Main.main.undoRedo.afterAdd(); 968 PTRouteSegment wrongSegment = wrongSegments.get(testError); 969 wrongSegments.remove(testError); 970 wrongSegment.setPTWays(fix); 971 addCorrectSegment(wrongSegment); 972 PTAssistantPlugin.setLastFixNoGui(wrongSegment); 973 974 // get ways for the fix: 975 List<Way> primitives = new ArrayList<>(); 976 for (PTWay ptway : fix) { 977 primitives.addAll(ptway.getWays()); 978 } 979 980 // get layer: 981 OsmDataLayer layer = null; 982 List<OsmDataLayer> listOfLayers = Main.getLayerManager().getLayersOfType(OsmDataLayer.class); 983 for (OsmDataLayer osmDataLayer : listOfLayers) { 984 if (osmDataLayer.data == originalRelation.getDataSet()) { 985 layer = osmDataLayer; 986 break; 987 } 988 } 989 990 // create editor: 991 GenericRelationEditor editor = (GenericRelationEditor) RelationEditor.getEditor(layer, originalRelation, 992 originalRelation.getMembersFor(primitives)); 993 994 // open editor: 995 editor.setVisible(true); 996 997 } 998 999 /** 1000 * Carries out the fix (i.e. modifies the route) when there is only one fix 1001 * variant. 1002 * 1003 * @param testError 1004 * test error 1005 * @param fix 1006 * fix 1007 */ 1008 private static Command carryOutSingleFix(TestError testError, List<PTWay> fix) { 1009 // Zoom to the problematic ways: 1010 final Collection<OsmPrimitive> waysToZoom = new ArrayList<>(); 1011 for (Object highlightedPrimitive : testError.getHighlighted()) { 1012 waysToZoom.add((OsmPrimitive) highlightedPrimitive); 1013 } 1014 if (SwingUtilities.isEventDispatchThread()) { 1015 AutoScaleAction.zoomTo(waysToZoom); 1016 } else { 1017 SwingUtilities.invokeLater(new Runnable() { 1018 @Override 1019 public void run() { 1020 AutoScaleAction.zoomTo(waysToZoom); 1021 } 1022 }); 1023 } 1024 1025 // wait: 1026 synchronized (SegmentChecker.class) { 1027 try { 1028 SegmentChecker.class.wait(1500); 1029 } catch (InterruptedException e) { 1030 // TODO Auto-generated catch block 1031 e.printStackTrace(); 1032 } 1033 } 1034 1035 // modify the route: 1036 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 1037 Relation modifiedRelation = new Relation(originalRelation); 1038 modifiedRelation.setMembers(getModifiedRelationMembers(testError, fix)); 1039 wrongSegments.remove(testError); 1040 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 1041 return changeCommand; 1042 } 1043 1044 /** 1045 * Returns a list of the modified relation members. This list can be used by 1046 * the calling method (relation.setMemers()) to modify the modify the route 1047 * relation. The route relation is not modified by this method. The lists of 1048 * wrong and correct segments are not updated. 1049 * 1050 * @param testError 1051 * test error to be fixed 1052 * @param fix 1053 * the fix variant to be adopted 1054 * @return List of modified relation members to be applied to the route 1055 * relation 1056 */ 1057 private static List<RelationMember> getModifiedRelationMembers(TestError testError, List<PTWay> fix) { 1058 PTRouteSegment wrongSegment = wrongSegments.get(testError); 1059 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 1060 1061 // copy stops first: 1062 List<RelationMember> modifiedRelationMembers = listStopMembers(originalRelation); 1063 1064 // copy PTWays last: 1065 List<RelationMember> waysOfOriginalRelation = listNotStopMembers(originalRelation); 1066 for (int i = 0; i < waysOfOriginalRelation.size(); i++) { 1067 if (waysOfOriginalRelation.get(i).getWay() == wrongSegment.getPTWays().get(0).getWays().get(0)) { 1068 modifiedRelationMembers.addAll(fix); 1069 i = i + wrongSegment.getPTWays().size() - 1; 1070 } else { 1071 modifiedRelationMembers.add(waysOfOriginalRelation.get(i)); 1072 } 1073 } 1074 1075 return modifiedRelationMembers; 1076 } 1077 1078 public static void carryOutRepeatLastFix(PTRouteSegment segment) { 1079 1080 List<TestError> wrongSegmentsToRemove = new ArrayList<>(); 1081 1082 // find all wrong ways that have the same segment: 1083 for (TestError testError : wrongSegments.keySet()) { 1084 PTRouteSegment wrongSegment = wrongSegments.get(testError); 1085 if (wrongSegment.getFirstWay() == segment.getFirstWay() 1086 && wrongSegment.getLastWay() == segment.getLastWay()) { 1087 // modify the route: 1088 Relation originalRelation = wrongSegment.getRelation(); 1089 Relation modifiedRelation = new Relation(originalRelation); 1090 modifiedRelation.setMembers(getModifiedRelationMembers(testError, segment.getPTWays())); 1091 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 1092 Main.main.undoRedo.addNoRedraw(changeCommand); 1093 Main.main.undoRedo.afterAdd(); 1094 wrongSegmentsToRemove.add(testError); 1095 } 1096 } 1097 1098 // update the errors displayed in the validator dialog: 1099 List<TestError> modifiedValidatorTestErrors = new ArrayList<>(); 1100 for (TestError validatorTestError : Main.map.validatorDialog.tree.getErrors()) { 1101 if (!wrongSegmentsToRemove.contains(validatorTestError)) { 1102 modifiedValidatorTestErrors.add(validatorTestError); 1103 } 1104 } 1105 Main.map.validatorDialog.tree.setErrors(modifiedValidatorTestErrors); 1106 1107 // update wrong segments: 1108 for (TestError testError : wrongSegmentsToRemove) { 1109 wrongSegments.remove(testError); 1110 } 1111 1112 } 1113 1114 /** 1115 * Resets the static list variables (used for unit tests and in Test.startTest() method) 1116 */ 1117 protected static void reset() { 1118 correctSegments.clear(); 1119 wrongSegments.clear(); 1120 wrongSegmentBuilders.clear(); 1121 } 1082 1122 1083 1123 }
Note:
See TracChangeset
for help on using the changeset viewer.