]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/save/GpxExporter.java
Version 4, January 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.data.Altitude;
29 import tim.prune.data.Coordinate;
30 import tim.prune.data.DataPoint;
31 import tim.prune.data.Timestamp;
32 import tim.prune.data.Track;
33 import tim.prune.data.TrackInfo;
34
35 /**
36  * Class to export track information
37  * into a specified Gpx file
38  */
39 public class GpxExporter implements Runnable
40 {
41         private JFrame _parentFrame = null;
42         private Track _track = null;
43         private JDialog _dialog = null;
44         private JTextField _nameField = null;
45         private JTextField _descriptionField = null;
46         private JFileChooser _fileChooser = null;
47         private File _exportFile = null;
48
49         /** version number of Gpx */
50         private static final String GPX_VERSION_NUMBER = "1.1";
51         /** this program name */
52         private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
53
54
55         /**
56          * Constructor giving frame and track
57          * @param inParentFrame parent frame
58          * @param inTrackInfo track info object to save
59          */
60         public GpxExporter(JFrame inParentFrame, TrackInfo inTrackInfo)
61         {
62                 _parentFrame = inParentFrame;
63                 _track = inTrackInfo.getTrack();
64         }
65
66
67         /**
68          * Show the dialog to select options and export file
69          */
70         public void showDialog()
71         {
72                 // Make dialog window
73                 if (_dialog == null)
74                 {
75                         _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.exportgpx.title"), true);
76                         _dialog.setLocationRelativeTo(_parentFrame);
77                         _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
78                         _dialog.getContentPane().add(makeDialogComponents());
79                         _dialog.pack();
80                 }
81                 _dialog.show();
82         }
83
84
85         /**
86          * Create dialog components
87          * @return Panel containing all gui elements in dialog
88          */
89         private Component makeDialogComponents()
90         {
91                 JPanel dialogPanel = new JPanel();
92                 dialogPanel.setLayout(new BorderLayout());
93                 JPanel mainPanel = new JPanel();
94                 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
95                 // Make a central panel with the text boxes
96                 JPanel descPanel = new JPanel();
97                 descPanel.setLayout(new GridLayout(2, 2));
98                 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
99                 _nameField = new JTextField(10);
100                 descPanel.add(_nameField);
101                 descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.desc")));
102                 _descriptionField = new JTextField(10);
103                 descPanel.add(_descriptionField);
104                 mainPanel.add(descPanel);
105                 dialogPanel.add(mainPanel, BorderLayout.CENTER);
106
107                 // button panel at bottom
108                 JPanel buttonPanel = new JPanel();
109                 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
110                 JButton okButton = new JButton(I18nManager.getText("button.ok"));
111                 ActionListener okListener = new ActionListener() {
112                         public void actionPerformed(ActionEvent e)
113                         {
114                                 startExport();
115                         }
116                 };
117                 okButton.addActionListener(okListener);
118                 _descriptionField.addActionListener(okListener);
119                 buttonPanel.add(okButton);
120                 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
121                 cancelButton.addActionListener(new ActionListener() {
122                         public void actionPerformed(ActionEvent e)
123                         {
124                                 _dialog.dispose();
125                         }
126                 });
127                 buttonPanel.add(cancelButton);
128                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
129                 return dialogPanel;
130         }
131
132
133         /**
134          * Start the export process based on the input parameters
135          */
136         private void startExport()
137         {
138                 // OK pressed, so choose output file
139                 if (_fileChooser == null)
140                         {_fileChooser = new JFileChooser();}
141                 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
142                 _fileChooser.setFileFilter(new FileFilter() {
143                         public boolean accept(File f)
144                         {
145                                 return (f != null && (f.isDirectory()
146                                         || f.getName().toLowerCase().endsWith(".gpx")));
147                         }
148                         public String getDescription()
149                         {
150                                 return I18nManager.getText("dialog.exportgpx.filetype");
151                         }
152                 });
153                 _fileChooser.setAcceptAllFileFilterUsed(false);
154                 // Allow choose again if an existing file is selected
155                 boolean chooseAgain = false;
156                 do
157                 {
158                         chooseAgain = false;
159                         if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
160                         {
161                                 // OK pressed and file chosen
162                                 File file = _fileChooser.getSelectedFile();
163                                 // Check file extension
164                                 if (!file.getName().toLowerCase().endsWith(".gpx"))
165                                 {
166                                         file = new File(file.getAbsolutePath() + ".gpx");
167                                 }
168                                 // Check if file exists and if necessary prompt for overwrite
169                                 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
170                                 if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
171                                                 I18nManager.getText("dialog.save.overwrite.text"),
172                                                 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
173                                                 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
174                                         == JOptionPane.YES_OPTION)
175                                 {
176                                         // New file or overwrite confirmed, so initiate export in separate thread
177                                         _exportFile = file;
178                                         new Thread(this).start();
179                                 }
180                                 else
181                                 {
182                                         chooseAgain = true;
183                                 }
184                         }
185                 } while (chooseAgain);
186         }
187
188
189         /**
190          * Run method for controlling separate thread for exporting
191          */
192         public void run()
193         {
194                 OutputStreamWriter writer = null;
195                 try
196                 {
197                         // normal writing to file
198                         writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
199                         // write file
200                         int numPoints = exportData(writer);
201
202                         // close file
203                         writer.close();
204                         JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.ok1")
205                                  + " " + numPoints + " " + I18nManager.getText("dialog.save.ok2")
206                                  + " " + _exportFile.getAbsolutePath(),
207                                 I18nManager.getText("dialog.save.oktitle"), JOptionPane.INFORMATION_MESSAGE);
208                         // export successful so need to close dialog and return
209                         _dialog.dispose();
210                         return;
211                 }
212                 catch (IOException ioe)
213                 {
214                         // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
215                         try {
216                                 if (writer != null) writer.close();
217                         }
218                         catch (IOException ioe2) {}
219                         JOptionPane.showMessageDialog(_parentFrame,
220                                 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
221                                 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
222                 }
223                 // if not returned already, export failed so need to recall the file selection
224                 startExport();
225         }
226
227
228         /**
229          * Export the information to the given writer
230          * @param inWriter writer object
231          * @return number of points written
232          */
233         private int exportData(OutputStreamWriter inWriter) throws IOException
234         {
235                 inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
236                 inWriter.write(GPX_VERSION_NUMBER);
237                 inWriter.write("\" creator=\"");
238                 inWriter.write(GPX_CREATOR);
239                 inWriter.write("\">\n");
240                 // Name field
241                 if (_nameField != null && _nameField.getText() != null && !_nameField.getText().equals(""))
242                 {
243                         inWriter.write("\t<name>");
244                         inWriter.write(_nameField.getText());
245                         inWriter.write("</name>\n");
246                 }
247                 // Description field
248                 inWriter.write("\t<desc>");
249                 if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
250                 {
251                         inWriter.write(_descriptionField.getText());
252                 }
253                 else
254                 {
255                         inWriter.write("Export from Prune");
256                 }
257                 inWriter.write("</desc>\n");
258
259                 int i = 0;
260                 DataPoint point = null;
261                 boolean hasTrackpoints = false;
262                 // Loop over waypoints
263                 int numPoints = _track.getNumPoints();
264                 for (i=0; i<numPoints; i++)
265                 {
266                         point = _track.getPoint(i);
267                         // Make a blob for each waypoint
268                         if (point.isWaypoint())
269                         {
270                                 exportWaypoint(point, inWriter);
271                         }
272                         else
273                         {
274                                 hasTrackpoints = true;
275                         }
276                 }
277                 // Make a line for the track, if there is one
278                 // TODO: Look at segments of track, and split into separate track segments in Gpx if necessary
279                 if (hasTrackpoints)
280                 {
281                         inWriter.write("\t<trk><trkseg>\n");
282                         // Loop over track points
283                         for (i=0; i<numPoints; i++)
284                         {
285                                 point = _track.getPoint(i);
286                                 if (!point.isWaypoint())
287                                 {
288                                         exportTrackpoint(point, inWriter);
289                                 }
290                         }
291                         inWriter.write("\t</trkseg></trk>\n");
292                 }
293                 inWriter.write("</gpx>\n");
294                 return numPoints;
295         }
296
297
298         /**
299          * Export the specified waypoint into the file
300          * @param inPoint waypoint to export
301          * @param inWriter writer object
302          * @throws IOException on write failure
303          */
304         private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
305         {
306                 inWriter.write("\t<wpt lat=\"");
307                 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
308                 inWriter.write("\" lon=\"");
309                 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
310                 inWriter.write("\">\n");
311                 inWriter.write("\t\t<name>");
312                 inWriter.write(inPoint.getWaypointName().trim());
313                 inWriter.write("</name>\n");
314                 // altitude if available
315                 if (inPoint.hasAltitude())
316                 {
317                         inWriter.write("\t\t<ele>");
318                         inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
319                         inWriter.write("</ele>\n");
320                 }
321                 // timestamp if available (point might have altitude and then be turned into a waypoint)
322                 if (inPoint.hasTimestamp())
323                 {
324                         inWriter.write("\t\t<time>");
325                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
326                         inWriter.write("</time>\n");
327                 }
328                 // TODO: Include waypt type in Gpx
329                 inWriter.write("\t</wpt>\n");
330         }
331
332
333         /**
334          * Export the specified trackpoint into the file
335          * @param inPoint trackpoint to export
336          * @param inWriter writer object
337          */
338         private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
339         {
340                 inWriter.write("\t\t<trkpt lat=\"");
341                 inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
342                 inWriter.write("\" lon=\"");
343                 inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
344                 inWriter.write("\">");
345                 // altitude
346                 if (inPoint.hasAltitude())
347                 {
348                         inWriter.write("<ele>");
349                         inWriter.write("" + inPoint.getAltitude().getValue(Altitude.FORMAT_METRES));
350                         inWriter.write("</ele>");
351                 }
352                 // timestamp if available
353                 if (inPoint.hasTimestamp())
354                 {
355                         inWriter.write("<time>");
356                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
357                         inWriter.write("</time>");
358                 }
359                 inWriter.write("</trkpt>\n");
360         }
361 }