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.JLabel;
22 import javax.swing.JOptionPane;
23 import javax.swing.JPanel;
24 import javax.swing.JTextField;
27 import tim.prune.Config;
28 import tim.prune.GenericFunction;
29 import tim.prune.GpsPruner;
30 import tim.prune.I18nManager;
31 import tim.prune.UpdateMessageBroker;
32 import tim.prune.data.Altitude;
33 import tim.prune.data.Coordinate;
34 import tim.prune.data.DataPoint;
35 import tim.prune.data.Timestamp;
36 import tim.prune.data.Track;
37 import tim.prune.load.GenericFileFilter;
40 * Class to export track information
41 * into a specified Gpx file
43 public class GpxExporter extends GenericFunction implements Runnable
45 private Track _track = null;
46 private JDialog _dialog = null;
47 private JTextField _nameField = null;
48 private JTextField _descriptionField = null;
49 private JCheckBox _timestampsCheckbox = null;
50 private JFileChooser _fileChooser = null;
51 private File _exportFile = null;
53 /** version number of Gpx */
54 private static final String GPX_VERSION_NUMBER = "1.0";
55 /** this program name */
56 private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
61 * @param inApp app object
63 public GpxExporter(App inApp)
66 _track = inApp.getTrackInfo().getTrack();
70 public String getNameKey() {
71 return "function.exportgpx";
75 * Show the dialog to select options and export file
82 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
83 _dialog.setLocationRelativeTo(_parentFrame);
84 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
85 _dialog.getContentPane().add(makeDialogComponents());
88 _dialog.setVisible(true);
93 * Create dialog components
94 * @return Panel containing all gui elements in dialog
96 private Component makeDialogComponents()
98 JPanel dialogPanel = new JPanel();
99 dialogPanel.setLayout(new BorderLayout());
100 JPanel mainPanel = new JPanel();
101 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
102 // Make a central panel with the text boxes
103 JPanel descPanel = new JPanel();
104 descPanel.setLayout(new GridLayout(2, 2));
105 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
106 _nameField = new JTextField(10);
107 descPanel.add(_nameField);
108 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
109 _descriptionField = new JTextField(10);
110 descPanel.add(_descriptionField);
111 mainPanel.add(descPanel);
112 // checkbox for timestamps
113 _timestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.includetimestamps"));
114 _timestampsCheckbox.setSelected(true);
115 mainPanel.add(_timestampsCheckbox);
116 dialogPanel.add(mainPanel, BorderLayout.CENTER);
118 // button panel at bottom
119 JPanel buttonPanel = new JPanel();
120 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
121 JButton okButton = new JButton(I18nManager.getText("button.ok"));
122 ActionListener okListener = new ActionListener() {
123 public void actionPerformed(ActionEvent e)
128 okButton.addActionListener(okListener);
129 _descriptionField.addActionListener(okListener);
130 buttonPanel.add(okButton);
131 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
132 cancelButton.addActionListener(new ActionListener() {
133 public void actionPerformed(ActionEvent e)
138 buttonPanel.add(cancelButton);
139 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
140 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
146 * Start the export process based on the input parameters
148 private void startExport()
150 // OK pressed, so choose output file
151 if (_fileChooser == null)
153 _fileChooser = new JFileChooser();
154 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
155 _fileChooser.setFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
156 _fileChooser.setAcceptAllFileFilterUsed(false);
157 // start from directory in config which should be set
158 File configDir = Config.getWorkingDirectory();
159 if (configDir != null) {_fileChooser.setCurrentDirectory(configDir);}
161 // Allow choose again if an existing file is selected
162 boolean chooseAgain = false;
166 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
168 // OK pressed and file chosen
169 File file = _fileChooser.getSelectedFile();
170 // Check file extension
171 if (!file.getName().toLowerCase().endsWith(".gpx"))
173 file = new File(file.getAbsolutePath() + ".gpx");
175 // Check if file exists and if necessary prompt for overwrite
176 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
177 if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
178 I18nManager.getText("dialog.save.overwrite.text"),
179 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
180 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
181 == JOptionPane.YES_OPTION)
183 // New file or overwrite confirmed, so initiate export in separate thread
185 new Thread(this).start();
192 } while (chooseAgain);
197 * Run method for controlling separate thread for exporting
201 OutputStreamWriter writer = null;
204 // normal writing to file
205 writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
207 int numPoints = exportData(writer, _track, _nameField.getText(),
208 _descriptionField.getText(), _timestampsCheckbox.isSelected());
212 // Store directory in config for later
213 Config.setWorkingDirectory(_exportFile.getParentFile());
215 UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
216 + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
217 + " " + _exportFile.getAbsolutePath());
218 // export successful so need to close dialog and return
222 catch (IOException ioe)
224 // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
226 if (writer != null) writer.close();
228 catch (IOException ioe2) {}
229 JOptionPane.showMessageDialog(_parentFrame,
230 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
231 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
233 // if not returned already, export failed so need to recall the file selection
239 * Export the information to the given writer
240 * @param inWriter writer object
241 * @param inTrack track object containing data
242 * @param inName name of track (optional)
243 * @param inDesc description of track (optional)
244 * @param inTimestamps true to export timestamps
245 * @return number of points written
246 * @throws IOException if io errors occur on write
248 public static int exportData(OutputStreamWriter inWriter, Track inTrack, String inName,
249 String inDesc, boolean inTimestamps) throws IOException
251 inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
252 inWriter.write(GPX_VERSION_NUMBER);
253 inWriter.write("\" creator=\"");
254 inWriter.write(GPX_CREATOR);
255 inWriter.write("\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
256 + " xmlns=\"http://www.topografix.com/GPX/1/0\""
257 + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
259 String trackName = "PruneTrack";
260 if (inName != null && !inName.equals(""))
263 inWriter.write("\t<name>");
264 inWriter.write(trackName);
265 inWriter.write("</name>\n");
268 inWriter.write("\t<desc>");
269 if (inDesc != null && !inDesc.equals("")) {
270 inWriter.write(inDesc);
274 inWriter.write("Export from Prune");
276 inWriter.write("</desc>\n");
279 DataPoint point = null;
280 boolean hasTrackpoints = false;
281 // Loop over waypoints
282 int numPoints = inTrack.getNumPoints();
283 for (i=0; i<numPoints; i++)
285 point = inTrack.getPoint(i);
286 // Make a wpt element for each waypoint
287 if (point.isWaypoint())
289 exportWaypoint(point, inWriter, inTimestamps);
293 hasTrackpoints = true;
296 // Output the track, if there is one
299 boolean firstPoint = true;
300 inWriter.write("\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n");
301 // Loop over track points
302 for (i=0; i<numPoints; i++)
304 point = inTrack.getPoint(i);
305 // restart track segment if necessary
306 if (point.getSegmentStart() && !firstPoint) {
307 inWriter.write("\t</trkseg>\n\t<trkseg>\n");
309 if (!point.isWaypoint()) {
310 // export the track point
311 exportTrackpoint(point, inWriter, inTimestamps);
315 inWriter.write("\t</trkseg></trk>\n");
317 inWriter.write("</gpx>\n");
323 * Export the specified waypoint into the file
324 * @param inPoint waypoint to export
325 * @param inWriter writer object
326 * @param inTimestamps true to export timestamps too
327 * @throws IOException on write failure
329 private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
332 inWriter.write("\t<wpt lat=\"");
333 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
334 inWriter.write("\" lon=\"");
335 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
336 inWriter.write("\">\n");
337 // altitude if available
338 if (inPoint.hasAltitude())
340 inWriter.write("\t\t<ele>");
341 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
342 inWriter.write("</ele>\n");
344 // write waypoint name after elevation
345 inWriter.write("\t\t<name>");
346 inWriter.write(inPoint.getWaypointName().trim());
347 inWriter.write("</name>\n");
348 // timestamp if available (point might have timestamp and then be turned into a waypoint)
349 if (inPoint.hasTimestamp() && inTimestamps)
351 inWriter.write("\t\t<time>");
352 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
353 inWriter.write("</time>\n");
355 // TODO: Include waypt type in Gpx
356 inWriter.write("\t</wpt>\n");
361 * Export the specified trackpoint into the file
362 * @param inPoint trackpoint to export
363 * @param inWriter writer object
364 * @param inTimestamps true to export timestamps too
366 private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
369 inWriter.write("\t\t<trkpt lat=\"");
370 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
371 inWriter.write("\" lon=\"");
372 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
373 inWriter.write("\">");
375 if (inPoint.hasAltitude())
377 inWriter.write("<ele>");
378 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
379 inWriter.write("</ele>");
381 // timestamp if available (and selected)
382 if (inPoint.hasTimestamp() && inTimestamps)
384 inWriter.write("<time>");
385 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
386 inWriter.write("</time>");
388 inWriter.write("</trkpt>\n");