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.BoxLayout;
16 import javax.swing.JButton;
17 import javax.swing.JDialog;
18 import javax.swing.JFileChooser;
19 import javax.swing.JFrame;
20 import javax.swing.JLabel;
21 import javax.swing.JOptionPane;
22 import javax.swing.JPanel;
23 import javax.swing.JTextField;
24 import javax.swing.filechooser.FileFilter;
26 import tim.prune.GpsPruner;
27 import tim.prune.I18nManager;
28 import tim.prune.data.Altitude;
29 import tim.prune.data.Coordinate;
30 import tim.prune.data.DataPoint;
31 import tim.prune.data.Timestamp;
32 import tim.prune.data.Track;
33 import tim.prune.data.TrackInfo;
36 * Class to export track information
37 * into a specified Gpx file
39 public class GpxExporter implements Runnable
41 private JFrame _parentFrame = null;
42 private Track _track = null;
43 private JDialog _dialog = null;
44 private JTextField _nameField = null;
45 private JTextField _descriptionField = null;
46 private JFileChooser _fileChooser = null;
47 private File _exportFile = null;
49 /** version number of Gpx */
50 private static final String GPX_VERSION_NUMBER = "1.1";
51 /** this program name */
52 private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
56 * Constructor giving frame and track
57 * @param inParentFrame parent frame
58 * @param inTrackInfo track info object to save
60 public GpxExporter(JFrame inParentFrame, TrackInfo inTrackInfo)
62 _parentFrame = inParentFrame;
63 _track = inTrackInfo.getTrack();
68 * Show the dialog to select options and export file
70 public void showDialog()
75 _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.exportgpx.title"), true);
76 _dialog.setLocationRelativeTo(_parentFrame);
77 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
78 _dialog.getContentPane().add(makeDialogComponents());
86 * Create dialog components
87 * @return Panel containing all gui elements in dialog
89 private Component makeDialogComponents()
91 JPanel dialogPanel = new JPanel();
92 dialogPanel.setLayout(new BorderLayout());
93 JPanel mainPanel = new JPanel();
94 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
95 // Make a central panel with the text boxes
96 JPanel descPanel = new JPanel();
97 descPanel.setLayout(new GridLayout(2, 2));
98 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
99 _nameField = new JTextField(10);
100 descPanel.add(_nameField);
101 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
102 _descriptionField = new JTextField(10);
103 descPanel.add(_descriptionField);
104 mainPanel.add(descPanel);
105 dialogPanel.add(mainPanel, BorderLayout.CENTER);
107 // button panel at bottom
108 JPanel buttonPanel = new JPanel();
109 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
110 JButton okButton = new JButton(I18nManager.getText("button.ok"));
111 ActionListener okListener = new ActionListener() {
112 public void actionPerformed(ActionEvent e)
117 okButton.addActionListener(okListener);
118 _descriptionField.addActionListener(okListener);
119 buttonPanel.add(okButton);
120 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
121 cancelButton.addActionListener(new ActionListener() {
122 public void actionPerformed(ActionEvent e)
127 buttonPanel.add(cancelButton);
128 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
134 * Start the export process based on the input parameters
136 private void startExport()
138 // OK pressed, so choose output file
139 if (_fileChooser == null)
140 {_fileChooser = new JFileChooser();}
141 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
142 _fileChooser.setFileFilter(new FileFilter() {
143 public boolean accept(File f)
145 return (f != null && (f.isDirectory()
146 || f.getName().toLowerCase().endsWith(".gpx")));
148 public String getDescription()
150 return I18nManager.getText("dialog.exportgpx.filetype");
153 _fileChooser.setAcceptAllFileFilterUsed(false);
154 // Allow choose again if an existing file is selected
155 boolean chooseAgain = false;
159 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
161 // OK pressed and file chosen
162 File file = _fileChooser.getSelectedFile();
163 // Check file extension
164 if (!file.getName().toLowerCase().endsWith(".gpx"))
166 file = new File(file.getAbsolutePath() + ".gpx");
168 // Check if file exists and if necessary prompt for overwrite
169 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
170 if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
171 I18nManager.getText("dialog.save.overwrite.text"),
172 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
173 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
174 == JOptionPane.YES_OPTION)
176 // New file or overwrite confirmed, so initiate export in separate thread
178 new Thread(this).start();
185 } while (chooseAgain);
190 * Run method for controlling separate thread for exporting
194 OutputStreamWriter writer = null;
197 // normal writing to file
198 writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
200 int numPoints = exportData(writer);
204 JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.ok1")
205 + " " + numPoints + " " + I18nManager.getText("dialog.save.ok2")
206 + " " + _exportFile.getAbsolutePath(),
207 I18nManager.getText("dialog.save.oktitle"), JOptionPane.INFORMATION_MESSAGE);
208 // export successful so need to close dialog and return
212 catch (IOException ioe)
214 // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
216 if (writer != null) writer.close();
218 catch (IOException ioe2) {}
219 JOptionPane.showMessageDialog(_parentFrame,
220 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
221 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
223 // if not returned already, export failed so need to recall the file selection
229 * Export the information to the given writer
230 * @param inWriter writer object
231 * @return number of points written
233 private int exportData(OutputStreamWriter inWriter) throws IOException
235 inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
236 inWriter.write(GPX_VERSION_NUMBER);
237 inWriter.write("\" creator=\"");
238 inWriter.write(GPX_CREATOR);
239 inWriter.write("\">\n");
241 if (_nameField != null && _nameField.getText() != null && !_nameField.getText().equals(""))
243 inWriter.write("\t<name>");
244 inWriter.write(_nameField.getText());
245 inWriter.write("</name>\n");
248 inWriter.write("\t<desc>");
249 if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
251 inWriter.write(_descriptionField.getText());
255 inWriter.write("Export from Prune");
257 inWriter.write("</desc>\n");
260 DataPoint point = null;
261 boolean hasTrackpoints = false;
262 // Loop over waypoints
263 int numPoints = _track.getNumPoints();
264 for (i=0; i<numPoints; i++)
266 point = _track.getPoint(i);
267 // Make a blob for each waypoint
268 if (point.isWaypoint())
270 exportWaypoint(point, inWriter);
274 hasTrackpoints = true;
277 // Make a line for the track, if there is one
278 // TODO: Look at segments of track, and split into separate track segments in Gpx if necessary
281 inWriter.write("\t<trk><trkseg>\n");
282 // Loop over track points
283 for (i=0; i<numPoints; i++)
285 point = _track.getPoint(i);
286 if (!point.isWaypoint())
288 exportTrackpoint(point, inWriter);
291 inWriter.write("\t</trkseg></trk>\n");
293 inWriter.write("</gpx>\n");
299 * Export the specified waypoint into the file
300 * @param inPoint waypoint to export
301 * @param inWriter writer object
302 * @throws IOException on write failure
304 private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
306 inWriter.write("\t<wpt lat=\"");
307 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
308 inWriter.write("\" lon=\"");
309 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
310 inWriter.write("\">\n");
311 inWriter.write("\t\t<name>");
312 inWriter.write(inPoint.getWaypointName().trim());
313 inWriter.write("</name>\n");
314 // altitude if available
315 if (inPoint.hasAltitude())
317 inWriter.write("\t\t<ele>");
318 inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
319 inWriter.write("</ele>\n");
321 // timestamp if available (point might have altitude and then be turned into a waypoint)
322 if (inPoint.hasTimestamp())
324 inWriter.write("\t\t<time>");
325 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
326 inWriter.write("</time>\n");
328 // TODO: Include waypt type in Gpx
329 inWriter.write("\t</wpt>\n");
334 * Export the specified trackpoint into the file
335 * @param inPoint trackpoint to export
336 * @param inWriter writer object
338 private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
340 inWriter.write("\t\t<trkpt lat=\"");
341 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
342 inWriter.write("\" lon=\"");
343 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
344 inWriter.write("\">");
346 if (inPoint.hasAltitude())
348 inWriter.write("<ele>");
349 inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
350 inWriter.write("</ele>");
352 // timestamp if available
353 if (inPoint.hasTimestamp())
355 inWriter.write("<time>");
356 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
357 inWriter.write("</time>");
359 inWriter.write("</trkpt>\n");