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.UpdateMessageBroker;
29 import tim.prune.data.Altitude;
30 import tim.prune.data.Coordinate;
31 import tim.prune.data.DataPoint;
32 import tim.prune.data.Timestamp;
33 import tim.prune.data.Track;
34 import tim.prune.data.TrackInfo;
37 * Class to export track information
38 * into a specified Gpx file
40 public class GpxExporter implements Runnable
42 private JFrame _parentFrame = null;
43 private Track _track = null;
44 private JDialog _dialog = null;
45 private JTextField _nameField = null;
46 private JTextField _descriptionField = null;
47 private JFileChooser _fileChooser = null;
48 private File _exportFile = null;
50 /** version number of Gpx */
51 private static final String GPX_VERSION_NUMBER = "1.1";
52 /** this program name */
53 private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
57 * Constructor giving frame and track
58 * @param inParentFrame parent frame
59 * @param inTrackInfo track info object to save
61 public GpxExporter(JFrame inParentFrame, TrackInfo inTrackInfo)
63 _parentFrame = inParentFrame;
64 _track = inTrackInfo.getTrack();
69 * Show the dialog to select options and export file
71 public void showDialog()
76 _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.exportgpx.title"), true);
77 _dialog.setLocationRelativeTo(_parentFrame);
78 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
79 _dialog.getContentPane().add(makeDialogComponents());
87 * Create dialog components
88 * @return Panel containing all gui elements in dialog
90 private Component makeDialogComponents()
92 JPanel dialogPanel = new JPanel();
93 dialogPanel.setLayout(new BorderLayout());
94 JPanel mainPanel = new JPanel();
95 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
96 // Make a central panel with the text boxes
97 JPanel descPanel = new JPanel();
98 descPanel.setLayout(new GridLayout(2, 2));
99 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
100 _nameField = new JTextField(10);
101 descPanel.add(_nameField);
102 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
103 _descriptionField = new JTextField(10);
104 descPanel.add(_descriptionField);
105 mainPanel.add(descPanel);
106 dialogPanel.add(mainPanel, BorderLayout.CENTER);
108 // button panel at bottom
109 JPanel buttonPanel = new JPanel();
110 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
111 JButton okButton = new JButton(I18nManager.getText("button.ok"));
112 ActionListener okListener = new ActionListener() {
113 public void actionPerformed(ActionEvent e)
118 okButton.addActionListener(okListener);
119 _descriptionField.addActionListener(okListener);
120 buttonPanel.add(okButton);
121 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
122 cancelButton.addActionListener(new ActionListener() {
123 public void actionPerformed(ActionEvent e)
128 buttonPanel.add(cancelButton);
129 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
135 * Start the export process based on the input parameters
137 private void startExport()
139 // OK pressed, so choose output file
140 if (_fileChooser == null)
141 {_fileChooser = new JFileChooser();}
142 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
143 _fileChooser.setFileFilter(new FileFilter() {
144 public boolean accept(File f)
146 return (f != null && (f.isDirectory()
147 || f.getName().toLowerCase().endsWith(".gpx")));
149 public String getDescription()
151 return I18nManager.getText("dialog.exportgpx.filetype");
154 _fileChooser.setAcceptAllFileFilterUsed(false);
155 // Allow choose again if an existing file is selected
156 boolean chooseAgain = false;
160 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
162 // OK pressed and file chosen
163 File file = _fileChooser.getSelectedFile();
164 // Check file extension
165 if (!file.getName().toLowerCase().endsWith(".gpx"))
167 file = new File(file.getAbsolutePath() + ".gpx");
169 // Check if file exists and if necessary prompt for overwrite
170 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
171 if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
172 I18nManager.getText("dialog.save.overwrite.text"),
173 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
174 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
175 == JOptionPane.YES_OPTION)
177 // New file or overwrite confirmed, so initiate export in separate thread
179 new Thread(this).start();
186 } while (chooseAgain);
191 * Run method for controlling separate thread for exporting
195 OutputStreamWriter writer = null;
198 // normal writing to file
199 writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
201 int numPoints = exportData(writer);
206 UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
207 + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
208 + " " + _exportFile.getAbsolutePath());
209 // export successful so need to close dialog and return
213 catch (IOException ioe)
215 // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
217 if (writer != null) writer.close();
219 catch (IOException ioe2) {}
220 JOptionPane.showMessageDialog(_parentFrame,
221 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
222 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
224 // if not returned already, export failed so need to recall the file selection
230 * Export the information to the given writer
231 * @param inWriter writer object
232 * @return number of points written
234 private int exportData(OutputStreamWriter inWriter) throws IOException
236 inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
237 inWriter.write(GPX_VERSION_NUMBER);
238 inWriter.write("\" creator=\"");
239 inWriter.write(GPX_CREATOR);
240 inWriter.write("\">\n");
242 if (_nameField != null && _nameField.getText() != null && !_nameField.getText().equals(""))
244 inWriter.write("\t<name>");
245 inWriter.write(_nameField.getText());
246 inWriter.write("</name>\n");
249 inWriter.write("\t<desc>");
250 if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
252 inWriter.write(_descriptionField.getText());
256 inWriter.write("Export from Prune");
258 inWriter.write("</desc>\n");
261 DataPoint point = null;
262 boolean hasTrackpoints = false;
263 // Loop over waypoints
264 int numPoints = _track.getNumPoints();
265 for (i=0; i<numPoints; i++)
267 point = _track.getPoint(i);
268 // Make a wpt element for each waypoint
269 if (point.isWaypoint())
271 exportWaypoint(point, inWriter);
275 hasTrackpoints = true;
278 // Output the track, if there is one
281 boolean firstPoint = true;
282 inWriter.write("\t<trk><trkseg>\n");
283 // Loop over track points
284 for (i=0; i<numPoints; i++)
286 point = _track.getPoint(i);
287 if (point.getSegmentStart() && !firstPoint) {
288 inWriter.write("\t</trkseg>\n\t<trkseg>\n");
290 if (!point.isWaypoint()) {
291 // restart track segment if necessary
292 // export the track point
293 exportTrackpoint(point, inWriter);
297 inWriter.write("\t</trkseg></trk>\n");
299 inWriter.write("</gpx>\n");
305 * Export the specified waypoint into the file
306 * @param inPoint waypoint to export
307 * @param inWriter writer object
308 * @throws IOException on write failure
310 private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
312 inWriter.write("\t<wpt lat=\"");
313 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
314 inWriter.write("\" lon=\"");
315 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
316 inWriter.write("\">\n");
317 inWriter.write("\t\t<name>");
318 inWriter.write(inPoint.getWaypointName().trim());
319 inWriter.write("</name>\n");
320 // altitude if available
321 if (inPoint.hasAltitude())
323 inWriter.write("\t\t<ele>");
324 inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
325 inWriter.write("</ele>\n");
327 // timestamp if available (point might have altitude and then be turned into a waypoint)
328 if (inPoint.hasTimestamp())
330 inWriter.write("\t\t<time>");
331 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
332 inWriter.write("</time>\n");
334 // TODO: Include waypt type in Gpx
335 inWriter.write("\t</wpt>\n");
340 * Export the specified trackpoint into the file
341 * @param inPoint trackpoint to export
342 * @param inWriter writer object
344 private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
346 inWriter.write("\t\t<trkpt lat=\"");
347 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
348 inWriter.write("\" lon=\"");
349 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
350 inWriter.write("\">");
352 if (inPoint.hasAltitude())
354 inWriter.write("<ele>");
355 inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
356 inWriter.write("</ele>");
358 // timestamp if available
359 if (inPoint.hasTimestamp())
361 inWriter.write("<time>");
362 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
363 inWriter.write("</time>");
365 inWriter.write("</trkpt>\n");