// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.pt_assistant.validation; import static org.openstreetmap.josm.tools.I18n.tr; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.openstreetmap.josm.command.ChangeCommand; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.data.validation.Severity; import org.openstreetmap.josm.data.validation.Test; import org.openstreetmap.josm.data.validation.TestError; import org.openstreetmap.josm.data.validation.TestError.Builder; import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter; import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType; import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionTypeCalculator; import org.openstreetmap.josm.plugins.pt_assistant.utils.RouteUtils; /** * Performs tests of a route at the level of the whole route: sorting test * * @author darya * */ public class RouteChecker extends Checker { private boolean hasGap; List<RelationMember> sortedMembers; public RouteChecker(Relation relation, Test test) { super(relation, test); this.hasGap = false; } protected void performSortingTest() { final List<RelationMember> waysToCheck = new ArrayList<>(); for (RelationMember rm : relation.getMembers()) { if (RouteUtils.isPTWay(rm) && rm.getType().equals(OsmPrimitiveType.WAY)) { waysToCheck.add(rm); } } if (waysToCheck.isEmpty()) { return; } if (hasGap(waysToCheck)) { this.hasGap = true; RelationSorter sorter = new RelationSorter(); sortedMembers = sorter.sortMembers(waysToCheck); if (!hasGap(sortedMembers)) { Builder builder = TestError.builder(this.test, Severity.WARNING, PTAssistantValidatorTest.ERROR_CODE_SORTING); builder.message(tr("PT: Route contains a gap that can be fixed by sorting")); builder.primitives(relation); TestError e = builder.build(); this.errors.add(e); } } } /** * Checks if there is a gap for a given list of ways. It does not check if * the way actually stands for a public transport platform - that should be * checked beforehand. * * @param waysToCheck ways to check * @return true if has gap (in the sense of continuity of ways in the * Relation Editor), false otherwise */ private boolean hasGap(List<RelationMember> waysToCheck) { WayConnectionTypeCalculator connectionTypeCalculator = new WayConnectionTypeCalculator(); final List<WayConnectionType> links = connectionTypeCalculator.updateLinks(waysToCheck); for (int i = 0; i < links.size(); i++) { final WayConnectionType link = links.get(i); final boolean hasError = !(i == 0 || link.linkPrev) || !(i == links.size() - 1 || link.linkNext) || link.direction == null || WayConnectionType.Direction.NONE.equals(link.direction); if (hasError) { return true; } } return false; } public List<RelationMember> getSortedMembers() { return sortedMembers; } public boolean getHasGap() { return this.hasGap; } protected static Command fixSortingError(TestError testError) { if (testError.getCode() != PTAssistantValidatorTest.ERROR_CODE_SORTING) { return null; } Collection<? extends OsmPrimitive> primitives = testError.getPrimitives(); Relation originalRelation = (Relation) primitives.iterator().next(); // separate ways from stops (because otherwise the order of // stops/platforms can be messed up by the sorter: List<RelationMember> members = originalRelation.getMembers(); final List<RelationMember> stops = new ArrayList<>(); final List<RelationMember> ways = new ArrayList<>(); for (RelationMember member : members) { if (RouteUtils.isPTWay(member)) { if (member.getRole().equals("")) { ways.add(member); } else { RelationMember modifiedMember = new RelationMember("", member.getWay()); ways.add(modifiedMember); } } else { // stops: if (member.getRole().equals("stop_positon")) { // it is not expected that stop_positions could // be relations if (member.getType().equals(OsmPrimitiveType.NODE)) { RelationMember modifiedMember = new RelationMember("stop", member.getNode()); stops.add(modifiedMember); } else { // if it is a primitive of type way: RelationMember modifiedMember = new RelationMember("stop", member.getWay()); stops.add(modifiedMember); } } else { // if it is not a stop_position: stops.add(member); } } } // sort the ways: RelationSorter sorter = new RelationSorter(); List<RelationMember> sortedWays = sorter.sortMembers(ways); // create a new relation to pass to the command: Relation sortedRelation = new Relation(originalRelation); List<RelationMember> sortedRelationMembers = new ArrayList<>(members.size()); for (RelationMember rm : stops) { sortedRelationMembers.add(rm); } for (RelationMember rm : sortedWays) { sortedRelationMembers.add(rm); } sortedRelation.setMembers(sortedRelationMembers); ChangeCommand changeCommand = new ChangeCommand(originalRelation, sortedRelation); return changeCommand; } }