From 023ec80bb9ccedc98b6bdc79c84e78c7afe71a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=A4fer?= Date: Mon, 24 Aug 2020 10:08:19 +0200 Subject: [PATCH 01/17] Start creating the routing helper rewrite, at the moment just a bit of UI that doesn't do much yet --- gradle.properties | 2 +- .../routinghelper/BusTransportMode.java | 44 +++++++++ .../actions/routinghelper/ITransportMode.java | 43 ++++++++ .../routinghelper/RoutingHelperAction.java | 78 +++++++++++++++ .../routinghelper/RoutingHelperPanel.java | 97 +++++++++++++++++++ .../routinghelper/WayTraversalDirection.java | 48 +++++++++ .../pt_assistant/utils/BoundsUtils.java | 10 ++ 7 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java diff --git a/gradle.properties b/gradle.properties index 914aa89a..98c7099d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,6 @@ plugin.canloadatruntime=true plugin.icon=images/bus.svg # Minimum JOSM version (any numeric version) -plugin.main.version=15238 +plugin.main.version=15418 # JOSM version to compile against (any numeric version available for download, or the special values "tested" or "latest") plugin.compile.version=17084 diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java new file mode 100644 index 00000000..003d17ef --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java @@ -0,0 +1,44 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; + +public class BusTransportMode implements ITransportMode { + @Override + public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { + final String onewayValue = way.get("oneway"); + return way.hasTag("highway", "primary", "secondary", "tertiary", "residential") && ( + onewayValue == null || "no".equals(way.get("oneway:bus")) || + ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || + ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); + } + + @Override + public boolean canBeUsedForRelation(@NotNull final IRelation relation) { + return relation.hasTag("route", "bus"); + } + + @Override + public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { + final Set restrictionRelations = from.getReferrers().stream() + .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) + .filter(Objects::nonNull) + .filter(it -> "restriction".equals(it.get("type"))) + .filter(it -> it.findRelationMembers("from").contains(from)) + .filter(it -> it.findRelationMembers("via").contains(via)) + .filter(it -> it.findRelationMembers("to").contains(to)) + .collect(Collectors.toSet()); + // TODO: Use the `restrictionRelations` to figure out the turning restrictions that apply + return from.containsNode(via) && to.containsNode(via); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java new file mode 100644 index 00000000..3f6f9b15 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java @@ -0,0 +1,43 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.Way; + +public interface ITransportMode { + /** + * Just a convenience method for {@link #canTraverseWay(IWay, WayTraversalDirection)} that assumes {@link WayTraversalDirection#FORWARD} + * @param way the way for which we check, if it can be traversed by the transport mode + * @return {@code true} if the transport mode can travel along the way in the forward direction. Otherwise {@code false}. + */ + default boolean canTraverseWay(final Way way) { + return canTraverseWay(way, WayTraversalDirection.FORWARD); + } + + /** + * @param way the way that is checked, if the transport mode can traverse + * @param direction the travel direction for which we check + * @return {@code true} iff the transport mode can travel along the given way in the given direction. Otherwise {@code false}. + */ + boolean canTraverseWay(@NotNull IWay way, @NotNull WayTraversalDirection direction); + + /** + * Checks if this transport mode should be used for the given relation + * @param relation the relation that is checked, if it is suitable for the transport mode + * @return {@code true} if the transport mode is suitable for the relation. Otherwise {@code false}. + */ + boolean canBeUsedForRelation(@NotNull final IRelation relation); + + /** + * @param from the way from which the vehicle is coming + * @param via the node that the vehicle travels through, must be part of {@code from} and {@code to} ways, + * or this method will return false + * @param to the way onto which the vehicle makes the turn + * @return {@code true} iff the transport mode can make a turn from the given {@code from} way, + * via the given {@code via} node to the given {@code to} way. Otherwise {@code false}. + * This method assumes that both ways can be traversed by the transport mode, it does not check that. + */ + boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to); +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java new file mode 100644 index 00000000..fd7743b9 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java @@ -0,0 +1,78 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.awt.event.ActionEvent; +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +import javax.swing.JOptionPane; + +import com.drew.lang.annotations.NotNull; +import com.drew.lang.annotations.Nullable; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.gui.MainApplication; +import org.openstreetmap.josm.gui.MapFrame; +import org.openstreetmap.josm.gui.dialogs.relation.actions.AbstractRelationEditorAction; +import org.openstreetmap.josm.gui.dialogs.relation.actions.IRelationEditorActionAccess; +import org.openstreetmap.josm.gui.dialogs.relation.actions.IRelationEditorUpdateOn; +import org.openstreetmap.josm.plugins.pt_assistant.utils.BoundsUtils; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; + +public class RoutingHelperAction extends AbstractRelationEditorAction { + private static final Set TRANSPORT_MODES = Collections.singleton(new BusTransportMode()); + + private Optional activeTransportMode; + + private final RoutingHelperPanel routingHelperPanel = new RoutingHelperPanel(this); + + public RoutingHelperAction(IRelationEditorActionAccess editorAccess) { + super(editorAccess, IRelationEditorUpdateOn.TAG_CHANGE); + new ImageProvider("dialogs/relation", "routing_assistance.svg").getResource().attachImageIcon(this, true); + putValue(SHORT_DESCRIPTION, I18n.tr("Routing helper")); + } + + @Override + protected void updateEnabledState() { + final Relation currentRelation = getEditor().getRelation(); + final Optional newActiveTransportMode = TRANSPORT_MODES.stream() + .filter(mode -> mode.canBeUsedForRelation(currentRelation)) + .findFirst(); + this.activeTransportMode = newActiveTransportMode; + setEnabled(newActiveTransportMode.isPresent() && MainApplication.getMap().getTopPanel(RoutingHelperPanel.class) == null); + } + + @Override + public void actionPerformed(@NotNull final ActionEvent actionEvent) { + final MapFrame mapFrame = MainApplication.getMap(); + + if (mapFrame.getTopPanel(RoutingHelperPanel.class) == null) { + mapFrame.addTopPanel(routingHelperPanel); + updateEnabledState(); + } + + final Way currentWay = editorAccess.getEditor().getRelation().getMembers().stream().map(it -> it.isWay() ? it.getWay() : null).filter(Objects::nonNull).findFirst().orElse(null); + if (currentWay != null) { + MainApplication.getMap().mapView.zoomTo(BoundsUtils.fromBBox(currentWay.getBBox())); + } + routingHelperPanel.onCurrentWayChange(currentWay); + } + + public void goToPreviousGap() { + JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); + } + + public void goToPreviousWay() { + JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); + } + + public void goToNextWay() { + JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); + } + + public void goToNextGap() { + JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java new file mode 100644 index 00000000..8296bec4 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java @@ -0,0 +1,97 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.util.Optional; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import com.drew.lang.annotations.Nullable; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.gui.MainApplication; +import org.openstreetmap.josm.gui.MapFrame; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; + +/** + * The top panel that is added via {@link MapFrame#addTopPanel(Component)}. + * This class should just handle the display and input. The state of the routing helper should be handled in {@link RoutingHelperAction}. + */ +public class RoutingHelperPanel extends JPanel { + + private final JLabel wayLabel = new JLabel("Way"); + + private final RoutingHelperAction routingHelperAction; + + public RoutingHelperPanel(final RoutingHelperAction routingHelperAction) { + this.routingHelperAction = routingHelperAction; + + + // Style main label + wayLabel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + JButton closeButton = new JButton(ImageProvider.get("misc", "black_x")); + closeButton.setContentAreaFilled(false); + closeButton.setRolloverEnabled(true); + closeButton.setBorderPainted(false); + closeButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + closeButton.setToolTipText(I18n.tr("Close the routing helper")); + closeButton.addActionListener(e -> + Optional.ofNullable(MainApplication.getMap()) + .ifPresent(map -> { + map.removeTopPanel(RoutingHelperPanel.class); + routingHelperAction.updateEnabledState(); + }) + ); + + final JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setOpaque(false); + mainPanel.add(wayLabel, BorderLayout.CENTER); + mainPanel.add(closeButton, BorderLayout.EAST); + + // build the left and right button panels + final JPanel buttonLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + buttonLeftPanel.setOpaque(false); + final JButton prevGapButton = new JButton("« to previous gap"); + prevGapButton.addActionListener(e -> routingHelperAction.goToPreviousGap()); + buttonLeftPanel.add(prevGapButton); + final JButton prevButton = new JButton("‹ to previous way"); + prevButton.addActionListener(e -> routingHelperAction.goToPreviousWay()); + buttonLeftPanel.add(prevButton); + + final JPanel buttonRightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonRightPanel.setOpaque(false); + final JButton nextButton = new JButton("to next way ›"); + nextButton.addActionListener(e -> routingHelperAction.goToNextWay()); + buttonRightPanel.add(nextButton); + final JButton nextgapButton = new JButton("to next gap »"); + nextgapButton.addActionListener(e -> routingHelperAction.goToNextGap()); + buttonRightPanel.add(nextgapButton); + + // Combine both button panels into one + final JPanel buttonPanel = new JPanel(new BorderLayout()); + buttonPanel.setOpaque(false); + buttonPanel.add(buttonLeftPanel, BorderLayout.WEST); + buttonPanel.add(buttonRightPanel, BorderLayout.EAST); + + // put everything together + setBackground(new Color(0xFF9966)); + setLayout(new BorderLayout()); + add(mainPanel, BorderLayout.CENTER); + add(buttonPanel, BorderLayout.SOUTH); + } + + public void onCurrentWayChange(@Nullable final Way way) { + if (way == null) { + wayLabel.setText(I18n.tr("No way found in relation")); + } else { + wayLabel.setText(I18n.tr("Active way: {0} ({1} nodes)", way.getId(), way.getNodesCount())); + } + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java new file mode 100644 index 00000000..603d0b96 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java @@ -0,0 +1,48 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.util.function.Function; + +import com.drew.lang.annotations.NotNull; +import com.drew.lang.annotations.Nullable; +import org.openstreetmap.josm.data.osm.INode; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Way; + +public enum WayTraversalDirection { + FORWARD(IWay::firstNode, IWay::lastNode), + BACKWARD(IWay::lastNode, IWay::firstNode); + + private final Function, INode> startNodeGetter; + private final Function, INode> endNodeGetter; + + WayTraversalDirection( + @NotNull final Function, INode> startNodeGetter, + @NotNull final Function, INode> endNodeGetter + ) { + this.startNodeGetter = startNodeGetter; + this.endNodeGetter = endNodeGetter; + } + + /** + * Finds the first node that you come across when traversin the given way. + * + * @param way the way for which the first or last node is returned, depending on the direction + * @return {@link #FORWARD} returns the {@link Way#firstNode()}, {@link #BACKWARD} returns the {@link Way#lastNode()} + */ + @Nullable + public INode getStartNodeFor(@NotNull final IWay way) { + return startNodeGetter.apply(way); + } + + + /** + * Finds the node where traversal of the given way ends. + * + * @param way the way for which the first or last node is returned, depending on the direction + * @return {@link #FORWARD} returns the {@link Way#lastNode()}, {@link #BACKWARD} returns the {@link Way#firstNode()} + */ + @Nullable + public INode getEndNodeFor(@NotNull final IWay way) { + return endNodeGetter.apply(way); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/BoundsUtils.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/BoundsUtils.java index 9cca26a3..be800a2f 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/BoundsUtils.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/BoundsUtils.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.Optional; +import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.osm.BBox; import org.openstreetmap.josm.data.osm.Way; @@ -53,4 +54,13 @@ public static Optional createBoundsWithPadding(final BBox bbox, final do ); }); } + + /** + * Converts the given {@link BBox} to {@link Bounds} + * @param bbox the bbox to convert + * @return the bounds equivalent to the passed bbox + */ + public static Bounds fromBBox(@NotNull final BBox bbox) { + return new Bounds(bbox.getBottomRightLat(), bbox.getTopLeftLon(), bbox.getTopLeftLat(), bbox.getBottomRightLon()); + } } From 08c27067202da6089ee52f47b7d5403269ca2f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=A4fer?= Date: Sun, 15 Nov 2020 11:56:32 +0100 Subject: [PATCH 02/17] Automatically open routing helper when selecting a route relation, make forward/backward navigation work and show connectivity (very primitive check) --- .../pt_assistant/PTAssistantPlugin.java | 2 + .../routinghelper/RoutingHelperAction.java | 166 ++++++++++++--- .../routinghelper/RoutingHelperPanel.java | 200 +++++++++++++----- .../pt_assistant/utils/ColorPalette.java | 1 + .../plugins/pt_assistant/utils/GuiUtils.java | 58 +++++ .../plugins/pt_assistant/utils/PTIcons.java | 4 + .../pt_assistant/utils/RouteUtils.java | 14 ++ .../utils/UtilityClassesTest.java | 1 + 8 files changed, 363 insertions(+), 83 deletions(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/GuiUtils.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java index 43a1c058..90ddc35b 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java @@ -39,6 +39,7 @@ import org.openstreetmap.josm.plugins.pt_assistant.actions.SortPTRouteMembersAction; import org.openstreetmap.josm.plugins.pt_assistant.actions.SortPTRouteMembersMenuBar; import org.openstreetmap.josm.plugins.pt_assistant.actions.SplitRoundaboutAction; +import org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper.RoutingHelperAction; import org.openstreetmap.josm.plugins.pt_assistant.data.PTRouteSegment; import org.openstreetmap.josm.plugins.pt_assistant.gui.PTAssistantLayerManager; import org.openstreetmap.josm.plugins.pt_assistant.validation.BicycleFootRouteValidatorTest; @@ -79,6 +80,7 @@ public PTAssistantPlugin(PluginInformation info) { .addMenu("File", trc("menu", "Public Transport"), KeyEvent.VK_P, 5, ht("/Menu/Public Transport")); addToMenu(PublicTransportMenu); + SelectionEventManager.getInstance().addSelectionListener(new RoutingHelperAction()); SelectionEventManager.getInstance().addSelectionListener(PTAssistantLayerManager.PTLM); KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(PTAssistantLayerManager.PTLM); initialiseWizard(); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java index fd7743b9..692e69d8 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java @@ -1,63 +1,130 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; -import java.awt.event.ActionEvent; import java.util.Collections; -import java.util.Objects; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.stream.Collectors; import javax.swing.JOptionPane; import com.drew.lang.annotations.NotNull; -import com.drew.lang.annotations.Nullable; +import org.openstreetmap.josm.data.osm.AbstractPrimitive; +import org.openstreetmap.josm.data.osm.DataSelectionListener; import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.MapFrame; -import org.openstreetmap.josm.gui.dialogs.relation.actions.AbstractRelationEditorAction; -import org.openstreetmap.josm.gui.dialogs.relation.actions.IRelationEditorActionAccess; -import org.openstreetmap.josm.gui.dialogs.relation.actions.IRelationEditorUpdateOn; -import org.openstreetmap.josm.plugins.pt_assistant.utils.BoundsUtils; +import org.openstreetmap.josm.gui.Notification; +import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask; +import org.openstreetmap.josm.plugins.pt_assistant.utils.DialogUtils; +import org.openstreetmap.josm.plugins.pt_assistant.utils.RouteUtils; +import org.openstreetmap.josm.plugins.pt_assistant.utils.WayUtils; import org.openstreetmap.josm.tools.I18n; -import org.openstreetmap.josm.tools.ImageProvider; -public class RoutingHelperAction extends AbstractRelationEditorAction { +public class RoutingHelperAction implements DataSelectionListener { private static final Set TRANSPORT_MODES = Collections.singleton(new BusTransportMode()); private Optional activeTransportMode; private final RoutingHelperPanel routingHelperPanel = new RoutingHelperPanel(this); - public RoutingHelperAction(IRelationEditorActionAccess editorAccess) { - super(editorAccess, IRelationEditorUpdateOn.TAG_CHANGE); - new ImageProvider("dialogs/relation", "routing_assistance.svg").getResource().attachImageIcon(this, true); - putValue(SHORT_DESCRIPTION, I18n.tr("Routing helper")); - } + @NotNull + private Optional currentRelation = Optional.empty(); + + private Optional currentMember = Optional.empty(); @Override - protected void updateEnabledState() { - final Relation currentRelation = getEditor().getRelation(); - final Optional newActiveTransportMode = TRANSPORT_MODES.stream() - .filter(mode -> mode.canBeUsedForRelation(currentRelation)) - .findFirst(); - this.activeTransportMode = newActiveTransportMode; - setEnabled(newActiveTransportMode.isPresent() && MainApplication.getMap().getTopPanel(RoutingHelperPanel.class) == null); + public void selectionChanged(SelectionChangeEvent event) { + final MapFrame mapframe = MainApplication.getMap(); + if (mapframe != null) { + final Optional singleRelationSelection = Optional.of(event.getSelection()) + .filter(selection -> selection.size() == 1) + .map(selection -> selection.iterator().next()) + .map(selectedPrimitive -> selectedPrimitive instanceof Relation ? (Relation) selectedPrimitive : null) + .filter(RouteUtils::isRoute); + this.currentRelation = singleRelationSelection; + if (singleRelationSelection.isPresent()) { + routingHelperPanel.onRelationChange(singleRelationSelection.get()); + if (mapframe.getTopPanel(RoutingHelperPanel.class) == null) { + mapframe.addTopPanel(routingHelperPanel); + } + } else { + mapframe.removeTopPanel(RoutingHelperPanel.class); + } + } } - @Override - public void actionPerformed(@NotNull final ActionEvent actionEvent) { - final MapFrame mapFrame = MainApplication.getMap(); + public void goToFirstWay() { + final Optional currentRelation = this.currentRelation; + final long missingMembersCount = currentRelation + .map(it -> + it.getMembers().stream() + .filter(member -> member.getMember().isIncomplete()) + .count() + ) + .orElse(0L); + if (missingMembersCount > 0) { + if ( + DialogUtils.showYesNoQuestion( + routingHelperPanel, + I18n.tr("Relation is incomplete"), + I18n.trn( + "The relations has {0} missing member. Would you like to download the missing member now?", + "The relations has {0} missing members. Would you like to download the missing members now?", + missingMembersCount, + missingMembersCount + ) + ) + ) { + final Future f = MainApplication.worker.submit(new DownloadRelationMemberTask( + currentRelation.get(), + currentRelation.get().getMembers().stream() + .map(RelationMember::getMember) + .filter(AbstractPrimitive::isIncomplete) + .collect(Collectors.toSet()), + MainApplication.getLayerManager().getActiveDataLayer() + )); + new Thread(() -> { + try { + f.get(); - if (mapFrame.getTopPanel(RoutingHelperPanel.class) == null) { - mapFrame.addTopPanel(routingHelperPanel); - updateEnabledState(); - } + // try again, now the missingMembersCount should be 0, so we should go to the else-branch this time + goToFirstWay(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + JOptionPane.showMessageDialog( + routingHelperPanel, + I18n.tr("The download of missing members has failed!"), + I18n.tr("Download failed"), + JOptionPane.ERROR_MESSAGE + ); + } - final Way currentWay = editorAccess.getEditor().getRelation().getMembers().stream().map(it -> it.isWay() ? it.getWay() : null).filter(Objects::nonNull).findFirst().orElse(null); - if (currentWay != null) { - MainApplication.getMap().mapView.zoomTo(BoundsUtils.fromBBox(currentWay.getBBox())); + }).start(); + } + } else { + final List wayMembers = currentRelation.map(relation -> relation.getMembers().stream().filter(RouteUtils::isRouteWayMember).collect(Collectors.toList())).orElse(Collections.emptyList()); + this.currentMember = wayMembers.stream().findFirst(); + if (wayMembers.isEmpty()) { + JOptionPane.showMessageDialog(routingHelperPanel, "No way found to traverse", "Could not find a way to traverse", JOptionPane.ERROR_MESSAGE); + } else { + routingHelperPanel.onCurrentWayChange( + currentRelation.get(), + wayMembers.get(0), + RoutingHelperPanel.ConnectionType.END, + wayMembers.size() == 1 + ? RoutingHelperPanel.ConnectionType.END + : ( + WayUtils.isTouchingOtherWay(wayMembers.get(0).getWay(), wayMembers.get(1).getWay()) + ? RoutingHelperPanel.ConnectionType.CONNECTED + : RoutingHelperPanel.ConnectionType.NOT_CONNECTED + ) + ); + } } - routingHelperPanel.onCurrentWayChange(currentWay); } public void goToPreviousGap() { @@ -65,14 +132,43 @@ public void goToPreviousGap() { } public void goToPreviousWay() { - JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); + goNWaysForward(-1); } public void goToNextWay() { - JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); + goNWaysForward(1); + } + + private void goNWaysForward(final int n) { + currentRelation.ifPresent(relation -> + currentMember.ifPresent(member -> { + final List wayMembers = relation.getMembers().stream().filter(RouteUtils::isRouteWayMember).collect(Collectors.toList()); + final int targetIndex = wayMembers.indexOf(member) + n; + if (targetIndex < 0 || targetIndex >= wayMembers.size() - 1) { + new Notification(I18n.tr("You reached the end of the route")).setIcon(JOptionPane.INFORMATION_MESSAGE).setDuration(Notification.TIME_SHORT).show(); + } else { + currentMember = Optional.of(wayMembers.get(targetIndex)); + routingHelperPanel.onCurrentWayChange( + relation, + wayMembers.get(targetIndex), + targetIndex <= 0 ? RoutingHelperPanel.ConnectionType.END : ( + WayUtils.isTouchingOtherWay(wayMembers.get(targetIndex).getWay(), wayMembers.get(targetIndex - 1).getWay()) + ? RoutingHelperPanel.ConnectionType.CONNECTED + : RoutingHelperPanel.ConnectionType.NOT_CONNECTED + ), + targetIndex >= wayMembers.size() - 1 ? RoutingHelperPanel.ConnectionType.END : ( + WayUtils.isTouchingOtherWay(wayMembers.get(targetIndex).getWay(), wayMembers.get(targetIndex + 1).getWay()) + ? RoutingHelperPanel.ConnectionType.CONNECTED + : RoutingHelperPanel.ConnectionType.NOT_CONNECTED + ) + ); + } + }) + ); } public void goToNextGap() { JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); } + } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java index 8296bec4..6578ea01 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java @@ -1,21 +1,33 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.util.Collections; import java.util.Optional; +import javax.swing.AbstractAction; +import javax.swing.BoxLayout; +import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; +import com.drew.lang.annotations.NotNull; import com.drew.lang.annotations.Nullable; -import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.MapFrame; +import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor; +import org.openstreetmap.josm.gui.util.HighlightHelper; +import org.openstreetmap.josm.plugins.pt_assistant.utils.BoundsUtils; +import org.openstreetmap.josm.plugins.pt_assistant.utils.ColorPalette; +import org.openstreetmap.josm.plugins.pt_assistant.utils.GuiUtils; +import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; @@ -25,16 +37,30 @@ */ public class RoutingHelperPanel extends JPanel { - private final JLabel wayLabel = new JLabel("Way"); + private final HighlightHelper highlighter = new HighlightHelper(); - private final RoutingHelperAction routingHelperAction; + private final JLabel relationLabel = GuiUtils.createJLabel(); - public RoutingHelperPanel(final RoutingHelperAction routingHelperAction) { - this.routingHelperAction = routingHelperAction; + private final JLabel activeWayLabel = GuiUtils.createJLabel(true); + private final JLabel previousWayConnectionLabel = GuiUtils.createJLabel(); + private final JLabel nextWayConnectionLabel = GuiUtils.createJLabel(); + private final JButton openRelationEditorButton = new JButton(); + private final JPanel containerPanel = GuiUtils.createJPanel(new BorderLayout()); - // Style main label - wayLabel.setBorder(new EmptyBorder(10, 10, 10, 10)); + private final JPanel defaultPanel; + + private final JPanel wayTraversalPanel; + + public RoutingHelperPanel(@NotNull final RoutingHelperAction routingHelperAction) { + this.defaultPanel = createDefaultPanel(routingHelperAction); + this.wayTraversalPanel = createWayTraversalPanel( + routingHelperAction, + activeWayLabel, + previousWayConnectionLabel, + nextWayConnectionLabel, + openRelationEditorButton + ); JButton closeButton = new JButton(ImageProvider.get("misc", "black_x")); closeButton.setContentAreaFilled(false); @@ -42,56 +68,134 @@ public RoutingHelperPanel(final RoutingHelperAction routingHelperAction) { closeButton.setBorderPainted(false); closeButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); closeButton.setToolTipText(I18n.tr("Close the routing helper")); - closeButton.addActionListener(e -> + closeButton.addActionListener(__ -> Optional.ofNullable(MainApplication.getMap()) - .ifPresent(map -> { - map.removeTopPanel(RoutingHelperPanel.class); - routingHelperAction.updateEnabledState(); - }) + .ifPresent(map -> map.removeTopPanel(RoutingHelperPanel.class)) ); - final JPanel mainPanel = new JPanel(new BorderLayout()); - mainPanel.setOpaque(false); - mainPanel.add(wayLabel, BorderLayout.CENTER); + final JPanel mainPanel = GuiUtils.createJPanel(new BorderLayout()); + relationLabel.setBorder(new EmptyBorder(10, 10, 10, 10)); + mainPanel.add(relationLabel, BorderLayout.CENTER); mainPanel.add(closeButton, BorderLayout.EAST); - // build the left and right button panels - final JPanel buttonLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - buttonLeftPanel.setOpaque(false); - final JButton prevGapButton = new JButton("« to previous gap"); - prevGapButton.addActionListener(e -> routingHelperAction.goToPreviousGap()); - buttonLeftPanel.add(prevGapButton); - final JButton prevButton = new JButton("‹ to previous way"); - prevButton.addActionListener(e -> routingHelperAction.goToPreviousWay()); - buttonLeftPanel.add(prevButton); - - final JPanel buttonRightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - buttonRightPanel.setOpaque(false); - final JButton nextButton = new JButton("to next way ›"); - nextButton.addActionListener(e -> routingHelperAction.goToNextWay()); - buttonRightPanel.add(nextButton); - final JButton nextgapButton = new JButton("to next gap »"); - nextgapButton.addActionListener(e -> routingHelperAction.goToNextGap()); - buttonRightPanel.add(nextgapButton); - - // Combine both button panels into one - final JPanel buttonPanel = new JPanel(new BorderLayout()); - buttonPanel.setOpaque(false); - buttonPanel.add(buttonLeftPanel, BorderLayout.WEST); - buttonPanel.add(buttonRightPanel, BorderLayout.EAST); - // put everything together - setBackground(new Color(0xFF9966)); + setBackground(ColorPalette.ROUTING_HELPER_BACKGROUND); setLayout(new BorderLayout()); add(mainPanel, BorderLayout.CENTER); - add(buttonPanel, BorderLayout.SOUTH); + add(containerPanel, BorderLayout.SOUTH); + } + + public void onRelationChange(@Nullable final Relation relation) { + relationLabel.setText(relation == null ? "‹no relation selected›" : "Relation #" + relation.getId()); + onCurrentWayCleared(); + } + + public void onCurrentWayCleared() { + highlighter.clear(); + activeWayLabel.setText(""); + previousWayConnectionLabel.setText(""); + nextWayConnectionLabel.setText(""); + updateContainerPanel(defaultPanel); + } + + public void onCurrentWayChange( + @NotNull final Relation parentRelation, + @NotNull final RelationMember member, + @NotNull final ConnectionType previousWayConnection, + @NotNull final ConnectionType nextWayConnection + ) { + highlighter.highlightOnly(member.getMember()); + Optional.ofNullable(MainApplication.getMap()).ifPresent(map -> map.mapView.zoomTo(BoundsUtils.fromBBox(member.getMember().getBBox()))); + activeWayLabel.setText("Way #" + member.getMember().getId()); + previousWayConnectionLabel.setIcon(previousWayConnection.icon); + previousWayConnectionLabel.setText(previousWayConnection.previousWayText); + nextWayConnectionLabel.setIcon(nextWayConnection.icon); + nextWayConnectionLabel.setText(nextWayConnection.nextWayText); + openRelationEditorButton.setAction(new AbstractAction(I18n.tr("Open in relation editor")) { + @Override + public void actionPerformed(ActionEvent __) { + new GenericRelationEditor(MainApplication.getLayerManager().getActiveDataLayer(), parentRelation, Collections.singleton(member)).setVisible(true); + } + }); + updateContainerPanel(wayTraversalPanel); + } + + private void updateContainerPanel(final JPanel newPanel) { + if (containerPanel.getComponentCount() != 1 || containerPanel.getComponent(0) != newPanel) { + containerPanel.removeAll(); + containerPanel.add(newPanel, BorderLayout.CENTER); + containerPanel.revalidate(); + containerPanel.repaint(); + } + } + + /** + * Initializes the panel shown when traversing the ways one by one + */ + private static JPanel createWayTraversalPanel( + final RoutingHelperAction routingHelperAction, + final JLabel activeWayLabel, + final JLabel previousWayConnectionLabel, + final JLabel nextWayConnectionLabel, + final JButton openRelationEditorButton + ) { + final JPanel wayTraversalPanel = GuiUtils.createJPanel(new BorderLayout()); + + // Left side: buttons to navigate backwards + wayTraversalPanel.add( + GuiUtils.createJPanel( + new FlowLayout(FlowLayout.LEFT), + GuiUtils.createJButton("‹ to previous way", routingHelperAction::goToPreviousWay) + ), + BorderLayout.WEST + ); + + // Center: label for active element + final JPanel activeWayPanel = new JPanel(); + activeWayPanel.setOpaque(false); + activeWayPanel.setLayout(new BoxLayout(activeWayPanel, BoxLayout.PAGE_AXIS)); + + activeWayPanel.add(activeWayLabel); + activeWayPanel.add(previousWayConnectionLabel); + activeWayPanel.add(nextWayConnectionLabel); + activeWayPanel.add(openRelationEditorButton); + wayTraversalPanel.add(activeWayPanel, BorderLayout.CENTER); + + // Right side: buttons to navigate forwards + wayTraversalPanel.add( + GuiUtils.createJPanel( + new FlowLayout(FlowLayout.RIGHT), + GuiUtils.createJButton("to next way ›", routingHelperAction::goToNextWay) + ), + BorderLayout.EAST + ); + + return wayTraversalPanel; + } + + private static JPanel createDefaultPanel(final RoutingHelperAction routingHelperAction) { + return GuiUtils.createJPanel( + new FlowLayout(FlowLayout.CENTER), + GuiUtils.createJButton( + I18n.tr("Start way traversal"), + routingHelperAction::goToFirstWay + ) + ); } - public void onCurrentWayChange(@Nullable final Way way) { - if (way == null) { - wayLabel.setText(I18n.tr("No way found in relation")); - } else { - wayLabel.setText(I18n.tr("Active way: {0} ({1} nodes)", way.getId(), way.getNodesCount())); + public enum ConnectionType { + CONNECTED(PTIcons.GREEN_CHECKMARK, I18n.tr("connected with previous way"), I18n.tr("connected with next way")), + END(PTIcons.BUFFER_STOP, I18n.tr("first way in the relation"), I18n.tr("last way in the relation")), + NOT_CONNECTED(PTIcons.STOP_SIGN, I18n.tr("not connected with previous way"), I18n.tr("not connected with next way")); + + final Icon icon; + final String previousWayText; + final String nextWayText; + + ConnectionType(final ImageProvider imageProvider, final String previousWayText, final String nextWayText) { + this.icon = imageProvider.setSize(ImageProvider.ImageSizes.SMALLICON).get(); + this.previousWayText = previousWayText; + this.nextWayText = nextWayText; } } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/ColorPalette.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/ColorPalette.java index 5ce9bb07..62dd0f19 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/ColorPalette.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/ColorPalette.java @@ -18,6 +18,7 @@ private ColorPalette() { public static final Color WHITE = new Color(255, 255, 255, 180); public static final Color REF_LABEL_COLOR = new Color(0x80FFFFFF, true); + public static final Color ROUTING_HELPER_BACKGROUND = new Color(0xFF9966); public static Color[] FIVE_COLORS = { GREEN, RED, BLUE, YELLOW, CYAN }; } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/GuiUtils.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/GuiUtils.java new file mode 100644 index 00000000..caf2dc29 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/GuiUtils.java @@ -0,0 +1,58 @@ +package org.openstreetmap.josm.plugins.pt_assistant.utils; + +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.LayoutManager; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public final class GuiUtils { + private GuiUtils() { + // Private constructor to avoid instantiation + } + + public static JPanel createJPanel(final FlowLayout layout, final Component... components) { + final JPanel panel = createJPanel(layout); + for (final Component component : components) { + panel.add(component); + } + return panel; + } + + public static JPanel createJPanel(final LayoutManager layoutManager) { + final JPanel panel = new JPanel(layoutManager); + panel.setOpaque(false); + return panel; + } + + public static JButton createJButton(final String text, final Runnable action) { + return createJButton(text, __ -> action.run()); + } + + public static JButton createJButton(final String text, final ActionListener action) { + return new JButton(new AbstractAction(text) { + @Override + public void actionPerformed(ActionEvent e) { + action.actionPerformed(e); + } + }); + } + + public static JLabel createJLabel() { + return createJLabel(false); + } + + public static JLabel createJLabel(final boolean bold) { + final JLabel label = new JLabel(); + label.setFont(label.getFont().deriveFont(bold ? label.getFont().getStyle() | Font.BOLD : label.getFont().getStyle() & ~Font.BOLD)); + return label; + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java index c59df706..fcb5354b 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java @@ -8,4 +8,8 @@ private PTIcons() { } public static final ImageProvider BUS = new ImageProvider("bus"); + + public static final ImageProvider STOP_SIGN = new ImageProvider("misc", "error"); + public static final ImageProvider GREEN_CHECKMARK = new ImageProvider("misc", "green_check"); + public static final ImageProvider BUFFER_STOP = new ImageProvider("presets/transport/railway", "buffer_stop"); } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/RouteUtils.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/RouteUtils.java index 1cd766f5..df9b15a5 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/RouteUtils.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/RouteUtils.java @@ -5,6 +5,7 @@ import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.TO_LAYER; import static org.openstreetmap.josm.plugins.pt_assistant.data.PTStop.isPTPlatform; +import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; @@ -127,6 +128,19 @@ public static boolean isPTRoute(Relation r) { return r != null && r.hasTag(OSMTags.KEY_ROUTE, acceptedRouteTags); } + /** + * Checks, if the given relation member is a way that represents part of the route itself + * (i.e. not something along the way like a stop area for public transport). + * At the moment this check just checks the primitive type and the role of the member. + * + * @param member the relation member to check + * @return {@code true} iff the given member contains a primitive of type {@link Way} and has role + * {@code forward}, {@code backward} or the empty role. Otherwise {@code false}. + */ + public static boolean isRouteWayMember(final RelationMember member) { + return member.isWay() && Arrays.asList("", "forward", "backward").contains(member.getRole()); + } + public static boolean isRoute(Relation r) { return r.get(OSMTags.KEY_ROUTE) != null; } diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/utils/UtilityClassesTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/utils/UtilityClassesTest.java index 3196a336..885670eb 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/utils/UtilityClassesTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/utils/UtilityClassesTest.java @@ -16,6 +16,7 @@ public void testAllUtilityClasses() { TestUtil.testUtilityClass(ColorPalette.class); TestUtil.testUtilityClass(DialogUtils.class); TestUtil.testUtilityClass(GeometryUtils.class); + TestUtil.testUtilityClass(GuiUtils.class); TestUtil.testUtilityClass(NodeUtils.class); TestUtil.testUtilityClass(NotificationUtils.class); TestUtil.testUtilityClass(PrimitiveUtils.class); From 928c168fc8e17cf2c960991672bf9c50a7f9ca95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=A4fer?= Date: Sun, 15 Nov 2020 14:33:51 +0100 Subject: [PATCH 03/17] Add really basic transport mode detection to routing help, currently just shows an icon next to the relation ID --- .../routinghelper/BusTransportMode.java | 17 ++++++++++++++++- .../actions/routinghelper/ITransportMode.java | 12 ++++++++++++ .../routinghelper/RoutingHelperAction.java | 19 ++++++++++++++----- .../routinghelper/RoutingHelperPanel.java | 9 ++++++--- .../plugins/pt_assistant/utils/PTIcons.java | 1 + 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java index 003d17ef..69bdc7b8 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java @@ -4,6 +4,8 @@ import java.util.Set; import java.util.stream.Collectors; +import javax.swing.Icon; + import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; @@ -11,6 +13,9 @@ import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; public class BusTransportMode implements ITransportMode { @Override @@ -25,7 +30,7 @@ public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTrav @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("route", "bus"); + return relation.hasTag("type", "route") && relation.hasTag("route", "bus"); } @Override @@ -41,4 +46,14 @@ public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNul // TODO: Use the `restrictionRelations` to figure out the turning restrictions that apply return from.containsNode(via) && to.containsNode(via); } + + @Override + public ImageProvider getIcon() { + return PTIcons.BUS; + } + + @Override + public String getName() { + return I18n.marktr("bus"); + } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java index 3f6f9b15..2fcf3a41 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java @@ -5,6 +5,8 @@ import org.openstreetmap.josm.data.osm.IWay; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; public interface ITransportMode { /** @@ -40,4 +42,14 @@ default boolean canTraverseWay(final Way way) { * This method assumes that both ways can be traversed by the transport mode, it does not check that. */ boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to); + + /** + * @return an icon representing the transport mode + */ + ImageProvider getIcon(); + + /** + * @return a unique name for the transport mode. This string should be translatable, so please use {@link I18n#marktr} on the string that's returned! + */ + String getName(); } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java index 692e69d8..b2bdf0f6 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java @@ -1,6 +1,8 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -26,15 +28,17 @@ import org.openstreetmap.josm.tools.I18n; public class RoutingHelperAction implements DataSelectionListener { - private static final Set TRANSPORT_MODES = Collections.singleton(new BusTransportMode()); + private static final Set TRANSPORT_MODES = new HashSet<>(Arrays.asList(new BicycleTransportMode(), new BusTransportMode())); - private Optional activeTransportMode; + @NotNull + private Optional activeTransportMode = Optional.empty(); private final RoutingHelperPanel routingHelperPanel = new RoutingHelperPanel(this); @NotNull private Optional currentRelation = Optional.empty(); + @NotNull private Optional currentMember = Optional.empty(); @Override @@ -47,8 +51,9 @@ public void selectionChanged(SelectionChangeEvent event) { .map(selectedPrimitive -> selectedPrimitive instanceof Relation ? (Relation) selectedPrimitive : null) .filter(RouteUtils::isRoute); this.currentRelation = singleRelationSelection; + this.activeTransportMode = currentRelation.flatMap(relation -> TRANSPORT_MODES.stream().filter(it -> it.canBeUsedForRelation(relation)).findFirst()); if (singleRelationSelection.isPresent()) { - routingHelperPanel.onRelationChange(singleRelationSelection.get()); + routingHelperPanel.onRelationChange(singleRelationSelection.get(), activeTransportMode); if (mapframe.getTopPanel(RoutingHelperPanel.class) == null) { mapframe.addTopPanel(routingHelperPanel); } @@ -121,7 +126,9 @@ public void goToFirstWay() { WayUtils.isTouchingOtherWay(wayMembers.get(0).getWay(), wayMembers.get(1).getWay()) ? RoutingHelperPanel.ConnectionType.CONNECTED : RoutingHelperPanel.ConnectionType.NOT_CONNECTED - ) + ), + 0, + wayMembers.size() ); } } @@ -160,7 +167,9 @@ private void goNWaysForward(final int n) { WayUtils.isTouchingOtherWay(wayMembers.get(targetIndex).getWay(), wayMembers.get(targetIndex + 1).getWay()) ? RoutingHelperPanel.ConnectionType.CONNECTED : RoutingHelperPanel.ConnectionType.NOT_CONNECTED - ) + ), + targetIndex, + wayMembers.size() ); } }) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java index 6578ea01..50eadb99 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java @@ -85,7 +85,8 @@ public RoutingHelperPanel(@NotNull final RoutingHelperAction routingHelperAction add(containerPanel, BorderLayout.SOUTH); } - public void onRelationChange(@Nullable final Relation relation) { + public void onRelationChange(@Nullable final Relation relation, @NotNull Optional activeTransportMode) { + relationLabel.setIcon(activeTransportMode.map(it -> it.getIcon().setSize(ImageProvider.ImageSizes.SMALLICON).get()).orElse(null)); relationLabel.setText(relation == null ? "‹no relation selected›" : "Relation #" + relation.getId()); onCurrentWayCleared(); } @@ -102,11 +103,13 @@ public void onCurrentWayChange( @NotNull final Relation parentRelation, @NotNull final RelationMember member, @NotNull final ConnectionType previousWayConnection, - @NotNull final ConnectionType nextWayConnection + @NotNull final ConnectionType nextWayConnection, + final int currentWayIndex, + final int numWaysTotal ) { highlighter.highlightOnly(member.getMember()); Optional.ofNullable(MainApplication.getMap()).ifPresent(map -> map.mapView.zoomTo(BoundsUtils.fromBBox(member.getMember().getBBox()))); - activeWayLabel.setText("Way #" + member.getMember().getId()); + activeWayLabel.setText("Way" + (currentWayIndex + 1) + '/' + numWaysTotal + ": #" + member.getMember().getId()); previousWayConnectionLabel.setIcon(previousWayConnection.icon); previousWayConnectionLabel.setText(previousWayConnection.previousWayText); nextWayConnectionLabel.setIcon(nextWayConnection.icon); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java index fcb5354b..075b00d0 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java @@ -7,6 +7,7 @@ private PTIcons() { // Private constructor to avoid instantiation } + public static final ImageProvider BICYCLE_DESIGNATED = new ImageProvider("presets/vehicle/restriction", "bicycle-designated"); public static final ImageProvider BUS = new ImageProvider("bus"); public static final ImageProvider STOP_SIGN = new ImageProvider("misc", "error"); From 0aa7bb6d2ec618443168e924b2bb3f31f16fe40b Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sun, 15 Nov 2020 17:34:51 +0100 Subject: [PATCH 04/17] I completed the BusTransportMode, so it works for all kinds of buses. Extended the highways they are allowed to use. And tried to make sure turn restriction relations are processed correctly. This needs some unit tests... I also added a class for trolley buses. Did I do that correctly? --- .../routinghelper/BusTransportMode.java | 31 ++++++++++++--- .../TrolleyBusTransportMode.java | 38 +++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java index 69bdc7b8..f0b814e3 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java @@ -1,5 +1,8 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -21,16 +24,25 @@ public class BusTransportMode implements ITransportMode { @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); - return way.hasTag("highway", "primary", "secondary", "tertiary", "residential") && ( + List majorHighways = Arrays.asList( + "motorway", "trunk", "primary", "secondary", "tertiary"); + majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); + List suitableHighwaysForBus = Arrays.asList( + "unclassified", "residential", "service", "living_street", "cyclestreet"); + suitableHighwaysForBus.addAll(majorHighways); // TODO do this only once when plugin starts + return (way.hasTag("highway", suitableHighwaysForBus) || + way.hasTag("psv", "yes") || way.hasTag ("bus", "yes")) + && ( onewayValue == null || "no".equals(way.get("oneway:bus")) || - ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || - ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || + ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) ); } @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("type", "route") && relation.hasTag("route", "bus"); + return relation.hasTag("type", "route") && relation.hasTag("route", + "bus", "coach", "minibus"); } @Override @@ -38,12 +50,19 @@ public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNul final Set restrictionRelations = from.getReferrers().stream() .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) .filter(Objects::nonNull) - .filter(it -> "restriction".equals(it.get("type"))) + .filter(it -> "restriction".equals(it.get("type")) || "restriction:bus".equals(it.get("type"))) .filter(it -> it.findRelationMembers("from").contains(from)) .filter(it -> it.findRelationMembers("via").contains(via)) .filter(it -> it.findRelationMembers("to").contains(to)) .collect(Collectors.toSet()); - // TODO: Use the `restrictionRelations` to figure out the turning restrictions that apply + for (Relation restrictionRelation : restrictionRelations) { + final String restriction = restrictionRelation.get("restriction"); + final String except = restrictionRelation.get("except"); + if (restriction.startsWith("no_") && !except.contains("psv")) { + return false; + } + } + return from.containsNode(via) && to.containsNode(via); } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java new file mode 100644 index 00000000..035a2c22 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java @@ -0,0 +1,38 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; + +public class TrolleyBusTransportMode extends BusTransportMode { + @Override + public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { + return canTraverseWay(way, direction) && way.hasTag("trolley_wire", "yes"); + } + + @Override + public boolean canBeUsedForRelation(@NotNull final IRelation relation) { + return canBeUsedForRelation(relation) && relation.hasTag("route", "trolleybus"); + } + + @Override + public ImageProvider getIcon() { + return PTIcons.BUS; + } // TODO: find proper icon + + @Override + public String getName() { + return I18n.marktr("trolley bus"); + } +} From 2395f155e5c067fdcfc001325d3f69be9438212e Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sun, 15 Nov 2020 18:27:13 +0100 Subject: [PATCH 05/17] I created a BicycleTransportMode --- .../routinghelper/BicycleTransportMode.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java new file mode 100644 index 00000000..24904cdd --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java @@ -0,0 +1,76 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; + +public class BicycleTransportMode implements ITransportMode { + @Override + public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { + final String onewayValue = way.get("oneway"); + List majorHighways = Arrays.asList( + "tertiary", "secondary", "primary", "trunk"); + majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); + // This list is ordered from most suitable to least suitable + List suitableHighwaysForBicycle = Arrays.asList( + "cycleway", "cyclestreet", "path", "residential", "unclassified", "service", "track", "living_street"); + suitableHighwaysForBicycle.addAll(majorHighways); // TODO do this only once when plugin starts + return !way.hasTag("bicycle", "no") && + (way.hasTag("highway", suitableHighwaysForBicycle) || + way.hasTag("bicycle", "yes")) + && ( + onewayValue == null || "no".equals(way.get("oneway:bicycle")) || + ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || + ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); + } + + @Override + public boolean canBeUsedForRelation(@NotNull final IRelation relation) { + return relation.hasTag("type", "route") && relation.hasTag("route", "bicycle"); + } + + @Override + public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { + final Set restrictionRelations = from.getReferrers().stream() + .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) + .filter(Objects::nonNull) + .filter(it -> "restriction".equals(it.get("type")) || "restriction:bicycle".equals(it.get("type"))) + .filter(it -> it.findRelationMembers("from").contains(from)) + .filter(it -> it.findRelationMembers("via").contains(via)) + .filter(it -> it.findRelationMembers("to").contains(to)) + .collect(Collectors.toSet()); + for (Relation restrictionRelation : restrictionRelations) { + final String restriction = restrictionRelation.get("restriction"); + final String except = restrictionRelation.get("except"); + if (restriction.startsWith("no_") && !except.contains("bicycle")) { + return false; + } + } + + return from.containsNode(via) && to.containsNode(via); + } + + @Override + public ImageProvider getIcon() { + return PTIcons.BICYCLE_DESIGNATED; + } + + @Override + public String getName() { + return I18n.marktr("bicycle"); + } +} From 3d3cc48f6912ce16c63719f1069f7a00e1a6e602 Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sun, 15 Nov 2020 19:14:49 +0100 Subject: [PATCH 06/17] I created a PedestrianTransportMode and a HorseTransportMode. I also added some Icons. --- .../routinghelper/BicycleTransportMode.java | 2 +- .../routinghelper/HorseTransportMode.java | 75 ++++++++++++++++++ .../PedestrianTransportMode.java | 78 +++++++++++++++++++ .../TrolleyBusTransportMode.java | 4 +- .../plugins/pt_assistant/utils/PTIcons.java | 4 + 5 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java index 24904cdd..341857fa 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java @@ -66,7 +66,7 @@ public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNul @Override public ImageProvider getIcon() { - return PTIcons.BICYCLE_DESIGNATED; + return PTIcons.BICYCLE; } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java new file mode 100644 index 00000000..f6928738 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java @@ -0,0 +1,75 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; + +public class HorseTransportMode implements ITransportMode { + @Override + public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { + final String onewayValue = way.get("oneway"); + List majorHighways = Arrays.asList( + "tertiary", "secondary", "primary", "trunk"); + majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); + // This list is ordered from most suitable to least suitable + List suitableHighwaysForHorseRiders = Arrays.asList( + "bridleway", "pedestrian", "footway", "path", "track", "living_street", "residential", "unclassified", "cyclestreet", "service", "cycleway"); + suitableHighwaysForHorseRiders.addAll(majorHighways); // TODO do this only once when plugin starts + return !way.hasTag("horse", "no") && + (way.hasTag("highway", suitableHighwaysForHorseRiders) || + way.hasTag("horse", "yes")) + && ( + onewayValue == null || + ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || + ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); + } + + @Override + public boolean canBeUsedForRelation(@NotNull final IRelation relation) { + return relation.hasTag("type", "route") && relation.hasTag("route", "horse"); + } + + @Override + public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { + final Set restrictionRelations = from.getReferrers().stream() + .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) + .filter(Objects::nonNull) + .filter(it -> "restriction".equals(it.get("type"))) + .filter(it -> it.findRelationMembers("from").contains(from)) + .filter(it -> it.findRelationMembers("via").contains(via)) + .filter(it -> it.findRelationMembers("to").contains(to)) + .collect(Collectors.toSet()); + for (Relation restrictionRelation : restrictionRelations) { + final String restriction = restrictionRelation.get("restriction"); + if (restriction.startsWith("no_")) { + return false; + } + } + + return from.containsNode(via) && to.containsNode(via); + } + + @Override + public ImageProvider getIcon() { + return PTIcons.HORSE; + } + + @Override + public String getName() { + return I18n.marktr("equestrian"); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java new file mode 100644 index 00000000..8fb9e2c3 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java @@ -0,0 +1,78 @@ +package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.swing.Icon; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; +import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.ImageProvider; + +public class PedestrianTransportMode implements ITransportMode { + @Override + public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { + final String onewayValue = way.get("oneway"); + List majorHighways = Arrays.asList( + "tertiary", "secondary", "primary", "trunk"); + majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); + // This list is ordered from most suitable to least suitable + List suitableHighwaysForPedestrians = Arrays.asList( + "pedestrian", "footway", "path", "track", "living_street", "residential", "unclassified", "cyclestreet", "service", "cycleway", "bridleway"); + suitableHighwaysForPedestrians.addAll(majorHighways); // TODO do this only once when plugin starts + return !way.hasTag("foot", "no") && + (way.hasTag("highway", suitableHighwaysForPedestrians) || + way.hasTag("foot", "yes")) + && ( + onewayValue == null || "no".equals(way.get("foot:backward")) || "no".equals(way.get("oneway:foot")) || + ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || + ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); + } + + @Override + public boolean canBeUsedForRelation(@NotNull final IRelation relation) { + return relation.hasTag("type", "route") && relation.hasTag("route", + "foot", "walking", "hiking"); + } + + @Override + public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { + final Set restrictionRelations = from.getReferrers().stream() + .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) + .filter(Objects::nonNull) + .filter(it -> "restriction".equals(it.get("type"))) + .filter(it -> it.findRelationMembers("from").contains(from)) + .filter(it -> it.findRelationMembers("via").contains(via)) + .filter(it -> it.findRelationMembers("to").contains(to)) + .collect(Collectors.toSet()); + for (Relation restrictionRelation : restrictionRelations) { + final String restriction = restrictionRelation.get("restriction"); + if (restriction.startsWith("no_")) { + return false; + } + } + + return from.containsNode(via) && to.containsNode(via); + } + + @Override + public ImageProvider getIcon() { + return PTIcons.PEDESTRIAN; + } + + @Override + public String getName() { + return I18n.marktr("pedestrian"); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java index 035a2c22..12614a34 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java @@ -28,8 +28,8 @@ public boolean canBeUsedForRelation(@NotNull final IRelation relation) { @Override public ImageProvider getIcon() { - return PTIcons.BUS; - } // TODO: find proper icon + return PTIcons.TROLLEY_BUS; + } @Override public String getName() { diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java index 075b00d0..84a8f784 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/utils/PTIcons.java @@ -8,7 +8,11 @@ private PTIcons() { } public static final ImageProvider BICYCLE_DESIGNATED = new ImageProvider("presets/vehicle/restriction", "bicycle-designated"); + public static final ImageProvider BICYCLE = new ImageProvider("presets/sport", "cycling"); public static final ImageProvider BUS = new ImageProvider("bus"); + public static final ImageProvider TROLLEY_BUS = new ImageProvider("presets/transport", "trolleybus"); + public static final ImageProvider PEDESTRIAN = new ImageProvider("presets/vehicle/restriction","foot-designated"); + public static final ImageProvider HORSE = new ImageProvider("presets/leisure","horse_riding"); public static final ImageProvider STOP_SIGN = new ImageProvider("misc", "error"); public static final ImageProvider GREEN_CHECKMARK = new ImageProvider("misc", "green_check"); From bc7747689bb67feeb1c575c05f7bd4d4d65f1da9 Mon Sep 17 00:00:00 2001 From: Jo Date: Mon, 16 Nov 2020 13:15:15 +0100 Subject: [PATCH 07/17] Make the suitable ways in transport modes a static field --- .../routinghelper/BicycleTransportMode.java | 33 ++++++++------- .../routinghelper/BusTransportMode.java | 35 ++++++++-------- .../routinghelper/HorseTransportMode.java | 35 +++++++++------- .../PedestrianTransportMode.java | 42 ++++++++++--------- .../TrolleyBusTransportMode.java | 12 +----- 5 files changed, 77 insertions(+), 80 deletions(-) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java index 341857fa..23eaf1aa 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java @@ -1,10 +1,10 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; @@ -18,24 +18,25 @@ import org.openstreetmap.josm.tools.ImageProvider; public class BicycleTransportMode implements ITransportMode { + + private static final List suitableHighwaysForBicycle = Stream.concat( + // This list is ordered from most suitable to least suitable + Stream.of("cycleway", "cyclestreet", "path", "residential", "unclassified", "service", "track", "living_street"), + Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) + ).collect(Collectors.toList()); + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); - List majorHighways = Arrays.asList( - "tertiary", "secondary", "primary", "trunk"); - majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); - // This list is ordered from most suitable to least suitable - List suitableHighwaysForBicycle = Arrays.asList( - "cycleway", "cyclestreet", "path", "residential", "unclassified", "service", "track", "living_street"); - suitableHighwaysForBicycle.addAll(majorHighways); // TODO do this only once when plugin starts - return !way.hasTag("bicycle", "no") && - (way.hasTag("highway", suitableHighwaysForBicycle) || - way.hasTag("bicycle", "yes")) - && ( - onewayValue == null || "no".equals(way.get("oneway:bicycle")) || - ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || - ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) - ); + return + !way.hasTag("bicycle", "no") + && (way.hasTag("highway", suitableHighwaysForBicycle) || way.hasTag("bicycle", "yes")) + && ( + onewayValue == null + || "no".equals(way.get("oneway:bicycle")) + || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) + || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java index f0b814e3..8665607c 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java @@ -1,13 +1,10 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; - -import javax.swing.Icon; +import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; @@ -21,28 +18,30 @@ import org.openstreetmap.josm.tools.ImageProvider; public class BusTransportMode implements ITransportMode { + + private static final List suitableHighwaysForBus = Stream.concat( + Stream.of("unclassified", "residential", "service", "living_street", "cyclestreet"), + Stream.of("motorway", "trunk", "primary", "secondary", "tertiary").flatMap(it -> Stream.of(it, it + "_link")) + ).collect(Collectors.toList()); + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); - List majorHighways = Arrays.asList( - "motorway", "trunk", "primary", "secondary", "tertiary"); - majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); - List suitableHighwaysForBus = Arrays.asList( - "unclassified", "residential", "service", "living_street", "cyclestreet"); - suitableHighwaysForBus.addAll(majorHighways); // TODO do this only once when plugin starts - return (way.hasTag("highway", suitableHighwaysForBus) || - way.hasTag("psv", "yes") || way.hasTag ("bus", "yes")) - && ( - onewayValue == null || "no".equals(way.get("oneway:bus")) || - ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || - ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + return ( + way.hasTag("highway", suitableHighwaysForBus) + || way.hasTag("psv", "yes") || way.hasTag ("bus", "yes") + ) + && ( + onewayValue == null + || "no".equals(way.get("oneway:bus")) + || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) + || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) ); } @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("type", "route") && relation.hasTag("route", - "bus", "coach", "minibus"); + return relation.hasTag("type", "route") && relation.hasTag("route", "bus", "coach", "minibus"); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java index f6928738..36d44026 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java @@ -1,10 +1,10 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; @@ -18,24 +18,27 @@ import org.openstreetmap.josm.tools.ImageProvider; public class HorseTransportMode implements ITransportMode { + + private static final List suitableHighwaysForHorseRiders = Stream.concat( + // This list is ordered from most suitable to least suitable + Stream.of( + "bridleway", "pedestrian", "footway", "path", "track", "living_street", "residential", + "unclassified", "cyclestreet", "service", "cycleway" + ), + Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) + ).collect(Collectors.toList()); + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); - List majorHighways = Arrays.asList( - "tertiary", "secondary", "primary", "trunk"); - majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); - // This list is ordered from most suitable to least suitable - List suitableHighwaysForHorseRiders = Arrays.asList( - "bridleway", "pedestrian", "footway", "path", "track", "living_street", "residential", "unclassified", "cyclestreet", "service", "cycleway"); - suitableHighwaysForHorseRiders.addAll(majorHighways); // TODO do this only once when plugin starts - return !way.hasTag("horse", "no") && - (way.hasTag("highway", suitableHighwaysForHorseRiders) || - way.hasTag("horse", "yes")) - && ( - onewayValue == null || - ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || - ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) - ); + return + !way.hasTag("horse", "no") + && (way.hasTag("highway", suitableHighwaysForHorseRiders) || way.hasTag("horse", "yes")) + && ( + onewayValue == null + || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) + || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java index 8fb9e2c3..0d4313e3 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java @@ -1,12 +1,10 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; - -import javax.swing.Icon; +import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; @@ -20,30 +18,34 @@ import org.openstreetmap.josm.tools.ImageProvider; public class PedestrianTransportMode implements ITransportMode { + + private static final List suitableHighwaysForPedestrians = Stream.concat( + // This list is ordered from most suitable to least suitable + Stream.of( + "pedestrian", "footway", "path", "track", "living_street", "residential", + "unclassified", "cyclestreet", "service", "cycleway", "bridleway" + ), + Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) + ).collect(Collectors.toList()); + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); - List majorHighways = Arrays.asList( - "tertiary", "secondary", "primary", "trunk"); - majorHighways.forEach(v -> majorHighways.add(String.format("%s_link", v))); - // This list is ordered from most suitable to least suitable - List suitableHighwaysForPedestrians = Arrays.asList( - "pedestrian", "footway", "path", "track", "living_street", "residential", "unclassified", "cyclestreet", "service", "cycleway", "bridleway"); - suitableHighwaysForPedestrians.addAll(majorHighways); // TODO do this only once when plugin starts - return !way.hasTag("foot", "no") && - (way.hasTag("highway", suitableHighwaysForPedestrians) || - way.hasTag("foot", "yes")) - && ( - onewayValue == null || "no".equals(way.get("foot:backward")) || "no".equals(way.get("oneway:foot")) || - ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) || - ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) - ); + return + !way.hasTag("foot", "no") + && (way.hasTag("highway", suitableHighwaysForPedestrians) || way.hasTag("foot", "yes")) + && ( + onewayValue == null + || "no".equals(way.get("foot:backward")) + || "no".equals(way.get("oneway:foot")) + || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) + || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) + ); } @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("type", "route") && relation.hasTag("route", - "foot", "walking", "hiking"); + return relation.hasTag("type", "route") && relation.hasTag("route", "foot", "walking", "hiking"); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java index 12614a34..c089ff9f 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java @@ -1,16 +1,8 @@ package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; @@ -18,12 +10,12 @@ public class TrolleyBusTransportMode extends BusTransportMode { @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { - return canTraverseWay(way, direction) && way.hasTag("trolley_wire", "yes"); + return super.canTraverseWay(way, direction) && way.hasTag("trolley_wire", "yes"); } @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return canBeUsedForRelation(relation) && relation.hasTag("route", "trolleybus"); + return relation.hasTag("type", "route") && relation.hasTag("route", "trolleybus"); } @Override From ff36ed1105c0e35c1c5b2ffb4c3b24c5367c37a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=A4fer?= Date: Mon, 16 Nov 2020 15:40:21 +0100 Subject: [PATCH 08/17] Rename routing helper to route explorer I think the name fits better and also makes it easier to distinguish between the original routing helper and this new one. --- .../RouteExplorer.java} | 42 +++++++++---------- .../RouteExplorerPanel.java} | 16 +++---- .../WayTraversalDirection.java | 3 +- .../transportmode}/BicycleTransportMode.java | 8 +++- .../transportmode}/BusTransportMode.java | 8 +++- .../transportmode}/HorseTransportMode.java | 8 +++- .../transportmode}/ITransportMode.java | 16 ++++++- .../PedestrianTransportMode.java | 8 +++- .../TrolleyBusTransportMode.java | 8 +++- 9 files changed, 80 insertions(+), 37 deletions(-) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper/RoutingHelperAction.java => routeexplorer/RouteExplorer.java} (84%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper/RoutingHelperPanel.java => routeexplorer/RouteExplorerPanel.java} (93%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer}/WayTraversalDirection.java (93%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer/transportmode}/BicycleTransportMode.java (90%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer/transportmode}/BusTransportMode.java (90%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer/transportmode}/HorseTransportMode.java (90%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer/transportmode}/ITransportMode.java (81%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer/transportmode}/PedestrianTransportMode.java (90%) rename src/main/java/org/openstreetmap/josm/plugins/pt_assistant/{actions/routinghelper => routeexplorer/transportmode}/TrolleyBusTransportMode.java (74%) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/RouteExplorer.java similarity index 84% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/RouteExplorer.java index b2bdf0f6..7686cce3 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperAction.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/RouteExplorer.java @@ -1,11 +1,9 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer; -import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -22,18 +20,17 @@ import org.openstreetmap.josm.gui.MapFrame; import org.openstreetmap.josm.gui.Notification; import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode.ITransportMode; import org.openstreetmap.josm.plugins.pt_assistant.utils.DialogUtils; import org.openstreetmap.josm.plugins.pt_assistant.utils.RouteUtils; import org.openstreetmap.josm.plugins.pt_assistant.utils.WayUtils; import org.openstreetmap.josm.tools.I18n; -public class RoutingHelperAction implements DataSelectionListener { - private static final Set TRANSPORT_MODES = new HashSet<>(Arrays.asList(new BicycleTransportMode(), new BusTransportMode())); - +public class RouteExplorer implements DataSelectionListener { @NotNull private Optional activeTransportMode = Optional.empty(); - private final RoutingHelperPanel routingHelperPanel = new RoutingHelperPanel(this); + private final RouteExplorerPanel routingHelperPanel = new RouteExplorerPanel(this); @NotNull private Optional currentRelation = Optional.empty(); @@ -42,7 +39,7 @@ public class RoutingHelperAction implements DataSelectionListener { private Optional currentMember = Optional.empty(); @Override - public void selectionChanged(SelectionChangeEvent event) { + public void selectionChanged(final SelectionChangeEvent event) { final MapFrame mapframe = MainApplication.getMap(); if (mapframe != null) { final Optional singleRelationSelection = Optional.of(event.getSelection()) @@ -51,14 +48,14 @@ public void selectionChanged(SelectionChangeEvent event) { .map(selectedPrimitive -> selectedPrimitive instanceof Relation ? (Relation) selectedPrimitive : null) .filter(RouteUtils::isRoute); this.currentRelation = singleRelationSelection; - this.activeTransportMode = currentRelation.flatMap(relation -> TRANSPORT_MODES.stream().filter(it -> it.canBeUsedForRelation(relation)).findFirst()); + this.activeTransportMode = currentRelation.flatMap(relation -> ITransportMode.TRANSPORT_MODES.stream().filter(it -> it.canBeUsedForRelation(relation)).findFirst()); if (singleRelationSelection.isPresent()) { routingHelperPanel.onRelationChange(singleRelationSelection.get(), activeTransportMode); - if (mapframe.getTopPanel(RoutingHelperPanel.class) == null) { + if (mapframe.getTopPanel(RouteExplorerPanel.class) == null) { mapframe.addTopPanel(routingHelperPanel); } } else { - mapframe.removeTopPanel(RoutingHelperPanel.class); + mapframe.removeTopPanel(RouteExplorerPanel.class); } } } @@ -119,13 +116,13 @@ public void goToFirstWay() { routingHelperPanel.onCurrentWayChange( currentRelation.get(), wayMembers.get(0), - RoutingHelperPanel.ConnectionType.END, + RouteExplorerPanel.ConnectionType.END, wayMembers.size() == 1 - ? RoutingHelperPanel.ConnectionType.END + ? RouteExplorerPanel.ConnectionType.END : ( WayUtils.isTouchingOtherWay(wayMembers.get(0).getWay(), wayMembers.get(1).getWay()) - ? RoutingHelperPanel.ConnectionType.CONNECTED - : RoutingHelperPanel.ConnectionType.NOT_CONNECTED + ? RouteExplorerPanel.ConnectionType.CONNECTED + : RouteExplorerPanel.ConnectionType.NOT_CONNECTED ), 0, wayMembers.size() @@ -158,15 +155,15 @@ private void goNWaysForward(final int n) { routingHelperPanel.onCurrentWayChange( relation, wayMembers.get(targetIndex), - targetIndex <= 0 ? RoutingHelperPanel.ConnectionType.END : ( + targetIndex <= 0 ? RouteExplorerPanel.ConnectionType.END : ( WayUtils.isTouchingOtherWay(wayMembers.get(targetIndex).getWay(), wayMembers.get(targetIndex - 1).getWay()) - ? RoutingHelperPanel.ConnectionType.CONNECTED - : RoutingHelperPanel.ConnectionType.NOT_CONNECTED + ? RouteExplorerPanel.ConnectionType.CONNECTED + : RouteExplorerPanel.ConnectionType.NOT_CONNECTED ), - targetIndex >= wayMembers.size() - 1 ? RoutingHelperPanel.ConnectionType.END : ( + targetIndex >= wayMembers.size() - 1 ? RouteExplorerPanel.ConnectionType.END : ( WayUtils.isTouchingOtherWay(wayMembers.get(targetIndex).getWay(), wayMembers.get(targetIndex + 1).getWay()) - ? RoutingHelperPanel.ConnectionType.CONNECTED - : RoutingHelperPanel.ConnectionType.NOT_CONNECTED + ? RouteExplorerPanel.ConnectionType.CONNECTED + : RouteExplorerPanel.ConnectionType.NOT_CONNECTED ), targetIndex, wayMembers.size() @@ -179,5 +176,4 @@ private void goNWaysForward(final int n) { public void goToNextGap() { JOptionPane.showMessageDialog(routingHelperPanel, "Not implemented yet", "Not implemented", JOptionPane.ERROR_MESSAGE); } - } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/RouteExplorerPanel.java similarity index 93% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/RouteExplorerPanel.java index 50eadb99..dc657e7b 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/RoutingHelperPanel.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/RouteExplorerPanel.java @@ -1,4 +1,5 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer; import java.awt.BorderLayout; import java.awt.Component; @@ -24,6 +25,7 @@ import org.openstreetmap.josm.gui.MapFrame; import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor; import org.openstreetmap.josm.gui.util.HighlightHelper; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode.ITransportMode; import org.openstreetmap.josm.plugins.pt_assistant.utils.BoundsUtils; import org.openstreetmap.josm.plugins.pt_assistant.utils.ColorPalette; import org.openstreetmap.josm.plugins.pt_assistant.utils.GuiUtils; @@ -33,9 +35,9 @@ /** * The top panel that is added via {@link MapFrame#addTopPanel(Component)}. - * This class should just handle the display and input. The state of the routing helper should be handled in {@link RoutingHelperAction}. + * This class should just handle the display and input. The state of the routing helper should be handled in {@link RouteExplorer}. */ -public class RoutingHelperPanel extends JPanel { +public class RouteExplorerPanel extends JPanel { private final HighlightHelper highlighter = new HighlightHelper(); @@ -52,7 +54,7 @@ public class RoutingHelperPanel extends JPanel { private final JPanel wayTraversalPanel; - public RoutingHelperPanel(@NotNull final RoutingHelperAction routingHelperAction) { + public RouteExplorerPanel(@NotNull final RouteExplorer routingHelperAction) { this.defaultPanel = createDefaultPanel(routingHelperAction); this.wayTraversalPanel = createWayTraversalPanel( routingHelperAction, @@ -70,7 +72,7 @@ public RoutingHelperPanel(@NotNull final RoutingHelperAction routingHelperAction closeButton.setToolTipText(I18n.tr("Close the routing helper")); closeButton.addActionListener(__ -> Optional.ofNullable(MainApplication.getMap()) - .ifPresent(map -> map.removeTopPanel(RoutingHelperPanel.class)) + .ifPresent(map -> map.removeTopPanel(RouteExplorerPanel.class)) ); final JPanel mainPanel = GuiUtils.createJPanel(new BorderLayout()); @@ -136,7 +138,7 @@ private void updateContainerPanel(final JPanel newPanel) { * Initializes the panel shown when traversing the ways one by one */ private static JPanel createWayTraversalPanel( - final RoutingHelperAction routingHelperAction, + final RouteExplorer routingHelperAction, final JLabel activeWayLabel, final JLabel previousWayConnectionLabel, final JLabel nextWayConnectionLabel, @@ -176,7 +178,7 @@ private static JPanel createWayTraversalPanel( return wayTraversalPanel; } - private static JPanel createDefaultPanel(final RoutingHelperAction routingHelperAction) { + private static JPanel createDefaultPanel(final RouteExplorer routingHelperAction) { return GuiUtils.createJPanel( new FlowLayout(FlowLayout.CENTER), GuiUtils.createJButton( diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/WayTraversalDirection.java similarity index 93% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/WayTraversalDirection.java index 603d0b96..d4f1bdf4 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/WayTraversalDirection.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/WayTraversalDirection.java @@ -1,4 +1,5 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer; import java.util.function.Function; diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java similarity index 90% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java index 23eaf1aa..addb5805 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BicycleTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java @@ -1,4 +1,5 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; import java.util.Objects; @@ -13,6 +14,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; @@ -25,6 +27,10 @@ public class BicycleTransportMode implements ITransportMode { Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) ).collect(Collectors.toList()); + protected BicycleTransportMode() { + // should only be instantiable in `ITransportMode` + } + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java similarity index 90% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java index 8665607c..4d65c49f 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java @@ -1,4 +1,5 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; import java.util.Objects; @@ -13,6 +14,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; @@ -24,6 +26,10 @@ public class BusTransportMode implements ITransportMode { Stream.of("motorway", "trunk", "primary", "secondary", "tertiary").flatMap(it -> Stream.of(it, it + "_link")) ).collect(Collectors.toList()); + protected BusTransportMode() { + // should only be instantiable in `ITransportMode` + } + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java similarity index 90% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java index 36d44026..20170992 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/HorseTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java @@ -1,4 +1,5 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; import java.util.Objects; @@ -13,6 +14,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; @@ -28,6 +30,10 @@ public class HorseTransportMode implements ITransportMode { Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) ).collect(Collectors.toList()); + protected HorseTransportMode() { + // should only be instantiable in `ITransportMode` + } + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java similarity index 81% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java index 2fcf3a41..7486faa0 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/ITransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java @@ -1,14 +1,28 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; public interface ITransportMode { + Set TRANSPORT_MODES = Stream.of( + new BicycleTransportMode(), + new BusTransportMode(), + new HorseTransportMode(), + new PedestrianTransportMode(), + new TrolleyBusTransportMode() + ).collect(Collectors.toSet()); + /** * Just a convenience method for {@link #canTraverseWay(IWay, WayTraversalDirection)} that assumes {@link WayTraversalDirection#FORWARD} * @param way the way for which we check, if it can be traversed by the transport mode diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/PedestrianTransportMode.java similarity index 90% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/PedestrianTransportMode.java index 0d4313e3..3fa7b4c1 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/PedestrianTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/PedestrianTransportMode.java @@ -1,4 +1,5 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; import java.util.Objects; @@ -13,6 +14,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; @@ -28,6 +30,10 @@ public class PedestrianTransportMode implements ITransportMode { Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) ).collect(Collectors.toList()); + protected PedestrianTransportMode() { + // should only be instantiable in `ITransportMode` + } + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { final String onewayValue = way.get("oneway"); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java similarity index 74% rename from src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java rename to src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java index c089ff9f..6341659f 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/actions/routinghelper/TrolleyBusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java @@ -1,13 +1,19 @@ -package org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper; +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; public class TrolleyBusTransportMode extends BusTransportMode { + protected TrolleyBusTransportMode() { + // should only be instantiable in `ITransportMode` + } + @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { return super.canTraverseWay(way, direction) && way.hasTag("trolley_wire", "yes"); From f23c46eb0623d8518d32f3f1536e3c345a5b80da Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Mon, 16 Nov 2020 20:35:12 +0100 Subject: [PATCH 09/17] I factored out the code duplication. It made the code less readable though. Does it make sense to do this? --- .../transportmode/AbstractTransportMode.java | 59 +++++++++++++++++++ .../transportmode/BicycleTransportMode.java | 49 ++++----------- .../transportmode/BusTransportMode.java | 47 ++++----------- .../transportmode/HorseTransportMode.java | 35 ++--------- .../TrolleyBusTransportMode.java | 3 + 5 files changed, 88 insertions(+), 105 deletions(-) create mode 100644 src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java new file mode 100644 index 00000000..c352f72a --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java @@ -0,0 +1,59 @@ +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.drew.lang.annotations.NotNull; +import org.openstreetmap.josm.data.osm.IRelation; +import org.openstreetmap.josm.data.osm.IWay; +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.OsmPrimitiveType; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; + +public abstract class AbstractTransportMode implements ITransportMode { + String modeOfTransport = ""; + String additionalTypeForTurnRestriction = ""; + String oneWayExceptionFor = ""; + + @Override + public boolean canBeUsedForRelation(@NotNull final IRelation relation) { + return relation.hasTag("type", "route"); + } + + @Override + public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { + final String onewayValue = way.get("oneway"); + final Boolean onewayException = (!"".equals(oneWayExceptionFor)) ? "no".equals(way.get("oneway:" + oneWayExceptionFor)) : false; + return onewayValue == null + || onewayException + || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) + || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD); + } + + @Override + public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { + List types = new java.util.ArrayList<>(Collections.singletonList("restriction")); + if (!additionalTypeForTurnRestriction.equals("")) types.add("restriction:" + additionalTypeForTurnRestriction); + final Set restrictionRelations = from.getReferrers().stream() + .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) + .filter(Objects::nonNull) + .filter(it -> it.hasTag("type", types)) + .filter(it -> it.findRelationMembers("from").contains(from)) + .filter(it -> it.findRelationMembers("via").contains(via)) + .filter(it -> it.findRelationMembers("to").contains(to)) + .collect(Collectors.toSet()); + for (Relation restrictionRelation : restrictionRelations) { + final String restriction = restrictionRelation.get("restriction"); + final String except = ("".equals(modeOfTransport)) ? restrictionRelation.get("except") : ""; + if (restriction.startsWith("no_") && !except.contains(modeOfTransport)) { + return false; + } + } + return from.containsNode(via) && to.containsNode(via); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java index addb5805..9c51da65 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BicycleTransportMode.java @@ -2,8 +2,6 @@ package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; -import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -11,64 +9,37 @@ import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; -public class BicycleTransportMode implements ITransportMode { +public class BicycleTransportMode extends AbstractTransportMode { - private static final List suitableHighwaysForBicycle = Stream.concat( + private static final List suitableHighways = Stream.concat( // This list is ordered from most suitable to least suitable Stream.of("cycleway", "cyclestreet", "path", "residential", "unclassified", "service", "track", "living_street"), Stream.of("tertiary", "secondary", "primary", "trunk").flatMap(it -> Stream.of(it, it + "_link")) ).collect(Collectors.toList()); - protected BicycleTransportMode() { + public BicycleTransportMode() { // should only be instantiable in `ITransportMode` + modeOfTransport = "bicycle"; + additionalTypeForTurnRestriction = "bicycle"; + oneWayExceptionFor = "bicycle"; } @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { - final String onewayValue = way.get("oneway"); - return - !way.hasTag("bicycle", "no") - && (way.hasTag("highway", suitableHighwaysForBicycle) || way.hasTag("bicycle", "yes")) - && ( - onewayValue == null - || "no".equals(way.get("oneway:bicycle")) - || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) - || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) - ); + return !way.hasTag(modeOfTransport, "no") + && (way.hasTag("highway", suitableHighways) || way.hasTag(modeOfTransport, "yes")) + && super.canTraverseWay(way, direction); } @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("type", "route") && relation.hasTag("route", "bicycle"); - } - - @Override - public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { - final Set restrictionRelations = from.getReferrers().stream() - .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) - .filter(Objects::nonNull) - .filter(it -> "restriction".equals(it.get("type")) || "restriction:bicycle".equals(it.get("type"))) - .filter(it -> it.findRelationMembers("from").contains(from)) - .filter(it -> it.findRelationMembers("via").contains(via)) - .filter(it -> it.findRelationMembers("to").contains(to)) - .collect(Collectors.toSet()); - for (Relation restrictionRelation : restrictionRelations) { - final String restriction = restrictionRelation.get("restriction"); - final String except = restrictionRelation.get("except"); - if (restriction.startsWith("no_") && !except.contains("bicycle")) { - return false; - } - } - - return from.containsNode(via) && to.containsNode(via); + return relation.hasTag("route", modeOfTransport) && super.canBeUsedForRelation(relation); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java index 4d65c49f..4af3b470 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java @@ -19,56 +19,31 @@ import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; -public class BusTransportMode implements ITransportMode { +public class BusTransportMode extends AbstractTransportMode { - private static final List suitableHighwaysForBus = Stream.concat( + private static final List suitableHighways = Stream.concat( Stream.of("unclassified", "residential", "service", "living_street", "cyclestreet"), - Stream.of("motorway", "trunk", "primary", "secondary", "tertiary").flatMap(it -> Stream.of(it, it + "_link")) + Stream.of("tertiary", "secondary", "primary", "trunk", "motorway").flatMap(it -> Stream.of(it, it + "_link")) ).collect(Collectors.toList()); protected BusTransportMode() { // should only be instantiable in `ITransportMode` + modeOfTransport = "bus"; + additionalTypeForTurnRestriction = "bus"; + oneWayExceptionFor = "psv"; // TODO turn these 2 into lists, so they can also check for "oneway:bus", "except"="psv" } @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { - final String onewayValue = way.get("oneway"); - return ( - way.hasTag("highway", suitableHighwaysForBus) - || way.hasTag("psv", "yes") || way.hasTag ("bus", "yes") - ) - && ( - onewayValue == null - || "no".equals(way.get("oneway:bus")) - || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) - || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD) - ); + return ( way.hasTag("highway", suitableHighways) + || way.hasTag("psv", "yes") + || way.hasTag ("bus", "yes")) + && canTraverseWay(way, direction); } @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("type", "route") && relation.hasTag("route", "bus", "coach", "minibus"); - } - - @Override - public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { - final Set restrictionRelations = from.getReferrers().stream() - .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) - .filter(Objects::nonNull) - .filter(it -> "restriction".equals(it.get("type")) || "restriction:bus".equals(it.get("type"))) - .filter(it -> it.findRelationMembers("from").contains(from)) - .filter(it -> it.findRelationMembers("via").contains(via)) - .filter(it -> it.findRelationMembers("to").contains(to)) - .collect(Collectors.toSet()); - for (Relation restrictionRelation : restrictionRelations) { - final String restriction = restrictionRelation.get("restriction"); - final String except = restrictionRelation.get("except"); - if (restriction.startsWith("no_") && !except.contains("psv")) { - return false; - } - } - - return from.containsNode(via) && to.containsNode(via); + return relation.hasTag("route", "bus", "coach", "minibus") && super.canBeUsedForRelation(relation); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java index 20170992..68a123c8 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/HorseTransportMode.java @@ -2,26 +2,20 @@ package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; -import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; -public class HorseTransportMode implements ITransportMode { +public class HorseTransportMode extends AbstractTransportMode { - private static final List suitableHighwaysForHorseRiders = Stream.concat( + private static final List suitableHighways = Stream.concat( // This list is ordered from most suitable to least suitable Stream.of( "bridleway", "pedestrian", "footway", "path", "track", "living_street", "residential", @@ -32,6 +26,7 @@ public class HorseTransportMode implements ITransportMode { protected HorseTransportMode() { // should only be instantiable in `ITransportMode` + modeOfTransport = "horse"; } @Override @@ -39,7 +34,7 @@ public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTrav final String onewayValue = way.get("oneway"); return !way.hasTag("horse", "no") - && (way.hasTag("highway", suitableHighwaysForHorseRiders) || way.hasTag("horse", "yes")) + && (way.hasTag("highway", suitableHighways) || way.hasTag("horse", "yes")) && ( onewayValue == null || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) @@ -49,27 +44,7 @@ public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTrav @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { - return relation.hasTag("type", "route") && relation.hasTag("route", "horse"); - } - - @Override - public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { - final Set restrictionRelations = from.getReferrers().stream() - .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) - .filter(Objects::nonNull) - .filter(it -> "restriction".equals(it.get("type"))) - .filter(it -> it.findRelationMembers("from").contains(from)) - .filter(it -> it.findRelationMembers("via").contains(via)) - .filter(it -> it.findRelationMembers("to").contains(to)) - .collect(Collectors.toSet()); - for (Relation restrictionRelation : restrictionRelations) { - final String restriction = restrictionRelation.get("restriction"); - if (restriction.startsWith("no_")) { - return false; - } - } - - return from.containsNode(via) && to.containsNode(via); + return relation.hasTag("route", "horse") && super.canBeUsedForRelation(relation); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java index 6341659f..a8bb78ef 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/TrolleyBusTransportMode.java @@ -12,6 +12,9 @@ public class TrolleyBusTransportMode extends BusTransportMode { protected TrolleyBusTransportMode() { // should only be instantiable in `ITransportMode` + modeOfTransport = "trolleybus"; + additionalTypeForTurnRestriction = "trolleybus"; + oneWayExceptionFor = "psv"; } @Override From 92fc292f4e1b559f04da41018e4b536ef6648983 Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Fri, 20 Nov 2020 13:18:02 +0100 Subject: [PATCH 10/17] created a test for the BusTransportMode, canTurn still needs to be added --- .../pt_assistant/PTAssistantPlugin.java | 4 +- .../transportmode/BusTransportModeTest.java | 234 ++++++++++++++++++ 2 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java index 90ddc35b..a741c775 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/PTAssistantPlugin.java @@ -39,7 +39,7 @@ import org.openstreetmap.josm.plugins.pt_assistant.actions.SortPTRouteMembersAction; import org.openstreetmap.josm.plugins.pt_assistant.actions.SortPTRouteMembersMenuBar; import org.openstreetmap.josm.plugins.pt_assistant.actions.SplitRoundaboutAction; -import org.openstreetmap.josm.plugins.pt_assistant.actions.routinghelper.RoutingHelperAction; +import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.RouteExplorer; import org.openstreetmap.josm.plugins.pt_assistant.data.PTRouteSegment; import org.openstreetmap.josm.plugins.pt_assistant.gui.PTAssistantLayerManager; import org.openstreetmap.josm.plugins.pt_assistant.validation.BicycleFootRouteValidatorTest; @@ -80,7 +80,7 @@ public PTAssistantPlugin(PluginInformation info) { .addMenu("File", trc("menu", "Public Transport"), KeyEvent.VK_P, 5, ht("/Menu/Public Transport")); addToMenu(PublicTransportMenu); - SelectionEventManager.getInstance().addSelectionListener(new RoutingHelperAction()); + SelectionEventManager.getInstance().addSelectionListener(new RouteExplorer()); SelectionEventManager.getInstance().addSelectionListener(PTAssistantLayerManager.PTLM); KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(PTAssistantLayerManager.PTLM); initialiseWizard(); diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java new file mode 100644 index 00000000..04aaf6e0 --- /dev/null +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -0,0 +1,234 @@ +package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; + +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection.*; + +import org.openstreetmap.josm.data.osm.Node; +import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.Way; +import org.openstreetmap.josm.testutils.JOSMTestRules; + +public class BusTransportModeTest { + @Rule + public JOSMTestRules rules = new JOSMTestRules(); + + private static ITransportMode transportMode; + +// // Create stops: +// Node n1 = new Node(); +// n1.put("name", "Stop1"); +// n1.put("public_transport", "stop_position"); +// RelationMember rm1 = new RelationMember("stop", n1); +// members.add(rm1); +// Way w1 = new Way(); +// w1.put("name", "Stop2"); +// w1.put("highway", "platform"); +// RelationMember rm2 = new RelationMember("platform", w1); +// members.add(rm2); +// Node n2 = new Node(); +// n2.put("name", "Stop3"); +// n2.put("public_transport", "platform"); +// RelationMember rm3 = new RelationMember("platform", n2); +// members.add(rm3); +// Node n3 = new Node(); +// n3.put("name", "Stop4"); +// n3.put("public_transport", "stop_position"); +// RelationMember rm4 = new RelationMember("stop", n3); +// members.add(rm4); +// Node n4 = new Node(); +// n4.put("name", "Stop4"); +// n4.put("public_transport", "platform"); +// RelationMember rm5 = new RelationMember("platform", n4); +// members.add(rm5); +// Node n5 = new Node(); +// n5.put("name", "Stop5"); +// n5.put("highway", "platform"); +// RelationMember rm6 = new RelationMember("platform_exit_only", n5); +// members.add(rm6); +// +// // Create ways: +// Way w2 = new Way(); +// RelationMember rm7 = new RelationMember("", w2); +// members.add(rm7); +// Way w3 = new Way(); +// RelationMember rm8 = new RelationMember("", w3); +// members.add(rm8); +// Relation r3 = new Relation(); // nested relation +// Way w4 = new Way(); +// Way w5 = new Way(); +// Way w6 = new Way(); +// r3.addMember(new RelationMember("", w4)); +// r3.addMember(new RelationMember("", w5)); +// r3.addMember(new RelationMember("", w6)); +// RelationMember rm9 = new RelationMember("", r3); +// members.add(rm9); +// Way w7 = new Way(); +// RelationMember rm10 = new RelationMember("", w7); +// members.add(rm10); + +// public static final Relation route = new Relation(); +// +// @BeforeClass +// public static void setUp() { +// +// ArrayList members = new ArrayList<>(); +// +//// route.setMembers(members); +// route.put("type", "route"); +// route.put("route", "bus"); +// +// transportMode = new BusTransportMode(); +// } + + @Test + public void testCanBeUsedForRelation() { + transportMode = new BusTransportMode(); + Relation route = new Relation(); + route.put("type", "route"); + route.put("route", "bus"); + assertTrue(transportMode.canBeUsedForRelation(route)); + + route.put("route", "trolleybus"); + assertFalse(transportMode.canBeUsedForRelation(route)); + + route.put("route", "coach"); + assertTrue(transportMode.canBeUsedForRelation(route)); + + route.put("route", "tram"); + assertFalse(transportMode.canBeUsedForRelation(route)); + + route.put("route", "train"); + assertFalse(transportMode.canBeUsedForRelation(route)); + + route.put("route", "minibus"); + assertTrue(transportMode.canBeUsedForRelation(route)); + + route.remove("route"); + assertFalse(transportMode.canBeUsedForRelation(route)); + } + + @Test + public void testCanTraverseWay() { + Node n1 = new Node(); + Node n2 = new Node(); + + Way w12 = new Way(); + w12.addNode(n1); + w12.addNode(n2); + + Way residentialWay = new Way(w12); + residentialWay.put("highway", "residential"); + + Way unclassifiedWay = new Way(w12); + unclassifiedWay.put("highway", "unclassified"); + + Way serviceWay = new Way(w12); + serviceWay.put("highway", "service"); + + Way livingStreetWay = new Way(w12); + livingStreetWay.put("highway", "living_street"); + + Way cyclestreetWay = new Way(w12); + cyclestreetWay.put("highway", "cyclestreet"); + + Way primaryWay = new Way(w12); + primaryWay.put("highway", "primary"); + + Way secondaryWay = new Way(w12); + secondaryWay.put("highway", "secondary"); + + Way tertiaryWay = new Way(w12); + tertiaryWay.put("highway", "tertiary"); + + Way trunkWay = new Way(w12); + trunkWay.put("highway", "trunk"); + + Way motorWay = new Way(w12); + motorWay.put("highway", "motorway"); + + Way primaryLinkWay = new Way(w12); + primaryWay.put("highway", "primary"); + + Way secondaryLinkWay = new Way(w12); + secondaryWay.put("highway", "secondary"); + + Way tertiaryLinkWay = new Way(w12); + tertiaryWay.put("highway", "tertiary"); + + Way trunkLinkWay = new Way(w12); + trunkWay.put("highway", "trunk"); + + Way motorWayLinkWay = new Way(w12); + motorWay.put("highway", "motorway"); + + Way trackWay = new Way(w12); + trackWay.put("highway", "track"); + + Way pathWay = new Way(w12); + pathWay.put("highway", "path"); + + Way cycleWay = new Way(w12); + cycleWay.put("highway", "cycleway"); + + Way footWay = new Way(w12); + footWay.put("highway", "footway"); + + for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, + primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, + primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + assertTrue(transportMode.canTraverseWay(way)); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertTrue(transportMode.canTraverseWay(way, BACKWARD)); + } + + for (Way way : new Way[]{cycleWay, footWay}) { + assertFalse(transportMode.canTraverseWay(way)); + assertFalse(transportMode.canTraverseWay(way, FORWARD)); + assertFalse(transportMode.canTraverseWay(way, BACKWARD)); + } + + // what if there is a general oneway tag? + for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, + primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, + primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + way.put("oneway", "yes"); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertFalse(transportMode.canTraverseWay(way, BACKWARD)); + } + + // what if there is an exception for buses? + for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, + primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, + primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + way.put("oneway:bus", "no"); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertTrue(transportMode.canTraverseWay(way, BACKWARD)); + } + + // what if there is an additional exception for psv? + for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, + primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, + primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + way.put("oneway:psv", "no"); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertTrue(transportMode.canTraverseWay(way, BACKWARD)); + } + + // what if there is just an exception for psv? + for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, + primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, + primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + way.remove("oneway:bus"); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertTrue(transportMode.canTraverseWay(way, BACKWARD)); + } + } + + @Test + public void testCanTurn() { + } +} From 8e5752175efb3c5c234a9265aba2b19612540fbd Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Fri, 20 Nov 2020 18:14:52 +0100 Subject: [PATCH 11/17] fixed the tests and the implementation of canTraverseWay --- .../transportmode/AbstractTransportMode.java | 20 ++++++++++--------- .../transportmode/BusTransportMode.java | 13 ++++-------- .../transportmode/BusTransportModeTest.java | 14 ++++++------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java index c352f72a..48113486 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java @@ -1,5 +1,6 @@ package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -17,8 +18,8 @@ public abstract class AbstractTransportMode implements ITransportMode { String modeOfTransport = ""; - String additionalTypeForTurnRestriction = ""; - String oneWayExceptionFor = ""; + String[] additionalTypesForTurnRestriction; + String[] oneWayExceptionsFor; @Override public boolean canBeUsedForRelation(@NotNull final IRelation relation) { @@ -27,18 +28,19 @@ public boolean canBeUsedForRelation(@NotNull final IRelation relation) { @Override public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTraversalDirection direction) { - final String onewayValue = way.get("oneway"); - final Boolean onewayException = (!"".equals(oneWayExceptionFor)) ? "no".equals(way.get("oneway:" + oneWayExceptionFor)) : false; - return onewayValue == null - || onewayException - || ("yes".equals(onewayValue) && direction == WayTraversalDirection.FORWARD) - || ("-1".equals(onewayValue) && direction == WayTraversalDirection.BACKWARD); + final String oneway = way.get("oneway"); + return oneway == null + || Arrays.stream(oneWayExceptionsFor) + .map(mode -> way.get("oneway:" + mode)) + .anyMatch("no"::equals) + || ("yes".equals(oneway) && direction == WayTraversalDirection.FORWARD) + || ("-1".equals(oneway) && direction == WayTraversalDirection.BACKWARD); } @Override public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { List types = new java.util.ArrayList<>(Collections.singletonList("restriction")); - if (!additionalTypeForTurnRestriction.equals("")) types.add("restriction:" + additionalTypeForTurnRestriction); + if (!additionalTypesForTurnRestriction.equals("")) types.add("restriction:" + additionalTypesForTurnRestriction); final Set restrictionRelations = from.getReferrers().stream() .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) .filter(Objects::nonNull) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java index 4af3b470..dd1bd075 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java @@ -2,18 +2,12 @@ package org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.transportmode; import java.util.List; -import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import com.drew.lang.annotations.NotNull; import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; -import org.openstreetmap.josm.data.osm.Node; -import org.openstreetmap.josm.data.osm.OsmPrimitiveType; -import org.openstreetmap.josm.data.osm.Relation; -import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; import org.openstreetmap.josm.plugins.pt_assistant.utils.PTIcons; import org.openstreetmap.josm.tools.I18n; @@ -29,8 +23,9 @@ public class BusTransportMode extends AbstractTransportMode { protected BusTransportMode() { // should only be instantiable in `ITransportMode` modeOfTransport = "bus"; - additionalTypeForTurnRestriction = "bus"; - oneWayExceptionFor = "psv"; // TODO turn these 2 into lists, so they can also check for "oneway:bus", "except"="psv" + additionalTypesForTurnRestriction = new String[]{"bus", "psv"}; + oneWayExceptionsFor = new String[]{"bus", "psv"}; + } @Override @@ -38,7 +33,7 @@ public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTrav return ( way.hasTag("highway", suitableHighways) || way.hasTag("psv", "yes") || way.hasTag ("bus", "yes")) - && canTraverseWay(way, direction); + && super.canTraverseWay(way, direction); } @Override diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index 04aaf6e0..cca6e95a 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -16,7 +16,7 @@ public class BusTransportModeTest { @Rule public JOSMTestRules rules = new JOSMTestRules(); - private static ITransportMode transportMode; + private static final ITransportMode transportMode = new BusTransportMode(); // // Create stops: // Node n1 = new Node(); @@ -86,7 +86,7 @@ public class BusTransportModeTest { @Test public void testCanBeUsedForRelation() { - transportMode = new BusTransportMode(); + Relation route = new Relation(); route.put("type", "route"); route.put("route", "bus"); @@ -151,19 +151,19 @@ public void testCanTraverseWay() { motorWay.put("highway", "motorway"); Way primaryLinkWay = new Way(w12); - primaryWay.put("highway", "primary"); + primaryLinkWay.put("highway", "primary_link"); Way secondaryLinkWay = new Way(w12); - secondaryWay.put("highway", "secondary"); + secondaryLinkWay.put("highway", "secondary_link"); Way tertiaryLinkWay = new Way(w12); - tertiaryWay.put("highway", "tertiary"); + tertiaryLinkWay.put("highway", "tertiary_link"); Way trunkLinkWay = new Way(w12); - trunkWay.put("highway", "trunk"); + trunkLinkWay.put("highway", "trunk_link"); Way motorWayLinkWay = new Way(w12); - motorWay.put("highway", "motorway"); + motorWayLinkWay.put("highway", "motorway_link"); Way trackWay = new Way(w12); trackWay.put("highway", "track"); From 1d15ad9e9a1ac4c92af830410f1e9eda70c640fe Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Fri, 20 Nov 2020 21:31:15 +0100 Subject: [PATCH 12/17] made the BusTransportModeTest somewhat more extensive for testCanTraverseWay --- .../transportmode/BusTransportModeTest.java | 127 ++++++------------ 1 file changed, 43 insertions(+), 84 deletions(-) diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index cca6e95a..4cee88f6 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -18,75 +18,8 @@ public class BusTransportModeTest { private static final ITransportMode transportMode = new BusTransportMode(); -// // Create stops: -// Node n1 = new Node(); -// n1.put("name", "Stop1"); -// n1.put("public_transport", "stop_position"); -// RelationMember rm1 = new RelationMember("stop", n1); -// members.add(rm1); -// Way w1 = new Way(); -// w1.put("name", "Stop2"); -// w1.put("highway", "platform"); -// RelationMember rm2 = new RelationMember("platform", w1); -// members.add(rm2); -// Node n2 = new Node(); -// n2.put("name", "Stop3"); -// n2.put("public_transport", "platform"); -// RelationMember rm3 = new RelationMember("platform", n2); -// members.add(rm3); -// Node n3 = new Node(); -// n3.put("name", "Stop4"); -// n3.put("public_transport", "stop_position"); -// RelationMember rm4 = new RelationMember("stop", n3); -// members.add(rm4); -// Node n4 = new Node(); -// n4.put("name", "Stop4"); -// n4.put("public_transport", "platform"); -// RelationMember rm5 = new RelationMember("platform", n4); -// members.add(rm5); -// Node n5 = new Node(); -// n5.put("name", "Stop5"); -// n5.put("highway", "platform"); -// RelationMember rm6 = new RelationMember("platform_exit_only", n5); -// members.add(rm6); -// -// // Create ways: -// Way w2 = new Way(); -// RelationMember rm7 = new RelationMember("", w2); -// members.add(rm7); -// Way w3 = new Way(); -// RelationMember rm8 = new RelationMember("", w3); -// members.add(rm8); -// Relation r3 = new Relation(); // nested relation -// Way w4 = new Way(); -// Way w5 = new Way(); -// Way w6 = new Way(); -// r3.addMember(new RelationMember("", w4)); -// r3.addMember(new RelationMember("", w5)); -// r3.addMember(new RelationMember("", w6)); -// RelationMember rm9 = new RelationMember("", r3); -// members.add(rm9); -// Way w7 = new Way(); -// RelationMember rm10 = new RelationMember("", w7); -// members.add(rm10); - -// public static final Relation route = new Relation(); -// -// @BeforeClass -// public static void setUp() { -// -// ArrayList members = new ArrayList<>(); -// -//// route.setMembers(members); -// route.put("type", "route"); -// route.put("route", "bus"); -// -// transportMode = new BusTransportMode(); -// } - @Test public void testCanBeUsedForRelation() { - Relation route = new Relation(); route.put("type", "route"); route.put("route", "bus"); @@ -177,51 +110,77 @@ public void testCanTraverseWay() { Way footWay = new Way(w12); footWay.put("highway", "footway"); - for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, - primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, - primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + Way pedestrianWay = new Way(w12); + footWay.put("highway", "pedestrian"); + + Way railWay = new Way(w12); + footWay.put("railway", "rail"); + + Way tramWay = new Way(w12); + footWay.put("railway", "tram"); + + Way subWay = new Way(w12); + footWay.put("railway", "subway"); + + Way light_railWay = new Way(w12); + footWay.put("railway", "light_rail"); + + Way[] suitableWaysForBuses = new Way[]{ + residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, + primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, + primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}; + + Way[] unSuitableWaysForBuses = new Way[]{cycleWay, footWay, pedestrianWay, railWay, tramWay, subWay, light_railWay}; + + for (Way way : suitableWaysForBuses) { assertTrue(transportMode.canTraverseWay(way)); assertTrue(transportMode.canTraverseWay(way, FORWARD)); assertTrue(transportMode.canTraverseWay(way, BACKWARD)); } - for (Way way : new Way[]{cycleWay, footWay}) { + for (Way way : unSuitableWaysForBuses) { assertFalse(transportMode.canTraverseWay(way)); assertFalse(transportMode.canTraverseWay(way, FORWARD)); assertFalse(transportMode.canTraverseWay(way, BACKWARD)); } + for (Way way : unSuitableWaysForBuses) { + way.put("bus", "yes"); + assertTrue(transportMode.canTraverseWay(way)); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertTrue(transportMode.canTraverseWay(way, BACKWARD)); + } + + for (Way way : unSuitableWaysForBuses) { + way.put("psv", "yes"); + assertTrue(transportMode.canTraverseWay(way)); + assertTrue(transportMode.canTraverseWay(way, FORWARD)); + assertTrue(transportMode.canTraverseWay(way, BACKWARD)); + } + // what if there is a general oneway tag? - for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, - primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, - primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + for (Way way : suitableWaysForBuses) { way.put("oneway", "yes"); assertTrue(transportMode.canTraverseWay(way, FORWARD)); assertFalse(transportMode.canTraverseWay(way, BACKWARD)); } // what if there is an exception for buses? - for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, - primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, - primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + for (Way way : suitableWaysForBuses) { way.put("oneway:bus", "no"); assertTrue(transportMode.canTraverseWay(way, FORWARD)); assertTrue(transportMode.canTraverseWay(way, BACKWARD)); } // what if there is an additional exception for psv? - for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, - primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, - primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + for (Way way : suitableWaysForBuses) { way.put("oneway:psv", "no"); assertTrue(transportMode.canTraverseWay(way, FORWARD)); assertTrue(transportMode.canTraverseWay(way, BACKWARD)); } - // what if there is just an exception for psv? - for (Way way : new Way[]{residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, - primaryWay, secondaryWay, tertiaryWay, trunkWay, motorWay, - primaryLinkWay, secondaryLinkWay, tertiaryLinkWay, trunkLinkWay, motorWayLinkWay}) { + // what if there is just an exception for psv?; + for (Way way : suitableWaysForBuses) { way.remove("oneway:bus"); assertTrue(transportMode.canTraverseWay(way, FORWARD)); assertTrue(transportMode.canTraverseWay(way, BACKWARD)); From d3ec98408cba73560542f5c9e0c52ebdd7d7de7f Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Fri, 20 Nov 2020 23:22:41 +0100 Subject: [PATCH 13/17] I tried to add tests for canTurn, but I'm stuck on line 46 of AbstractTransportMode, where it's not getting the referrers of the from Way. I do see those referrers in my variables list while single stepping though. --- .../transportmode/AbstractTransportMode.java | 16 +++-- .../transportmode/BusTransportModeTest.java | 66 +++++++++++++++++++ 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java index 48113486..4c78512a 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -11,6 +12,7 @@ import org.openstreetmap.josm.data.osm.IRelation; import org.openstreetmap.josm.data.osm.IWay; import org.openstreetmap.josm.data.osm.Node; +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.Way; @@ -40,15 +42,15 @@ public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTrav @Override public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { List types = new java.util.ArrayList<>(Collections.singletonList("restriction")); - if (!additionalTypesForTurnRestriction.equals("")) types.add("restriction:" + additionalTypesForTurnRestriction); + Arrays.stream(additionalTypesForTurnRestriction).map(at -> "restriction:" + at).forEach(types::add); final Set restrictionRelations = from.getReferrers().stream() .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) - .filter(Objects::nonNull) - .filter(it -> it.hasTag("type", types)) - .filter(it -> it.findRelationMembers("from").contains(from)) - .filter(it -> it.findRelationMembers("via").contains(via)) - .filter(it -> it.findRelationMembers("to").contains(to)) - .collect(Collectors.toSet()); + .filter(relation -> + relation != null && types.contains(relation.get("type")) + && relation.findRelationMembers("from").contains(from) + && relation.findRelationMembers("via").contains(via) + && relation.findRelationMembers("to").contains(to) + ).collect(Collectors.toSet()); for (Relation restrictionRelation : restrictionRelations) { final String restriction = restrictionRelation.get("restriction"); final String except = ("".equals(modeOfTransport)) ? restrictionRelation.get("except") : ""; diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index 4cee88f6..a714c589 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -7,8 +7,11 @@ import static org.junit.Assert.assertTrue; import static org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection.*; +import org.openstreetmap.josm.data.coor.LatLon; +import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Relation; +import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.testutils.JOSMTestRules; @@ -189,5 +192,68 @@ public void testCanTraverseWay() { @Test public void testCanTurn() { + DataSet ds = new DataSet(); + Node n1 = new Node(); + Node n2 = new Node(); + Node n3 = new Node(); + n1.setCoor(new LatLon(50.0, 2.0)); + n2.setCoor(new LatLon(50.1, 2.1)); + n3.setCoor(new LatLon(50.2, 2.2)); + + Way w12 = new Way(); + w12.addNode(n1); + w12.addNode(n2); + + Way w23 = new Way(); + w23.addNode(n2); + w23.addNode(n3); + + Relation turnRestriction = new Relation(); + turnRestriction.put("type", "restriction"); + + RelationMember rm1 = new RelationMember("", w12); + RelationMember rm2 = new RelationMember("", w23); + + ds.addPrimitive(n1); + ds.addPrimitive(n2); + ds.addPrimitive(n3); + ds.addPrimitive(w12); + ds.addPrimitive(w23); + ds.addPrimitive(turnRestriction); + + String[] noRestrictionTypes = {"no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on", "no_entry", "no_exit"}; + String[] onlyRestrictionTypes = {"only_right_turn", "only_left_turn", "only_u_turn", "only_straight_on"}; + String[] appliesForOtherModeOfTransport = {"hgv", "caravan", "motorcar", "agricultural", "motorcycle", "bicycle", "hazmat"}; + String[] exceptForOtherModeOfTransport = {"bicycle", "hgv", "motorcar", "emergency"}; + String[] exceptForThisModeOfTransport = {"bus", "psv"}; + + for (String noType : noRestrictionTypes) { + Relation rel = new Relation(turnRestriction); + rel.addMember(rm1); + rel.addMember(rm2); + for (String mot : appliesForOtherModeOfTransport) { + rel.put("restriction:" + mot, noType); + assertTrue(transportMode.canTurn(w12, n2, w23)); + rel.remove("restriction:" + mot); + } + rel.put("restriction:bus", noType); + assertFalse(transportMode.canTurn(w12, n2, w23)); + rel.remove("restriction:bus"); + + rel.put("restriction", noType); + assertFalse(transportMode.canTurn(w12, n2, w23)); + + for (String exc : exceptForOtherModeOfTransport) { + rel.put("except", exc); + assertFalse(transportMode.canTurn(w12, n2, w23)); + } + + for (String exc : exceptForThisModeOfTransport) { + rel.put("except", exc); + assertTrue(transportMode.canTurn(w12, n2, w23)); + } + + } + } } From cab5fd503a3c064fbc18ef85e6d48996c8403d1c Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sat, 21 Nov 2020 12:38:37 +0100 Subject: [PATCH 14/17] All the tests are passing. Prohibiting turn restrictions "no_" are tested for bus, mandatory "only_" still need to be added --- .../transportmode/AbstractTransportMode.java | 19 +++++++-- .../transportmode/BusTransportMode.java | 2 +- .../transportmode/BusTransportModeTest.java | 39 +++++++++++-------- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java index 4c78512a..adf28cb3 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -21,6 +22,7 @@ public abstract class AbstractTransportMode implements ITransportMode { String modeOfTransport = ""; String[] additionalTypesForTurnRestriction; + String[] turnRestrictionExceptionsFor; String[] oneWayExceptionsFor; @Override @@ -52,10 +54,19 @@ public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNul && relation.findRelationMembers("to").contains(to) ).collect(Collectors.toSet()); for (Relation restrictionRelation : restrictionRelations) { - final String restriction = restrictionRelation.get("restriction"); - final String except = ("".equals(modeOfTransport)) ? restrictionRelation.get("except") : ""; - if (restriction.startsWith("no_") && !except.contains(modeOfTransport)) { - return false; + for (String type : types) { + final String restriction = restrictionRelation.get(type); + if (restriction != null && restriction.startsWith("no_")) { + final String except = !"".equals(modeOfTransport) ? restrictionRelation.get("except") : ""; + if (except != null) { + for (String tre : turnRestrictionExceptionsFor) { + if (except.contains(tre)) { + return from.containsNode(via) && to.containsNode(via); + } + } + } + return false; + } } } return from.containsNode(via) && to.containsNode(via); diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java index dd1bd075..4eace45e 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportMode.java @@ -24,8 +24,8 @@ protected BusTransportMode() { // should only be instantiable in `ITransportMode` modeOfTransport = "bus"; additionalTypesForTurnRestriction = new String[]{"bus", "psv"}; + turnRestrictionExceptionsFor = new String[]{"bus", "psv"}; oneWayExceptionsFor = new String[]{"bus", "psv"}; - } @Override diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index a714c589..eff4fae7 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -192,7 +192,6 @@ public void testCanTraverseWay() { @Test public void testCanTurn() { - DataSet ds = new DataSet(); Node n1 = new Node(); Node n2 = new Node(); Node n3 = new Node(); @@ -211,9 +210,11 @@ public void testCanTurn() { Relation turnRestriction = new Relation(); turnRestriction.put("type", "restriction"); - RelationMember rm1 = new RelationMember("", w12); - RelationMember rm2 = new RelationMember("", w23); + RelationMember fromWayMember = new RelationMember("from", w12); + RelationMember viaNodeMember = new RelationMember("via", n2); + RelationMember toWayMember = new RelationMember("to", w23); + DataSet ds = new DataSet(); ds.addPrimitive(n1); ds.addPrimitive(n2); ds.addPrimitive(n3); @@ -221,29 +222,35 @@ public void testCanTurn() { ds.addPrimitive(w23); ds.addPrimitive(turnRestriction); - String[] noRestrictionTypes = {"no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on", "no_entry", "no_exit"}; + String[] prohibitingRestrictionTypes = {"no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on", "no_entry", "no_exit"}; String[] onlyRestrictionTypes = {"only_right_turn", "only_left_turn", "only_u_turn", "only_straight_on"}; - String[] appliesForOtherModeOfTransport = {"hgv", "caravan", "motorcar", "agricultural", "motorcycle", "bicycle", "hazmat"}; - String[] exceptForOtherModeOfTransport = {"bicycle", "hgv", "motorcar", "emergency"}; + String[] appliesForOtherModesOfTransport = {"hgv", "caravan", "motorcar", "agricultural", "motorcycle", "bicycle", "hazmat"}; + String[] exceptForOtherModesOfTransport = {"bicycle", "hgv", "motorcar", "emergency"}; String[] exceptForThisModeOfTransport = {"bus", "psv"}; - for (String noType : noRestrictionTypes) { - Relation rel = new Relation(turnRestriction); - rel.addMember(rm1); - rel.addMember(rm2); - for (String mot : appliesForOtherModeOfTransport) { - rel.put("restriction:" + mot, noType); + Relation rel = new Relation(); + ds.addPrimitive(rel); + + for (String prohibitingType : prohibitingRestrictionTypes) { + rel = new Relation(turnRestriction); + rel.addMember(fromWayMember); + rel.addMember(viaNodeMember); + rel.addMember(toWayMember); + ds.removePrimitive(rel); + ds.addPrimitive(rel); + for (String mot : appliesForOtherModesOfTransport) { + rel.put("restriction:" + mot, prohibitingType); assertTrue(transportMode.canTurn(w12, n2, w23)); rel.remove("restriction:" + mot); } - rel.put("restriction:bus", noType); + rel.put("restriction:bus", prohibitingType); assertFalse(transportMode.canTurn(w12, n2, w23)); rel.remove("restriction:bus"); - rel.put("restriction", noType); + rel.put("restriction", prohibitingType); assertFalse(transportMode.canTurn(w12, n2, w23)); - for (String exc : exceptForOtherModeOfTransport) { + for (String exc : exceptForOtherModesOfTransport) { rel.put("except", exc); assertFalse(transportMode.canTurn(w12, n2, w23)); } @@ -252,8 +259,6 @@ public void testCanTurn() { rel.put("except", exc); assertTrue(transportMode.canTurn(w12, n2, w23)); } - } - } } From 58f97e5d96a1a13fe8dff9c39bc04a3184ac9eeb Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sat, 21 Nov 2020 12:46:40 +0100 Subject: [PATCH 15/17] I added tests for the mandatory types, but the result with only assertTrue statements most likely means not all cases are tested. So this needs some more thought. --- .../transportmode/BusTransportModeTest.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index eff4fae7..f5b09921 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -223,7 +223,7 @@ public void testCanTurn() { ds.addPrimitive(turnRestriction); String[] prohibitingRestrictionTypes = {"no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on", "no_entry", "no_exit"}; - String[] onlyRestrictionTypes = {"only_right_turn", "only_left_turn", "only_u_turn", "only_straight_on"}; + String[] mandatoryRestrictionTypes = {"only_right_turn", "only_left_turn", "only_u_turn", "only_straight_on"}; String[] appliesForOtherModesOfTransport = {"hgv", "caravan", "motorcar", "agricultural", "motorcycle", "bicycle", "hazmat"}; String[] exceptForOtherModesOfTransport = {"bicycle", "hgv", "motorcar", "emergency"}; String[] exceptForThisModeOfTransport = {"bus", "psv"}; @@ -260,5 +260,35 @@ public void testCanTurn() { assertTrue(transportMode.canTurn(w12, n2, w23)); } } + + for (String mandatoryType : mandatoryRestrictionTypes) { + rel = new Relation(turnRestriction); + rel.addMember(fromWayMember); + rel.addMember(viaNodeMember); + rel.addMember(toWayMember); + ds.removePrimitive(rel); + ds.addPrimitive(rel); + for (String mot : appliesForOtherModesOfTransport) { + rel.put("restriction:" + mot, mandatoryType); + assertTrue(transportMode.canTurn(w12, n2, w23)); + rel.remove("restriction:" + mot); + } + rel.put("restriction:bus", mandatoryType); + assertTrue(transportMode.canTurn(w12, n2, w23)); + rel.remove("restriction:bus"); + + rel.put("restriction", mandatoryType); + assertTrue(transportMode.canTurn(w12, n2, w23)); + + for (String exc : exceptForOtherModesOfTransport) { + rel.put("except", exc); + assertTrue(transportMode.canTurn(w12, n2, w23)); + } + + for (String exc : exceptForThisModeOfTransport) { + rel.put("except", exc); + assertTrue(transportMode.canTurn(w12, n2, w23)); + } + } } } From 5055bc51fd319ee9fdbf35ada991d9fb9a2c1c8b Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sat, 21 Nov 2020 16:56:53 +0100 Subject: [PATCH 16/17] Oops, fix copy/paste error --- .../transportmode/BusTransportModeTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index f5b09921..266a6551 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -114,19 +114,19 @@ public void testCanTraverseWay() { footWay.put("highway", "footway"); Way pedestrianWay = new Way(w12); - footWay.put("highway", "pedestrian"); + pedestrianWay.put("highway", "pedestrian"); Way railWay = new Way(w12); - footWay.put("railway", "rail"); + railWay.put("railway", "rail"); Way tramWay = new Way(w12); - footWay.put("railway", "tram"); + tramWay.put("railway", "tram"); Way subWay = new Way(w12); - footWay.put("railway", "subway"); + subWay.put("railway", "subway"); Way light_railWay = new Way(w12); - footWay.put("railway", "light_rail"); + light_railWay.put("railway", "light_rail"); Way[] suitableWaysForBuses = new Way[]{ residentialWay, unclassifiedWay, serviceWay, livingStreetWay, cyclestreetWay, From 56b6c9b9401ae7ca1d5134160a1517b52ac89074 Mon Sep 17 00:00:00 2001 From: PolyglotOpenstreetmap Date: Sat, 21 Nov 2020 17:58:07 +0100 Subject: [PATCH 17/17] Turn restrictions seem to be a can of worms... I added support for TRs which have a via way instead of a via node --- .../transportmode/AbstractTransportMode.java | 46 +++++++++--- .../transportmode/ITransportMode.java | 12 +++- .../transportmode/BusTransportModeTest.java | 72 ++++++++++++++++--- 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java index adf28cb3..6278077f 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/AbstractTransportMode.java @@ -18,6 +18,7 @@ import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.plugins.pt_assistant.routeexplorer.WayTraversalDirection; +import org.openstreetmap.josm.plugins.pt_assistant.utils.WayUtils; public abstract class AbstractTransportMode implements ITransportMode { String modeOfTransport = ""; @@ -45,15 +46,8 @@ public boolean canTraverseWay(@NotNull final IWay way, @NotNull final WayTrav public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to) { List types = new java.util.ArrayList<>(Collections.singletonList("restriction")); Arrays.stream(additionalTypesForTurnRestriction).map(at -> "restriction:" + at).forEach(types::add); - final Set restrictionRelations = from.getReferrers().stream() - .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) - .filter(relation -> - relation != null && types.contains(relation.get("type")) - && relation.findRelationMembers("from").contains(from) - && relation.findRelationMembers("via").contains(via) - && relation.findRelationMembers("to").contains(to) - ).collect(Collectors.toSet()); - for (Relation restrictionRelation : restrictionRelations) { + final boolean bothWaysAreConnectedAtViaNode = from.containsNode(via) && to.containsNode(via); + for (Relation restrictionRelation : getTurnRestrictions(from, via, to, types)) { for (String type : types) { final String restriction = restrictionRelation.get(type); if (restriction != null && restriction.startsWith("no_")) { @@ -61,7 +55,7 @@ public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNul if (except != null) { for (String tre : turnRestrictionExceptionsFor) { if (except.contains(tre)) { - return from.containsNode(via) && to.containsNode(via); + return bothWaysAreConnectedAtViaNode; } } } @@ -69,6 +63,36 @@ public boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNul } } } - return from.containsNode(via) && to.containsNode(via); + return bothWaysAreConnectedAtViaNode; + } + + @Override + public boolean canTurn(@NotNull final Way from, @NotNull final Way via, @NotNull final Way to) { + List types = new java.util.ArrayList<>(Collections.singletonList("restriction")); + Arrays.stream(additionalTypesForTurnRestriction).map(at -> "restriction:" + at).forEach(types::add); + for (Relation restrictionRelation : getTurnRestrictions(from, via, to, types)) { + for (String type : types) { + final String restriction = restrictionRelation.get(type); + if (restriction != null && restriction.startsWith("no_")) { + final String except = !"".equals(modeOfTransport) ? restrictionRelation.get("except") : ""; + if (except != null) { + return Arrays.stream(turnRestrictionExceptionsFor).anyMatch(except::contains); + } + return false; + } + } + } + return true; + } + + private Set getTurnRestrictions(Way from, OsmPrimitive via, Way to, List types) { + return from.getReferrers().stream() + .map(it -> it.getType() == OsmPrimitiveType.RELATION ? (Relation) it : null) + .filter(relation -> + relation != null && types.contains(relation.get("type")) + && relation.findRelationMembers("from").contains(from) + && relation.findRelationMembers("via").contains(via) + && relation.findRelationMembers("to").contains(to) + ).collect(Collectors.toSet()); } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java index 7486faa0..68b1df65 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java +++ b/src/main/java/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/ITransportMode.java @@ -51,12 +51,22 @@ default boolean canTraverseWay(final Way way) { * @param via the node that the vehicle travels through, must be part of {@code from} and {@code to} ways, * or this method will return false * @param to the way onto which the vehicle makes the turn - * @return {@code true} iff the transport mode can make a turn from the given {@code from} way, + * @return {@code true} if the transport mode can make a turn from the given {@code from} way, * via the given {@code via} node to the given {@code to} way. Otherwise {@code false}. * This method assumes that both ways can be traversed by the transport mode, it does not check that. */ boolean canTurn(@NotNull final Way from, @NotNull final Node via, @NotNull final Way to); + /** + * @param from the way from which the vehicle is coming + * @param via the way that the vehicle travels through + * @param to the way onto which the vehicle makes the turn + * @return {@code true} if the transport mode can make a turn from the given {@code from} way, + * through the given {@code via} way to the given {@code to} way. Otherwise {@code false}. + * This method assumes that all three ways can be traversed by the transport mode, it does not check that. + */ + boolean canTurn(@NotNull final Way from, @NotNull final Way via, @NotNull final Way to); + /** * @return an icon representing the transport mode */ diff --git a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java index 266a6551..58ef92ed 100644 --- a/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/pt_assistant/routeexplorer/transportmode/BusTransportModeTest.java @@ -195,9 +195,11 @@ public void testCanTurn() { Node n1 = new Node(); Node n2 = new Node(); Node n3 = new Node(); + Node n4 = new Node(); n1.setCoor(new LatLon(50.0, 2.0)); n2.setCoor(new LatLon(50.1, 2.1)); n3.setCoor(new LatLon(50.2, 2.2)); + n4.setCoor(new LatLon(50.3, 2.3)); Way w12 = new Way(); w12.addNode(n1); @@ -207,6 +209,10 @@ public void testCanTurn() { w23.addNode(n2); w23.addNode(n3); + Way w34 = new Way(); + w34.addNode(n2); + w34.addNode(n3); + Relation turnRestriction = new Relation(); turnRestriction.put("type", "restriction"); @@ -214,12 +220,17 @@ public void testCanTurn() { RelationMember viaNodeMember = new RelationMember("via", n2); RelationMember toWayMember = new RelationMember("to", w23); + RelationMember viaWayMember = new RelationMember("via", w23); + RelationMember toWayMember2 = new RelationMember("to", w34); + DataSet ds = new DataSet(); ds.addPrimitive(n1); ds.addPrimitive(n2); ds.addPrimitive(n3); + ds.addPrimitive(n4); ds.addPrimitive(w12); ds.addPrimitive(w23); + ds.addPrimitive(w34); ds.addPrimitive(turnRestriction); String[] prohibitingRestrictionTypes = {"no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on", "no_entry", "no_exit"}; @@ -231,6 +242,7 @@ public void testCanTurn() { Relation rel = new Relation(); ds.addPrimitive(rel); + String restrictionFor = ""; for (String prohibitingType : prohibitingRestrictionTypes) { rel = new Relation(turnRestriction); rel.addMember(fromWayMember); @@ -239,25 +251,60 @@ public void testCanTurn() { ds.removePrimitive(rel); ds.addPrimitive(rel); for (String mot : appliesForOtherModesOfTransport) { + restrictionFor = "restriction:" + mot; rel.put("restriction:" + mot, prohibitingType); - assertTrue(transportMode.canTurn(w12, n2, w23)); + assertTrue(String.format("%s %s", restrictionFor, prohibitingType), transportMode.canTurn(w12, n2, w23)); rel.remove("restriction:" + mot); } - rel.put("restriction:bus", prohibitingType); - assertFalse(transportMode.canTurn(w12, n2, w23)); - rel.remove("restriction:bus"); + restrictionFor = "restriction:bus"; + rel.put(restrictionFor, prohibitingType); + assertFalse(String.format("%s %s", restrictionFor, prohibitingType), transportMode.canTurn(w12, n2, w23)); + rel.remove(restrictionFor); rel.put("restriction", prohibitingType); - assertFalse(transportMode.canTurn(w12, n2, w23)); + assertFalse(String.format("%s", prohibitingType), transportMode.canTurn(w12, n2, w23)); for (String exc : exceptForOtherModesOfTransport) { rel.put("except", exc); - assertFalse(transportMode.canTurn(w12, n2, w23)); + assertFalse(String.format("%s", exc), transportMode.canTurn(w12, n2, w23)); } for (String exc : exceptForThisModeOfTransport) { rel.put("except", exc); - assertTrue(transportMode.canTurn(w12, n2, w23)); + assertTrue(String.format("%s", exc), transportMode.canTurn(w12, n2, w23)); + } + } + + for (String prohibitingType : prohibitingRestrictionTypes) { + rel = new Relation(turnRestriction); + rel.addMember(fromWayMember); + rel.addMember(viaWayMember); + rel.addMember(toWayMember2); + ds.removePrimitive(rel); + ds.addPrimitive(rel); + + for (String mot : appliesForOtherModesOfTransport) { + restrictionFor = "restriction:" + mot; + rel.put(restrictionFor, prohibitingType); + assertTrue(String.format("%s %s", restrictionFor, prohibitingType), transportMode.canTurn(w12, w23, w34)); + rel.remove(restrictionFor); + } + restrictionFor = "restriction:bus"; + rel.put(restrictionFor, prohibitingType); + assertFalse(String.format("%s %s", restrictionFor, prohibitingType), transportMode.canTurn(w12, w23, w34)); + rel.remove(restrictionFor); + + rel.put("restriction", prohibitingType); + assertFalse(String.format("%s", prohibitingType), transportMode.canTurn(w12, w23, w34)); + + for (String exc : exceptForOtherModesOfTransport) { + rel.put("except", exc); + assertFalse(String.format("%s", exc), transportMode.canTurn(w12, w23, w34)); + } + + for (String exc : exceptForThisModeOfTransport) { + rel.put("except", exc); + assertTrue(String.format("%s", exc), transportMode.canTurn(w12, w23, w34)); } } @@ -269,13 +316,16 @@ public void testCanTurn() { ds.removePrimitive(rel); ds.addPrimitive(rel); for (String mot : appliesForOtherModesOfTransport) { - rel.put("restriction:" + mot, mandatoryType); + restrictionFor = "restriction:" + mot; + rel.put(restrictionFor, mandatoryType); assertTrue(transportMode.canTurn(w12, n2, w23)); - rel.remove("restriction:" + mot); + rel.remove(restrictionFor); } - rel.put("restriction:bus", mandatoryType); + + restrictionFor = "restriction:bus"; + rel.put(restrictionFor, mandatoryType); assertTrue(transportMode.canTurn(w12, n2, w23)); - rel.remove("restriction:bus"); + rel.remove(restrictionFor); rel.put("restriction", mandatoryType); assertTrue(transportMode.canTurn(w12, n2, w23));