]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/save/GpxExporter.java
Version 8, September 2009
[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.JLabel;
22 import javax.swing.JOptionPane;
23 import javax.swing.JPanel;
24 import javax.swing.JTextField;
25
26 import tim.prune.App;
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;
38
39 /**
40  * Class to export track information
41  * into a specified Gpx file
42  */
43 public class GpxExporter extends GenericFunction implements Runnable
44 {
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;
53
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";
58
59
60         /**
61          * Constructor
62          * @param inApp app object
63          */
64         public GpxExporter(App inApp)
65         {
66                 super(inApp);
67                 _track = inApp.getTrackInfo().getTrack();
68         }
69
70         /** Get name key */
71         public String getNameKey() {
72                 return "function.exportgpx";
73         }
74
75         /**
76          * Show the dialog to select options and export file
77          */
78         public void begin()
79         {
80                 // Make dialog window
81                 if (_dialog == null)
82                 {
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());
87                         _dialog.pack();
88                 }
89                 _pointTypeSelector.init(_app.getTrackInfo());
90                 _dialog.setVisible(true);
91         }
92
93
94         /**
95          * Create dialog components
96          * @return Panel containing all gui elements in dialog
97          */
98         private Component makeDialogComponents()
99         {
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);
122
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)
129                         {
130                                 startExport();
131                         }
132                 };
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)
139                         {
140                                 _dialog.dispose();
141                         }
142                 });
143                 buttonPanel.add(cancelButton);
144                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
145                 dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
146                 return dialogPanel;
147         }
148
149
150         /**
151          * Start the export process based on the input parameters
152          */
153         private void startExport()
154         {
155                 // OK pressed, so check selections
156                 if (!_pointTypeSelector.getAnythingSelected()) {
157                         return;
158                 }
159                 // Choose output file
160                 if (_fileChooser == null)
161                 {
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));}
169                 }
170                 // Allow choose again if an existing file is selected
171                 boolean chooseAgain = false;
172                 do
173                 {
174                         chooseAgain = false;
175                         if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
176                         {
177                                 // OK pressed and file chosen
178                                 File file = _fileChooser.getSelectedFile();
179                                 // Check file extension
180                                 if (!file.getName().toLowerCase().endsWith(".gpx"))
181                                 {
182                                         file = new File(file.getAbsolutePath() + ".gpx");
183                                 }
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)
191                                 {
192                                         // New file or overwrite confirmed, so initiate export in separate thread
193                                         _exportFile = file;
194                                         new Thread(this).start();
195                                 }
196                                 else
197                                 {
198                                         chooseAgain = true;
199                                 }
200                         }
201                 } while (chooseAgain);
202         }
203
204
205         /**
206          * Run method for controlling separate thread for exporting
207          */
208         public void run()
209         {
210                 OutputStreamWriter writer = null;
211                 try
212                 {
213                         // normal writing to file
214                         writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
215                         boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
216                                 _pointTypeSelector.getPhotopointsSelected(), _timestampsCheckbox.isSelected()};
217                         // write file
218                         final int numPoints = exportData(writer, _track, _nameField.getText(),
219                                 _descriptionField.getText(), saveFlags);
220
221                         // close file
222                         writer.close();
223                         // Store directory in config for later
224                         Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
225                         // Show confirmation
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
230                         _dialog.dispose();
231                         return;
232                 }
233                 catch (IOException ioe)
234                 {
235                         // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
236                         try {
237                                 if (writer != null) writer.close();
238                         }
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);
243                 }
244                 // if not returned already, export failed so need to recall the file selection
245                 startExport();
246         }
247
248
249         /**
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
258          */
259         public static int exportData(OutputStreamWriter inWriter, Track inTrack, String inName,
260                 String inDesc, boolean[] inSaveFlags) throws IOException
261         {
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");
269                 // Name field
270                 String trackName = "PruneTrack";
271                 if (inName != null && !inName.equals(""))
272                 {
273                         trackName = inName;
274                         inWriter.write("\t<name>");
275                         inWriter.write(trackName);
276                         inWriter.write("</name>\n");
277                 }
278                 // Description field
279                 inWriter.write("\t<desc>");
280                 if (inDesc != null && !inDesc.equals("")) {
281                         inWriter.write(inDesc);
282                 }
283                 else
284                 {
285                         inWriter.write("Export from Prune");
286                 }
287                 inWriter.write("</desc>\n");
288
289                 int i = 0;
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();
298                 int numSaved = 0;
299                 for (i=0; i<numPoints; i++)
300                 {
301                         point = inTrack.getPoint(i);
302                         // Make a wpt element for each waypoint
303                         if (point.isWaypoint())
304                         {
305                                 if (exportWaypoints) {
306                                         exportWaypoint(point, inWriter, exportTimestamps);
307                                         numSaved++;
308                                 }
309                         }
310                         else
311                         {
312                                 hasTrackpoints = true;
313                         }
314                 }
315                 // Output the track, if there is one
316                 if (hasTrackpoints)
317                 {
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++)
322                         {
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");
327                                 }
328                                 if (!point.isWaypoint())
329                                 {
330                                         if ((point.getPhoto()==null && exportTrackpoints) || (point.getPhoto()!=null && exportPhotos))
331                                         {
332                                                 // export the point
333                                                 exportTrackpoint(point, inWriter, exportTimestamps);
334                                                 numSaved++;
335                                                 firstPoint = false;
336                                         }
337                                 }
338                         }
339                         inWriter.write("\t</trkseg></trk>\n");
340                 }
341                 inWriter.write("</gpx>\n");
342                 return numSaved;
343         }
344
345         /**
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
351          */
352         private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
353                 throws IOException
354         {
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())
362                 {
363                         inWriter.write("\t\t<ele>");
364                         inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
365                         inWriter.write("</ele>\n");
366                 }
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)
373                 {
374                         inWriter.write("\t\t<time>");
375                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
376                         inWriter.write("</time>\n");
377                 }
378                 // TODO: Include waypt type in Gpx
379                 inWriter.write("\t</wpt>\n");
380         }
381
382
383         /**
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
388          */
389         private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
390                 throws IOException
391         {
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("\">");
397                 // altitude
398                 if (inPoint.hasAltitude())
399                 {
400                         inWriter.write("<ele>");
401                         inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
402                         inWriter.write("</ele>");
403                 }
404                 // timestamp if available (and selected)
405                 if (inPoint.hasTimestamp() && inTimestamps)
406                 {
407                         inWriter.write("<time>");
408                         inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
409                         inWriter.write("</time>");
410                 }
411                 inWriter.write("</trkpt>\n");
412         }
413 }