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 PointTypeSelector _pointTypeSelector = 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.0";
56 /** this program name */
57 private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
62 * @param inApp app object
64 public GpxExporter(App inApp)
67 _track = inApp.getTrackInfo().getTrack();
71 public String getNameKey() {
72 return "function.exportgpx";
76 * Show the dialog to select options and export file
83 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
84 _dialog.setLocationRelativeTo(_parentFrame);
85 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
86 _dialog.getContentPane().add(makeDialogComponents());
89 _pointTypeSelector.init(_app.getTrackInfo());
90 _dialog.setVisible(true);
95 * Create dialog components
96 * @return Panel containing all gui elements in dialog
98 private Component makeDialogComponents()
100 JPanel dialogPanel = new JPanel();
101 dialogPanel.setLayout(new BorderLayout());
102 JPanel mainPanel = new JPanel();
103 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
104 // Make a central panel with the text boxes
105 JPanel descPanel = new JPanel();
106 descPanel.setLayout(new GridLayout(2, 2));
107 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
108 _nameField = new JTextField(10);
109 descPanel.add(_nameField);
110 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
111 _descriptionField = new JTextField(10);
112 descPanel.add(_descriptionField);
113 mainPanel.add(descPanel);
114 // point type selection (track points, waypoints, photo points)
115 _pointTypeSelector = new PointTypeSelector();
116 mainPanel.add(_pointTypeSelector);
117 // checkbox for timestamps
118 _timestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.includetimestamps"));
119 _timestampsCheckbox.setSelected(true);
120 mainPanel.add(_timestampsCheckbox);
121 dialogPanel.add(mainPanel, BorderLayout.CENTER);
123 // button panel at bottom
124 JPanel buttonPanel = new JPanel();
125 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
126 JButton okButton = new JButton(I18nManager.getText("button.ok"));
127 ActionListener okListener = new ActionListener() {
128 public void actionPerformed(ActionEvent e)
133 okButton.addActionListener(okListener);
134 _descriptionField.addActionListener(okListener);
135 buttonPanel.add(okButton);
136 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
137 cancelButton.addActionListener(new ActionListener() {
138 public void actionPerformed(ActionEvent e)
143 buttonPanel.add(cancelButton);
144 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
145 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
151 * Start the export process based on the input parameters
153 private void startExport()
155 // OK pressed, so check selections
156 if (!_pointTypeSelector.getAnythingSelected()) {
159 // Choose output file
160 if (_fileChooser == null)
162 _fileChooser = new JFileChooser();
163 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
164 _fileChooser.setFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
165 _fileChooser.setAcceptAllFileFilterUsed(false);
166 // start from directory in config which should be set
167 String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
168 if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
170 // Allow choose again if an existing file is selected
171 boolean chooseAgain = false;
175 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
177 // OK pressed and file chosen
178 File file = _fileChooser.getSelectedFile();
179 // Check file extension
180 if (!file.getName().toLowerCase().endsWith(".gpx"))
182 file = new File(file.getAbsolutePath() + ".gpx");
184 // Check if file exists and if necessary prompt for overwrite
185 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
186 if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
187 I18nManager.getText("dialog.save.overwrite.text"),
188 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
189 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
190 == JOptionPane.YES_OPTION)
192 // New file or overwrite confirmed, so initiate export in separate thread
194 new Thread(this).start();
201 } while (chooseAgain);
206 * Run method for controlling separate thread for exporting
210 OutputStreamWriter writer = null;
213 // normal writing to file
214 writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
215 boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
216 _pointTypeSelector.getPhotopointsSelected(), _timestampsCheckbox.isSelected()};
218 final int numPoints = exportData(writer, _track, _nameField.getText(),
219 _descriptionField.getText(), saveFlags);
223 // Store directory in config for later
224 Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
226 UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
227 + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
228 + " " + _exportFile.getAbsolutePath());
229 // export successful so need to close dialog and return
233 catch (IOException ioe)
235 // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
237 if (writer != null) writer.close();
239 catch (IOException ioe2) {}
240 JOptionPane.showMessageDialog(_parentFrame,
241 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
242 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
244 // if not returned already, export failed so need to recall the file selection
250 * Export the information to the given writer
251 * @param inWriter writer object
252 * @param inTrack track object containing data
253 * @param inName name of track (optional)
254 * @param inDesc description of track (optional)
255 * @param inSaveFlags array of booleans to export tracks, waypoints, photos, timestamps
256 * @return number of points written
257 * @throws IOException if io errors occur on write
259 public static int exportData(OutputStreamWriter inWriter, Track inTrack, String inName,
260 String inDesc, boolean[] inSaveFlags) throws IOException
262 inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
263 inWriter.write(GPX_VERSION_NUMBER);
264 inWriter.write("\" creator=\"");
265 inWriter.write(GPX_CREATOR);
266 inWriter.write("\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
267 + " xmlns=\"http://www.topografix.com/GPX/1/0\""
268 + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
270 String trackName = "PruneTrack";
271 if (inName != null && !inName.equals(""))
274 inWriter.write("\t<name>");
275 inWriter.write(trackName);
276 inWriter.write("</name>\n");
279 inWriter.write("\t<desc>");
280 if (inDesc != null && !inDesc.equals("")) {
281 inWriter.write(inDesc);
285 inWriter.write("Export from Prune");
287 inWriter.write("</desc>\n");
290 DataPoint point = null;
291 boolean hasTrackpoints = false;
292 final boolean exportTrackpoints = inSaveFlags[0];
293 final boolean exportWaypoints = inSaveFlags[1];
294 final boolean exportPhotos = inSaveFlags[2];
295 final boolean exportTimestamps = inSaveFlags[3];
296 // Loop over waypoints
297 final int numPoints = inTrack.getNumPoints();
299 for (i=0; i<numPoints; i++)
301 point = inTrack.getPoint(i);
302 // Make a wpt element for each waypoint
303 if (point.isWaypoint())
305 if (exportWaypoints) {
306 exportWaypoint(point, inWriter, exportTimestamps);
312 hasTrackpoints = true;
315 // Output the track, if there is one
318 boolean firstPoint = true;
319 inWriter.write("\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n");
320 // Loop over track points
321 for (i=0; i<numPoints; i++)
323 point = inTrack.getPoint(i);
324 // restart track segment if necessary
325 if (point.getSegmentStart() && !firstPoint) {
326 inWriter.write("\t</trkseg>\n\t<trkseg>\n");
328 if (!point.isWaypoint())
330 if ((point.getPhoto()==null && exportTrackpoints) || (point.getPhoto()!=null && exportPhotos))
333 exportTrackpoint(point, inWriter, exportTimestamps);
339 inWriter.write("\t</trkseg></trk>\n");
341 inWriter.write("</gpx>\n");
346 * Export the specified waypoint into the file
347 * @param inPoint waypoint to export
348 * @param inWriter writer object
349 * @param inTimestamps true to export timestamps too
350 * @throws IOException on write failure
352 private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
355 inWriter.write("\t<wpt lat=\"");
356 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
357 inWriter.write("\" lon=\"");
358 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
359 inWriter.write("\">\n");
360 // altitude if available
361 if (inPoint.hasAltitude())
363 inWriter.write("\t\t<ele>");
364 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
365 inWriter.write("</ele>\n");
367 // write waypoint name after elevation
368 inWriter.write("\t\t<name>");
369 inWriter.write(inPoint.getWaypointName().trim());
370 inWriter.write("</name>\n");
371 // timestamp if available (point might have timestamp and then be turned into a waypoint)
372 if (inPoint.hasTimestamp() && inTimestamps)
374 inWriter.write("\t\t<time>");
375 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
376 inWriter.write("</time>\n");
378 // TODO: Include waypt type in Gpx
379 inWriter.write("\t</wpt>\n");
384 * Export the specified trackpoint into the file
385 * @param inPoint trackpoint to export
386 * @param inWriter writer object
387 * @param inTimestamps true to export timestamps too
389 private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
392 inWriter.write("\t\t<trkpt lat=\"");
393 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
394 inWriter.write("\" lon=\"");
395 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
396 inWriter.write("\">");
398 if (inPoint.hasAltitude())
400 inWriter.write("<ele>");
401 inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
402 inWriter.write("</ele>");
404 // timestamp if available (and selected)
405 if (inPoint.hasTimestamp() && inTimestamps)
407 inWriter.write("<time>");
408 inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
409 inWriter.write("</time>");
411 inWriter.write("</trkpt>\n");