]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/save/GpxExporter.java
Version 6, October 2008
[GpsPrune.git] / tim / prune / save / GpxExporter.java
1 package tim.prune.save;
2
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;
9 import java.io.File;
10 import java.io.FileOutputStream;
11 import java.io.IOException;
12 import java.io.OutputStreamWriter;
13 import java.io.Writer;
14
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;
26
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;
38
39 /**
40  * Class to export track information
41  * into a specified Gpx file
42  */
43 public class GpxExporter implements Runnable
44 {
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;
53
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";
58
59
60         /**
61          * Constructor giving frame and track
62          * @param inParentFrame parent frame
63          * @param inTrackInfo track info object to save
64          */
65         public GpxExporter(JFrame inParentFrame, TrackInfo inTrackInfo)
66         {
67                 _parentFrame = inParentFrame;
68                 _track = inTrackInfo.getTrack();
69         }
70
71
72         /**
73          * Show the dialog to select options and export file
74          */
75         public void showDialog()
76         {
77                 // Make dialog window
78                 if (_dialog == null)
79                 {
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());
84                         _dialog.pack();
85                 }
86                 _dialog.show();
87         }
88
89
90         /**
91          * Create dialog components
92          * @return Panel containing all gui elements in dialog
93          */
94         private Component makeDialogComponents()
95         {
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);
115
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)
122                         {
123                                 startExport();
124                         }
125                 };
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)
132                         {
133                                 _dialog.dispose();
134                         }
135                 });
136                 buttonPanel.add(cancelButton);
137                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
138                 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
139                 return dialogPanel;
140         }
141
142
143         /**
144          * Start the export process based on the input parameters
145          */
146         private void startExport()
147         {
148                 // OK pressed, so choose output file
149                 if (_fileChooser == null)
150                 {
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);}
158                 }
159                 // Allow choose again if an existing file is selected
160                 boolean chooseAgain = false;
161                 do
162                 {
163                         chooseAgain = false;
164                         if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
165                         {
166                                 // OK pressed and file chosen
167                                 File file = _fileChooser.getSelectedFile();
168                                 // Check file extension
169                                 if (!file.getName().toLowerCase().endsWith(".gpx"))
170                                 {
171                                         file = new File(file.getAbsolutePath() + ".gpx");
172                                 }
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)
180                                 {
181                                         // New file or overwrite confirmed, so initiate export in separate thread
182                                         _exportFile = file;
183                                         new Thread(this).start();
184                                 }
185                                 else
186                                 {
187                                         chooseAgain = true;
188                                 }
189                         }
190                 } while (chooseAgain);
191         }
192
193
194         /**
195          * Run method for controlling separate thread for exporting
196          */
197         public void run()
198         {
199                 OutputStreamWriter writer = null;
200                 try
201                 {
202                         // normal writing to file
203                         writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
204                         // write file
205                         int numPoints = exportData(writer);
206
207                         // close file
208                         writer.close();
209                         // Store directory in config for later
210                         Config.setWorkingDirectory(_exportFile.getParentFile());
211                         // Show confirmation
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
216                         _dialog.dispose();
217                         return;
218                 }
219                 catch (IOException ioe)
220                 {
221                         // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
222                         try {
223                                 if (writer != null) writer.close();
224                         }
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);
229                 }
230                 // if not returned already, export failed so need to recall the file selection
231                 startExport();
232         }
233
234
235         /**
236          * Export the information to the given writer
237          * @param inWriter writer object
238          * @return number of points written
239          */
240         private int exportData(OutputStreamWriter inWriter) throws IOException
241         {
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");
249                 // Name field
250                 if (_nameField != null && _nameField.getText() != null && !_nameField.getText().equals(""))
251                 {
252                         inWriter.write("\t<name>");
253                         inWriter.write(_nameField.getText());
254                         inWriter.write("</name>\n");
255                 }
256                 // Description field
257                 inWriter.write("\t<desc>");
258                 if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
259                 {
260                         inWriter.write(_descriptionField.getText());
261                 }
262                 else
263                 {
264                         inWriter.write("Export from Prune");
265                 }
266                 inWriter.write("</desc>\n");
267
268                 int i = 0;
269                 DataPoint point = null;
270                 boolean hasTrackpoints = false;
271                 // Loop over waypoints
272                 int numPoints = _track.getNumPoints();
273                 for (i=0; i<numPoints; i++)
274                 {
275                         point = _track.getPoint(i);
276                         // Make a wpt element for each waypoint
277                         if (point.isWaypoint())
278                         {
279                                 exportWaypoint(point, inWriter);
280                         }
281                         else
282                         {
283                                 hasTrackpoints = true;
284                         }
285                 }
286                 // Output the track, if there is one
287                 if (hasTrackpoints)
288                 {
289                         boolean firstPoint = true;
290                         inWriter.write("\t<trk><trkseg>\n");
291                         // Loop over track points
292                         for (i=0; i<numPoints; i++)
293                         {
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");
298                                 }
299                                 if (!point.isWaypoint()) {
300                                         // export the track point
301                                         exportTrackpoint(point, inWriter);
302                                         firstPoint = false;
303                                 }
304                         }
305                         inWriter.write("\t</trkseg></trk>\n");
306                 }
307                 inWriter.write("</gpx>\n");
308                 return numPoints;
309         }
310
311
312         /**
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
317          */
318         private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
319         {
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())
330                 {
331                         inWriter.write("\t\t<ele>");
332                         inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.FORMAT_METRES));
333                         inWriter.write("</ele>\n");
334                 }
335                 // timestamp if available (point might have altitude and then be turned into a waypoint)
336                 if (inPoint.hasTimestamp() && _timestampsCheckbox.isSelected())
337                 {
338                         inWriter.write("\t\t<time>");
339                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
340                         inWriter.write("</time>\n");
341                 }
342                 // TODO: Include waypt type in Gpx
343                 inWriter.write("\t</wpt>\n");
344         }
345
346
347         /**
348          * Export the specified trackpoint into the file
349          * @param inPoint trackpoint to export
350          * @param inWriter writer object
351          */
352         private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
353         {
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("\">");
359                 // altitude
360                 if (inPoint.hasAltitude())
361                 {
362                         inWriter.write("<ele>");
363                         inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.FORMAT_METRES));
364                         inWriter.write("</ele>");
365                 }
366                 // timestamp if available (and selected)
367                 if (inPoint.hasTimestamp() && _timestampsCheckbox.isSelected())
368                 {
369                         inWriter.write("<time>");
370                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
371                         inWriter.write("</time>");
372                 }
373                 inWriter.write("</trkpt>\n");
374         }
375 }