From 468d79b7e5b99e9f774f57a098f5cac18ae27af7 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] 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);