]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/save/GpxExporter.java
Version 5, May 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.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;
25
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;
35
36 /**
37  * Class to export track information
38  * into a specified Gpx file
39  */
40 public class GpxExporter implements Runnable
41 {
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;
49
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";
54
55
56         /**
57          * Constructor giving frame and track
58          * @param inParentFrame parent frame
59          * @param inTrackInfo track info object to save
60          */
61         public GpxExporter(JFrame inParentFrame, TrackInfo inTrackInfo)
62         {
63                 _parentFrame = inParentFrame;
64                 _track = inTrackInfo.getTrack();
65         }
66
67
68         /**
69          * Show the dialog to select options and export file
70          */
71         public void showDialog()
72         {
73                 // Make dialog window
74                 if (_dialog == null)
75                 {
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());
80                         _dialog.pack();
81                 }
82                 _dialog.show();
83         }
84
85
86         /**
87          * Create dialog components
88          * @return Panel containing all gui elements in dialog
89          */
90         private Component makeDialogComponents()
91         {
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);
107
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)
114                         {
115                                 startExport();
116                         }
117                 };
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)
124                         {
125                                 _dialog.dispose();
126                         }
127                 });
128                 buttonPanel.add(cancelButton);
129                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
130                 return dialogPanel;
131         }
132
133
134         /**
135          * Start the export process based on the input parameters
136          */
137         private void startExport()
138         {
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)
145                         {
146                                 return (f != null && (f.isDirectory()
147                                         || f.getName().toLowerCase().endsWith(".gpx")));
148                         }
149                         public String getDescription()
150                         {
151                                 return I18nManager.getText("dialog.exportgpx.filetype");
152                         }
153                 });
154                 _fileChooser.setAcceptAllFileFilterUsed(false);
155                 // Allow choose again if an existing file is selected
156                 boolean chooseAgain = false;
157                 do
158                 {
159                         chooseAgain = false;
160                         if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
161                         {
162                                 // OK pressed and file chosen
163                                 File file = _fileChooser.getSelectedFile();
164                                 // Check file extension
165                                 if (!file.getName().toLowerCase().endsWith(".gpx"))
166                                 {
167                                         file = new File(file.getAbsolutePath() + ".gpx");
168                                 }
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)
176                                 {
177                                         // New file or overwrite confirmed, so initiate export in separate thread
178                                         _exportFile = file;
179                                         new Thread(this).start();
180                                 }
181                                 else
182                                 {
183                                         chooseAgain = true;
184                                 }
185                         }
186                 } while (chooseAgain);
187         }
188
189
190         /**
191          * Run method for controlling separate thread for exporting
192          */
193         public void run()
194         {
195                 OutputStreamWriter writer = null;
196                 try
197                 {
198                         // normal writing to file
199                         writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
200                         // write file
201                         int numPoints = exportData(writer);
202
203                         // close file
204                         writer.close();
205                         // Show confirmation
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
210                         _dialog.dispose();
211                         return;
212                 }
213                 catch (IOException ioe)
214                 {
215                         // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
216                         try {
217                                 if (writer != null) writer.close();
218                         }
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);
223                 }
224                 // if not returned already, export failed so need to recall the file selection
225                 startExport();
226         }
227
228
229         /**
230          * Export the information to the given writer
231          * @param inWriter writer object
232          * @return number of points written
233          */
234         private int exportData(OutputStreamWriter inWriter) throws IOException
235         {
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");
241                 // Name field
242                 if (_nameField != null && _nameField.getText() != null && !_nameField.getText().equals(""))
243                 {
244                         inWriter.write("\t<name>");
245                         inWriter.write(_nameField.getText());
246                         inWriter.write("</name>\n");
247                 }
248                 // Description field
249                 inWriter.write("\t<desc>");
250                 if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
251                 {
252                         inWriter.write(_descriptionField.getText());
253                 }
254                 else
255                 {
256                         inWriter.write("Export from Prune");
257                 }
258                 inWriter.write("</desc>\n");
259
260                 int i = 0;
261                 DataPoint point = null;
262                 boolean hasTrackpoints = false;
263                 // Loop over waypoints
264                 int numPoints = _track.getNumPoints();
265                 for (i=0; i<numPoints; i++)
266                 {
267                         point = _track.getPoint(i);
268                         // Make a wpt element for each waypoint
269                         if (point.isWaypoint())
270                         {
271                                 exportWaypoint(point, inWriter);
272                         }
273                         else
274                         {
275                                 hasTrackpoints = true;
276                         }
277                 }
278                 // Output the track, if there is one
279                 if (hasTrackpoints)
280                 {
281                         boolean firstPoint = true;
282                         inWriter.write("\t<trk><trkseg>\n");
283                         // Loop over track points
284                         for (i=0; i<numPoints; i++)
285                         {
286                                 point = _track.getPoint(i);
287                                 if (point.getSegmentStart() && !firstPoint) {
288                                         inWriter.write("\t</trkseg>\n\t<trkseg>\n");
289                                 }
290                                 if (!point.isWaypoint()) {
291                                         // restart track segment if necessary
292                                         // export the track point
293                                         exportTrackpoint(point, inWriter);
294                                         firstPoint = false;
295                                 }
296                         }
297                         inWriter.write("\t</trkseg></trk>\n");
298                 }
299                 inWriter.write("</gpx>\n");
300                 return numPoints;
301         }
302
303
304         /**
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
309          */
310         private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
311         {
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())
322                 {
323                         inWriter.write("\t\t<ele>");
324                         inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
325                         inWriter.write("</ele>\n");
326                 }
327                 // timestamp if available (point might have altitude and then be turned into a waypoint)
328                 if (inPoint.hasTimestamp())
329                 {
330                         inWriter.write("\t\t<time>");
331                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
332                         inWriter.write("</time>\n");
333                 }
334                 // TODO: Include waypt type in Gpx
335                 inWriter.write("\t</wpt>\n");
336         }
337
338
339         /**
340          * Export the specified trackpoint into the file
341          * @param inPoint trackpoint to export
342          * @param inWriter writer object
343          */
344         private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
345         {
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("\">");
351                 // altitude
352                 if (inPoint.hasAltitude())
353                 {
354                         inWriter.write("<ele>");
355                         inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
356                         inWriter.write("</ele>");
357                 }
358                 // timestamp if available
359                 if (inPoint.hasTimestamp())
360                 {
361                         inWriter.write("<time>");
362                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
363                         inWriter.write("</time>");
364                 }
365                 inWriter.write("</trkpt>\n");
366         }
367 }