// License: GPL. For details, see LICENSE file.
package public_transport;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.Format;
import java.util.Collections;
import java.util.Iterator;
import java.util.Vector;
import java.util.zip.GZIPInputStream;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.gpx.GpxData;
import org.openstreetmap.josm.data.gpx.GpxTrack;
import org.openstreetmap.josm.data.gpx.WayPoint;
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.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.io.GpxReader;
import org.xml.sax.SAXException;
public class StopImporterAction extends JosmAction {
private static StopImporterDialog dialog = null;
private static DefaultListModel<TrackReference> tracksListModel = null;
private static GpxData data = null;
private static TrackReference currentTrack = null;
private static WaypointTableModel waypointTM = null;
public boolean inEvent = false;
/**
* Constructs a new {@code StopImporterAction}.
*/
public StopImporterAction() {
super(tr("Create Stops from GPX ..."), null, tr("Create Stops from a GPX file"), null,
false);
putValue("toolbar", "publictransport/stopimporter");
Main.toolbar.register(this);
}
public WaypointTableModel getWaypointTableModel() {
return waypointTM;
}
public StopImporterDialog getDialog() {
return dialog;
}
public DefaultListModel<TrackReference> getTracksListModel() {
if (tracksListModel == null)
tracksListModel = new DefaultListModel<>();
return tracksListModel;
}
public TrackReference getCurrentTrack() {
return currentTrack;
}
@Override
public void actionPerformed(ActionEvent event) {
if (dialog == null)
dialog = new StopImporterDialog(this);
dialog.setVisible(true);
if (tr("Create Stops from GPX ...").equals(event.getActionCommand())) {
String curDir = Main.pref.get("lastDirectory");
if (curDir.equals("")) {
curDir = ".";
}
JFileChooser fc = new JFileChooser(new File(curDir));
fc.setDialogTitle(tr("Select GPX file"));
fc.setMultiSelectionEnabled(false);
int answer = fc.showOpenDialog(Main.parent);
if (answer != JFileChooser.APPROVE_OPTION)
return;
if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
importData(fc.getSelectedFile());
refreshData();
} else if ("stopImporter.settingsGPSTimeStart".equals(event.getActionCommand())) {
if ((!inEvent) && (dialog.gpsTimeStartValid()) && (currentTrack != null))
Main.main.undoRedo.add(new TrackStoplistRelocateCommand(this));
} else if ("stopImporter.settingsStopwatchStart".equals(event.getActionCommand())) {
if ((!inEvent) && (dialog.stopwatchStartValid()) && (currentTrack != null))
Main.main.undoRedo.add(new TrackStoplistRelocateCommand(this));
} else if ("stopImporter.settingsTimeWindow".equals(event.getActionCommand())) {
if (currentTrack != null)
currentTrack.timeWindow = dialog.getTimeWindow();
} else if ("stopImporter.settingsThreshold".equals(event.getActionCommand())) {
if (currentTrack != null)
currentTrack.threshold = dialog.getThreshold();
} else if ("stopImporter.settingsSuggestStops".equals(event.getActionCommand()))
Main.main.undoRedo.add(new TrackSuggestStopsCommand(this));
else if ("stopImporter.stoplistFind".equals(event.getActionCommand()))
findNodesInTable(dialog.getStoplistTable(), currentTrack.stoplistTM.getNodes());
else if ("stopImporter.stoplistShow".equals(event.getActionCommand()))
showNodesFromTable(dialog.getStoplistTable(), currentTrack.stoplistTM.getNodes());
else if ("stopImporter.stoplistMark".equals(event.getActionCommand()))
markNodesFromTable(dialog.getStoplistTable(), currentTrack.stoplistTM.getNodes());
else if ("stopImporter.stoplistDetach".equals(event.getActionCommand())) {
Main.main.undoRedo.add(new TrackStoplistDetachCommand(this));
dialog.getStoplistTable().clearSelection();
} else if ("stopImporter.stoplistAdd".equals(event.getActionCommand()))
Main.main.undoRedo.add(new TrackStoplistAddCommand(this));
else if ("stopImporter.stoplistDelete".equals(event.getActionCommand()))
Main.main.undoRedo.add(new TrackStoplistDeleteCommand(this));
else if ("stopImporter.stoplistSort".equals(event.getActionCommand()))
Main.main.undoRedo.add(new TrackStoplistSortCommand(this));
else if ("stopImporter.waypointsFind".equals(event.getActionCommand()))
findNodesInTable(dialog.getWaypointsTable(), waypointTM.nodes);
else if ("stopImporter.waypointsShow".equals(event.getActionCommand()))
showNodesFromTable(dialog.getWaypointsTable(), waypointTM.nodes);
else if ("stopImporter.waypointsMark".equals(event.getActionCommand()))
markNodesFromTable(dialog.getWaypointsTable(), waypointTM.nodes);
else if ("stopImporter.waypointsDetach".equals(event.getActionCommand())) {
Main.main.undoRedo.add(new WaypointsDetachCommand(this));
dialog.getWaypointsTable().clearSelection();
} else if ("stopImporter.waypointsAdd".equals(event.getActionCommand()))
Main.main.undoRedo.add(new WaypointsEnableCommand(this));
else if ("stopImporter.waypointsDelete".equals(event.getActionCommand()))
Main.main.undoRedo.add(new WaypointsDisableCommand(this));
else if ("stopImporter.settingsStoptype".equals(event.getActionCommand()))
Main.main.undoRedo.add(new SettingsStoptypeCommand(this));
}
private void importData(final File file) {
try {
InputStream is;
if (file.getName().endsWith(".gpx.gz"))
is = new GZIPInputStream(new FileInputStream(file));
else
is = new FileInputStream(file);
// Workaround for SAX BOM bug
// https://bugs.openjdk.java.net/browse/JDK-6206835
if (!((is.read() == 0xef) && (is.read() == 0xbb) && (is.read() == 0xbf))) {
is.close();
if (file.getName().endsWith(".gpx.gz"))
is = new GZIPInputStream(new FileInputStream(file));
else
is = new FileInputStream(file);
}
final GpxReader r = new GpxReader(is);
final boolean parsedProperly = r.parse(true);
data = r.getGpxData();
if (!parsedProperly) {
JOptionPane.showMessageDialog(null,
tr("Error occurred while parsing gpx file {0}. Only a part of the file will be available.",
file.getName()));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, tr("File \"{0}\" does not exist", file.getName()));
} catch (SAXException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, tr("Parsing file \"{0}\" failed", file.getName()));
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, tr("IOException \"{0}\" occurred", e.toString()));
}
}
private void refreshData() {
tracksListModel.clear();
if (data != null) {
Vector<TrackReference> trackRefs = new Vector<>();
Iterator<GpxTrack> trackIter = data.tracks.iterator();
while (trackIter.hasNext()) {
GpxTrack track = trackIter.next();
trackRefs.add(new TrackReference(track, this));
}
Collections.sort(trackRefs);
Iterator<TrackReference> iter = trackRefs.iterator();
while (iter.hasNext()) {
tracksListModel.addElement(iter.next());
}
waypointTM = new WaypointTableModel(this);
Iterator<WayPoint> waypointIter = data.waypoints.iterator();
while (waypointIter.hasNext()) {
WayPoint waypoint = waypointIter.next();
waypointTM.addRow(waypoint);
}
dialog.setWaypointsTableModel(waypointTM);
} else {
JOptionPane.showMessageDialog(null,
tr("The GPX file contained no tracks or waypoints."), tr("No data found"),
JOptionPane.ERROR_MESSAGE);
}
}
public void tracksSelectionChanged(int selectedPos) {
if (selectedPos >= 0) {
currentTrack = (tracksListModel.elementAt(selectedPos));
dialog.setTrackValid(true);
// Prepare Settings
dialog.setSettings(currentTrack.gpsSyncTime, currentTrack.stopwatchStart,
currentTrack.timeWindow, currentTrack.threshold);
// Prepare Stoplist
dialog.setStoplistTableModel(tracksListModel.elementAt(selectedPos).stoplistTM);
} else {
currentTrack = null;
dialog.setTrackValid(false);
}
}
public Node createNode(LatLon latLon, String name) {
return createNode(latLon, dialog.getStoptype(), name);
}
public static Node createNode(LatLon latLon, String type, String name) {
Node node = new Node(latLon);
setTagsWrtType(node, type);
node.put("name", name);
DataSet ds = Main.getLayerManager().getEditDataSet();
if (ds == null) {
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);
return null;
}
ds.addPrimitive(node);
return node;
}
/** sets the tags of the node according to the type */
public static void setTagsWrtType(Node node, String type) {
node.remove("highway");
node.remove("railway");
if ("bus".equals(type))
node.put("highway", "bus_stop");
else if ("tram".equals(type))
node.put("railway", "tram_stop");
else if ("light_rail".equals(type))
node.put("railway", "station");
else if ("subway".equals(type))
node.put("railway", "station");
else if ("rail".equals(type))
node.put("railway", "station");
}
/**
* returns a collection of all selected lines or a collection of all lines otherwise
*/
public static Vector<Integer> getConsideredLines(JTable table) {
int[] selectedLines = table.getSelectedRows();
Vector<Integer> consideredLines = new Vector<>();
if (selectedLines.length > 0) {
for (int i = 0; i < selectedLines.length; ++i) {
consideredLines.add(selectedLines[i]);
}
} else {
for (int i = 0; i < table.getRowCount(); ++i) {
consideredLines.add(Integer.valueOf(i));
}
}
return consideredLines;
}
/** marks the table items whose nodes are marked on the map */
public static void findNodesInTable(JTable table, Vector<Node> nodes) {
DataSet ds = Main.getLayerManager().getEditDataSet();
if (ds == null)
return;
table.clearSelection();
for (int i = 0; i < table.getRowCount(); ++i) {
if ((nodes.elementAt(i) != null) && (ds.isSelected(nodes.elementAt(i))))
table.addRowSelectionInterval(i, i);
}
}
/**
* shows the nodes that correspond to the marked lines in the table. If no lines are marked in the table, show all nodes from the vector
*/
public static void showNodesFromTable(JTable table, Vector<Node> nodes) {
BoundingXYVisitor box = new BoundingXYVisitor();
Vector<Integer> consideredLines = getConsideredLines(table);
for (int i = 0; i < consideredLines.size(); ++i) {
int j = consideredLines.elementAt(i);
if (nodes.elementAt(j) != null)
nodes.elementAt(j).accept(box);
}
if (box.getBounds() == null)
return;
box.enlargeBoundingBox();
Main.map.mapView.zoomTo(box);
}
/**
* marks the nodes that correspond to the marked lines in the table. If no lines are marked in the table, mark all nodes from the vector
*/
public static void markNodesFromTable(JTable table, Vector<Node> nodes) {
OsmPrimitive[] osmp = {null};
DataSet ds = Main.getLayerManager().getEditDataSet();
ds.setSelected(osmp);
Vector<Integer> consideredLines = getConsideredLines(table);
for (int i = 0; i < consideredLines.size(); ++i) {
int j = consideredLines.elementAt(i);
if (nodes.elementAt(j) != null)
ds.addSelected(nodes.elementAt(j));
}
}
public static String timeOf(double t) {
t -= Math.floor(t / 24 / 60 / 60) * 24 * 60 * 60;
int hour = (int) Math.floor(t / 60 / 60);
t -= Math.floor(t / 60 / 60) * 60 * 60;
int minute = (int) Math.floor(t / 60);
t -= Math.floor(t / 60) * 60;
double second = t;
Format format = new DecimalFormat("00");
Format formatS = new DecimalFormat("00.###");
return (format.format(hour) + ":" + format.format(minute) + ":" + formatS.format(second));
}
public Action getFocusWaypointNameAction() {
return new FocusWaypointNameAction();
}
public Action getFocusWaypointShelterAction(String shelter) {
return new FocusWaypointShelterAction(shelter);
}
public Action getFocusWaypointDeleteAction() {
return new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
JTable table = dialog.getWaypointsTable();
int row = table.getEditingRow();
if (row < 0)
return;
table.clearSelection();
table.addRowSelectionInterval(row, row);
Main.main.undoRedo.add(new WaypointsDisableCommand(StopImporterAction.this));
}
};
}
public Action getFocusTrackStoplistNameAction() {
return new FocusTrackStoplistNameAction();
}
public Action getFocusTrackStoplistShelterAction(String shelter) {
return new FocusTrackStoplistShelterAction(shelter);
}
public Action getFocusStoplistDeleteAction() {
return new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
JTable table = dialog.getStoplistTable();
int row = table.getEditingRow();
if (row < 0)
return;
table.clearSelection();
table.addRowSelectionInterval(row, row);
Main.main.undoRedo.add(new TrackStoplistDeleteCommand(StopImporterAction.this));
}
};
}
private static class FocusWaypointNameAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
JTable table = dialog.getWaypointsTable();
showNodesFromTable(table, waypointTM.nodes);
markNodesFromTable(table, waypointTM.nodes);
int row = table.getEditingRow();
if (row < 0)
row = 0;
waypointTM.inEvent = true;
if (table.getCellEditor() != null) {
if (!table.getCellEditor().stopCellEditing())
table.getCellEditor().cancelCellEditing();
}
table.editCellAt(row, 1);
table.getCellEditor().getTableCellEditorComponent(table, "", true, row, 1);
waypointTM.inEvent = false;
}
}
private static class FocusWaypointShelterAction extends AbstractAction {
private String defaultShelter = null;
FocusWaypointShelterAction(String defaultShelter) {
this.defaultShelter = defaultShelter;
}
@Override
public void actionPerformed(ActionEvent e) {
JTable table = dialog.getWaypointsTable();
showNodesFromTable(table, waypointTM.nodes);
markNodesFromTable(table, waypointTM.nodes);
int row = table.getEditingRow();
if (row < 0)
row = 0;
waypointTM.inEvent = true;
if (table.getCellEditor() != null) {
if (!table.getCellEditor().stopCellEditing())
table.getCellEditor().cancelCellEditing();
}
table.editCellAt(row, 2);
waypointTM.inEvent = false;
table.getCellEditor().getTableCellEditorComponent(table, defaultShelter, true, row, 2);
}
}
private static class FocusTrackStoplistNameAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
JTable table = dialog.getStoplistTable();
showNodesFromTable(table, currentTrack.stoplistTM.getNodes());
markNodesFromTable(table, currentTrack.stoplistTM.getNodes());
int row = table.getEditingRow();
if (row < 0)
row = 0;
currentTrack.inEvent = true;
if (table.getCellEditor() != null) {
if (!table.getCellEditor().stopCellEditing())
table.getCellEditor().cancelCellEditing();
}
table.editCellAt(row, 1);
table.getCellEditor().getTableCellEditorComponent(table, "", true, row, 1);
currentTrack.inEvent = false;
}
}
private static class FocusTrackStoplistShelterAction extends AbstractAction {
private String defaultShelter = null;
FocusTrackStoplistShelterAction(String defaultShelter) {
this.defaultShelter = defaultShelter;
}
@Override
public void actionPerformed(ActionEvent e) {
JTable table = dialog.getStoplistTable();
showNodesFromTable(table, currentTrack.stoplistTM.getNodes());
markNodesFromTable(table, currentTrack.stoplistTM.getNodes());
int row = table.getEditingRow();
if (row < 0)
row = 0;
currentTrack.inEvent = true;
if (table.getCellEditor() != null) {
if (!table.getCellEditor().stopCellEditing())
table.getCellEditor().cancelCellEditing();
}
table.editCellAt(row, 2);
currentTrack.inEvent = false;
table.getCellEditor().getTableCellEditorComponent(table, defaultShelter, true, row, 2);
}
}
}