Skip to content

Commit

Permalink
Start creating the routing helper rewrite, at the moment just a bit o…
Browse files Browse the repository at this point in the history
…f UI that doesn't do much yet
  • Loading branch information
floscher committed Nov 15, 2020
1 parent e677e7e commit 610fbc3
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<Relation> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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<ITransportMode> TRANSPORT_MODES = Collections.singleton(new BusTransportMode());

private Optional<ITransportMode> 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<ITransportMode> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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()));
}
}
}
Original file line number Diff line number Diff line change
@@ -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<IWay<? extends INode>, INode> startNodeGetter;
private final Function<IWay<? extends INode>, INode> endNodeGetter;

WayTraversalDirection(
@NotNull final Function<IWay<? extends INode>, INode> startNodeGetter,
@NotNull final Function<IWay<? extends INode>, 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<? extends INode> 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<? extends INode> way) {
return endNodeGetter.apply(way);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,4 +54,13 @@ public static Optional<Bounds> 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());
}
}

0 comments on commit 610fbc3

Please sign in to comment.