Changeset 33417 in osm for applications/editors
- Timestamp:
- 2017-06-28T18:00:52+02:00 (7 years ago)
- Location:
- applications/editors/josm/plugins/pt_assistant
- Files:
-
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java
r33399 r33417 105 105 lastFix = segment; 106 106 SwingUtilities.invokeLater(() -> 107 107 repeatLastFixMenu.setEnabled(segment != null)); 108 108 } 109 109 … … 117 117 } 118 118 119 120 121 119 public static List<Relation> getHighlightedRelations() { 120 return new ArrayList<>(highlightedRelations); 121 } 122 122 123 124 125 126 127 128 129 123 public static void addHighlightedRelation(Relation highlightedRelation) { 124 highlightedRelations.add(highlightedRelation); 125 if(!editHighlightedRelationsMenu.isEnabled()) { 126 SwingUtilities.invokeLater(() -> 127 editHighlightedRelationsMenu.setEnabled(true)); 128 } 129 } 130 130 131 132 133 134 135 131 public static void clearHighlightedRelations() { 132 highlightedRelations.clear(); 133 SwingUtilities.invokeLater(() -> 134 editHighlightedRelationsMenu.setEnabled(false)); 135 } 136 136 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/AddStopPositionAction.java
r33409 r33417 33 33 public class AddStopPositionAction extends MapMode { 34 34 35 35 private static final String mapModeName = "Add stop position"; 36 36 37 38 37 private transient Set<OsmPrimitive> newHighlights = new HashSet<>(); 38 private transient Set<OsmPrimitive> oldHighlights = new HashSet<>(); 39 39 40 40 private final Cursor cursorJoinNode; … … 44 44 * Creates a new AddStopPositionAction 45 45 */ 46 47 48 46 public AddStopPositionAction() { 47 super(tr(mapModeName), "bus", tr(mapModeName), 48 Shortcut.registerShortcut("mapmode:stop_position", 49 49 tr("Mode: {0}", tr(mapModeName)), 50 50 KeyEvent.VK_K, Shortcut.CTRL_SHIFT), 51 51 getCursor()); 52 52 53 53 cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode"); 54 54 cursorJoinWay = ImageProvider.getCursor("crosshair", "joinway"); 55 55 } 56 56 57 57 private static Cursor getCursor() { 58 59 60 61 58 Cursor cursor = ImageProvider.getCursor("crosshair", "bus"); 59 if(cursor == null) 60 cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); 61 return cursor; 62 62 } 63 63 … … 79 79 public void mouseMoved(MouseEvent e) { 80 80 81 82 83 84 81 //while the mouse is moving, surroundings are checked 82 //if anything is found, it will be highlighted. 83 //priority is given to nodes 84 Cursor newCurs = getCursor(); 85 85 86 87 88 89 90 91 92 86 Node n = Main.map.mapView.getNearestNode(e.getPoint(), OsmPrimitive::isUsable); 87 if(n != null) { 88 newHighlights.add(n); 89 newCurs = cursorJoinNode; 90 } else { 91 List<WaySegment> wss = 92 Main.map.mapView.getNearestWaySegments(e.getPoint(), OsmPrimitive::isSelectable); 93 93 94 95 96 97 98 99 100 94 if(!wss.isEmpty()) { 95 for(WaySegment ws : wss) { 96 newHighlights.add(ws.way); 97 } 98 newCurs = cursorJoinWay; 99 } 100 } 101 101 102 103 102 Main.map.mapView.setCursor(newCurs); 103 updateHighlights(); 104 104 } 105 105 … … 107 107 public void mouseClicked(MouseEvent e) { 108 108 109 110 109 Boolean newNode = false; 110 Node newStopPos; 111 111 112 113 112 //check if the user as selected an existing node, or a new one 113 Node n = Main.map.mapView.getNearestNode(e.getPoint(), OsmPrimitive::isUsable); 114 114 if (n == null) { 115 116 115 newNode = true; 116 newStopPos = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY())); 117 117 } else { 118 118 newStopPos = new Node(n); 119 119 clearNodeTags(newStopPos); 120 120 } 121 121 122 122 //add the tags of the stop position 123 124 123 newStopPos.put("bus", "yes"); 124 newStopPos.put("public_transport", "stop_position"); 125 125 126 127 128 129 130 126 if(newNode) { 127 Main.main.undoRedo.add(new AddCommand(newStopPos)); 128 } else { 129 Main.main.undoRedo.add(new ChangeCommand(n, newStopPos)); 130 } 131 131 132 133 132 DataSet ds = Main.getLayerManager().getEditLayer().data; 133 ds.setSelected(newStopPos); 134 134 135 136 137 138 139 135 //join the node to the way only if the node is new 136 if(newNode) { 137 JoinNodeWayAction joinNodeWayAction = JoinNodeWayAction.createJoinNodeToWayAction(); 138 joinNodeWayAction.actionPerformed(null); 139 } 140 140 141 141 // split the way in any case … … 145 145 146 146 private void clearNodeTags(Node newStopPos) { 147 148 149 147 for(String key : newStopPos.keySet()) { 148 newStopPos.put(key, null); 149 } 150 150 151 151 } 152 152 153 153 //turn off what has been highlighted on last mouse move and highlight what has to be highlighted now 154 154 private void updateHighlights() 155 155 { 156 157 158 156 if(oldHighlights.isEmpty() && newHighlights.isEmpty()) { 157 return; 158 } 159 159 160 161 162 160 for(OsmPrimitive osm : oldHighlights) { 161 osm.setHighlighted(false); 162 } 163 163 164 165 166 164 for(OsmPrimitive osm : newHighlights) { 165 osm.setHighlighted(true); 166 } 167 167 168 168 Main.getLayerManager().getEditLayer().invalidate(); 169 169 170 171 172 170 oldHighlights.clear(); 171 oldHighlights.addAll(newHighlights); 172 newHighlights.clear(); 173 173 } 174 174 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/EdgeSelectionAction.java
r33409 r33417 30 30 public class EdgeSelectionAction extends MapMode { 31 31 32 33 32 private static final String mapModeName = "Edge Selection"; 33 private static final long serialVersionUID = 2414977774504904238L; 34 34 35 35 private transient Set<Way> highlighted; 36 36 37 38 37 private Cursor selectionCursor; 38 private Cursor waySelectCursor; 39 39 40 41 42 40 public EdgeSelectionAction() { 41 super(tr(mapModeName), "edgeSelection", tr(mapModeName), 42 Shortcut.registerShortcut("mapmode:edge_selection", 43 43 tr("Mode: {0}", tr(mapModeName)), 44 44 KeyEvent.VK_K, Shortcut.CTRL), 45 46 45 ImageProvider.getCursor("normal", "selection")); 46 highlighted = new HashSet<>(); 47 47 48 49 50 48 selectionCursor = ImageProvider.getCursor("normal", "selection"); 49 waySelectCursor = ImageProvider.getCursor("normal", "select_way"); 50 } 51 51 52 52 /* … … 55 55 */ 56 56 private List<Way> getEdgeFromWay(Way initial, String modeOfTravel) { 57 58 59 57 List<Way> edge = new ArrayList<>(); 58 if(!isWaySuitableForMode(initial, modeOfTravel)) 59 return edge; 60 60 61 62 63 64 65 66 67 68 69 61 Way curr = initial; 62 while(true) { 63 List<Way> options = curr.firstNode(true).getParentWays(); 64 options.remove(curr); 65 curr = chooseBestWay(options, modeOfTravel); 66 if(curr == null || edge.contains(curr)) 67 break; 68 edge.add(curr); 69 } 70 70 71 72 73 74 75 76 77 78 79 71 curr = initial; 72 while(true) { 73 List<Way> options = curr.lastNode(true).getParentWays(); 74 options.remove(curr); 75 curr = chooseBestWay(options, modeOfTravel); 76 if(curr == null || edge.contains(curr)) 77 break; 78 edge.add(curr); 79 } 80 80 81 82 81 edge.add(initial); 82 return edge; 83 83 } 84 84 85 85 private Boolean isWaySuitableForMode(Way toCheck, String modeOfTravel) { 86 87 86 if("bus".equals(modeOfTravel)) 87 return RouteUtils.isWaySuitableForBuses(toCheck); 88 88 89 89 return RouteUtils.isWaySuitableForPublicTransport(toCheck); 90 90 } 91 91 … … 94 94 */ 95 95 private Way chooseBestWay(List<Way> ways, String modeOfTravel) { 96 97 98 99 100 96 ways.removeIf(w -> !isWaySuitableForMode(w, modeOfTravel)); 97 if(ways.isEmpty()) 98 return null; 99 if(ways.size() == 1) 100 return ways.get(0); 101 101 102 102 Way theChoosenOne = null; 103 103 104 105 104 if("bus".equals(modeOfTravel)) 105 { 106 106 107 108 109 107 } 108 if("tram".equals(modeOfTravel)) 109 { 110 110 111 111 } 112 112 113 113 return theChoosenOne; 114 114 } 115 115 116 116 private String getModeOfTravel() { 117 118 119 120 117 //find a way to get the currently opened relation editor and get the 118 //from there the current type of route 119 return "bus"; 120 } 121 121 122 122 @Override 123 123 public void mouseClicked(MouseEvent e) { 124 124 125 126 127 128 129 130 131 125 DataSet ds = Main.getLayerManager().getEditLayer().data; 126 Way initial = Main.map.mapView.getNearestWay(e.getPoint(), OsmPrimitive::isUsable); 127 if(initial != null){ 128 ds.setSelected(getEdgeFromWay(initial, getModeOfTravel())); 129 } 130 else 131 ds.clearSelection(); 132 132 } 133 133 134 134 @Override 135 135 public void mouseMoved(MouseEvent e) { 136 136 super.mouseMoved(e); 137 137 138 139 140 138 for(Way way : highlighted) 139 way.setHighlighted(false); 140 highlighted.clear(); 141 141 142 143 144 145 146 147 148 149 142 Way initial = Main.map.mapView.getNearestWay(e.getPoint(), OsmPrimitive::isUsable); 143 if(initial == null) { 144 Main.map.mapView.setCursor(selectionCursor); 145 } 146 else { 147 Main.map.mapView.setCursor(waySelectCursor); 148 highlighted.addAll(getEdgeFromWay(initial, getModeOfTravel())); 149 } 150 150 151 152 151 for(Way way : highlighted) 152 way.setHighlighted(true); 153 153 } 154 154 -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/EditHighlightedRelationsAction.java
r33409 r33417 33 33 super(tr(actionName), new ImageProvider("dialogs", "edit"), tr(actionName), 34 34 Shortcut.registerShortcut("Edit Highlighted Relation", tr(actionName), 35 35 KeyEvent.VK_K, Shortcut.ALT), 36 36 false, "editHighlightedRelations", false); 37 37 } … … 39 39 @Override 40 40 public void actionPerformed(ActionEvent e) { 41 42 43 41 for(Relation relation : PTAssistantPlugin.getHighlightedRelations()) { 42 RelationEditor editor = RelationEditor.getEditor( 43 Main.getLayerManager().getEditLayer(), relation, null); 44 44 editor.setVisible(true); 45 45 } 46 46 } 47 47 -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/SplitRoundaboutAction.java
r33416 r33417 111 111 List<Node> splitNodes = getSplitNodes(roundabout); 112 112 SplitWayResult result = SplitWayAction.split(getLayerManager().getEditLayer(), 113 113 roundabout, splitNodes, Collections.emptyList()); 114 114 Main.main.undoRedo.add(result.getCommand()); 115 115 Collection<Way> splitWays = result.getNewWays(); -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/data/PTRouteSegment.java
r33387 r33417 187 187 public boolean equalsRouteSegment(PTRouteSegment other) { 188 188 189 // 190 // 189 // if(!firstStop.equalsStop(firstStop) || !lastStop.equalsStop(other.lastStop)) 190 // return false; 191 191 192 192 List<Way> thisWays = new ArrayList<>(); -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/gui/PTAssistantLayer.java
r33414 r33417 128 128 public void setPrimitives(List<OsmPrimitive> primitives) 129 129 { 130 131 130 this.primitives.clear(); 131 this.primitives.addAll(primitives); 132 132 } 133 133 … … 267 267 268 268 if(event.getRemovedLayer() == this) { 269 270 269 PTAssistantLayerManager.PTLM.resetLayer(); 270 PTAssistantPlugin.clearHighlightedRelations(); 271 271 } 272 272 } … … 274 274 @Override 275 275 public synchronized void destroy() { 276 276 KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(this); 277 277 Main.getLayerManager().removeLayerChangeListener(this); 278 278 super.destroy(); 279 279 } 280 280 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/gui/PTAssistantLayerManager.java
r33414 r33417 14 14 public class PTAssistantLayerManager implements SelectionChangedListener { 15 15 16 17 16 public final static PTAssistantLayerManager PTLM = new PTAssistantLayerManager(); 17 private PTAssistantLayer layer; 18 18 19 19 public PTAssistantLayer getLayer() { … … 25 25 26 26 public void resetLayer() { 27 27 layer = null; 28 28 } 29 29 … … 38 38 for (OsmPrimitive primitive : newSelection) { 39 39 if (primitive.getType().equals(OsmPrimitiveType.RELATION) 40 41 40 && RouteUtils.isVersionTwoPTRoute((Relation) primitive)) { 41 routes.add(primitive); 42 42 } 43 43 } 44 44 45 45 if (!routes.isEmpty()) { 46 47 48 49 46 getLayer().setPrimitives(routes); 47 PTAssistantPlugin.clearHighlightedRelations(); 48 for(OsmPrimitive primitive : routes) 49 PTAssistantPlugin.addHighlightedRelation((Relation) primitive); 50 50 } 51 51 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/gui/PTAssistantPaintVisitor.java
r33414 r33417 286 286 @Override 287 287 protected void drawNode(Node n, Color color) { 288 289 return;290 288 if (mv == null || g == null) { 289 ; 290 } 291 291 Point p = mv.getPoint(n); 292 293 294 292 if (p == null) { 293 return; 294 } 295 295 g.setColor(color); 296 296 g.drawOval(p.x - 5, p.y - 5, 10, 10); -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/utils/RouteUtils.java
r33414 r33417 19 19 public final class RouteUtils { 20 20 21 21 private final static String ptVersionTag = "public_transport:version"; 22 22 private RouteUtils() { 23 23 // private constructor for util classes -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/NodeChecker.java
r33082 r33417 28 28 public class NodeChecker extends Checker { 29 29 30 31 30 protected NodeChecker(Node node, Test test) { 31 super(node, test); 32 32 33 33 } 34 34 35 36 37 38 35 /** 36 * Checks if the given stop_position node belongs to any way 37 */ 38 protected void performSolitaryStopPositionTest() { 39 39 40 40 List<OsmPrimitive> referrers = node.getReferrers(); 41 41 42 43 44 45 46 47 42 for (OsmPrimitive referrer : referrers) { 43 if (referrer.getType().equals(OsmPrimitiveType.WAY)) { 44 Way referrerWay = (Way) referrer; 45 if (RouteUtils.isWaySuitableForPublicTransport(referrerWay)) { 46 return; 47 } 48 48 49 50 49 } 50 } 51 51 52 53 54 55 56 57 58 52 List<OsmPrimitive> primitives = new ArrayList<>(1); 53 primitives.add(node); 54 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_SOLITARY_STOP_POSITION); 55 builder.message(tr("PT: Stop_position is not part of a way")); 56 builder.primitives(primitives); 57 TestError e = builder.build(); 58 errors.add(e); 59 59 60 60 } 61 61 62 63 64 65 62 /** 63 * Checks if the given platform node belongs to any way 64 */ 65 protected void performPlatformPartOfWayTest() { 66 66 67 67 List<OsmPrimitive> referrers = node.getReferrers(); 68 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 69 for (OsmPrimitive referrer : referrers) { 70 List<Node> primitives = new ArrayList<>(1); 71 primitives.add(node); 72 if (referrer.getType().equals(OsmPrimitiveType.WAY)) { 73 Way referringWay = (Way) referrer; 74 if (RouteUtils.isWaySuitableForPublicTransport(referringWay)) { 75 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_PLATFORM_PART_OF_HIGHWAY); 76 builder.message(tr("PT: Platform should not be part of a way")); 77 builder.primitives(primitives); 78 TestError e = builder.build(); 79 errors.add(e); 80 return; 81 } 82 } 83 } 84 } 85 85 86 87 88 * 89 90 91 86 /** 87 * Checks if the given stop_position node belongs to any stop_area relation 88 * 89 * @author xamanu 90 */ 91 protected void performNodePartOfStopAreaTest() { 92 92 93 93 if (!StopUtils.verifyIfMemberOfStopArea(node)) { 94 94 95 96 97 98 99 100 101 102 103 95 List<OsmPrimitive> primitives = new ArrayList<>(1); 96 primitives.add(node); 97 Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_NOT_PART_OF_STOP_AREA); 98 builder.message(tr("PT: Stop position or platform is not part of a stop area relation")); 99 builder.primitives(primitives); 100 TestError e = builder.build(); 101 errors.add(e); 102 } 103 } 104 104 105 106 107 108 109 110 111 112 113 105 /** 106 * Fixes errors: solitary stop position and platform which is part of a way. 107 * Asks the user first. 108 * 109 * @param testError 110 * test error 111 * @return fix command 112 */ 113 protected static Command fixError(TestError testError) { 114 114 115 116 117 118 115 if (testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_SOLITARY_STOP_POSITION 116 && testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_PLATFORM_PART_OF_HIGHWAY) { 117 return null; 118 } 119 119 120 120 Node problematicNode = (Node) testError.getPrimitives().iterator().next(); 121 121 122 123 124 122 final int[] userSelection = { JOptionPane.YES_OPTION }; 123 final TestError errorParameter = testError; 124 if (SwingUtilities.isEventDispatchThread()) { 125 125 126 126 userSelection[0] = showFixNodeTagDialog(errorParameter); 127 127 128 128 } else { 129 129 130 131 132 133 134 135 136 137 138 139 140 141 130 try { 131 SwingUtilities.invokeAndWait(new Runnable() { 132 @Override 133 public void run() { 134 userSelection[0] = showFixNodeTagDialog(errorParameter); 135 } 136 }); 137 } catch (InvocationTargetException | InterruptedException e) { 138 e.printStackTrace(); 139 return null; 140 } 141 } 142 142 143 143 if (userSelection[0] == JOptionPane.YES_OPTION) { 144 144 145 146 147 148 149 150 151 152 153 154 155 145 Node modifiedNode = new Node(problematicNode); 146 if (testError.getCode() == PTAssistantValidatorTest.ERROR_CODE_SOLITARY_STOP_POSITION) { 147 modifiedNode.put("public_transport", "platform"); 148 ChangeCommand command = new ChangeCommand(problematicNode, modifiedNode); 149 return command; 150 } else { 151 modifiedNode.put("public_transport", "stop_position"); 152 ChangeCommand command = new ChangeCommand(problematicNode, modifiedNode); 153 return command; 154 } 155 } 156 156 157 157 return null; 158 158 159 159 } 160 160 161 162 163 164 165 166 167 161 private static int showFixNodeTagDialog(TestError e) { 162 Node problematicNode = (Node) e.getPrimitives().iterator().next(); 163 // Main.map.mapView.zoomTo(problematicNode.getCoor()); 164 // zoom to problem: 165 Collection<OsmPrimitive> primitives = new ArrayList<>(1); 166 primitives.add(problematicNode); 167 AutoScaleAction.zoomTo(primitives); 168 168 169 170 171 172 173 174 175 176 177 178 169 String[] options = { tr("Yes"), tr("No") }; 170 String message; 171 if (e.getCode() == PTAssistantValidatorTest.ERROR_CODE_SOLITARY_STOP_POSITION) { 172 message = "Do you want to change the tag public_transport=stop_position to public_transport=platform?"; 173 } else { 174 message = "Do you want to change the tag public_transport=platform to public_transport=stop_position?"; 175 } 176 return JOptionPane.showOptionDialog(null, message, tr("PT_Assistant Message"), JOptionPane.YES_NO_OPTION, 177 JOptionPane.QUESTION_MESSAGE, null, options, 0); 178 } 179 179 180 180 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/PTAssistantValidatorTest.java
r33414 r33417 44 44 public class PTAssistantValidatorTest extends Test { 45 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 46 public static final int ERROR_CODE_SORTING = 3711; 47 public static final int ERROR_CODE_PARTIAL_SORTING = 3712; 48 public static final int ERROR_CODE_ROAD_TYPE = 3721; 49 public static final int ERROR_CODE_CONSTRUCTION = 3722; 50 public static final int ERROR_CODE_DIRECTION = 3731; 51 public static final int ERROR_CODE_END_STOP = 3741; 52 public static final int ERROR_CODE_SPLIT_WAY = 3742; 53 public static final int ERROR_CODE_RELATION_MEMBER_ROLES = 3743; 54 public static final int ERROR_CODE_SOLITARY_STOP_POSITION = 3751; 55 public static final int ERROR_CODE_PLATFORM_PART_OF_HIGHWAY = 3752; 56 public static final int ERROR_CODE_STOP_NOT_SERVED = 3753; 57 public static final int ERROR_CODE_STOP_BY_STOP = 3754; 58 public static final int ERROR_CODE_NOT_PART_OF_STOP_AREA = 3761; 59 public static final int ERROR_CODE_STOP_AREA_NO_STOPS = 3762; 60 public static final int ERROR_CODE_STOP_AREA_NO_PLATFORM = 3763; 61 public static final int ERROR_CODE_STOP_AREA_COMPARE_RELATIONS = 3764; 62 63 public PTAssistantValidatorTest() { 64 super(tr("Public Transport Assistant tests"), 65 tr("Check if route relations are compatible with public transport version 2")); 66 67 DataSet.addSelectionListener(PTAssistantLayerManager.PTLM); 68 69 } 70 71 @Override 72 public void visit(Node n) { 73 74 if (n.isIncomplete()) { 75 return; 76 } 77 78 NodeChecker nodeChecker = new NodeChecker(n, this); 79 80 // select only stop_positions 81 if (n.hasTag("public_transport", "stop_position")) { 82 83 // check if stop positions are on a way: 84 nodeChecker.performSolitaryStopPositionTest(); 85 86 if (Main.pref.getBoolean("pt_assistant.stop-area-tests", false) == true) { 87 // check if stop positions are in any stop_area relation: 88 nodeChecker.performNodePartOfStopAreaTest(); 89 } 90 91 } 92 93 // select only platforms 94 if (n.hasTag("public_transport", "platform")) { 95 96 // check that platforms are not part of any way: 97 nodeChecker.performPlatformPartOfWayTest(); 98 99 if (Main.pref.getBoolean("pt_assistant.stop-area-tests", false) == true) { 100 // check if platforms are in any stop_area relation: 101 nodeChecker.performNodePartOfStopAreaTest(); 102 } 103 104 } 105 106 this.errors.addAll(nodeChecker.getErrors()); 107 108 } 109 110 @Override 111 public void visit(Relation r) { 112 113 // Download incomplete members. If the download does not work, return 114 // and do not do any testing. 115 if (r.hasIncompleteMembers()) { 116 117 boolean downloadSuccessful = this.downloadIncompleteMembers(); 118 if (!downloadSuccessful) { 119 return; 120 } 121 } 122 123 if (r.hasIncompleteMembers()) { 124 return; 125 } 126 127 // Do some testing on stop area relations 128 if (Main.pref.getBoolean("pt_assistant.stop-area-tests", false) == true && StopUtils.isStopArea(r)) { 129 130 StopChecker stopChecker = new StopChecker(r, this); 131 132 // Check if stop area relation has one stop position. 133 stopChecker.performStopAreaStopPositionTest(); 134 135 // Check if stop area relation has one platform. 136 stopChecker.performStopAreaPlatformTest(); 137 138 // Check if stop position(s) belong the same route relation as 139 // related platform(s) 140 stopChecker.performStopAreaRelationsTest(); 141 142 // Attach thrown errors 143 this.errors.addAll(stopChecker.getErrors()); 144 } 145 146 if (!RouteUtils.isVersionTwoPTRoute(r)) { 147 return; 148 } 149 150 // Check individual ways using the oneway direction test and the road 151 // type test: 152 WayChecker wayChecker = new WayChecker(r, this); 153 wayChecker.performDirectionTest(); 154 wayChecker.performRoadTypeTest(); 155 this.errors.addAll(wayChecker.getErrors()); 156 157 proceedWithSorting(r); 158 159 // This allows to modify the route before the sorting and 160 // SegmentChecker are carried out: 161 // if (this.errors.isEmpty()) { 162 // proceedWithSorting(r); 163 // } else { 164 // this.proceedAfterWayCheckerErrors(r); 165 // } 166 167 } 168 169 /** 170 * Downloads incomplete relation members in an extra thread (user input 171 * required) 172 * 173 * @return true if successful, false if not successful 174 */ 175 private boolean downloadIncompleteMembers() { 176 177 final int[] userSelection = { 0 }; 178 179 try { 180 181 if (SwingUtilities.isEventDispatchThread()) { 182 183 userSelection[0] = showIncompleteMembersDownloadDialog(); 184 185 } else { 186 187 SwingUtilities.invokeAndWait(new Runnable() { 188 @Override 189 public void run() { 190 try { 191 userSelection[0] = showIncompleteMembersDownloadDialog(); 192 } catch (InterruptedException e) { 193 e.printStackTrace(); 194 } 195 196 } 197 }); 198 199 } 200 201 } catch (InterruptedException | InvocationTargetException e) { 202 return false; 203 } 204 205 if (userSelection[0] == JOptionPane.YES_OPTION) { 206 207 Thread t = new IncompleteMembersDownloadThread(); 208 t.start(); 209 synchronized (t) { 210 try { 211 t.wait(); 212 } catch (InterruptedException e) { 213 return false; 214 } 215 } 216 217 } 218 219 return true; 220 221 } 222 223 /** 224 * Shows the dialog asking the user about an incomplete member download 225 * 226 * @return user's selection 227 * @throws InterruptedException 228 * if interrupted 229 */ 230 private int showIncompleteMembersDownloadDialog() throws InterruptedException { 231 232 if (Main.pref.getBoolean("pt_assistant.download-incomplete", false) == true) { 233 return JOptionPane.YES_OPTION; 234 } 235 236 if (Main.pref.getBoolean("pt_assistant.download-incomplete", false) == false) { 237 return JOptionPane.NO_OPTION; 238 } 239 240 IncompleteMembersDownloadDialog incompleteMembersDownloadDialog = new IncompleteMembersDownloadDialog(); 241 return incompleteMembersDownloadDialog.getUserSelection(); 242 243 } 244 245 /** 246 * Gets user input after errors were detected by WayChecker. Although this 247 * method is not used in the current implementation, it can be used to fix 248 * errors from the previous testing stage and modify the route before the 249 * second stage of testing is carried out. 250 */ 251 @SuppressWarnings("unused") 252 private void proceedAfterWayCheckerErrors(Relation r) { 253 254 // count errors of each type: 255 int numberOfDirectionErrors = 0; 256 int numberOfRoadTypeErrors = 0; 257 for (TestError e : this.errors) { 258 if (e.getCode() == ERROR_CODE_DIRECTION) { 259 numberOfDirectionErrors++; 260 } 261 if (e.getCode() == ERROR_CODE_ROAD_TYPE) { 262 numberOfRoadTypeErrors++; 263 } 264 } 265 266 final int[] userInput = { 0 }; 267 final long idParameter = r.getId(); 268 final int directionErrorParameter = numberOfDirectionErrors; 269 final int roadTypeErrorParameter = numberOfRoadTypeErrors; 270 271 if (SwingUtilities.isEventDispatchThread()) { 272 273 userInput[0] = showProceedDialog(idParameter, directionErrorParameter, roadTypeErrorParameter); 274 275 } else { 276 277 try { 278 SwingUtilities.invokeAndWait(new Runnable() { 279 @Override 280 public void run() { 281 userInput[0] = showProceedDialog(idParameter, directionErrorParameter, roadTypeErrorParameter); 282 283 } 284 }); 285 } catch (InvocationTargetException | InterruptedException e1) { 286 e1.printStackTrace(); 287 } 288 289 } 290 291 if (userInput[0] == 0) { 292 this.fixErrorFromPlugin(this.errors); 293 proceedWithSorting(r); 294 return; 295 } 296 297 if (userInput[0] == 1) { 298 JOptionPane.showMessageDialog(null, "This is not implemented yet!"); 299 return; 300 } 301 302 if (userInput[0] == 2) { 303 proceedWithSorting(r); 304 } 305 306 // if userInput==-1 (i.e. no input), do nothing and stop testing of the 307 // route. 308 309 } 310 311 private int showProceedDialog(long id, int numberOfDirectionErrors, int numberOfRoadTypeErrors) { 312 313 if (numberOfDirectionErrors == 0 && numberOfRoadTypeErrors == 0) { 314 return 2; 315 } 316 317 if (Main.pref.getBoolean("pt_assistant.proceed-without-fix", true) == false) { 318 return 0; 319 } 320 321 if (Main.pref.getBoolean("pt_assistant.proceed-without-fix", true) == true) { 322 return 2; 323 } 324 325 ProceedDialog proceedDialog = new ProceedDialog(id, numberOfDirectionErrors, numberOfRoadTypeErrors); 326 return proceedDialog.getUserSelection(); 327 328 } 329 330 /** 331 * Carries out the second stage of the testing: sorting 332 * 333 * @param r 334 * relation 335 */ 336 private void proceedWithSorting(Relation r) { 337 338 // Check if the relation is correct, or only has a wrong sorting order: 339 RouteChecker routeChecker = new RouteChecker(r, this); 340 routeChecker.performSortingTest(); 341 List<TestError> routeCheckerErrors = routeChecker.getErrors(); 342 343 /*- At this point, there are 3 variants: 344 * 345 * 1) There are no errors => route is correct 346 * 2) There is only a sorting error (can only be 1), but otherwise 347 * correct. 348 * 3) There are some other errors/gaps that cannot be fixed by 349 * sorting => start further test (stop-by-stop) 350 * 351 * */ 352 353 if (!routeCheckerErrors.isEmpty()) { 354 // Variant 2 355 // If there is only the sorting error, add it 356 this.errors.addAll(routeChecker.getErrors()); 357 } 358 359 // if (!routeChecker.getHasGap()) { 360 // // Variant 1 361 // storeCorrectRouteSegments(r); 362 // } 363 364 // Variant 3: 365 proceedAfterSorting(r); 366 367 } 368 369 /** 370 * Carries out the stop-by-stop testing which includes building the route 371 * data model. 372 * 373 * @param r 374 * route relation 375 */ 376 private void proceedAfterSorting(Relation r) { 377 378 SegmentChecker segmentChecker = new SegmentChecker(r, this); 379 380 // Check if the creation of the route data model in the segment checker 381 // worked. If it did not, it means the roles in the route relation do 382 // not match the tags of the route members. 383 if (!segmentChecker.getErrors().isEmpty()) { 384 this.errors.addAll(segmentChecker.getErrors()); 385 } 386 387 segmentChecker.performFirstStopTest(); 388 segmentChecker.performLastStopTest(); 389 segmentChecker.performStopNotServedTest(); 390 391 boolean sortingErrorFound = false; 392 for (TestError error : this.errors) { 393 if (error.getCode() == ERROR_CODE_SORTING 394 || error.getCode() == ERROR_CODE_PARTIAL_SORTING) { 395 sortingErrorFound = true; 396 break; 397 } 398 } 399 if (!sortingErrorFound) { 400 segmentChecker.performStopByStopTest(); 401 segmentChecker.findFixes(); 402 } 403 404 for (TestError error : segmentChecker.getErrors()) { 405 if (error.getCode() != PTAssistantValidatorTest.ERROR_CODE_RELATION_MEMBER_ROLES) { 406 this.errors.add(error); 407 } 408 } 409 } 410 411 @Override 412 public void startTest(ProgressMonitor progressMonitor) { 413 super.startTest(progressMonitor); 414 415 // reset the static collections in SegmentChecker: 416 SegmentChecker.reset(); 417 } 418 419 /** 420 * Method is called after all primitives has been visited, overrides the 421 * method of the superclass. 422 */ 423 @Override 424 public void endTest() { 425 426 // modify the error messages for the stop-by-stop test: 427 SegmentChecker.modifyStopByStopErrorMessages(); 428 429 // add the stop-by-stop errors with modified messages: 430 for (Entry<Builder, PTRouteSegment> entry : SegmentChecker.wrongSegmentBuilders.entrySet()) { 431 TestError error = entry.getKey().build(); 432 SegmentChecker.wrongSegments.put(error, entry.getValue()); 433 this.errors.add(error); 434 } 435 436 super.endTest(); 437 438 } 439 440 /** 441 * Creates the PTRouteSegments of a route that has been found correct and 442 * stores them in the list of correct route segments 443 * 444 * @param r 445 * route relation 446 */ 447 @SuppressWarnings("unused") 448 private void storeCorrectRouteSegments(Relation r) { 449 PTRouteDataManager manager = new PTRouteDataManager(r); 450 StopToWayAssigner assigner = new StopToWayAssigner(manager.getPTWays()); 451 if (manager.getPTStops().size() > 1) { 452 for (int i = 1; i < manager.getPTStops().size(); i++) { 453 PTStop segmentStartStop = manager.getPTStops().get(i - 1); 454 PTStop segmentEndStop = manager.getPTStops().get(i); 455 Way segmentStartWay = assigner.get(segmentStartStop); 456 Way segmentEndWay = assigner.get(segmentEndStop); 457 List<PTWay> waysBetweenStops = manager.getPTWaysBetween(segmentStartWay, segmentEndWay); 458 PTRouteSegment routeSegment = new PTRouteSegment(segmentStartStop, segmentEndStop, waysBetweenStops, r); 459 SegmentChecker.addCorrectSegment(routeSegment); 460 } 461 } 462 } 463 464 /** 465 * Checks if the test error is fixable 466 */ 467 @Override 468 public boolean isFixable(TestError testError) { 469 if (testError.getCode() == ERROR_CODE_DIRECTION 470 || testError.getCode() == ERROR_CODE_ROAD_TYPE 471 || testError.getCode() == ERROR_CODE_CONSTRUCTION 472 || testError.getCode() == ERROR_CODE_SORTING 473 || testError.getCode() == ERROR_CODE_PARTIAL_SORTING 474 || testError.getCode() == ERROR_CODE_END_STOP 475 || testError.getCode() == ERROR_CODE_PLATFORM_PART_OF_HIGHWAY) { 476 return true; 477 } 478 479 if (testError.getCode() == ERROR_CODE_STOP_BY_STOP && SegmentChecker.isFixable(testError)) { 480 return true; 481 } 482 483 return false; 484 } 485 486 /** 487 * Fixes the given error 488 */ 489 @Override 490 public Command fixError(TestError testError) { 491 492 // repaint the relation in the pt_assistant layer: 493 if (testError.getPrimitives().iterator().next().getType().equals(OsmPrimitiveType.RELATION)) { 494 Relation relationToBeFixed = (Relation) testError.getPrimitives().iterator().next(); 495 PTAssistantLayerManager.PTLM.getLayer().repaint(relationToBeFixed); 496 } 497 498 // reset the last fix: 499 PTAssistantPlugin.setLastFix(null); 500 501 List<Command> commands = new ArrayList<>(); 502 503 if (testError.getCode() == ERROR_CODE_ROAD_TYPE 504 || testError.getCode() == ERROR_CODE_CONSTRUCTION 505 || testError.getCode() == ERROR_CODE_DIRECTION 506 || testError.getCode() == ERROR_CODE_END_STOP) { 507 commands.add(WayChecker.fixErrorByZooming(testError)); 508 } 509 510 if (testError.getCode() == ERROR_CODE_SORTING 511 || testError.getCode() == ERROR_CODE_PARTIAL_SORTING) { 512 commands.add(RouteChecker.fixSortingError(testError)); 513 } 514 515 if (testError.getCode() == ERROR_CODE_SOLITARY_STOP_POSITION 516 || testError.getCode() == ERROR_CODE_PLATFORM_PART_OF_HIGHWAY) { 517 commands.add(NodeChecker.fixError(testError)); 518 } 519 520 if (testError.getCode() == ERROR_CODE_STOP_BY_STOP) { 521 commands.add(SegmentChecker.fixError(testError)); 522 // make sure the primitives of this testError are selected: 523 Collection<OsmPrimitive> primitivesToSelect = new ArrayList<>(); 524 for (Object obj : testError.getPrimitives()) { 525 primitivesToSelect.add((OsmPrimitive) obj); 526 } 527 SelectCommand selectCommand = new SelectCommand(primitivesToSelect); 528 SwingUtilities.invokeLater(new Runnable() { 529 @Override 530 public void run() { 531 selectCommand.executeCommand(); 532 } 533 }); 534 } 535 536 if (commands.isEmpty()) { 537 return null; 538 } 539 540 if (commands.size() == 1) { 541 return commands.get(0); 542 } 543 544 return new SequenceCommand(tr("Fix error"), commands); 545 } 546 547 /** 548 * This method is the counterpart of the fixError(TestError testError) 549 * method. The fixError method is invoked from the core validator (e.g. when 550 * user presses the "Fix" button in the validator). This method is invoken 551 * when the fix is initiated from within the plugin (e.g. automated fixes). 552 */ 553 private void fixErrorFromPlugin(List<TestError> testErrors) { 554 555 // run fix task asynchronously 556 FixTask fixTask = new FixTask(testErrors); 557 558 Thread t = new Thread(fixTask); 559 t.start(); 560 try { 561 t.join(); 562 errors.removeAll(testErrors); 563 564 } catch (InterruptedException e) { 565 JOptionPane.showMessageDialog(null, "Error occurred during fixing"); 566 } 567 568 } 569 570 public void addFixVariants(List<List<PTWay>> fixVariants) { 571 PTAssistantLayerManager.PTLM.getLayer().addFixVariants(fixVariants); 572 } 573 574 public void clearFixVariants() { 575 PTAssistantLayerManager.PTLM.getLayer().clearFixVariants(); 576 } 577 578 public List<PTWay> getFixVariant(Character c) { 579 return PTAssistantLayerManager.PTLM.getLayer().getFixVariant(c); 580 } 581 582 @SuppressWarnings("unused") 583 private void performDummyTest(Relation r) { 584 List<Relation> primitives = new ArrayList<>(1); 585 primitives.add(r); 586 Builder builder = TestError.builder(this, Severity.WARNING, ERROR_CODE_DIRECTION); 587 builder.message(tr("PT: dummy test warning")); 588 builder.primitives(primitives); 589 errors.add(builder.build()); 590 } 591 591 592 592 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/RouteChecker.java
r33405 r33417 94 94 */ 95 95 private int countGaps(List<RelationMember> waysToCheck) { 96 96 int numberOfGaps = 0; 97 97 98 98 WayConnectionTypeCalculator connectionTypeCalculator = new WayConnectionTypeCalculator(); 99 99 final List<WayConnectionType> links = connectionTypeCalculator.updateLinks(waysToCheck); 100 100 for (int i = 0; i < links.size(); i++) { … … 102 102 if(!(i == 0 || link.linkPrev) || !(i == links.size() - 1 || link.linkNext) 103 103 || link.direction == null || WayConnectionType.Direction.NONE.equals(link.direction)) { 104 105 104 numberOfGaps++; 105 i++; 106 106 } 107 107 } … … 124 124 protected static Command fixSortingError(TestError testError) { 125 125 if (testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_SORTING 126 126 && testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_PARTIAL_SORTING) { 127 127 return null; 128 128 } -
applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/SegmentChecker.java
r33387 r33417 49 49 public class SegmentChecker extends Checker { 50 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 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_RELATION_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 if (firstNode == null) { 299 // check if this error has just been reported: 300 if (wrongSegmentBuilders.isEmpty() && lastCreatedBuilderHighlighted != null && lastCreatedBuilderHighlighted.size() == 1 301 && lastCreatedBuilderHighlighted.get(0) == startWay) { 302 // do nothing, this error has already been reported in 303 // the previous route segment 304 } else { 305 List<Relation> primitives = new ArrayList<>(1); 306 primitives.add(relation); 307 List<OsmPrimitive> highlighted = new ArrayList<>(); 308 highlighted.add(startWay); 309 Builder builder = TestError.builder(this.test, Severity.WARNING, 310 PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 311 builder.primitives(primitives); 312 builder.highlight(highlighted); 313 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 314 wrongSegmentBuilders.put(builder, routeSegment); 315 } 316 continue; 317 } 318 319 PTWay wronglySortedPtway = existingWaySortingIsWrong(segmentWays.get(0), firstNode, 320 segmentWays.get(segmentWays.size() - 1)); 321 if (wronglySortedPtway == null) { // i.e. if the sorting is correct: 322 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 323 addCorrectSegment(routeSegment); 324 } else { // i.e. if the sorting is wrong: 325 PTRouteSegment routeSegment = new PTRouteSegment(startStop, endStop, segmentWays, relation); 326 // TestError error = this.errors.get(this.errors.size() - 1); 327 // wrongSegments.put(error, routeSegment); 328 329 List<Relation> primitives = new ArrayList<>(1); 330 primitives.add(relation); 331 List<OsmPrimitive> highlighted = new ArrayList<>(); 332 highlighted.add(startStop.getStopPosition()); 333 highlighted.add(endStop.getStopPosition()); 334 Builder builder = TestError.builder(this.test, Severity.WARNING, 335 PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP); 336 builder.primitives(primitives); 337 builder.highlight(highlighted); 338 lastCreatedBuilderHighlighted = highlighted; 339 wrongSegmentBuilders.put(builder, routeSegment); 340 } 341 } 342 } 343 344 /** 345 * Creates a TestError and adds it to the list of errors for a stop that is 346 * not served. 347 * 348 * @param stop 349 * stop 350 */ 351 private void createStopError(PTStop stop) { 352 List<Relation> primitives = new ArrayList<>(1); 353 primitives.add(relation); 354 List<OsmPrimitive> highlighted = new ArrayList<>(); 355 OsmPrimitive stopPrimitive = stop.getPlatform(); 356 if (stopPrimitive == null) { 357 stopPrimitive = stop.getStopPosition(); 358 } 359 highlighted.add(stopPrimitive); 360 Builder builder = TestError.builder(this.test, Severity.WARNING, 361 PTAssistantValidatorTest.ERROR_CODE_STOP_NOT_SERVED); 362 builder.message(tr("PT: Stop not served")); 363 builder.primitives(primitives); 364 builder.highlight(highlighted); 365 TestError e = builder.build(); 366 this.errors.add(e); 367 } 368 369 private Node findFirstNodeOfRouteSegmentInDirectionOfTravel(PTWay startWay) { 370 371 // 1) at first check if one of the first or last node of the first ptway 372 // is a deadend node: 373 Node[] startWayEndnodes = startWay.getEndNodes(); 374 if (isDeadendNode(startWayEndnodes[0])) { 375 return startWayEndnodes[0]; 376 } 377 if (isDeadendNode(startWayEndnodes[1])) { 378 return startWayEndnodes[1]; 379 } 380 381 // 2) failing that, check which node this startWay shares with the 382 // following way: 383 PTWay nextWay = manager.getNextPTWay(startWay); 384 if (nextWay == null) { 385 return null; 386 } 387 PTWay wayAfterNext = manager.getNextPTWay(nextWay); 388 Node[] nextWayEndnodes = nextWay.getEndNodes(); 389 if ((startWayEndnodes[0] == nextWayEndnodes[0] && startWayEndnodes[1] == nextWayEndnodes[1]) 390 || (startWayEndnodes[0] == nextWayEndnodes[1] && startWayEndnodes[1] == nextWayEndnodes[0])) { 391 // if this is a split roundabout: 392 Node[] wayAfterNextEndnodes = wayAfterNext.getEndNodes(); 393 if (startWayEndnodes[0] == wayAfterNextEndnodes[0] || startWayEndnodes[0] == wayAfterNextEndnodes[1]) { 394 return startWayEndnodes[0]; 395 } 396 if (startWayEndnodes[1] == wayAfterNextEndnodes[0] || startWayEndnodes[1] == wayAfterNextEndnodes[1]) { 397 return startWayEndnodes[1]; 398 } 399 } 400 401 if (startWayEndnodes[0] == nextWayEndnodes[0] || startWayEndnodes[0] == nextWayEndnodes[1]) { 402 return startWayEndnodes[1]; 403 } 404 if (startWayEndnodes[1] == nextWayEndnodes[0] || startWayEndnodes[1] == nextWayEndnodes[1]) { 405 return startWayEndnodes[0]; 406 } 407 408 return null; 409 410 } 411 412 private boolean isDeadendNode(Node node) { 413 int count = 0; 414 for (PTWay ptway : manager.getPTWays()) { 415 List<Way> ways = ptway.getWays(); 416 for (Way way : ways) { 417 if (way.firstNode() == node || way.lastNode() == node) { 418 count++; 419 } 420 } 421 } 422 return count == 1; 423 } 424 425 /** 426 * Finds the deadend node closest to the given node represented by its 427 * coordinates 428 * 429 * @param coord 430 * coordinates of the givenn node 431 * @param deadendNodes 432 * dead end nodes 433 * @return the closest deadend node 434 */ 435 @SuppressWarnings("unused") 436 private Node findClosestDeadendNode(LatLon coord, List<Node> deadendNodes) { 437 438 Node closestDeadendNode = null; 439 double minSqDistance = Double.MAX_VALUE; 440 for (Node deadendNode : deadendNodes) { 441 double distanceSq = coord.distanceSq(deadendNode.getCoor()); 442 if (distanceSq < minSqDistance) { 443 minSqDistance = distanceSq; 444 closestDeadendNode = deadendNode; 445 } 446 } 447 return closestDeadendNode; 448 449 } 450 451 /** 452 * Checks if the existing sorting of the given route segment is correct 453 * 454 * @param start 455 * PTWay assigned to the first stop of the segment 456 * @param startWayPreviousNodeInDirectionOfTravel 457 * Node if the start way which is furthest away from the rest of 458 * the route 459 * @param end 460 * PTWay assigned to the end stop of the segment 461 * @return null if the sorting is correct, or the wrongly sorted PTWay 462 * otherwise. 463 */ 464 private PTWay existingWaySortingIsWrong(PTWay start, Node startWayPreviousNodeInDirectionOfTravel, PTWay end) { 465 466 if (start == end) { 467 // if both PTStops are on the same PTWay 468 // return true; 469 return null; 470 } 471 472 PTWay current = start; 473 Node currentNode = startWayPreviousNodeInDirectionOfTravel; 474 475 while (!current.equals(end)) { 476 // "equals" is used here instead of "==" because when the same way 477 // is passed multiple times by the bus, the algorithm should stop no 478 // matter which of the geometrically equal PTWays it finds 479 480 PTWay nextPTWayAccortingToExistingSorting = manager.getNextPTWay(current); 481 482 // if current contains an unsplit roundabout: 483 if (current.containsUnsplitRoundabout()) { 484 currentNode = manager.getCommonNode(current, nextPTWayAccortingToExistingSorting); 485 if (currentNode == null) { 486 487 return current; 488 489 } 490 } else { 491 // if this is a regular way, not an unsplit roundabout 492 493 // find the next node in direction of travel (which is part of 494 // the PTWay start): 495 currentNode = getOppositeEndNode(current, currentNode); 496 497 List<PTWay> nextWaysInDirectionOfTravel = this.findNextPTWaysInDirectionOfTravel(current, currentNode); 498 499 if (!nextWaysInDirectionOfTravel.contains(nextPTWayAccortingToExistingSorting)) { 500 return current; 501 502 } 503 } 504 505 current = nextPTWayAccortingToExistingSorting; 506 507 } 508 509 return null; 510 } 511 512 /** 513 * Will return the same node if the way is an unsplit roundabout 514 * 515 * @param way 516 * way 517 * @param node 518 * node 519 * @return the same node if the way is an unsplit roundabout 520 */ 521 private Node getOppositeEndNode(Way way, Node node) { 522 523 if (node == way.firstNode()) { 524 return way.lastNode(); 525 } 526 527 if (node == way.lastNode()) { 528 return way.firstNode(); 529 } 530 531 return null; 532 } 533 534 /** 535 * Does not work correctly for unsplit roundabouts 536 * 537 * @param ptway 538 * way 539 * @param node 540 * node 541 * @return node 542 */ 543 private Node getOppositeEndNode(PTWay ptway, Node node) { 544 if (ptway.isWay()) { 545 return getOppositeEndNode(ptway.getWays().get(0), node); 546 } 547 548 Way firstWay = ptway.getWays().get(0); 549 Way lastWay = ptway.getWays().get(ptway.getWays().size() - 1); 550 Node oppositeNode = node; 551 if (firstWay.firstNode() == node || firstWay.lastNode() == node) { 552 for (int i = 0; i < ptway.getWays().size(); i++) { 553 oppositeNode = getOppositeEndNode(ptway.getWays().get(i), oppositeNode); 554 } 555 return oppositeNode; 556 } else if (lastWay.firstNode() == node || lastWay.lastNode() == node) { 557 for (int i = ptway.getWays().size() - 1; i >= 0; i--) { 558 oppositeNode = getOppositeEndNode(ptway.getWays().get(i), oppositeNode); 559 } 560 return oppositeNode; 561 } 562 563 return null; 564 565 } 566 567 /** 568 * Finds the next ways for the route stop-by-stop parsing procedure 569 * 570 * @param currentWay 571 * current way 572 * @param nextNodeInDirectionOfTravel 573 * next node in direction of travel 574 * @return the next ways for the route stop-by-stop parsing procedure 575 */ 576 private List<PTWay> findNextPTWaysInDirectionOfTravel(PTWay currentWay, Node nextNodeInDirectionOfTravel) { 577 578 List<PTWay> nextPtways = new ArrayList<>(); 579 580 List<PTWay> ptways = manager.getPTWays(); 581 582 for (PTWay ptway : ptways) { 583 584 if (ptway != currentWay) { 585 for (Way way : ptway.getWays()) { 586 if (way.containsNode(nextNodeInDirectionOfTravel)) { 587 nextPtways.add(ptway); 588 } 589 } 590 } 591 } 592 593 return nextPtways; 594 595 } 596 597 protected static boolean isFixable(TestError testError) { 598 599 /*- 600 * When is an error fixable (outdated)? 601 * - if there is a correct segment 602 * - if it can be fixed by sorting 603 * - if the route is compete even without some ways 604 * - if simple routing closes the gap 605 */ 606 607 if (testError.getCode() == PTAssistantValidatorTest.ERROR_CODE_STOP_BY_STOP) { 608 return true; 609 } 610 611 return false; 612 613 } 614 615 @SuppressWarnings("unused") 616 private static boolean isFixableByUsingCorrectSegment(TestError testError) { 617 PTRouteSegment wrongSegment = wrongSegments.get(testError); 618 PTRouteSegment correctSegment = null; 619 for (PTRouteSegment segment : correctSegments) { 620 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 621 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 622 correctSegment = segment; 623 break; 624 } 625 } 626 return correctSegment != null; 627 } 628 629 @SuppressWarnings("unused") 630 private static boolean isFixableBySortingAndRemoval(TestError testError) { 631 PTRouteSegment wrongSegment = wrongSegments.get(testError); 632 List<List<PTWay>> fixVariants = wrongSegment.getFixVariants(); 633 if (!fixVariants.isEmpty()) { 634 return true; 635 } 636 return false; 637 } 638 639 /** 640 * Finds fixes using sorting and removal. 641 */ 642 protected void findFixes() { 643 644 for (Builder builder : wrongSegmentBuilders.keySet()) { 645 646 if (wrongSegmentBuilders.get(builder).getRelation() == this.relation) { 647 648 findFix(builder); 649 650 } 651 } 652 653 } 654 655 /** 656 * Modifies the error messages of the stop-by-stop test errors depending on how many fixes each of them has. 657 */ 658 protected static void modifyStopByStopErrorMessages() { 659 660 for (Entry<Builder, PTRouteSegment> entry : SegmentChecker.wrongSegmentBuilders.entrySet()) { 661 662 // change the error code based on the availability of fixes: 663 Builder builder = entry.getKey(); 664 PTRouteSegment wrongSegment = entry.getValue(); 665 List<PTRouteSegment> correctSegmentsForThisError = new ArrayList<>(); 666 for (PTRouteSegment segment : correctSegments) { 667 if (wrongSegment.getFirstWay().getUniqueId() == segment.getFirstWay().getUniqueId() 668 && wrongSegment.getLastWay().getUniqueId() == segment.getLastWay().getUniqueId()) { 669 correctSegmentsForThisError.add(segment); 670 } 671 } 672 673 int numberOfFixes = correctSegmentsForThisError.size(); 674 675 if (numberOfFixes == 0) { 676 numberOfFixes = wrongSegment.getFixVariants().size(); 677 } 678 if (numberOfFixes == 0) { 679 for (PTRouteSegment segment : correctSegments) { 680 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 681 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 682 correctSegmentsForThisError.add(segment); 683 } 684 } 685 numberOfFixes = correctSegmentsForThisError.size(); 686 } 687 688 // change the error message: 689 if (numberOfFixes == 0) { 690 builder.message(tr("PT: Problem in the route segment with no automatic fix")); 691 } else if (numberOfFixes == 1) { 692 builder.message(tr("PT: Problem in the route segment with one automatic fix")); 693 } else { 694 builder.message("PT: Problem in the route segment with several automatic fixes"); 695 } 696 697 } 698 699 } 700 701 /** 702 * This method assumes that the first and the second ways of the route 703 * segment are correctly connected. If they are not, the error will be 704 * marked as not fixable. 705 * 706 * @param testError 707 * test error 708 */ 709 private void findFix(Builder builder) { 710 711 PTRouteSegment wrongSegment = wrongSegmentBuilders.get(builder); 712 PTWay startPTWay = wrongSegment.getFirstPTWay(); 713 PTWay endPTWay = wrongSegment.getLastPTWay(); 714 715 Node previousNode = findFirstNodeOfRouteSegmentInDirectionOfTravel(startPTWay); 716 if (previousNode == null) { 717 return; 718 } 719 720 List<List<PTWay>> initialFixes = new ArrayList<>(); 721 List<PTWay> initialFix = new ArrayList<>(); 722 initialFix.add(startPTWay); 723 initialFixes.add(initialFix); 724 725 List<List<PTWay>> allFixes = findWaysForFix(initialFixes, initialFix, previousNode, endPTWay); 726 for (List<PTWay> fix : allFixes) { 727 if (!fix.isEmpty() && fix.get(fix.size() - 1).equals(endPTWay)) { 728 wrongSegment.addFixVariant(fix); 729 } 730 } 731 732 } 733 734 /** 735 * Recursive method to parse the route segment 736 * 737 * @param allFixes 738 * all fixes 739 * @param currentFix 740 * current fix 741 * @param previousNode 742 * previous node 743 * @param endWay 744 * end way 745 * @return list of list of ways 746 */ 747 private List<List<PTWay>> findWaysForFix(List<List<PTWay>> allFixes, List<PTWay> currentFix, Node previousNode, 748 PTWay endWay) { 749 750 PTWay currentWay = currentFix.get(currentFix.size() - 1); 751 Node nextNode = getOppositeEndNode(currentWay, previousNode); 752 753 List<PTWay> nextWays = this.findNextPTWaysInDirectionOfTravel(currentWay, nextNode); 754 755 if (nextWays.size() > 1) { 756 for (int i = 1; i < nextWays.size(); i++) { 757 List<PTWay> newFix = new ArrayList<>(); 758 newFix.addAll(currentFix); 759 newFix.add(nextWays.get(i)); 760 allFixes.add(newFix); 761 if (!nextWays.get(i).equals(endWay) && !currentFix.contains(nextWays.get(i))) { 762 allFixes = findWaysForFix(allFixes, newFix, nextNode, endWay); 763 } 764 } 765 } 766 767 if (!nextWays.isEmpty()) { 768 boolean contains = currentFix.contains(nextWays.get(0)); 769 currentFix.add(nextWays.get(0)); 770 if (!nextWays.get(0).equals(endWay) && !contains) { 771 allFixes = findWaysForFix(allFixes, currentFix, nextNode, endWay); 772 } 773 } 774 775 return allFixes; 776 } 777 778 /** 779 * Fixes the error by first searching in the list of correct segments and 780 * then trying to sort and remove existing route relation members 781 * 782 * @param testError 783 * test error 784 * @return fix command 785 */ 786 protected static Command fixError(TestError testError) { 787 788 // if fix options for another route are displayed in the pt_assistant 789 // layer, clear them: 790 ((PTAssistantValidatorTest) testError.getTester()).clearFixVariants(); 791 792 PTRouteSegment wrongSegment = wrongSegments.get(testError); 793 794 // 1) try to fix by using the correct segment: 795 List<PTRouteSegment> correctSegmentsForThisError = new ArrayList<>(); 796 for (PTRouteSegment segment : correctSegments) { 797 if (wrongSegment.getFirstWay().getUniqueId() == segment.getFirstWay().getUniqueId() 798 && wrongSegment.getLastWay().getUniqueId() == segment.getLastWay().getUniqueId()) { 799 correctSegmentsForThisError.add(segment); 800 } 801 } 802 803 // if no correct segment found, apply less strict criteria to look for 804 // one: 805 if (correctSegmentsForThisError.isEmpty() && wrongSegment.getFixVariants().isEmpty()) { 806 for (PTRouteSegment segment : correctSegments) { 807 if (wrongSegment.getFirstStop().equalsStop(segment.getFirstStop()) 808 && wrongSegment.getLastStop().equalsStop(segment.getLastStop())) { 809 correctSegmentsForThisError.add(segment); 810 } 811 } 812 if (!correctSegmentsForThisError.isEmpty()) { 813 // display the notification: 814 if (SwingUtilities.isEventDispatchThread()) { 815 Notification notification = new Notification( 816 tr("Warning: the diplayed fix variants are based on less strict criteria")); 817 notification.show(); 818 } else { 819 SwingUtilities.invokeLater(new Runnable() { 820 @Override 821 public void run() { 822 Notification notification = new Notification( 823 tr("Warning: the diplayed fix variants are based on less strict criteria")); 824 notification.show(); 825 } 826 }); 827 } 828 } 829 } 830 831 if (!correctSegmentsForThisError.isEmpty()) { 832 833 if (correctSegmentsForThisError.size() > 1) { 834 List<List<PTWay>> fixVariants = new ArrayList<>(); 835 for (PTRouteSegment segment : correctSegmentsForThisError) { 836 fixVariants.add(segment.getPTWays()); 837 } 838 displayFixVariants(fixVariants, testError); 839 return null; 840 } 841 842 PTAssistantPlugin.setLastFix(correctSegmentsForThisError.get(0)); 843 return carryOutSingleFix(testError, correctSegmentsForThisError.get(0).getPTWays()); 844 845 } else if (!wrongSegment.getFixVariants().isEmpty()) { 846 // 2) try to fix using the sorting and removal of existing ways 847 // of the wrong segment: 848 if (wrongSegment.getFixVariants().size() > 1) { 849 displayFixVariants(wrongSegment.getFixVariants(), testError); 850 return null; 851 } 852 853 PTAssistantPlugin.setLastFix(new PTRouteSegment(wrongSegment.getFirstStop(), wrongSegment.getLastStop(), 854 wrongSegment.getFixVariants().get(0), (Relation) testError.getPrimitives().iterator().next())); 855 return carryOutSingleFix(testError, wrongSegment.getFixVariants().get(0)); 856 } 857 858 // if there is no fix: 859 return fixErrorByZooming(testError); 860 861 } 862 863 /** 864 * This is largely a copy of the displayFixVariants() method, adapted for 865 * use with the key listener 866 * 867 * @param fixVariants 868 * fix variants 869 * @param testError 870 * test error 871 */ 872 private static void displayFixVariants(List<List<PTWay>> fixVariants, TestError testError) { 873 // find the letters of the fix variants: 874 char alphabet = 'A'; 875 final List<Character> allowedCharacters = new ArrayList<>(); 876 for (int i = 0; i < fixVariants.size(); i++) { 877 allowedCharacters.add(alphabet); 878 alphabet++; 879 } 880 881 // zoom to problem: 882 final Collection<OsmPrimitive> waysToZoom = new ArrayList<>(); 883 884 for (List<PTWay> variants : fixVariants) 885 for(PTWay variant : variants) 886 waysToZoom.add(variant.getWay()); 887 888 if (SwingUtilities.isEventDispatchThread()) { 889 AutoScaleAction.zoomTo(waysToZoom); 890 } else { 891 SwingUtilities.invokeLater(new Runnable() { 892 @Override 893 public void run() { 894 AutoScaleAction.zoomTo(waysToZoom); 895 } 896 }); 897 } 898 899 // display the fix variants: 900 final PTAssistantValidatorTest test = (PTAssistantValidatorTest) testError.getTester(); 901 test.addFixVariants(fixVariants); 902 PTAssistantLayerManager.PTLM.getLayer().repaint((Relation) testError.getPrimitives().iterator().next()); 903 904 // prepare the variables for the key listener: 905 final TestError testErrorParameter = testError; 906 907 // // add the key listener: 908 Main.map.mapView.requestFocus(); 909 Main.map.mapView.addKeyListener(new KeyListener() { 910 911 @Override 912 public void keyTyped(KeyEvent e) { 913 // TODO Auto-generated method stub 914 } 915 916 @Override 917 public void keyPressed(KeyEvent e) { 918 Character typedKey = e.getKeyChar(); 919 Character typedKeyUpperCase = typedKey.toString().toUpperCase().toCharArray()[0]; 920 if (allowedCharacters.contains(typedKeyUpperCase)) { 921 Main.map.mapView.removeKeyListener(this); 922 List<PTWay> selectedFix = test.getFixVariant(typedKeyUpperCase); 923 test.clearFixVariants(); 924 carryOutSelectedFix(testErrorParameter, selectedFix); 925 } 926 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 927 Main.map.mapView.removeKeyListener(this); 928 test.clearFixVariants(); 929 } 930 } 931 932 @Override 933 public void keyReleased(KeyEvent e) { 934 // TODO Auto-generated method stub 935 } 936 }); 937 938 // display the notification: 939 if (SwingUtilities.isEventDispatchThread()) { 940 Notification notification = new Notification( 941 tr("Type letter to select the fix variant or press Escape for no fix")); 942 notification.show(); 943 } else { 944 SwingUtilities.invokeLater(new Runnable() { 945 @Override 946 public void run() { 947 Notification notification = new Notification( 948 tr("Type letter to select the fix variant or press Escape for no fix")); 949 notification.show(); 950 } 951 }); 952 } 953 } 954 955 /** 956 * Carries out the fix (i.e. modifies the route) after the user has picked 957 * the fix from several fix variants. 958 * 959 * @param testError 960 * test error to be fixed 961 * @param fix 962 * the fix variant to be adopted 963 */ 964 private static void carryOutSelectedFix(TestError testError, List<PTWay> fix) { 965 // modify the route: 966 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 967 Relation modifiedRelation = new Relation(originalRelation); 968 modifiedRelation.setMembers(getModifiedRelationMembers(testError, fix)); 969 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 970 Main.main.undoRedo.addNoRedraw(changeCommand); 971 Main.main.undoRedo.afterAdd(); 972 PTRouteSegment wrongSegment = wrongSegments.get(testError); 973 wrongSegments.remove(testError); 974 wrongSegment.setPTWays(fix); 975 addCorrectSegment(wrongSegment); 976 PTAssistantPlugin.setLastFixNoGui(wrongSegment); 977 978 // get ways for the fix: 979 List<Way> primitives = new ArrayList<>(); 980 for (PTWay ptway : fix) { 981 primitives.addAll(ptway.getWays()); 982 } 983 984 // get layer: 985 OsmDataLayer layer = null; 986 List<OsmDataLayer> listOfLayers = Main.getLayerManager().getLayersOfType(OsmDataLayer.class); 987 for (OsmDataLayer osmDataLayer : listOfLayers) { 988 if (osmDataLayer.data == originalRelation.getDataSet()) { 989 layer = osmDataLayer; 990 break; 991 } 992 } 993 994 // create editor: 995 GenericRelationEditor editor = (GenericRelationEditor) RelationEditor.getEditor(layer, originalRelation, 996 originalRelation.getMembersFor(primitives)); 997 998 // open editor: 999 editor.setVisible(true); 1000 1001 } 1002 1003 /** 1004 * Carries out the fix (i.e. modifies the route) when there is only one fix 1005 * variant. 1006 * 1007 * @param testError 1008 * test error 1009 * @param fix 1010 * fix 1011 */ 1012 private static Command carryOutSingleFix(TestError testError, List<PTWay> fix) { 1013 1014 // wait: 1015 synchronized (SegmentChecker.class) { 1016 try { 1017 SegmentChecker.class.wait(1500); 1018 } catch (InterruptedException e) { 1019 e.printStackTrace(); 1020 } 1021 } 1022 1023 // Zoom to the problematic ways: 1024 final Collection<OsmPrimitive> waysToZoom = new ArrayList<>(); 1025 for (Object highlightedPrimitive : testError.getHighlighted()) { 1026 waysToZoom.add((OsmPrimitive) highlightedPrimitive); 1027 } 1028 if (SwingUtilities.isEventDispatchThread()) { 1029 AutoScaleAction.zoomTo(waysToZoom); 1030 } else { 1031 SwingUtilities.invokeLater(new Runnable() { 1032 @Override 1033 public void run() { 1034 AutoScaleAction.zoomTo(waysToZoom); 1035 } 1036 }); 1037 } 1038 1039 // wait: 1040 synchronized (SegmentChecker.class) { 1041 try { 1042 SegmentChecker.class.wait(1500); 1043 } catch (InterruptedException e) { 1044 e.printStackTrace(); 1045 } 1046 } 1047 1048 // modify the route: 1049 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 1050 Relation modifiedRelation = new Relation(originalRelation); 1051 modifiedRelation.setMembers(getModifiedRelationMembers(testError, fix)); 1052 wrongSegments.remove(testError); 1053 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 1054 return changeCommand; 1055 } 1056 1057 /** 1058 * Returns a list of the modified relation members. This list can be used by 1059 * the calling method (relation.setMemers()) to modify the modify the route 1060 * relation. The route relation is not modified by this method. The lists of 1061 * wrong and correct segments are not updated. 1062 * 1063 * @param testError 1064 * test error to be fixed 1065 * @param fix 1066 * the fix variant to be adopted 1067 * @return List of modified relation members to be applied to the route 1068 * relation 1069 */ 1070 private static List<RelationMember> getModifiedRelationMembers(TestError testError, List<PTWay> fix) { 1071 PTRouteSegment wrongSegment = wrongSegments.get(testError); 1072 Relation originalRelation = (Relation) testError.getPrimitives().iterator().next(); 1073 1074 // copy stops first: 1075 List<RelationMember> modifiedRelationMembers = listStopMembers(originalRelation); 1076 1077 // copy PTWays last: 1078 List<RelationMember> waysOfOriginalRelation = listNotStopMembers(originalRelation); 1079 for (int i = 0; i < waysOfOriginalRelation.size(); i++) { 1080 if (waysOfOriginalRelation.get(i).getWay() == wrongSegment.getPTWays().get(0).getWays().get(0)) { 1081 modifiedRelationMembers.addAll(fix); 1082 i = i + wrongSegment.getPTWays().size() - 1; 1083 } else { 1084 modifiedRelationMembers.add(waysOfOriginalRelation.get(i)); 1085 } 1086 } 1087 1088 return modifiedRelationMembers; 1089 } 1090 1091 public static void carryOutRepeatLastFix(PTRouteSegment segment) { 1092 1093 List<TestError> wrongSegmentsToRemove = new ArrayList<>(); 1094 1095 // find all wrong ways that have the same segment: 1096 for (TestError testError : wrongSegments.keySet()) { 1097 PTRouteSegment wrongSegment = wrongSegments.get(testError); 1098 if (wrongSegment.getFirstWay() == segment.getFirstWay() 1099 && wrongSegment.getLastWay() == segment.getLastWay()) { 1100 // modify the route: 1101 Relation originalRelation = wrongSegment.getRelation(); 1102 Relation modifiedRelation = new Relation(originalRelation); 1103 modifiedRelation.setMembers(getModifiedRelationMembers(testError, segment.getPTWays())); 1104 ChangeCommand changeCommand = new ChangeCommand(originalRelation, modifiedRelation); 1105 Main.main.undoRedo.addNoRedraw(changeCommand); 1106 Main.main.undoRedo.afterAdd(); 1107 wrongSegmentsToRemove.add(testError); 1108 } 1109 } 1110 1111 // update the errors displayed in the validator dialog: 1112 List<TestError> modifiedValidatorTestErrors = new ArrayList<>(); 1113 for (TestError validatorTestError : Main.map.validatorDialog.tree.getErrors()) { 1114 if (!wrongSegmentsToRemove.contains(validatorTestError)) { 1115 modifiedValidatorTestErrors.add(validatorTestError); 1116 } 1117 } 1118 Main.map.validatorDialog.tree.setErrors(modifiedValidatorTestErrors); 1119 1120 // update wrong segments: 1121 for (TestError testError : wrongSegmentsToRemove) { 1122 wrongSegments.remove(testError); 1123 } 1124 1125 } 1126 1127 /** 1128 * Resets the static list variables (used for unit tests and in Test.startTest() method) 1129 */ 1130 protected static void reset() { 1131 correctSegments.clear(); 1132 wrongSegments.clear(); 1133 wrongSegmentBuilders.clear(); 1134 } 1135 1135 1136 1136 } -
applications/editors/josm/plugins/pt_assistant/test/unit/org/openstreetmap/josm/plugins/pt_assistant/actions/SplitRoundaboutTest.java
r33415 r33417 71 71 @Test 72 72 public void test1() { 73 74 73 Collection<Way> sw1 = splitWay(r1); 74 assertEquals(4, sw1.size()); 75 75 sw1.forEach(w -> { 76 76 if (w.firstNode().getUniqueId() == 267843779L && w.lastNode().getUniqueId() == 2968718407L) … … 89 89 @Test 90 90 public void test2() { 91 92 91 Collection<Way> sw2 = splitWay(r2); 92 assertEquals(4, sw2.size()); 93 93 sw2.forEach(w -> { 94 94 if(w.firstNode().getUniqueId() == 2158181809L && w.lastNode().getUniqueId() == 2158181798L) … … 107 107 @Test 108 108 public void test3() { 109 110 109 Collection<Way> sw3 = splitWay(r3); 110 assertEquals(4, sw3.size()); 111 111 sw3.forEach(w -> { 112 112 if(w.firstNode().getUniqueId() == 280697532L && w.lastNode().getUniqueId() == 280697452L) … … 125 125 @Test 126 126 public void test4() { 127 128 129 130 131 132 133 134 135 136 137 138 127 Collection<Way> sw4 = splitWay(r4); 128 assertEquals(10, sw4.size()); 129 Node entry11 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nentry1-1")).iterator().next(); 130 Node exit11 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nexit1-1")).iterator().next(); 131 Node entry12 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nentry1-2")).iterator().next(); 132 Node exit12 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nexit1-2")).iterator().next(); 133 Node entry21 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nentry2-1")).iterator().next(); 134 Node exit21 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nexit2-1")).iterator().next(); 135 Node entry22 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nentry2-2")).iterator().next(); 136 Node exit22 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nexit2-2")).iterator().next(); 137 Node entry3 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nentry3")).iterator().next(); 138 Node exit3 = (Node) ds.getPrimitives(p -> p.hasTag("name", "nexit3")).iterator().next(); 139 139 140 140 sw4.forEach(w -> {
Note:
See TracChangeset
for help on using the changeset viewer.