// License: GPL. For details, see LICENSE file. package public_transport; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import javax.swing.DefaultCellEditor; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.JosmAction; import org.openstreetmap.josm.actions.mapmode.DeleteAction; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; 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.data.osm.visitor.BoundingXYVisitor; public class RoutePatternAction extends JosmAction { public static int STOPLIST_ROLE_COLUMN = 2; private static class RoutesLSL implements ListSelectionListener { RoutePatternAction root = null; RoutesLSL(RoutePatternAction rpa) { root = rpa; } @Override public void valueChanged(ListSelectionEvent e) { root.routesSelectionChanged(); } } private static class TagTableModel extends DefaultTableModel implements TableModelListener { Relation relation = null; TreeSet<String> blacklist = null; boolean hasFixedKeys = true; TagTableModel(boolean hasFixedKeys) { this.hasFixedKeys = hasFixedKeys; } @Override public boolean isCellEditable(int row, int column) { if ((column == 0) && (hasFixedKeys)) return false; return true; } public void readRelation(Relation rel) { relation = rel; for (int i = 0; i < getRowCount(); ++i) { String value = rel.get((String) getValueAt(i, 0)); if (value == null) value = ""; setValueAt(value, i, 1); } } public void readRelation(Relation rel, TreeSet<String> blacklist) { relation = rel; this.blacklist = blacklist; setRowCount(0); Iterator<Map.Entry<String, String>> iter = rel.getKeys().entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, String> entry = iter.next(); if (!blacklist.contains(entry.getKey())) { Vector<String> newRow = new Vector<>(); newRow.add(entry.getKey()); newRow.add(entry.getValue()); addRow(newRow); } } for (int i = 0; i < getRowCount(); ++i) { String value = rel.get((String) getValueAt(i, 0)); if (value == null) value = ""; setValueAt(value, i, 1); } } @Override public void tableChanged(TableModelEvent e) { if (e.getType() == TableModelEvent.UPDATE) { relation.setModified(true); String key = (String) getValueAt(e.getFirstRow(), 0); if (key == null) return; if ((blacklist == null) || (!blacklist.contains(key))) { relation.setModified(true); if ("".equals(getValueAt(e.getFirstRow(), 1))) relation.remove(key); else relation.put(key, (String) getValueAt(e.getFirstRow(), 1)); } else { if (e.getColumn() == 0) setValueAt("", e.getFirstRow(), 0); } } } } private static class CustomCellEditorTable extends JTable { TreeMap<Integer, TableCellEditor> col1 = null; TreeMap<Integer, TableCellEditor> col2 = null; CustomCellEditorTable() { col1 = new TreeMap<>(); col2 = new TreeMap<>(); } @Override public TableCellEditor getCellEditor(int row, int column) { TableCellEditor editor = null; if (column == 0) editor = col1.get(Integer.valueOf(row)); else editor = col2.get(Integer.valueOf(row)); if (editor == null) return new DefaultCellEditor(new JTextField()); else return editor; } public void setCellEditor(int row, int column, TableCellEditor editor) { if (column == 0) col1.put(Integer.valueOf(row), editor); else col2.put(Integer.valueOf(row), editor); } } private static class StoplistTableModel extends DefaultTableModel { public Vector<Node> nodes = new Vector<>(); @Override public boolean isCellEditable(int row, int column) { if (column != STOPLIST_ROLE_COLUMN) return false; return true; } @Override public void addRow(Object[] obj) { throw new UnsupportedOperationException(); } @Override public void insertRow(int insPos, Object[] obj) { throw new UnsupportedOperationException(); } public void addRow(Node node, String role, double distance) { insertRow(-1, node, role, distance); } public void insertRow(int insPos, Node node, String role, double distance) { String[] buf = {"", "", "", ""}; String curName = node.get("name"); if (curName != null) { buf[0] = curName; } else { buf[0] = tr("[ID] {0}", (Long.valueOf(node.getId())).toString()); } String curRef = node.get("ref"); if (curRef != null) { buf[1] = curRef; } buf[STOPLIST_ROLE_COLUMN] = role; buf[3] = Double.toString(((double) (int) (distance * 40000 / 360.0 * 100)) / 100); if (insPos == -1) { nodes.addElement(node); super.addRow(buf); } else { nodes.insertElementAt(node, insPos); super.insertRow(insPos, buf); } } public void clear() { nodes.clear(); super.setRowCount(0); } } private class StoplistTableModelListener implements TableModelListener { @Override public void tableChanged(TableModelEvent e) { if (e.getType() == TableModelEvent.UPDATE) { rebuildNodes(); } } } private static class SegmentMetric { public double aLat, aLon; public double length; public double d1, d2, o1, o2; public double distance; SegmentMetric(double fromLat, double fromLon, double toLat, double toLon, double distance) { this.distance = distance; aLat = fromLat; aLon = fromLon; // Compute length and direction // length is in units of latitude degrees d1 = toLat - fromLat; d2 = (toLon - fromLon) * Math.cos(fromLat * Math.PI / 180.0); length = Math.sqrt(d1 * d1 + d2 * d2); // Normalise direction d1 = d1 / length; d2 = d2 / length; // Compute orthogonal direction (right hand size is positive) o1 = -d2; o2 = d1; // Prepare lon direction to reduce the number of necessary multiplications d2 = d2 * Math.cos(fromLat * Math.PI / 180.0); o2 = o2 * Math.cos(fromLat * Math.PI / 180.0); } } private static JDialog jDialog = null; private static JTabbedPane tabbedPane = null; private static DefaultListModel<RouteReference> relsListModel = null; private static TagTableModel requiredTagsData = null; private static CustomCellEditorTable requiredTagsTable = null; private static TagTableModel commonTagsData = null; private static CustomCellEditorTable commonTagsTable = null; private static TagTableModel otherTagsData = null; private static TreeSet<String> tagBlacklist = null; private static CustomCellEditorTable otherTagsTable = null; private static ItineraryTableModel itineraryData = null; private static JTable itineraryTable = null; private static StoplistTableModel stoplistData = null; private static JTable stoplistTable = null; private static JList<RouteReference> relsList = null; private static JCheckBox cbRight = null; private static JCheckBox cbLeft = null; private static JTextField tfSuggestStopsLimit = null; private static Relation currentRoute = null; private static Vector<SegmentMetric> segmentMetrics = null; private static Vector<RelationMember> markedWays = new Vector<>(); private static Vector<RelationMember> markedNodes = new Vector<>(); public RoutePatternAction() { super(tr("Route patterns ..."), null, tr("Edit Route patterns for public transport"), null, false); putValue("toolbar", "publictransport/routepattern"); Main.toolbar.register(this); } @Override public void actionPerformed(ActionEvent event) { Frame frame = JOptionPane.getFrameForComponent(Main.parent); DataSet mainDataSet = Main.getLayerManager().getEditDataSet(); if (jDialog == null) { jDialog = new JDialog(frame, tr("Route Patterns"), false); tabbedPane = new JTabbedPane(); JPanel tabOverview = new JPanel(); tabbedPane.addTab(tr("Overview"), tabOverview); JPanel tabTags = new JPanel(); tabbedPane.addTab(tr("Tags"), tabTags); JPanel tabItinerary = new JPanel(); tabbedPane.addTab(tr("Itinerary"), tabItinerary); JPanel tabStoplist = new JPanel(); tabbedPane.addTab(tr("Stops"), tabStoplist); JPanel tabMeta = new JPanel(); tabbedPane.addTab(tr("Meta"), tabMeta); tabbedPane.setEnabledAt(0, true); tabbedPane.setEnabledAt(1, false); tabbedPane.setEnabledAt(2, false); tabbedPane.setEnabledAt(3, false); tabbedPane.setEnabledAt(4, false); jDialog.add(tabbedPane); // Overview Tab Container contentPane = tabOverview; GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints layoutCons = new GridBagConstraints(); contentPane.setLayout(gridbag); JLabel headline = new JLabel(tr("Existing route patterns:")); layoutCons.gridx = 0; layoutCons.gridy = 0; layoutCons.gridwidth = 3; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(headline, layoutCons); contentPane.add(headline); relsListModel = new DefaultListModel<>(); relsList = new JList<>(relsListModel); JScrollPane rpListSP = new JScrollPane(relsList); String[] data = {"1", "2", "3", "4", "5", "6"}; relsListModel.copyInto(data); relsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); relsList.addListSelectionListener(new RoutesLSL(this)); layoutCons.gridx = 0; layoutCons.gridy = 1; layoutCons.gridwidth = 3; layoutCons.weightx = 1.0; layoutCons.weighty = 1.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(rpListSP, layoutCons); contentPane.add(rpListSP); JButton bRefresh = new JButton(tr("Refresh")); bRefresh.setActionCommand("routePattern.refresh"); bRefresh.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.gridheight = 2; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bRefresh, layoutCons); contentPane.add(bRefresh); JButton bNew = new JButton(tr("New")); bNew.setActionCommand("routePattern.overviewNew"); bNew.addActionListener(this); layoutCons.gridx = 1; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.gridheight = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bNew, layoutCons); contentPane.add(bNew); JButton bDelete = new JButton(tr("Delete")); bDelete.setActionCommand("routePattern.overviewDelete"); bDelete.addActionListener(this); layoutCons.gridx = 1; layoutCons.gridy = 3; layoutCons.gridwidth = 1; layoutCons.gridheight = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bDelete, layoutCons); contentPane.add(bDelete); JButton bDuplicate = new JButton(tr("Duplicate")); bDuplicate.setActionCommand("routePattern.overviewDuplicate"); bDuplicate.addActionListener(this); layoutCons.gridx = 2; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.gridheight = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bDuplicate, layoutCons); contentPane.add(bDuplicate); JButton bReflect = new JButton(tr("Reflect")); bReflect.setActionCommand("routePattern.overviewReflect"); bReflect.addActionListener(this); layoutCons.gridx = 2; layoutCons.gridy = 3; layoutCons.gridwidth = 1; layoutCons.gridheight = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bReflect, layoutCons); contentPane.add(bReflect); // Tags Tab /* Container */ contentPane = tabTags; /* GridBagLayout */ gridbag = new GridBagLayout(); /* GridBagConstraints */ layoutCons = new GridBagConstraints(); contentPane.setLayout(gridbag); /* JLabel */ headline = new JLabel(tr("Required tags:")); layoutCons.gridx = 0; layoutCons.gridy = 0; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(headline, layoutCons); contentPane.add(headline); requiredTagsTable = new CustomCellEditorTable(); requiredTagsData = new TagTableModel(true); requiredTagsData.addColumn(tr("Key")); requiredTagsData.addColumn(tr("Value")); tagBlacklist = new TreeSet<>(); Vector<String> rowContent = new Vector<>(); /* TODO: keys and values should also be translated using TransText class */ rowContent.add("type"); tagBlacklist.add("type"); rowContent.add("route"); requiredTagsData.addRow(rowContent); JComboBox<String> comboBox = new JComboBox<>(); comboBox.addItem("route"); requiredTagsTable.setCellEditor(0, 1, new DefaultCellEditor(comboBox)); rowContent = new Vector<>(); rowContent.add(0, "route"); tagBlacklist.add("route"); rowContent.add(1, "bus"); requiredTagsData.addRow(rowContent); /* JComboBox */ comboBox = new JComboBox<>(); comboBox.addItem("bus"); comboBox.addItem("trolleybus"); comboBox.addItem("tram"); comboBox.addItem("light_rail"); comboBox.addItem("subway"); comboBox.addItem("rail"); requiredTagsTable.setCellEditor(1, 1, new DefaultCellEditor(comboBox)); rowContent = new Vector<>(); rowContent.add(0, "ref"); tagBlacklist.add("ref"); rowContent.add(1, ""); requiredTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "to"); tagBlacklist.add("to"); rowContent.add(1, ""); requiredTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "network"); tagBlacklist.add("network"); rowContent.add(1, ""); requiredTagsData.addRow(rowContent); requiredTagsTable.setModel(requiredTagsData); JScrollPane tableSP = new JScrollPane(requiredTagsTable); requiredTagsData.addTableModelListener(requiredTagsData); layoutCons.gridx = 0; layoutCons.gridy = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.25; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(tableSP, layoutCons); Dimension preferredSize = tableSP.getPreferredSize(); preferredSize.setSize(tableSP.getPreferredSize().getWidth(), tableSP.getPreferredSize().getHeight() / 4.0); tableSP.setPreferredSize(preferredSize); contentPane.add(tableSP); headline = new JLabel(tr("Common tags:")); layoutCons.gridx = 0; layoutCons.gridy = 2; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(headline, layoutCons); contentPane.add(headline); commonTagsTable = new CustomCellEditorTable(); commonTagsData = new TagTableModel(true); commonTagsData.addColumn(tr("Key")); commonTagsData.addColumn(tr("Value")); rowContent = new Vector<>(); rowContent.add(0, "loc_ref"); tagBlacklist.add("loc_ref"); rowContent.add(1, ""); commonTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "direction"); tagBlacklist.add("direction"); rowContent.add(1, ""); commonTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "from"); tagBlacklist.add("from"); rowContent.add(1, ""); commonTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "operator"); tagBlacklist.add("operator"); rowContent.add(1, ""); commonTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "color"); tagBlacklist.add("color"); rowContent.add(1, ""); commonTagsData.addRow(rowContent); rowContent = new Vector<>(); rowContent.add(0, "name"); tagBlacklist.add("name"); rowContent.add(1, ""); commonTagsData.addRow(rowContent); commonTagsTable.setModel(commonTagsData); /* JScrollPane */ tableSP = new JScrollPane(commonTagsTable); commonTagsData.addTableModelListener(commonTagsData); layoutCons.gridx = 0; layoutCons.gridy = 3; layoutCons.weightx = 1.0; layoutCons.weighty = 0.25; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(tableSP, layoutCons); /* Dimension */ preferredSize = tableSP.getPreferredSize(); preferredSize.setSize(tableSP.getPreferredSize().getWidth(), tableSP.getPreferredSize().getHeight() / 4.0); tableSP.setPreferredSize(preferredSize); contentPane.add(tableSP); headline = new JLabel(tr("Additional tags:")); layoutCons.gridx = 0; layoutCons.gridy = 4; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(headline, layoutCons); contentPane.add(headline); otherTagsTable = new CustomCellEditorTable(); otherTagsData = new TagTableModel(false); otherTagsData.addColumn(tr("Key")); otherTagsData.addColumn(tr("Value")); otherTagsTable.setModel(otherTagsData); /* JScrollPane */ tableSP = new JScrollPane(otherTagsTable); otherTagsData.addTableModelListener(otherTagsData); layoutCons.gridx = 0; layoutCons.gridy = 5; layoutCons.weightx = 1.0; layoutCons.weighty = 1.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(tableSP, layoutCons); /* Dimension */ preferredSize = tableSP.getPreferredSize(); preferredSize.setSize(tableSP.getPreferredSize().getWidth(), tableSP.getPreferredSize().getHeight() / 2.0); tableSP.setPreferredSize(preferredSize); contentPane.add(tableSP); JButton bAddTag = new JButton(tr("Add a new Tag")); bAddTag.setActionCommand("routePattern.tagAddTag"); bAddTag.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 6; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bAddTag, layoutCons); contentPane.add(bAddTag); // Itinerary Tab contentPane = tabItinerary; gridbag = new GridBagLayout(); layoutCons = new GridBagConstraints(); contentPane.setLayout(gridbag); itineraryTable = new JTable(); itineraryData = new ItineraryTableModel(); itineraryData.addColumn(tr("Name/Id")); itineraryData.addColumn(tr("Role")); itineraryTable.setModel(itineraryData); /* JScrollPane */ tableSP = new JScrollPane(itineraryTable); /* JComboBox */ comboBox = new JComboBox<>(); comboBox.addItem(""); comboBox.addItem("forward"); comboBox.addItem("backward"); itineraryTable.getColumnModel().getColumn(1) .setCellEditor(new DefaultCellEditor(comboBox)); itineraryData.addTableModelListener(itineraryData); layoutCons.gridx = 0; layoutCons.gridy = 0; layoutCons.gridwidth = 4; layoutCons.weightx = 1.0; layoutCons.weighty = 1.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(tableSP, layoutCons); contentPane.add(tableSP); JButton bFind = new JButton(tr("Find")); bFind.setActionCommand("routePattern.itineraryFind"); bFind.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 1; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bFind, layoutCons); contentPane.add(bFind); JButton bShow = new JButton(tr("Show")); bShow.setActionCommand("routePattern.itineraryShow"); bShow.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bShow, layoutCons); contentPane.add(bShow); JButton bMark = new JButton(tr("Mark")); bMark.setActionCommand("routePattern.itineraryMark"); bMark.addActionListener(this); layoutCons.gridx = 1; layoutCons.gridy = 1; layoutCons.gridheight = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bMark, layoutCons); contentPane.add(bMark); JButton bAdd = new JButton(tr("Add")); bAdd.setActionCommand("routePattern.itineraryAdd"); bAdd.addActionListener(this); layoutCons.gridx = 2; layoutCons.gridy = 1; layoutCons.gridheight = 1; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bAdd, layoutCons); contentPane.add(bAdd); /* JButton */ bDelete = new JButton(tr("Delete")); bDelete.setActionCommand("routePattern.itineraryDelete"); bDelete.addActionListener(this); layoutCons.gridx = 2; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bDelete, layoutCons); contentPane.add(bDelete); JButton bSort = new JButton(tr("Sort")); bSort.setActionCommand("routePattern.itinerarySort"); bSort.addActionListener(this); layoutCons.gridx = 3; layoutCons.gridy = 1; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bSort, layoutCons); contentPane.add(bSort); /* JButton */ bReflect = new JButton(tr("Reflect")); bReflect.setActionCommand("routePattern.itineraryReflect"); bReflect.addActionListener(this); layoutCons.gridx = 3; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bReflect, layoutCons); contentPane.add(bReflect); // Stoplist Tab contentPane = tabStoplist; gridbag = new GridBagLayout(); layoutCons = new GridBagConstraints(); contentPane.setLayout(gridbag); stoplistTable = new JTable(); stoplistData = new StoplistTableModel(); stoplistData.addColumn(tr("Name/Id")); stoplistData.addColumn(tr("Ref")); stoplistData.addColumn(tr("Role")); stoplistData.addColumn(tr("km")); stoplistTable.setModel(stoplistData); /* JScrollPane */ tableSP = new JScrollPane(stoplistTable); /* JComboBox */ comboBox = new JComboBox<>(); comboBox.addItem(""); comboBox.addItem("forward_stop"); comboBox.addItem("backward_stop"); stoplistTable.getColumnModel().getColumn(STOPLIST_ROLE_COLUMN) .setCellEditor(new DefaultCellEditor(comboBox)); stoplistData.addTableModelListener(new StoplistTableModelListener()); layoutCons.gridx = 0; layoutCons.gridy = 0; layoutCons.gridwidth = 4; layoutCons.weightx = 1.0; layoutCons.weighty = 1.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(tableSP, layoutCons); contentPane.add(tableSP); /* JButton */ bFind = new JButton(tr("Find")); bFind.setActionCommand("routePattern.stoplistFind"); bFind.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 1; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bFind, layoutCons); contentPane.add(bFind); /* JButton */ bShow = new JButton(tr("Show")); bShow.setActionCommand("routePattern.stoplistShow"); bShow.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bShow, layoutCons); contentPane.add(bShow); /* JButton */ bMark = new JButton(tr("Mark")); bMark.setActionCommand("routePattern.stoplistMark"); bMark.addActionListener(this); layoutCons.gridx = 1; layoutCons.gridy = 1; layoutCons.gridheight = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bMark, layoutCons); contentPane.add(bMark); /* JButton */ bAdd = new JButton(tr("Add")); bAdd.setActionCommand("routePattern.stoplistAdd"); bAdd.addActionListener(this); layoutCons.gridx = 2; layoutCons.gridy = 1; layoutCons.gridheight = 1; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bAdd, layoutCons); contentPane.add(bAdd); /* JButton */ bDelete = new JButton(tr("Delete")); bDelete.setActionCommand("routePattern.stoplistDelete"); bDelete.addActionListener(this); layoutCons.gridx = 2; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bDelete, layoutCons); contentPane.add(bDelete); /* JButton */ bSort = new JButton(tr("Sort")); bSort.setActionCommand("routePattern.stoplistSort"); bSort.addActionListener(this); layoutCons.gridx = 3; layoutCons.gridy = 1; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bSort, layoutCons); contentPane.add(bSort); /* JButton */ bReflect = new JButton(tr("Reflect")); bReflect.setActionCommand("routePattern.stoplistReflect"); bReflect.addActionListener(this); layoutCons.gridx = 3; layoutCons.gridy = 2; layoutCons.gridwidth = 1; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bReflect, layoutCons); contentPane.add(bReflect); // Meta Tab contentPane = tabMeta; gridbag = new GridBagLayout(); layoutCons = new GridBagConstraints(); contentPane.setLayout(gridbag); JLabel rightleft = new JLabel(tr("Stops are possible on the")); layoutCons.gridx = 0; layoutCons.gridy = 1; layoutCons.gridwidth = 2; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(rightleft, layoutCons); contentPane.add(rightleft); cbRight = new JCheckBox(tr("right hand side"), true); layoutCons.gridx = 0; layoutCons.gridy = 2; layoutCons.gridwidth = 2; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(cbRight, layoutCons); contentPane.add(cbRight); cbLeft = new JCheckBox(tr("left hand side"), false); layoutCons.gridx = 0; layoutCons.gridy = 3; layoutCons.gridwidth = 2; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(cbLeft, layoutCons); contentPane.add(cbLeft); JLabel maxdist = new JLabel(tr("Maximum distance from route")); layoutCons.gridx = 0; layoutCons.gridy = 4; layoutCons.gridwidth = 2; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(maxdist, layoutCons); contentPane.add(maxdist); tfSuggestStopsLimit = new JTextField("20", 4); layoutCons.gridx = 0; layoutCons.gridy = 5; layoutCons.gridwidth = 1; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(tfSuggestStopsLimit, layoutCons); contentPane.add(tfSuggestStopsLimit); JLabel meters = new JLabel(tr("meters")); layoutCons.gridx = 1; layoutCons.gridy = 5; layoutCons.gridwidth = 1; layoutCons.weightx = 0.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(meters, layoutCons); contentPane.add(meters); JButton bSuggestStops = new JButton(tr("Suggest Stops")); bSuggestStops.setActionCommand("routePattern.metaSuggestStops"); bSuggestStops.addActionListener(this); layoutCons.gridx = 0; layoutCons.gridy = 6; layoutCons.gridwidth = 3; layoutCons.weightx = 1.0; layoutCons.weighty = 0.0; layoutCons.fill = GridBagConstraints.BOTH; gridbag.setConstraints(bSuggestStops, layoutCons); contentPane.add(bSuggestStops); jDialog.pack(); } if ("routePattern.refresh".equals(event.getActionCommand())) { refreshData(); } else if ("routePattern.overviewNew".equals(event.getActionCommand())) { currentRoute = new Relation(); currentRoute.put("type", "route"); currentRoute.put("route", "bus"); mainDataSet.addPrimitive(currentRoute); refreshData(); for (int i = 0; i < relsListModel.size(); ++i) { if (currentRoute == relsListModel.elementAt(i).route) relsList.setSelectedIndex(i); } } else if ("routePattern.overviewDuplicate".equals(event.getActionCommand())) { currentRoute = new Relation(currentRoute, true); currentRoute.put("type", "route"); currentRoute.put("route", "bus"); mainDataSet.addPrimitive(currentRoute); refreshData(); for (int i = 0; i < relsListModel.size(); ++i) { if (currentRoute == relsListModel.elementAt(i).route) relsList.setSelectedIndex(i); } } else if ("routePattern.overviewReflect".equals(event.getActionCommand())) { currentRoute.setModified(true); String tag_from = currentRoute.get("from"); String tag_to = currentRoute.get("to"); currentRoute.put("from", tag_to); currentRoute.put("to", tag_from); Vector<RelationMember> itemsToReflect = new Vector<>(); Vector<RelationMember> otherItems = new Vector<>(); // Temp Node firstNode = null; // Node lastNode = null; for (int i = 0; i < currentRoute.getMembersCount(); ++i) { RelationMember item = currentRoute.getMember(i); if (item.isWay()) { String role = item.getRole(); if ("backward".equals(role)) role = "forward"; else if ("forward".equals(role)) role = "backward"; else role = "backward"; itemsToReflect.add(new RelationMember(role, item.getWay())); // Temp if (firstNode == null) { firstNode = item.getWay().getNode(0); } // lastNode = item.getWay().getNode(item.getWay().getNodesCount() - 1); } else if (item.isNode()) itemsToReflect.add(item); else otherItems.add(item); } currentRoute.setMembers(null); for (int i = itemsToReflect.size() - 1; i >= 0; --i) { currentRoute.addMember(itemsToReflect.elementAt(i)); } for (int i = 0; i < otherItems.size(); ++i) { currentRoute.addMember(otherItems.elementAt(i)); } refreshData(); for (int i = 0; i < relsListModel.size(); ++i) { if (currentRoute == relsListModel.elementAt(i).route) relsList.setSelectedIndex(i); } // Temp /* * if (firstNode != null) { * Vector< AStarAlgorithm.Edge > path = new PublicTransportAStar(firstNode, lastNode).shortestPath(); Iterator< * AStarAlgorithm.Edge > iter = path.iterator(); while (iter.hasNext()) { PublicTransportAStar.PartialWayEdge edge = * (PublicTransportAStar.PartialWayEdge)iter.next(); System.out.print(edge.way.getUniqueId()); System.out.print("\t"); * System.out.print(edge.beginIndex); System.out.print("\t"); System.out.print(edge.endIndex); System.out.print("\n"); } } */ } else if ("routePattern.overviewDelete".equals(event.getActionCommand())) { DeleteAction.deleteRelation(Main.getLayerManager().getEditLayer(), currentRoute); currentRoute = null; tabbedPane.setEnabledAt(1, false); tabbedPane.setEnabledAt(2, false); tabbedPane.setEnabledAt(3, false); tabbedPane.setEnabledAt(4, false); refreshData(); } else if ("routePattern.tagAddTag".equals(event.getActionCommand())) { Vector<String> rowContent = new Vector<>(); rowContent.add(""); rowContent.add(""); otherTagsData.addRow(rowContent); } else if ("routePattern.itineraryFind".equals(event.getActionCommand())) { if (mainDataSet == null) return; itineraryTable.clearSelection(); for (int i = 0; i < itineraryData.getRowCount(); ++i) { if ((itineraryData.ways.elementAt(i) != null) && (mainDataSet.isSelected(itineraryData.ways.elementAt(i)))) itineraryTable.addRowSelectionInterval(i, i); } } else if ("routePattern.itineraryShow".equals(event.getActionCommand())) { BoundingXYVisitor box = new BoundingXYVisitor(); if (itineraryTable.getSelectedRowCount() > 0) { for (int i = 0; i < itineraryData.getRowCount(); ++i) { if ((itineraryTable.isRowSelected(i)) && (itineraryData.ways.elementAt(i) != null)) { itineraryData.ways.elementAt(i).accept(box); } } } else { for (int i = 0; i < itineraryData.getRowCount(); ++i) { if (itineraryData.ways.elementAt(i) != null) { itineraryData.ways.elementAt(i).accept(box); } } } if (box.getBounds() == null) return; box.enlargeBoundingBox(); Main.map.mapView.zoomTo(box); } else if ("routePattern.itineraryMark".equals(event.getActionCommand())) { OsmPrimitive[] osmp = {null}; Main.getLayerManager().getEditDataSet().setSelected(osmp); markedWays.clear(); if (itineraryTable.getSelectedRowCount() > 0) { for (int i = 0; i < itineraryData.getRowCount(); ++i) { if ((itineraryTable.isRowSelected(i)) && (itineraryData.ways.elementAt(i) != null)) { mainDataSet.addSelected(itineraryData.ways.elementAt(i)); RelationMember markedWay = new RelationMember( (String) (itineraryData.getValueAt(i, 1)), itineraryData.ways.elementAt(i)); markedWays.addElement(markedWay); } } } else { for (int i = 0; i < itineraryData.getRowCount(); ++i) { if (itineraryData.ways.elementAt(i) != null) { mainDataSet.addSelected(itineraryData.ways.elementAt(i)); RelationMember markedWay = new RelationMember( (String) (itineraryData.getValueAt(i, 1)), itineraryData.ways.elementAt(i)); markedWays.addElement(markedWay); } } } } else if ("routePattern.itineraryAdd".equals(event.getActionCommand())) { int insPos = itineraryTable.getSelectedRow(); Iterator<RelationMember> relIter = markedWays.iterator(); TreeSet<Way> addedWays = new TreeSet<>(); if (mainDataSet == null) return; while (relIter.hasNext()) { RelationMember curMember = relIter.next(); if ((curMember.isWay()) && (mainDataSet.isSelected(curMember.getWay()))) { itineraryData.insertRow(insPos, curMember.getWay(), curMember.getRole()); if (insPos >= 0) ++insPos; addedWays.add(curMember.getWay()); } } Collection<Way> selectedWays = mainDataSet.getSelectedWays(); Iterator<Way> wayIter = selectedWays.iterator(); while (wayIter.hasNext()) { Way curMember = wayIter.next(); if (!(addedWays.contains(curMember))) { itineraryData.insertRow(insPos, curMember, ""); if (insPos >= 0) ++insPos; } } if ((insPos > 0) && (insPos < itineraryData.getRowCount())) { while ((insPos < itineraryData.getRowCount()) && (itineraryData.ways.elementAt(insPos) == null)) { ++insPos; } itineraryTable.removeRowSelectionInterval(0, itineraryData.getRowCount() - 1); if (insPos < itineraryData.getRowCount()) itineraryTable.addRowSelectionInterval(insPos, insPos); } itineraryData.cleanupGaps(); segmentMetrics = fillSegmentMetrics(); rebuildWays(); } else if ("routePattern.itineraryDelete".equals(event.getActionCommand())) { for (int i = itineraryData.getRowCount() - 1; i >= 0; --i) { if ((itineraryTable.isRowSelected(i)) && (itineraryData.ways.elementAt(i) != null)) { itineraryData.ways.removeElementAt(i); itineraryData.removeRow(i); } } itineraryData.cleanupGaps(); segmentMetrics = fillSegmentMetrics(); rebuildWays(); } else if ("routePattern.itinerarySort".equals(event.getActionCommand())) { TreeSet<Way> usedWays = new TreeSet<>(); TreeMap<Node, LinkedList<RelationMember>> frontNodes = new TreeMap<>(); TreeMap<Node, LinkedList<RelationMember>> backNodes = new TreeMap<>(); Vector<LinkedList<RelationMember>> loops = new Vector<>(); int insPos = itineraryTable.getSelectedRow(); if (itineraryTable.getSelectedRowCount() > 0) { for (int i = itineraryData.getRowCount() - 1; i >= 0; --i) { if ((itineraryTable.isRowSelected(i)) && (itineraryData.ways.elementAt(i) != null)) { if (!(usedWays.contains(itineraryData.ways.elementAt(i)))) { addWayToSortingData(itineraryData.ways.elementAt(i), frontNodes, backNodes, loops); usedWays.add(itineraryData.ways.elementAt(i)); } itineraryData.ways.removeElementAt(i); itineraryData.removeRow(i); } } } else { for (int i = itineraryData.getRowCount() - 1; i >= 0; --i) { if (itineraryData.ways.elementAt(i) != null) { if (!(usedWays.contains(itineraryData.ways.elementAt(i)))) { addWayToSortingData(itineraryData.ways.elementAt(i), frontNodes, backNodes, loops); usedWays.add(itineraryData.ways.elementAt(i)); } } } itineraryData.clear(); } Iterator<Map.Entry<Node, LinkedList<RelationMember>>> entryIter = frontNodes.entrySet() .iterator(); while (entryIter.hasNext()) { Iterator<RelationMember> relIter = entryIter.next().getValue().iterator(); while (relIter.hasNext()) { RelationMember curMember = relIter.next(); itineraryData.insertRow(insPos, curMember.getWay(), curMember.getRole()); if (insPos >= 0) ++insPos; } } Iterator<LinkedList<RelationMember>> listIter = loops.iterator(); while (listIter.hasNext()) { Iterator<RelationMember> relIter = listIter.next().iterator(); while (relIter.hasNext()) { RelationMember curMember = relIter.next(); itineraryData.insertRow(insPos, curMember.getWay(), curMember.getRole()); if (insPos >= 0) ++insPos; } } itineraryData.cleanupGaps(); segmentMetrics = fillSegmentMetrics(); rebuildWays(); } else if ("routePattern.itineraryReflect".equals(event.getActionCommand())) { Vector<RelationMember> itemsToReflect = new Vector<>(); int insPos = itineraryTable.getSelectedRow(); if (itineraryTable.getSelectedRowCount() > 0) { for (int i = itineraryData.getRowCount() - 1; i >= 0; --i) { if ((itineraryTable.isRowSelected(i)) && (itineraryData.ways.elementAt(i) != null)) { String role = (String) (itineraryData.getValueAt(i, 1)); if ("backward".equals(role)) role = "forward"; else if ("forward".equals(role)) role = "backward"; else role = "backward"; RelationMember markedWay = new RelationMember(role, itineraryData.ways.elementAt(i)); itemsToReflect.addElement(markedWay); itineraryData.ways.removeElementAt(i); itineraryData.removeRow(i); } } } else { for (int i = itineraryData.getRowCount() - 1; i >= 0; --i) { if (itineraryData.ways.elementAt(i) != null) { String role = (String) (itineraryData.getValueAt(i, 1)); if ("backward".equals(role)) role = "forward"; else if ("forward".equals(role)) role = "backward"; else role = "backward"; RelationMember markedWay = new RelationMember(role, itineraryData.ways.elementAt(i)); itemsToReflect.addElement(markedWay); } } itineraryData.clear(); } int startPos = insPos; Iterator<RelationMember> relIter = itemsToReflect.iterator(); while (relIter.hasNext()) { RelationMember curMember = relIter.next(); if (curMember.isWay()) { itineraryData.insertRow(insPos, curMember.getWay(), curMember.getRole()); if (insPos >= 0) ++insPos; } } if (insPos >= 0) itineraryTable.addRowSelectionInterval(startPos, insPos - 1); itineraryData.cleanupGaps(); segmentMetrics = fillSegmentMetrics(); rebuildWays(); } else if ("routePattern.stoplistFind".equals(event.getActionCommand())) { if (mainDataSet == null) return; stoplistTable.clearSelection(); for (int i = 0; i < stoplistData.getRowCount(); ++i) { if ((stoplistData.nodes.elementAt(i) != null) && (mainDataSet.isSelected(stoplistData.nodes.elementAt(i)))) stoplistTable.addRowSelectionInterval(i, i); } } else if ("routePattern.stoplistShow".equals(event.getActionCommand())) { BoundingXYVisitor box = new BoundingXYVisitor(); if (stoplistTable.getSelectedRowCount() > 0) { for (int i = 0; i < stoplistData.getRowCount(); ++i) { if (stoplistTable.isRowSelected(i)) { stoplistData.nodes.elementAt(i).accept(box); } } } else { for (int i = 0; i < stoplistData.getRowCount(); ++i) { stoplistData.nodes.elementAt(i).accept(box); } } if (box.getBounds() == null) return; box.enlargeBoundingBox(); Main.map.mapView.zoomTo(box); } else if ("routePattern.stoplistMark".equals(event.getActionCommand())) { OsmPrimitive[] osmp = {null}; Main.getLayerManager().getEditDataSet().setSelected(osmp); markedNodes.clear(); if (stoplistTable.getSelectedRowCount() > 0) { for (int i = 0; i < stoplistData.getRowCount(); ++i) { if (stoplistTable.isRowSelected(i)) { mainDataSet.addSelected(stoplistData.nodes.elementAt(i)); RelationMember markedNode = new RelationMember( (String) (stoplistData.getValueAt(i, 1)), stoplistData.nodes.elementAt(i)); markedNodes.addElement(markedNode); } } } else { for (int i = 0; i < stoplistData.getRowCount(); ++i) { mainDataSet.addSelected(stoplistData.nodes.elementAt(i)); RelationMember markedNode = new RelationMember( (String) (stoplistData.getValueAt(i, 1)), stoplistData.nodes.elementAt(i)); markedNodes.addElement(markedNode); } } } else if ("routePattern.stoplistAdd".equals(event.getActionCommand())) { int insPos = stoplistTable.getSelectedRow(); Iterator<RelationMember> relIter = markedNodes.iterator(); TreeSet<Node> addedNodes = new TreeSet<>(); if (mainDataSet == null) return; while (relIter.hasNext()) { RelationMember curMember = relIter.next(); if ((curMember.isNode()) && (mainDataSet.isSelected(curMember.getNode()))) { StopReference sr = detectMinDistance(curMember.getNode(), segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); stoplistData.insertRow(insPos, curMember.getNode(), curMember.getRole(), calcOffset(sr, segmentMetrics)); if (insPos >= 0) ++insPos; addedNodes.add(curMember.getNode()); } } Collection<Node> selectedNodes = mainDataSet.getSelectedNodes(); Iterator<Node> nodeIter = selectedNodes.iterator(); while (nodeIter.hasNext()) { Node curMember = nodeIter.next(); if (!(addedNodes.contains(curMember))) { StopReference sr = detectMinDistance(curMember, segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); stoplistData.insertRow(insPos, curMember, "", calcOffset(sr, segmentMetrics)); if (insPos >= 0) ++insPos; } } if ((insPos > 0) && (insPos < stoplistData.getRowCount())) { while ((insPos < stoplistData.getRowCount()) && (stoplistData.nodes.elementAt(insPos) == null)) { ++insPos; } stoplistTable.removeRowSelectionInterval(0, stoplistData.getRowCount() - 1); if (insPos < stoplistData.getRowCount()) stoplistTable.addRowSelectionInterval(insPos, insPos); } rebuildNodes(); } else if ("routePattern.stoplistDelete".equals(event.getActionCommand())) { for (int i = stoplistData.getRowCount() - 1; i >= 0; --i) { if (stoplistTable.isRowSelected(i)) { stoplistData.nodes.removeElementAt(i); stoplistData.removeRow(i); } } rebuildNodes(); } else if ("routePattern.stoplistSort".equals(event.getActionCommand())) { // Prepare Segments: The segments of all usable ways are arranged in a linear // list such that a coor can directly be checked concerning position and offset Vector<StopReference> srm = new Vector<>(); int insPos = stoplistTable.getSelectedRow(); if (stoplistTable.getSelectedRowCount() > 0) { // Determine for each member its position on the itinerary: position means here the // point on the itinerary that has minimal distance to the coor for (int i = stoplistData.getRowCount() - 1; i >= 0; --i) { if (stoplistTable.isRowSelected(i)) { StopReference sr = detectMinDistance(stoplistData.nodes.elementAt(i), segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); if (sr != null) { if (sr.distance < Double.parseDouble(tfSuggestStopsLimit.getText()) * 9.0 / 1000000.0) { sr.role = (String) stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN); srm.addElement(sr); } else { sr.role = (String) stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN); sr.index = segmentMetrics.size() * 2; sr.pos = 0; srm.addElement(sr); } stoplistData.nodes.removeElementAt(i); stoplistData.removeRow(i); } } } } else { // Determine for each member its position on the itinerary: position means here the // point on the itinerary that has minimal distance to the coor for (int i = stoplistData.getRowCount() - 1; i >= 0; --i) { StopReference sr = detectMinDistance(stoplistData.nodes.elementAt(i), segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); if (sr != null) { if (sr.distance < Double.parseDouble(tfSuggestStopsLimit.getText()) * 9.0 / 1000000.0) { sr.role = (String) stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN); srm.addElement(sr); } else { sr.role = (String) stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN); sr.index = segmentMetrics.size() * 2; sr.pos = 0; srm.addElement(sr); } } } stoplistData.clear(); } Collections.sort(srm); for (int i = 0; i < srm.size(); ++i) { StopReference sr = detectMinDistance(srm.elementAt(i).node, segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); stoplistData.insertRow(insPos, srm.elementAt(i).node, srm.elementAt(i).role, calcOffset(sr, segmentMetrics)); if (insPos >= 0) ++insPos; } rebuildNodes(); } else if ("routePattern.stoplistReflect".equals(event.getActionCommand())) { Vector<RelationMember> itemsToReflect = new Vector<>(); int insPos = stoplistTable.getSelectedRow(); if (stoplistTable.getSelectedRowCount() > 0) { for (int i = stoplistData.getRowCount() - 1; i >= 0; --i) { if (stoplistTable.isRowSelected(i)) { String role = (String) (stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN)); RelationMember markedNode = new RelationMember(role, stoplistData.nodes.elementAt(i)); itemsToReflect.addElement(markedNode); stoplistData.nodes.removeElementAt(i); stoplistData.removeRow(i); } } } else { for (int i = stoplistData.getRowCount() - 1; i >= 0; --i) { String role = (String) (stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN)); RelationMember markedNode = new RelationMember(role, stoplistData.nodes.elementAt(i)); itemsToReflect.addElement(markedNode); } stoplistData.clear(); } int startPos = insPos; Iterator<RelationMember> relIter = itemsToReflect.iterator(); while (relIter.hasNext()) { RelationMember curMember = relIter.next(); if (curMember.isNode()) { StopReference sr = detectMinDistance(curMember.getNode(), segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); stoplistData.insertRow(insPos, curMember.getNode(), curMember.getRole(), calcOffset(sr, segmentMetrics)); if (insPos >= 0) ++insPos; } } if (insPos >= 0) stoplistTable.addRowSelectionInterval(startPos, insPos - 1); rebuildNodes(); } else if ("routePattern.metaSuggestStops".equals(event.getActionCommand())) { // Prepare Segments: The segments of all usable ways are arranged in a linear // list such that a coor can directly be checked concerning position and offset Vector<StopReference> srm = new Vector<>(); // Determine for each member its position on the itinerary: position means here the // point on the itinerary that has minimal distance to the coor mainDataSet = Main.getLayerManager().getEditDataSet(); if (mainDataSet != null) { String stopKey = ""; String stopValue = ""; if ("bus".equals(currentRoute.get("route"))) { stopKey = "highway"; stopValue = "bus_stop"; } else if ("trolleybus".equals(currentRoute.get("route"))) { stopKey = "highway"; stopValue = "bus_stop"; } else if ("tram".equals(currentRoute.get("route"))) { stopKey = "railway"; stopValue = "tram_stop"; } else if ("light_rail".equals(currentRoute.get("route"))) { stopKey = "railway"; stopValue = "station"; } else if ("subway".equals(currentRoute.get("route"))) { stopKey = "railway"; stopValue = "station"; } else if ("rail".equals(currentRoute.get("route"))) { stopKey = "railway"; stopValue = "station"; } Collection<Node> nodeCollection = mainDataSet.getNodes(); Iterator<Node> nodeIter = nodeCollection.iterator(); while (nodeIter.hasNext()) { Node currentNode = nodeIter.next(); if (!currentNode.isUsable()) continue; if (stopValue.equals(currentNode.get(stopKey))) { StopReference sr = detectMinDistance(currentNode, segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); if ((sr != null) && (sr.distance < Double.parseDouble(tfSuggestStopsLimit.getText()) * 9.0 / 1000000.0)) srm.addElement(sr); } } } else { JOptionPane.showMessageDialog(null, tr("There exists no dataset." + " Try to download data from the server or open an OSM file."), tr("No data found"), JOptionPane.ERROR_MESSAGE); } Collections.sort(srm); stoplistData.clear(); for (int i = 0; i < srm.size(); ++i) { StopReference sr = detectMinDistance(srm.elementAt(i).node, segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); stoplistData.addRow(srm.elementAt(i).node, srm.elementAt(i).role, calcOffset(sr, segmentMetrics)); } rebuildNodes(); } else { refreshData(); jDialog.setLocationRelativeTo(frame); jDialog.setVisible(true); } } private void refreshData() { Relation copy = currentRoute; relsListModel.clear(); currentRoute = copy; DataSet mainDataSet = Main.getLayerManager().getEditDataSet(); if (mainDataSet != null) { Vector<RouteReference> relRefs = new Vector<>(); Collection<Relation> relCollection = mainDataSet.getRelations(); Iterator<Relation> relIter = relCollection.iterator(); while (relIter.hasNext()) { Relation currentRel = relIter.next(); if (!currentRel.isDeleted()) { String routeVal = currentRel.get("route"); if ("bus".equals(routeVal)) relRefs.add(new RouteReference(currentRel)); else if ("trolleybus".equals(routeVal)) relRefs.add(new RouteReference(currentRel)); else if ("tram".equals(routeVal)) relRefs.add(new RouteReference(currentRel)); else if ("light_rail".equals(routeVal)) relRefs.add(new RouteReference(currentRel)); else if ("subway".equals(routeVal)) relRefs.add(new RouteReference(currentRel)); else if ("rail".equals(routeVal)) relRefs.add(new RouteReference(currentRel)); } } Collections.sort(relRefs); Iterator<RouteReference> iter = relRefs.iterator(); while (iter.hasNext()) { relsListModel.addElement(iter.next()); } } else { JOptionPane.showMessageDialog(null, tr("There exists no dataset." + " Try to download data from the server or open an OSM file."), tr("No data found"), JOptionPane.ERROR_MESSAGE); } } // Rebuild ways in the relation currentRoute public static void rebuildWays() { currentRoute.setModified(true); List<RelationMember> members = currentRoute.getMembers(); ListIterator<RelationMember> iter = members.listIterator(); while (iter.hasNext()) { if (iter.next().isWay()) iter.remove(); } for (int i = 0; i < itineraryData.getRowCount(); ++i) { if (itineraryData.ways.elementAt(i) != null) { RelationMember member = new RelationMember( (String) (itineraryData.getValueAt(i, 1)), itineraryData.ways.elementAt(i)); members.add(member); } } currentRoute.setMembers(members); } // Rebuild nodes in the relation currentRoute private void rebuildNodes() { currentRoute.setModified(true); for (int i = currentRoute.getMembersCount() - 1; i >= 0; --i) { if (currentRoute.getMember(i).isNode()) { currentRoute.removeMember(i); } } for (int i = 0; i < stoplistData.getRowCount(); ++i) { RelationMember member = new RelationMember( (String) (stoplistData.getValueAt(i, STOPLIST_ROLE_COLUMN)), stoplistData.nodes.elementAt(i)); currentRoute.addMember(member); } } private void addWayToSortingData(Way way, TreeMap<Node, LinkedList<RelationMember>> frontNodes, TreeMap<Node, LinkedList<RelationMember>> backNodes, Vector<LinkedList<RelationMember>> loops) { if (way.getNodesCount() < 1) return; Node firstNode = way.getNode(0); Node lastNode = way.getNode(way.getNodesCount() - 1); if (frontNodes.get(firstNode) != null) { LinkedList<RelationMember> list = frontNodes.get(firstNode); list.addFirst(new RelationMember("backward", way)); frontNodes.remove(firstNode); Node lastListNode; if ("backward".equals(list.getLast().getRole())) lastListNode = list.getLast().getWay().getNode(0); else lastListNode = list.getLast().getWay() .getNode(list.getLast().getWay().getNodesCount() - 1); if (lastNode.equals(lastListNode)) { backNodes.remove(lastListNode); loops.add(list); } else if (frontNodes.get(lastNode) != null) { backNodes.remove(lastListNode); LinkedList<RelationMember> listToAppend = frontNodes.get(lastNode); Iterator<RelationMember> memberIter = list.iterator(); while (memberIter.hasNext()) { RelationMember member = memberIter.next(); if ("backward".equals(member.getRole())) listToAppend.addFirst(new RelationMember("forward", member.getWay())); else listToAppend.addFirst(new RelationMember("backward", member.getWay())); } frontNodes.remove(lastNode); frontNodes.put(lastListNode, listToAppend); } else if (backNodes.get(lastNode) != null) { backNodes.remove(lastListNode); LinkedList<RelationMember> listToAppend = backNodes.get(lastNode); Iterator<RelationMember> memberIter = list.iterator(); while (memberIter.hasNext()) { RelationMember member = memberIter.next(); listToAppend.addLast(member); } backNodes.remove(lastNode); backNodes.put(lastListNode, listToAppend); } else frontNodes.put(lastNode, list); } else if (backNodes.get(firstNode) != null) { LinkedList<RelationMember> list = backNodes.get(firstNode); list.addLast(new RelationMember("forward", way)); backNodes.remove(firstNode); Node firstListNode; if ("backward".equals(list.getFirst().getRole())) firstListNode = list.getFirst().getWay() .getNode(list.getFirst().getWay().getNodesCount() - 1); else firstListNode = list.getFirst().getWay().getNode(0); if (lastNode.equals(firstListNode)) { frontNodes.remove(firstListNode); loops.add(list); } else if (frontNodes.get(lastNode) != null) { frontNodes.remove(firstListNode); LinkedList<RelationMember> listToAppend = frontNodes.get(lastNode); ListIterator<RelationMember> memberIter = list.listIterator(list.size()); while (memberIter.hasPrevious()) { RelationMember member = memberIter.previous(); listToAppend.addFirst(member); } frontNodes.remove(lastNode); frontNodes.put(firstListNode, listToAppend); } else if (backNodes.get(lastNode) != null) { frontNodes.remove(firstListNode); LinkedList<RelationMember> listToAppend = backNodes.get(lastNode); ListIterator<RelationMember> memberIter = list.listIterator(list.size()); while (memberIter.hasPrevious()) { RelationMember member = memberIter.previous(); if ("backward".equals(member.getRole())) listToAppend.addLast(new RelationMember("forward", member.getWay())); else listToAppend.addLast(new RelationMember("backward", member.getWay())); } backNodes.remove(lastNode); backNodes.put(firstListNode, listToAppend); } else backNodes.put(lastNode, list); } else if (frontNodes.get(lastNode) != null) { LinkedList<RelationMember> list = frontNodes.get(lastNode); list.addFirst(new RelationMember("forward", way)); frontNodes.remove(lastNode); frontNodes.put(firstNode, list); } else if (backNodes.get(lastNode) != null) { LinkedList<RelationMember> list = backNodes.get(lastNode); list.addLast(new RelationMember("backward", way)); backNodes.remove(lastNode); backNodes.put(firstNode, list); } else { LinkedList<RelationMember> newList = new LinkedList<>(); newList.add(new RelationMember("forward", way)); frontNodes.put(firstNode, newList); backNodes.put(lastNode, newList); } } private void routesSelectionChanged() { int selectedPos = relsList.getAnchorSelectionIndex(); if (relsList.isSelectedIndex(selectedPos)) { currentRoute = relsListModel.elementAt(selectedPos).route; tabbedPane.setEnabledAt(1, true); tabbedPane.setEnabledAt(2, true); tabbedPane.setEnabledAt(3, true); tabbedPane.setEnabledAt(4, true); // Prepare Tags requiredTagsData.readRelation(currentRoute); commonTagsData.readRelation(currentRoute); otherTagsData.readRelation(currentRoute, tagBlacklist); // Prepare Itinerary itineraryData.clear(); List<RelationMember> relMembers = currentRoute.getMembers(); Iterator<RelationMember> relIter = relMembers.iterator(); fillItineraryTable(relIter, 0, -1); // Prepare Stoplist stoplistData.clear(); /* List<RelationMember> */ relMembers = currentRoute.getMembers(); /* Iterator<RelationMember> */ relIter = relMembers.iterator(); fillStoplistTable(relIter, -1); } else { currentRoute = null; tabbedPane.setEnabledAt(1, false); tabbedPane.setEnabledAt(2, false); tabbedPane.setEnabledAt(3, false); tabbedPane.setEnabledAt(4, false); } } private void fillItineraryTable(Iterator<RelationMember> relIter, long lastNodeId, int insPos) { while (relIter.hasNext()) { RelationMember curMember = relIter.next(); if (curMember.isWay()) { itineraryData.insertRow(insPos, curMember.getWay(), curMember.getRole()); if (insPos >= 0) ++insPos; } } itineraryData.cleanupGaps(); segmentMetrics = fillSegmentMetrics(); } private double calcOffset(StopReference sr, Vector<SegmentMetric> segmentMetrics) { double offset = 0; if ((sr.index + 1) / 2 < segmentMetrics.size()) { offset = segmentMetrics.elementAt((sr.index + 1) / 2).distance; if (sr.index % 2 == 0) offset += sr.pos; } else offset = segmentMetrics.elementAt(segmentMetrics.size() - 1).distance + segmentMetrics.elementAt(segmentMetrics.size() - 1).length; return offset; } private void fillStoplistTable(Iterator<RelationMember> relIter, int insPos) { while (relIter.hasNext()) { RelationMember curMember = relIter.next(); if (curMember.isNode()) { StopReference sr = detectMinDistance(curMember.getNode(), segmentMetrics, cbRight.isSelected(), cbLeft.isSelected()); if (sr == null) stoplistData.insertRow(insPos, curMember.getNode(), curMember.getRole(), 360.0); else { stoplistData.insertRow(insPos, curMember.getNode(), curMember.getRole(), calcOffset(sr, segmentMetrics)); if (insPos >= 0) ++insPos; } } } } private Vector<SegmentMetric> fillSegmentMetrics() { Vector<SegmentMetric> segmentMetrics = new Vector<>(); double distance = 0; for (int i = 0; i < itineraryData.getRowCount(); ++i) { if (itineraryData.ways.elementAt(i) != null) { Way way = itineraryData.ways.elementAt(i); if (!(way.isIncomplete())) { if ("backward".equals((itineraryData.getValueAt(i, 1)))) { for (int j = way.getNodesCount() - 2; j >= 0; --j) { SegmentMetric sm = new SegmentMetric(way.getNode(j + 1).getCoor().lat(), way.getNode(j + 1).getCoor().lon(), way.getNode(j).getCoor().lat(), way.getNode(j).getCoor().lon(), distance); segmentMetrics.add(sm); distance += sm.length; } } else { for (int j = 0; j < way.getNodesCount() - 1; ++j) { SegmentMetric sm = new SegmentMetric(way.getNode(j).getCoor().lat(), way.getNode(j).getCoor().lon(), way.getNode(j + 1).getCoor().lat(), way.getNode(j + 1).getCoor().lon(), distance); segmentMetrics.add(sm); distance += sm.length; } } } } else segmentMetrics.add(null); } return segmentMetrics; } private StopReference detectMinDistance(Node node, Vector<SegmentMetric> segmentMetrics, boolean rhsPossible, boolean lhsPossible) { if (node == null || node.getCoor() == null) return null; int minIndex = -1; double position = -1.0; double distance = 180.0; double lat = node.getCoor().lat(); double lon = node.getCoor().lon(); int curIndex = -2; double angleLat = 100.0; double angleLon = 200.0; Iterator<SegmentMetric> iter = segmentMetrics.iterator(); while (iter.hasNext()) { curIndex += 2; SegmentMetric sm = iter.next(); if (sm == null) { angleLat = 100.0; angleLon = 200.0; continue; } double curPosition = (lat - sm.aLat) * sm.d1 + (lon - sm.aLon) * sm.d2; if (curPosition < 0) { if (angleLat <= 90.0) { double lastSegAngle = Math.atan2(angleLat - sm.aLat, angleLon - sm.aLon); double segAngle = Math.atan2(sm.d1, -sm.o1); double vertexAngle = Math.atan2(lat - sm.aLat, lon - sm.aLon); boolean vertexOnSeg = (vertexAngle == segAngle) || (vertexAngle == lastSegAngle); boolean vertexOnTheLeft = (!vertexOnSeg) && (((lastSegAngle > vertexAngle) && (vertexAngle > segAngle)) || ((vertexAngle > segAngle) && (segAngle > lastSegAngle)) || ((segAngle > lastSegAngle) && (lastSegAngle > vertexAngle))); double currentDistance = Math .sqrt((lat - sm.aLat) * (lat - sm.aLat) + (lon - sm.aLon) * (lon - sm.aLon) * Math.cos(sm.aLat * Math.PI / 180.0) * Math.cos(sm.aLat * Math.PI / 180.0)); curPosition = vertexAngle - segAngle; if (vertexOnTheLeft) curPosition = -curPosition; if (curPosition < 0) curPosition += 2 * Math.PI; if ((Math.abs(currentDistance) < distance) && (((!vertexOnTheLeft) && (rhsPossible)) || ((vertexOnTheLeft) && (lhsPossible)) || (vertexOnSeg))) { distance = Math.abs(currentDistance); minIndex = curIndex - 1; position = curPosition; } } angleLat = 100.0; angleLon = 200.0; } else if (curPosition > sm.length) { angleLat = sm.aLat; angleLon = sm.aLon; } else { double currentDistance = (lat - sm.aLat) * sm.o1 + (lon - sm.aLon) * sm.o2; if ((Math.abs(currentDistance) < distance) && (((currentDistance >= 0) && (rhsPossible)) || ((currentDistance <= 0) && (lhsPossible)))) { distance = Math.abs(currentDistance); minIndex = curIndex; position = curPosition; } angleLat = 100.0; angleLon = 200.0; } } if (minIndex == -1) return new StopReference(segmentMetrics.size() * 2, 0, 180.0, node.get("name"), "", node); return new StopReference(minIndex, position, distance, node.get("name"), "", node); } }