1 package tim.prune.save;
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.FlowLayout;
6 import java.awt.GridLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
10 import java.io.FileOutputStream;
11 import java.io.IOException;
12 import java.io.OutputStreamWriter;
13 import java.io.Writer;
15 import javax.swing.BorderFactory;
16 import javax.swing.BoxLayout;
17 import javax.swing.JButton;
18 import javax.swing.JCheckBox;
19 import javax.swing.JDialog;
20 import javax.swing.JFileChooser;
21 import javax.swing.JFrame;
22 import javax.swing.JLabel;
23 import javax.swing.JOptionPane;
24 import javax.swing.JPanel;
25 import javax.swing.JTextField;
27 import tim.prune.Config;
28 import tim.prune.GpsPruner;
29 import tim.prune.I18nManager;
30 import tim.prune.UpdateMessageBroker;
31 import tim.prune.data.Altitude;
32 import tim.prune.data.Coordinate;
33 import tim.prune.data.DataPoint;
34 import tim.prune.data.Timestamp;
35 import tim.prune.data.Track;
36 import tim.prune.data.TrackInfo;
37 import tim.prune.load.GenericFileFilter;
40 * Class to export track information
41 * into a specified Gpx file
43 public class GpxExporter implements Runnable
45 private JFrame _parentFrame = null;
46 private Track _track = null;
47 private JDialog _dialog = null;
48 private JTextField _nameField = null;
49 private JTextField _descriptionField = null;
50 private JCheckBox _timestampsCheckbox = null;
51 private JFileChooser _fileChooser = null;
52 private File _exportFile = null;
54 /** version number of Gpx */
55 private static final String GPX_VERSION_NUMBER = "1.1";
56 /** this program name */
57 private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
61 * Constructor giving frame and track
62 * @param inParentFrame parent frame
63 * @param inTrackInfo track info object to save
65 public GpxExporter(JFrame inParentFrame, TrackInfo inTrackInfo)
67 _parentFrame = inParentFrame;
68 _track = inTrackInfo.getTrack();
73 * Show the dialog to select options and export file
75 public void showDialog()
80 _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.exportgpx.title"), true);
81 _dialog.setLocationRelativeTo(_parentFrame);
82 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
83 _dialog.getContentPane().add(makeDialogComponents());
91 * Create dialog components
92 * @return Panel containing all gui elements in dialog
94 private Component makeDialogComponents()
96 JPanel dialogPanel = new JPanel();
97 dialogPanel.setLayout(new BorderLayout());
98 JPanel mainPanel = new JPanel();
99 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
100 // Make a central panel with the text boxes
101 JPanel descPanel = new JPanel();
102 descPanel.setLayout(new GridLayout(2, 2));
103 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
104 _nameField = new JTextField(10);
105 descPanel.add(_nameField);
106 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
107 _descriptionField = new JTextField(10);
108 descPanel.add(_descriptionField);
109 mainPanel.add(descPanel);
110 // checkbox for timestamps
111 _timestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.includetimestamps"));
112 _timestampsCheckbox.setSelected(true);
113 mainPanel.add(_timestampsCheckbox);
114 dialogPanel.add(mainPanel, BorderLayout.CENTER);
116 // button panel at bottom
117 JPanel buttonPanel = new JPanel();
118 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
119 JButton okButton = new JButton(I18nManager.getText("button.ok"));
120 ActionListener okListener = new ActionListener() {
121 public void actionPerformed(ActionEvent e)
126 okButton.addActionListener(okListener);
127 _descriptionField.addActionListener(okListener);
128 buttonPanel.add(okButton);
129 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
130 cancelButton.addActionListener(new ActionListener() {
131 public void actionPerformed(ActionEvent e)
136 buttonPanel.add(cancelButton);
137 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
138 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
144 * Start the export process based on the input parameters
146 private void startExport()
148 // OK pressed, so choose output file
149 if (_fileChooser == null)
151 _fileChooser = new JFileChooser();
152 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
153 _fileChooser.setFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
154 _fileChooser.setAcceptAllFileFilterUsed(false);
155 // start from directory in config which should be set
156 File configDir = Config.getWorkingDirectory();
157 if (configDir != null) {_fileChooser.setCurrentDirectory(configDir);}
159 // Allow choose again if an existing file is selected
160 boolean chooseAgain = false;
164 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
166 // OK pressed and file chosen
167 File file = _fileChooser.getSelectedFile();
168 // Check file extension
169 if (!file.getName().toLowerCase().endsWith(".gpx"))
171 file = new File(file.getAbsolutePath() + ".gpx");
173 // Check if file exists and if necessary prompt for overwrite
174 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
175 if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
176 I18nManager.getText("dialog.save.overwrite.text"),
177 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
178 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
179 == JOptionPane.YES_OPTION)
181 // New file or overwrite confirmed, so initiate export in separate thread
183 new Thread(this).start();
190 } while (chooseAgain);
195 * Run method for controlling separate thread for exporting
199 OutputStreamWriter writer = null;
202 // normal writing to file
203 writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
205 int numPoints = exportData(writer);
209 // Store directory in config for later
210 Config.setWorkingDirectory(_exportFile.getParentFile());
212 UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
213 + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
214 + " " + _exportFile.getAbsolutePath());
215 // export successful so need to close dialog and return
219 catch (IOException ioe)
221 // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
223 if (writer != null) writer.close();
225 catch (IOException ioe2) {}
226 JOptionPane.showMessageDialog(_parentFrame,
227 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
228 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
230 // if not returned already, export failed so need to recall the file selection
236 * Export the information to the given writer
237 * @param inWriter writer object
238 * @return number of points written
240 private int exportData(OutputStreamWriter inWriter) throws IOException
242 inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
243 inWriter.write(GPX_VERSION_NUMBER);
244 inWriter.write("\" creator=\"");
245 inWriter.write(GPX_CREATOR);
246 inWriter.write("\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
247 + " xmlns=\"http://www.topografix.com/GPX/1/0\""
248 + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
250 if (_nameField != null && _nameField.getText() != null && !_nameField.getText().equals(""))
252 inWriter.write("\t<name>");
253 inWriter.write(_nameField.getText());
254 inWriter.write("</name>\n");
257 inWriter.write("\t<desc>");
258 if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
260 inWriter.write(_descriptionField.getText());
264 inWriter.write("Export from Prune");
266 inWriter.write("</desc>\n");
269 DataPoint point = null;
270 boolean hasTrackpoints = false;
271 // Loop over waypoints
272 int numPoints = _track.getNumPoints();
273 for (i=0; i<numPoints; i++)
275 point = _track.getPoint(i);
276 // Make a wpt element for each waypoint
277 if (point.isWaypoint())
279 exportWaypoint(point, inWriter);
283 hasTrackpoints = true;
286 // Output the track, if there is one
289 boolean firstPoint = true;
290 inWriter.write("\t<trk><trkseg>\n");
291 // Loop over track points
292 for (i=0; i<numPoints; i++)
294 point = _track.getPoint(i);
295 // restart track segment if necessary
296 if (point.getSegmentStart() && !firstPoint) {
297 inWriter.write("\t</trkseg>\n\t<trkseg>\n");
299 if (!point.isWaypoint()) {
300 // export the track point
301 exportTrackpoint(point, inWriter);
305 inWriter.write("\t</trkseg></trk>\n");
307 inWriter.write("</gpx>\n");
313 * Export the specified waypoint into the file
314 * @param inPoint waypoint to export
315 * @param inWriter writer object
316 * @throws IOException on write failure
318 private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
320 inWriter.write("\t<wpt lat=\"");
321 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
322 inWriter.write("\" lon=\"");
323 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
324 inWriter.write("\">\n");
325 inWriter.write("\t\t<name>");
326 inWriter.write(inPoint.getWaypointName().trim());
327 inWriter.write("</name>\n");
328 // altitude if available
329 if (inPoint.hasAltitude())
331 inWriter.write("\t\t<ele>");
332 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.FORMAT_METRES));
333 inWriter.write("</ele>\n");
335 // timestamp if available (point might have altitude and then be turned into a waypoint)
336 if (inPoint.hasTimestamp() && _timestampsCheckbox.isSelected())
338 inWriter.write("\t\t<time>");
339 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
340 inWriter.write("</time>\n");
342 // TODO: Include waypt type in Gpx
343 inWriter.write("\t</wpt>\n");
348 * Export the specified trackpoint into the file
349 * @param inPoint trackpoint to export
350 * @param inWriter writer object
352 private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
354 inWriter.write("\t\t<trkpt lat=\"");
355 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
356 inWriter.write("\" lon=\"");
357 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
358 inWriter.write("\">");
360 if (inPoint.hasAltitude())
362 inWriter.write("<ele>");
363 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.FORMAT_METRES));
364 inWriter.write("</ele>");
366 // timestamp if available (and selected)
367 if (inPoint.hasTimestamp() && _timestampsCheckbox.isSelected())
369 inWriter.write("<time>");
370 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
371 inWriter.write("</time>");
373 inWriter.write("</trkpt>\n");