Changeset 33417 in osm


Ignore:
Timestamp:
2017-06-28T18:00:52+02:00 (7 years ago)
Author:
giackserva
Message:

[pt_assistant] checkstyle

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  
    105105        lastFix = segment;
    106106        SwingUtilities.invokeLater(() ->
    107                 repeatLastFixMenu.setEnabled(segment != null));
     107        repeatLastFixMenu.setEnabled(segment != null));
    108108    }
    109109
     
    117117    }
    118118
    119         public static List<Relation> getHighlightedRelations() {
    120                 return new ArrayList<>(highlightedRelations);
    121         }
     119    public static List<Relation> getHighlightedRelations() {
     120        return new ArrayList<>(highlightedRelations);
     121    }
    122122
    123         public static void addHighlightedRelation(Relation highlightedRelation) {
    124                 highlightedRelations.add(highlightedRelation);
    125                 if(!editHighlightedRelationsMenu.isEnabled()) {
    126                         SwingUtilities.invokeLater(() ->
    127                                 editHighlightedRelationsMenu.setEnabled(true));
    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    }
    130130
    131         public static void clearHighlightedRelations() {
    132                 highlightedRelations.clear();
    133                 SwingUtilities.invokeLater(() ->
    134                 editHighlightedRelationsMenu.setEnabled(false));
    135         }
     131    public static void clearHighlightedRelations() {
     132        highlightedRelations.clear();
     133        SwingUtilities.invokeLater(() ->
     134        editHighlightedRelationsMenu.setEnabled(false));
     135    }
    136136}
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/AddStopPositionAction.java

    r33409 r33417  
    3333public class AddStopPositionAction extends MapMode {
    3434
    35         private static final String mapModeName = "Add stop position";
     35    private static final String mapModeName = "Add stop position";
    3636
    37         private transient Set<OsmPrimitive> newHighlights = new HashSet<>();
    38         private transient Set<OsmPrimitive> oldHighlights = new HashSet<>();
     37    private transient Set<OsmPrimitive> newHighlights = new HashSet<>();
     38    private transient Set<OsmPrimitive> oldHighlights = new HashSet<>();
    3939
    4040    private final Cursor cursorJoinNode;
     
    4444     * Creates a new AddStopPositionAction
    4545     */
    46         public AddStopPositionAction() {
    47                 super(tr(mapModeName), "bus", tr(mapModeName),
    48                                 Shortcut.registerShortcut("mapmode:stop_position",
     46    public AddStopPositionAction() {
     47        super(tr(mapModeName), "bus", tr(mapModeName),
     48                Shortcut.registerShortcut("mapmode:stop_position",
    4949                        tr("Mode: {0}", tr(mapModeName)),
    5050                        KeyEvent.VK_K, Shortcut.CTRL_SHIFT),
    51                                 getCursor());
     51                getCursor());
    5252
    53                 cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode");
     53        cursorJoinNode = ImageProvider.getCursor("crosshair", "joinnode");
    5454        cursorJoinWay = ImageProvider.getCursor("crosshair", "joinway");
    55         }
     55    }
    5656
    5757    private static Cursor getCursor() {
    58         Cursor cursor = ImageProvider.getCursor("crosshair", "bus");
    59         if(cursor == null)
    60                 cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
    61         return cursor;
     58        Cursor cursor = ImageProvider.getCursor("crosshair", "bus");
     59        if(cursor == null)
     60            cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
     61        return cursor;
    6262    }
    6363
     
    7979    public void mouseMoved(MouseEvent e) {
    8080
    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();
     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();
    8585
    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);
     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);
    9393
    94                 if(!wss.isEmpty()) {
    95                         for(WaySegment ws : wss) {
    96                                 newHighlights.add(ws.way);
    97                         }
    98                         newCurs = cursorJoinWay;
    99                 }
    100                 }
     94            if(!wss.isEmpty()) {
     95                for(WaySegment ws : wss) {
     96                    newHighlights.add(ws.way);
     97                }
     98                newCurs = cursorJoinWay;
     99            }
     100        }
    101101
    102         Main.map.mapView.setCursor(newCurs);
    103         updateHighlights();
     102        Main.map.mapView.setCursor(newCurs);
     103        updateHighlights();
    104104    }
    105105
     
    107107    public void mouseClicked(MouseEvent e) {
    108108
    109         Boolean newNode = false;
    110         Node newStopPos;
     109        Boolean newNode = false;
     110        Node newStopPos;
    111111
    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);
     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);
    114114        if (n == null) {
    115                 newNode = true;
    116                 newStopPos = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
     115            newNode = true;
     116            newStopPos = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
    117117        } else {
    118                 newStopPos = new Node(n);
     118            newStopPos = new Node(n);
    119119            clearNodeTags(newStopPos);
    120120        }
    121121
    122122        //add the tags of the stop position
    123         newStopPos.put("bus", "yes");
    124         newStopPos.put("public_transport", "stop_position");
     123        newStopPos.put("bus", "yes");
     124        newStopPos.put("public_transport", "stop_position");
    125125
    126         if(newNode) {
    127                 Main.main.undoRedo.add(new AddCommand(newStopPos));
    128         } else {
    129                 Main.main.undoRedo.add(new ChangeCommand(n, newStopPos));
    130         }
     126        if(newNode) {
     127            Main.main.undoRedo.add(new AddCommand(newStopPos));
     128        } else {
     129            Main.main.undoRedo.add(new ChangeCommand(n, newStopPos));
     130        }
    131131
    132         DataSet ds = Main.getLayerManager().getEditLayer().data;
    133         ds.setSelected(newStopPos);
     132        DataSet ds = Main.getLayerManager().getEditLayer().data;
     133        ds.setSelected(newStopPos);
    134134
    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         }
     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        }
    140140
    141141        // split the way in any case
     
    145145
    146146    private void clearNodeTags(Node newStopPos) {
    147                 for(String key : newStopPos.keySet()) {
    148                         newStopPos.put(key, null);
    149                 }
     147        for(String key : newStopPos.keySet()) {
     148            newStopPos.put(key, null);
     149        }
    150150
    151         }
     151    }
    152152
    153         //turn off what has been highlighted on last mouse move and highlight what has to be highlighted now
     153    //turn off what has been highlighted on last mouse move and highlight what has to be highlighted now
    154154    private void updateHighlights()
    155155    {
    156         if(oldHighlights.isEmpty() && newHighlights.isEmpty()) {
    157                 return;
    158         }
     156        if(oldHighlights.isEmpty() && newHighlights.isEmpty()) {
     157            return;
     158        }
    159159
    160                 for(OsmPrimitive osm : oldHighlights) {
    161                 osm.setHighlighted(false);
    162         }
     160        for(OsmPrimitive osm : oldHighlights) {
     161            osm.setHighlighted(false);
     162        }
    163163
    164         for(OsmPrimitive osm : newHighlights) {
    165                 osm.setHighlighted(true);
    166         }
     164        for(OsmPrimitive osm : newHighlights) {
     165            osm.setHighlighted(true);
     166        }
    167167
    168                 Main.getLayerManager().getEditLayer().invalidate();
     168        Main.getLayerManager().getEditLayer().invalidate();
    169169
    170         oldHighlights.clear();
    171         oldHighlights.addAll(newHighlights);
    172         newHighlights.clear();
     170        oldHighlights.clear();
     171        oldHighlights.addAll(newHighlights);
     172        newHighlights.clear();
    173173    }
    174174}
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/EdgeSelectionAction.java

    r33409 r33417  
    3030public class EdgeSelectionAction extends MapMode {
    3131
    32         private static final String mapModeName = "Edge Selection";
    33         private static final long serialVersionUID = 2414977774504904238L;
     32    private static final String mapModeName = "Edge Selection";
     33    private static final long serialVersionUID = 2414977774504904238L;
    3434
    35         private transient  Set<Way> highlighted;
     35    private transient  Set<Way> highlighted;
    3636
    37         private Cursor selectionCursor;
    38         private Cursor waySelectCursor;
     37    private Cursor selectionCursor;
     38    private Cursor waySelectCursor;
    3939
    40         public EdgeSelectionAction() {
    41                 super(tr(mapModeName), "edgeSelection", tr(mapModeName),
    42                                 Shortcut.registerShortcut("mapmode:edge_selection",
     40    public EdgeSelectionAction() {
     41        super(tr(mapModeName), "edgeSelection", tr(mapModeName),
     42                Shortcut.registerShortcut("mapmode:edge_selection",
    4343                        tr("Mode: {0}", tr(mapModeName)),
    4444                        KeyEvent.VK_K, Shortcut.CTRL),
    45                                 ImageProvider.getCursor("normal", "selection"));
    46                 highlighted = new HashSet<>();
     45                ImageProvider.getCursor("normal", "selection"));
     46        highlighted = new HashSet<>();
    4747
    48                 selectionCursor = ImageProvider.getCursor("normal", "selection");
    49                 waySelectCursor = ImageProvider.getCursor("normal", "select_way");
    50         }
     48        selectionCursor = ImageProvider.getCursor("normal", "selection");
     49        waySelectCursor = ImageProvider.getCursor("normal", "select_way");
     50    }
    5151
    5252    /*
     
    5555     */
    5656    private List<Way> getEdgeFromWay(Way initial, String modeOfTravel) {
    57         List<Way> edge = new ArrayList<>();
    58         if(!isWaySuitableForMode(initial, modeOfTravel))
    59                 return edge;
     57        List<Way> edge = new ArrayList<>();
     58        if(!isWaySuitableForMode(initial, modeOfTravel))
     59            return edge;
    6060
    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         }
     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        }
    7070
    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         }
     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        }
    8080
    81         edge.add(initial);
    82         return edge;
     81        edge.add(initial);
     82        return edge;
    8383    }
    8484
    8585    private Boolean isWaySuitableForMode(Way toCheck, String modeOfTravel) {
    86         if("bus".equals(modeOfTravel))
    87                 return RouteUtils.isWaySuitableForBuses(toCheck);
     86        if("bus".equals(modeOfTravel))
     87            return RouteUtils.isWaySuitableForBuses(toCheck);
    8888
    89         return RouteUtils.isWaySuitableForPublicTransport(toCheck);
     89        return RouteUtils.isWaySuitableForPublicTransport(toCheck);
    9090    }
    9191
     
    9494     */
    9595    private Way chooseBestWay(List<Way> ways, String modeOfTravel) {
    96         ways.removeIf(w -> !isWaySuitableForMode(w, modeOfTravel));
    97         if(ways.isEmpty())
    98                 return null;
    99         if(ways.size() == 1)
    100                 return ways.get(0);
     96        ways.removeIf(w -> !isWaySuitableForMode(w, modeOfTravel));
     97        if(ways.isEmpty())
     98            return null;
     99        if(ways.size() == 1)
     100            return ways.get(0);
    101101
    102         Way theChoosenOne = null;
     102        Way theChoosenOne = null;
    103103
    104         if("bus".equals(modeOfTravel))
    105         {
     104        if("bus".equals(modeOfTravel))
     105        {
    106106
    107         }
    108         if("tram".equals(modeOfTravel))
    109         {
     107        }
     108        if("tram".equals(modeOfTravel))
     109        {
    110110
    111         }
     111        }
    112112
    113         return theChoosenOne;
     113        return theChoosenOne;
    114114    }
    115115
    116116    private String getModeOfTravel() {
    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         }
     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    }
    121121
    122         @Override
     122    @Override
    123123    public void mouseClicked(MouseEvent e) {
    124124
    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();
     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();
    132132    }
    133133
    134134    @Override
    135135    public void mouseMoved(MouseEvent e) {
    136         super.mouseMoved(e);
     136        super.mouseMoved(e);
    137137
    138         for(Way way : highlighted)
    139                 way.setHighlighted(false);
    140         highlighted.clear();
     138        for(Way way : highlighted)
     139            way.setHighlighted(false);
     140        highlighted.clear();
    141141
    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         }
     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        }
    150150
    151         for(Way way : highlighted)
    152                 way.setHighlighted(true);
     151        for(Way way : highlighted)
     152            way.setHighlighted(true);
    153153    }
    154154
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/EditHighlightedRelationsAction.java

    r33409 r33417  
    3333        super(tr(actionName), new ImageProvider("dialogs", "edit"), tr(actionName),
    3434                Shortcut.registerShortcut("Edit Highlighted Relation", tr(actionName),
    35                                 KeyEvent.VK_K, Shortcut.ALT),
     35                        KeyEvent.VK_K, Shortcut.ALT),
    3636                false, "editHighlightedRelations", false);
    3737    }
     
    3939    @Override
    4040    public void actionPerformed(ActionEvent e) {
    41         for(Relation relation : PTAssistantPlugin.getHighlightedRelations()) {
    42                 RelationEditor editor = RelationEditor.getEditor(
    43                                 Main.getLayerManager().getEditLayer(), relation, null);
     41        for(Relation relation : PTAssistantPlugin.getHighlightedRelations()) {
     42            RelationEditor editor = RelationEditor.getEditor(
     43                    Main.getLayerManager().getEditLayer(), relation, null);
    4444            editor.setVisible(true);
    45         }
     45        }
    4646    }
    4747
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/actions/SplitRoundaboutAction.java

    r33416 r33417  
    111111        List<Node> splitNodes = getSplitNodes(roundabout);
    112112        SplitWayResult result = SplitWayAction.split(getLayerManager().getEditLayer(),
    113                         roundabout, splitNodes, Collections.emptyList());
     113                roundabout, splitNodes, Collections.emptyList());
    114114        Main.main.undoRedo.add(result.getCommand());
    115115        Collection<Way> splitWays = result.getNewWays();
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/data/PTRouteSegment.java

    r33387 r33417  
    187187    public boolean equalsRouteSegment(PTRouteSegment other) {
    188188
    189 //      if(!firstStop.equalsStop(firstStop) || !lastStop.equalsStop(other.lastStop))
    190 //              return false;
     189//      if(!firstStop.equalsStop(firstStop) || !lastStop.equalsStop(other.lastStop))
     190//          return false;
    191191
    192192        List<Way> thisWays = new ArrayList<>();
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/gui/PTAssistantLayer.java

    r33414 r33417  
    128128    public void setPrimitives(List<OsmPrimitive> primitives)
    129129    {
    130         this.primitives.clear();
    131         this.primitives.addAll(primitives);
     130        this.primitives.clear();
     131        this.primitives.addAll(primitives);
    132132    }
    133133
     
    267267
    268268        if(event.getRemovedLayer() == this) {
    269                 PTAssistantLayerManager.PTLM.resetLayer();
    270                 PTAssistantPlugin.clearHighlightedRelations();
     269            PTAssistantLayerManager.PTLM.resetLayer();
     270            PTAssistantPlugin.clearHighlightedRelations();
    271271        }
    272272    }
     
    274274    @Override
    275275    public synchronized void destroy() {
    276         KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(this);
     276        KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(this);
    277277        Main.getLayerManager().removeLayerChangeListener(this);
    278         super.destroy();
     278        super.destroy();
    279279    }
    280280}
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/gui/PTAssistantLayerManager.java

    r33414 r33417  
    1414public class PTAssistantLayerManager implements SelectionChangedListener {
    1515
    16         public final static PTAssistantLayerManager PTLM = new PTAssistantLayerManager();
    17         private PTAssistantLayer layer;
     16    public final static PTAssistantLayerManager PTLM = new PTAssistantLayerManager();
     17    private PTAssistantLayer layer;
    1818
    1919    public PTAssistantLayer getLayer() {
     
    2525
    2626    public void resetLayer() {
    27         layer = null;
     27        layer = null;
    2828    }
    2929
     
    3838        for (OsmPrimitive primitive : newSelection) {
    3939            if (primitive.getType().equals(OsmPrimitiveType.RELATION)
    40                         && RouteUtils.isVersionTwoPTRoute((Relation) primitive)) {
    41                 routes.add(primitive);
     40                    && RouteUtils.isVersionTwoPTRoute((Relation) primitive)) {
     41                routes.add(primitive);
    4242            }
    4343        }
    4444
    4545        if (!routes.isEmpty()) {
    46                 getLayer().setPrimitives(routes);
    47                 PTAssistantPlugin.clearHighlightedRelations();
    48                 for(OsmPrimitive primitive : routes)
    49                         PTAssistantPlugin.addHighlightedRelation((Relation) primitive);
     46            getLayer().setPrimitives(routes);
     47            PTAssistantPlugin.clearHighlightedRelations();
     48            for(OsmPrimitive primitive : routes)
     49                PTAssistantPlugin.addHighlightedRelation((Relation) primitive);
    5050        }
    5151    }
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/gui/PTAssistantPaintVisitor.java

    r33414 r33417  
    286286    @Override
    287287    protected void drawNode(Node n, Color color) {
    288                 if (mv == null || g == null) {
    289                         return;
    290                 }
     288        if (mv == null || g == null) {
     289            ;
     290        }
    291291        Point p = mv.getPoint(n);
    292                 if (p == null) {
    293                         return;
    294                 }
     292        if (p == null) {
     293            return;
     294        }
    295295        g.setColor(color);
    296296        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  
    1919public final class RouteUtils {
    2020
    21         private final static String ptVersionTag = "public_transport:version";
     21    private final static String ptVersionTag = "public_transport:version";
    2222    private RouteUtils() {
    2323        // private constructor for util classes
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/NodeChecker.java

    r33082 r33417  
    2828public class NodeChecker extends Checker {
    2929
    30         protected NodeChecker(Node node, Test test) {
    31                 super(node, test);
     30    protected NodeChecker(Node node, Test test) {
     31        super(node, test);
    3232
    33         }
     33    }
    3434
    35         /**
    36         * Checks if the given stop_position node belongs to any way
    37         */
    38         protected void performSolitaryStopPositionTest() {
     35    /**
     36    * Checks if the given stop_position node belongs to any way
     37    */
     38    protected void performSolitaryStopPositionTest() {
    3939
    40                 List<OsmPrimitive> referrers = node.getReferrers();
     40        List<OsmPrimitive> referrers = node.getReferrers();
    4141
    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                                 }
     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                }
    4848
    49                         }
    50                 }
     49            }
     50        }
    5151
    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);
     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);
    5959
    60         }
     60    }
    6161
    62         /**
    63         * Checks if the given platform node belongs to any way
    64         */
    65         protected void performPlatformPartOfWayTest() {
     62    /**
     63    * Checks if the given platform node belongs to any way
     64    */
     65    protected void performPlatformPartOfWayTest() {
    6666
    67                 List<OsmPrimitive> referrers = node.getReferrers();
     67        List<OsmPrimitive> referrers = node.getReferrers();
    6868
    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         }
     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    }
    8585
    86         /**
    87         * Checks if the given stop_position node belongs to any stop_area relation
    88          *
    89         * @author xamanu
    90         */
    91         protected void performNodePartOfStopAreaTest() {
     86    /**
     87    * Checks if the given stop_position node belongs to any stop_area relation
     88     *
     89    * @author xamanu
     90    */
     91    protected void performNodePartOfStopAreaTest() {
    9292
    93                 if (!StopUtils.verifyIfMemberOfStopArea(node)) {
     93        if (!StopUtils.verifyIfMemberOfStopArea(node)) {
    9494
    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         }
     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    }
    104104
    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) {
     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) {
    114114
    115                 if (testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_SOLITARY_STOP_POSITION
    116                                 && testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_PLATFORM_PART_OF_HIGHWAY) {
    117                         return null;
    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        }
    119119
    120                 Node problematicNode = (Node) testError.getPrimitives().iterator().next();
     120        Node problematicNode = (Node) testError.getPrimitives().iterator().next();
    121121
    122                 final int[] userSelection = { JOptionPane.YES_OPTION };
    123                 final TestError errorParameter = testError;
    124                 if (SwingUtilities.isEventDispatchThread()) {
     122        final int[] userSelection = { JOptionPane.YES_OPTION };
     123        final TestError errorParameter = testError;
     124        if (SwingUtilities.isEventDispatchThread()) {
    125125
    126                         userSelection[0] = showFixNodeTagDialog(errorParameter);
     126            userSelection[0] = showFixNodeTagDialog(errorParameter);
    127127
    128                 } else {
     128        } else {
    129129
    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                 }
     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        }
    142142
    143                 if (userSelection[0] == JOptionPane.YES_OPTION) {
     143        if (userSelection[0] == JOptionPane.YES_OPTION) {
    144144
    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                 }
     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        }
    156156
    157                 return null;
     157        return null;
    158158
    159         }
     159    }
    160160
    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);
     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);
    168168
    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         }
     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    }
    179179
    180180}
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/PTAssistantValidatorTest.java

    r33414 r33417  
    4444public class PTAssistantValidatorTest extends Test {
    4545
    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         }
     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    }
    591591
    592592}
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/RouteChecker.java

    r33405 r33417  
    9494     */
    9595    private int countGaps(List<RelationMember> waysToCheck) {
    96         int numberOfGaps = 0;
     96        int numberOfGaps = 0;
    9797
    98         WayConnectionTypeCalculator connectionTypeCalculator = new WayConnectionTypeCalculator();
     98        WayConnectionTypeCalculator connectionTypeCalculator = new WayConnectionTypeCalculator();
    9999        final List<WayConnectionType> links = connectionTypeCalculator.updateLinks(waysToCheck);
    100100        for (int i = 0; i < links.size(); i++) {
     
    102102            if(!(i == 0 || link.linkPrev) || !(i == links.size() - 1 || link.linkNext)
    103103                    || link.direction == null || WayConnectionType.Direction.NONE.equals(link.direction)) {
    104                 numberOfGaps++;
    105                 i++;
     104                numberOfGaps++;
     105                i++;
    106106            }
    107107        }
     
    124124    protected static Command fixSortingError(TestError testError) {
    125125        if (testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_SORTING
    126                         && testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_PARTIAL_SORTING) {
     126                && testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_PARTIAL_SORTING) {
    127127            return null;
    128128        }
  • applications/editors/josm/plugins/pt_assistant/src/org/openstreetmap/josm/plugins/pt_assistant/validation/SegmentChecker.java

    r33387 r33417  
    4949public class SegmentChecker extends Checker {
    5050
    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         }
     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    }
    11351135
    11361136}
  • applications/editors/josm/plugins/pt_assistant/test/unit/org/openstreetmap/josm/plugins/pt_assistant/actions/SplitRoundaboutTest.java

    r33415 r33417  
    7171    @Test
    7272    public void test1() {
    73         Collection<Way> sw1 = splitWay(r1);
    74         assertEquals(4, sw1.size());
     73        Collection<Way> sw1 = splitWay(r1);
     74        assertEquals(4, sw1.size());
    7575        sw1.forEach(w -> {
    7676            if (w.firstNode().getUniqueId() == 267843779L && w.lastNode().getUniqueId() == 2968718407L)
     
    8989    @Test
    9090    public void test2() {
    91         Collection<Way> sw2 = splitWay(r2);
    92         assertEquals(4, sw2.size());
     91        Collection<Way> sw2 = splitWay(r2);
     92        assertEquals(4, sw2.size());
    9393        sw2.forEach(w -> {
    9494            if(w.firstNode().getUniqueId() == 2158181809L && w.lastNode().getUniqueId() == 2158181798L)
     
    107107    @Test
    108108    public void test3() {
    109         Collection<Way> sw3 = splitWay(r3);
    110         assertEquals(4, sw3.size());
     109        Collection<Way> sw3 = splitWay(r3);
     110        assertEquals(4, sw3.size());
    111111        sw3.forEach(w -> {
    112112            if(w.firstNode().getUniqueId() == 280697532L && w.lastNode().getUniqueId() == 280697452L)
     
    125125    @Test
    126126    public void test4() {
    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();
     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();
    139139
    140140        sw4.forEach(w -> {
Note: See TracChangeset for help on using the changeset viewer.