]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/save/GpxExporter.java
Version 19, May 2018
[GpsPrune.git] / tim / prune / save / GpxExporter.java
index a4ac1a7de2e72ade39734e7f0099fe6e14f5faca..39b6a362589ce72150cdb4e8a56d85609618e92a 100644 (file)
@@ -11,7 +11,6 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.nio.charset.Charset;
 
 import javax.swing.BorderFactory;
 import javax.swing.Box;
@@ -67,7 +66,6 @@ public class GpxExporter extends GenericFunction implements Runnable
        private JPanel _encodingsPanel = null;
        private JRadioButton _useSystemRadio = null, _forceUtf8Radio = null;
        private File _exportFile = null;
-       private static String _systemEncoding = null;
 
        /** this program name */
        private static final String GPX_CREATOR = "GpsPrune v" + GpsPrune.VERSION_NUMBER + " activityworkshop.net";
@@ -99,15 +97,16 @@ public class GpxExporter extends GenericFunction implements Runnable
                        _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
                        _dialog.setLocationRelativeTo(_parentFrame);
                        _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-                       _systemEncoding = getSystemEncoding();
                        _dialog.getContentPane().add(makeDialogComponents());
                        _dialog.pack();
                }
                _pointTypeSelector.init(_app.getTrackInfo());
-               _encodingsPanel.setVisible(!isSystemUtf8());
-               if (!isSystemUtf8()) {
+               _encodingsPanel.setVisible(!XmlUtils.isSystemUtf8());
+               if (!XmlUtils.isSystemUtf8())
+               {
+                       String systemEncoding = XmlUtils.getSystemEncoding();
                        _useSystemRadio.setText(I18nManager.getText("dialog.exportgpx.encoding.system")
-                               + " (" + (_systemEncoding == null ? "unknown" : _systemEncoding) + ")");
+                               + " (" + (systemEncoding == null ? "unknown" : systemEncoding) + ")");
                }
                _dialog.setVisible(true);
        }
@@ -148,7 +147,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                mainPanel.add(checkPanel);
                // panel for selecting character encoding
                _encodingsPanel = new JPanel();
-               if (!isSystemUtf8())
+               if (!XmlUtils.isSystemUtf8())
                {
                        // only add this panel if system isn't utf8 (or can't be identified yet)
                        _encodingsPanel.setBorder(BorderFactory.createCompoundBorder(
@@ -291,12 +290,17 @@ public class GpxExporter extends GenericFunction implements Runnable
                                writer = new OutputStreamWriter(new FileOutputStream(_exportFile), "UTF-8");
                        else
                                writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
-                       final boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
-                               _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getAudiopointsSelected(),
-                               _pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()};
+                       // TODO: Move to new method
+                       SettingsForExport settings = new SettingsForExport();
+                       settings.setExportTrackPoints(_pointTypeSelector.getTrackpointsSelected());
+                       settings.setExportWaypoints(_pointTypeSelector.getWaypointsSelected());
+                       settings.setExportPhotoPoints(_pointTypeSelector.getPhotopointsSelected());
+                       settings.setExportAudiopoints(_pointTypeSelector.getAudiopointsSelected());
+                       settings.setExportJustSelection(_pointTypeSelector.getJustSelection());
+                       settings.setExportTimestamps(_timestampsCheckbox.isSelected());
                        // write file
                        final int numPoints = exportData(writer, _trackInfo, _nameField.getText(),
-                               _descriptionField.getText(), saveFlags, gpxCachers);
+                               _descriptionField.getText(), settings, gpxCachers);
 
                        // close file
                        writer.close();
@@ -335,31 +339,28 @@ public class GpxExporter extends GenericFunction implements Runnable
         * @param inInfo track info object
         * @param inName name of track (optional)
         * @param inDesc description of track (optional)
-        * @param inSaveFlags array of booleans to export tracks, waypoints, photos, audios, selection, timestamps
+        * @param inExportSettings flags for what to export and how
         * @param inGpxCachers list of Gpx cachers containing input data
         * @return number of points written
         * @throws IOException if io errors occur on write
         */
        public static int exportData(OutputStreamWriter inWriter, TrackInfo inInfo, String inName,
-               String inDesc, boolean[] inSaveFlags, GpxCacherList inGpxCachers) throws IOException
+               String inDesc, SettingsForExport inSettings, GpxCacherList inGpxCachers) throws IOException
        {
                // Write or copy headers
                inWriter.write(getXmlHeaderString(inWriter));
                final String gpxHeader = getGpxHeaderString(inGpxCachers);
                final boolean isVersion1_1 = (gpxHeader.toUpperCase().indexOf("GPX/1/1") > 0);
                inWriter.write(gpxHeader);
-               // Name field
-               String trackName = (inName != null && !inName.equals("")) ? inName : "GpsPruneTrack";
-               writeNameAndDescription(inWriter, inName, inDesc, isVersion1_1);
+               // name and description
+               String trackName = (inName != null && !inName.equals("")) ? XmlUtils.fixCdata(inName) : "GpsPruneTrack";
+               String desc      = (inDesc != null && !inDesc.equals("")) ? XmlUtils.fixCdata(inDesc) : "Export from GpsPrune";
+               writeNameAndDescription(inWriter, trackName, desc, isVersion1_1);
 
-               int i = 0;
                DataPoint point = null;
-               final boolean exportTrackpoints = inSaveFlags[0];
-               final boolean exportWaypoints = inSaveFlags[1];
-               final boolean exportPhotos = inSaveFlags[2];
-               final boolean exportAudios = inSaveFlags[3];
-               final boolean exportSelection = inSaveFlags[4];
-               final boolean exportTimestamps = inSaveFlags[5];
+               final boolean exportWaypoints = inSettings.getExportWaypoints();
+               final boolean exportSelection = inSettings.getExportJustSelection();
+               final boolean exportTimestamps = inSettings.getExportTimestamps();
                // Examine selection
                int selStart = -1, selEnd = -1;
                if (exportSelection) {
@@ -369,7 +370,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                // Loop over waypoints
                final int numPoints = inInfo.getTrack().getNumPoints();
                int numSaved = 0;
-               for (i=0; i<numPoints; i++)
+               for (int i=0; i<numPoints; i++)
                {
                        point = inInfo.getTrack().getPoint(i);
                        if (!exportSelection || (i>=selStart && i<=selEnd))
@@ -384,28 +385,29 @@ public class GpxExporter extends GenericFunction implements Runnable
                                                if (!exportTimestamps) {
                                                        pointSource = stripTime(pointSource);
                                                }
+                                               inWriter.write('\t');
                                                inWriter.write(pointSource);
                                                inWriter.write('\n');
                                        }
                                        else {
-                                               exportWaypoint(point, inWriter, exportTimestamps, exportPhotos, exportAudios);
+                                               exportWaypoint(point, inWriter, inSettings);
                                        }
                                        numSaved++;
                                }
                        }
                }
                // Export both route points and then track points
-               if (exportTrackpoints || exportPhotos || exportAudios)
+               if (inSettings.getExportTrackPoints() || inSettings.getExportPhotoPoints() || inSettings.getExportAudioPoints())
                {
                        // Output all route points (if any)
-                       numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
-                               exportAudios, exportTimestamps, true, inGpxCachers, "<rtept", "\t<rte><number>1</number>\n",
+                       numSaved += writeTrackPoints(inWriter, inInfo, inSettings,
+                               true, inGpxCachers, "<rtept", "\t<rte><number>1</number>\n",
                                null, "\t</rte>\n");
                        // Output all track points, if any
-                       String trackStart = "\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n";
-                       numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
-                               exportAudios, exportTimestamps, false, inGpxCachers, "<trkpt", trackStart,
-                               "\t</trkseg>\n\t<trkseg>\n", "\t</trkseg></trk>\n");
+                       String trackStart = "\t<trk>\n\t\t<name>" + trackName + "</name>\n\t\t<number>1</number>\n\t\t<trkseg>\n";
+                       numSaved += writeTrackPoints(inWriter, inInfo, inSettings,
+                               false, inGpxCachers, "<trkpt", trackStart,
+                               "\t</trkseg>\n\t<trkseg>\n", "\t\t</trkseg>\n\t</trk>\n");
                }
 
                inWriter.write("</gpx>\n");
@@ -423,7 +425,6 @@ public class GpxExporter extends GenericFunction implements Runnable
        private static void writeNameAndDescription(OutputStreamWriter inWriter,
                String inName, String inDesc, boolean inIsVersion1_1) throws IOException
        {
-               String desc = (inDesc != null && !inDesc.equals("")) ? inDesc : "Export from GpsPrune";
                // Position of name and description fields needs to be different for GPX1.0 and GPX1.1
                if (inIsVersion1_1)
                {
@@ -432,12 +433,14 @@ public class GpxExporter extends GenericFunction implements Runnable
                }
                if (inName != null && !inName.equals(""))
                {
-                       inWriter.write("\t\t<name>");
+                       if (inIsVersion1_1) {inWriter.write('\t');}
+                       inWriter.write("\t<name>");
                        inWriter.write(inName);
                        inWriter.write("</name>\n");
                }
-               inWriter.write("\t\t<desc>");
-               inWriter.write(desc);
+               if (inIsVersion1_1) {inWriter.write('\t');}
+               inWriter.write("\t<desc>");
+               inWriter.write(inDesc);
                inWriter.write("</desc>\n");
                if (inIsVersion1_1)
                {
@@ -449,11 +452,7 @@ public class GpxExporter extends GenericFunction implements Runnable
         * Loop through the track outputting the relevant track points
         * @param inWriter writer object for output
         * @param inInfo track info object containing track
-        * @param inExportSelection true to just output current selection
-        * @param inExportTrackpoints true to output track points
-        * @param inExportPhotos true to output photo points
-        * @param inExportAudios true to output audio points
-        * @param inExportTimestamps true to include timestamps in export
+        * @param inSettings export settings defining what should be exported
         * @param inOnlyCopies true to only export if source can be copied
         * @param inCachers list of GpxCachers
         * @param inPointTag tag to match for each point
@@ -462,31 +461,37 @@ public class GpxExporter extends GenericFunction implements Runnable
         * @param inEndTag end tag to output
         */
        private static int writeTrackPoints(OutputStreamWriter inWriter,
-               TrackInfo inInfo, boolean inExportSelection, boolean inExportTrackpoints,
-               boolean inExportPhotos, boolean inExportAudios, boolean exportTimestamps,
+               TrackInfo inInfo, SettingsForExport inSettings,
                boolean inOnlyCopies, GpxCacherList inCachers, String inPointTag,
                String inStartTag, String inSegmentTag, String inEndTag)
        throws IOException
        {
-               // Note: far too many input parameters to this method but avoids duplication
+               // Note: Too many input parameters to this method but avoids duplication
                // of output functionality for writing track points and route points
                int numPoints = inInfo.getTrack().getNumPoints();
                int selStart = inInfo.getSelection().getStart();
                int selEnd = inInfo.getSelection().getEnd();
                int numSaved = 0;
+               final boolean exportSelection = inSettings.getExportJustSelection();
+               final boolean exportTrackPoints = inSettings.getExportTrackPoints();
+               final boolean exportPhotos = inSettings.getExportPhotoPoints();
+               final boolean exportAudios = inSettings.getExportAudioPoints();
+               final boolean exportTimestamps = inSettings.getExportTimestamps();
                // Loop over track points
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = inInfo.getTrack().getPoint(i);
-                       if ((!inExportSelection || (i>=selStart && i<=selEnd)) && !point.isWaypoint())
+                       if ((!exportSelection || (i>=selStart && i<=selEnd)) && !point.isWaypoint())
                        {
-                               if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos)
-                                       || (point.getAudio()!=null && inExportAudios))
+                               if ((point.getPhoto()==null && exportTrackPoints) || (point.getPhoto()!=null && exportPhotos)
+                                       || (point.getAudio()!=null && exportAudios))
                                {
                                        // get the source from the point (if any)
                                        String pointSource = getPointSource(inCachers, point);
                                        // Clear point source if it's the wrong type of point (eg changed from waypoint or route point)
-                                       if (pointSource != null && !pointSource.toLowerCase().startsWith(inPointTag)) {pointSource = null;}
+                                       if (pointSource != null && !pointSource.trim().toLowerCase().startsWith(inPointTag)) {
+                                               pointSource = null;
+                                       }
                                        if (pointSource != null || !inOnlyCopies)
                                        {
                                                // restart track segment if necessary
@@ -503,15 +508,20 @@ public class GpxExporter extends GenericFunction implements Runnable
                                                        inWriter.write(pointSource);
                                                        inWriter.write('\n');
                                                }
-                                               else {
-                                                       if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps, inExportPhotos, inExportAudios);}
+                                               else
+                                               {
+                                                       if (!inOnlyCopies) {
+                                                               exportTrackpoint(point, inWriter, inSettings);
+                                                       }
                                                }
                                                numSaved++;
                                        }
                                }
                        }
                }
-               if (numSaved > 0) {inWriter.write(inEndTag);}
+               if (numSaved > 0) {
+                       inWriter.write(inEndTag);
+               }
                return numSaved;
        }
 
@@ -531,11 +541,15 @@ public class GpxExporter extends GenericFunction implements Runnable
                source = replaceGpxTags(source, "lat=\"", "\"", inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
                source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
                source = replaceGpxTags(source, "<ele>", "</ele>", inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES));
-               source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+               source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601, null));
                if (inPoint.isWaypoint())
                {
-                       source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());
-                       source = replaceGpxTags(source, "<description>", "</description>",
+                       source = replaceGpxTags(source, "<name>", "</name>", XmlUtils.fixCdata(inPoint.getWaypointName()));
+                       if (source != null)
+                       {
+                               source = source.replaceAll("<description>", "<desc>").replaceAll("</description>", "</desc>");
+                       }
+                       source = replaceGpxTags(source, "<desc>", "</desc>",
                                XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION)));
                }
                // photo / audio links
@@ -626,63 +640,10 @@ public class GpxExporter extends GenericFunction implements Runnable
         */
        private static String getXmlHeaderString(OutputStreamWriter inWriter)
        {
-               return "<?xml version=\"1.0\" encoding=\"" + getEncoding(inWriter) + "\"?>\n";
-       }
-
-
-       /**
-        * Get the default system encoding using a writer
-        * @param inWriter writer object
-        * @return string defining encoding
-        */
-       private static String getEncoding(OutputStreamWriter inWriter)
-       {
-               String encoding = inWriter.getEncoding();
-               try {
-                       encoding =  Charset.forName(encoding).name();
-               }
-               catch (Exception e) {} // ignore failure to find encoding
-               return encoding;
+               return "<?xml version=\"1.0\" encoding=\"" + XmlUtils.getEncoding(inWriter) + "\"?>\n";
        }
 
 
-       /**
-        * Use a temporary file to obtain the name of the default system encoding
-        * @return name of default system encoding, or null if write failed
-        */
-       private static String getSystemEncoding()
-       {
-               File tempFile = null;
-               String encoding = null;
-               try
-               {
-                       tempFile = File.createTempFile("prune", null);
-                       OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile));
-                       encoding = getEncoding(writer);
-                       writer.close();
-               }
-               catch (IOException e) {} // value stays null
-               // Delete temp file
-               if (tempFile != null && tempFile.exists()) {
-                       if (!tempFile.delete()) {
-                               System.err.println("Cannot delete temp file: " + tempFile.getAbsolutePath());
-                       }
-               }
-               // If writing failed (eg permissions) then just ask system for default
-               if (encoding == null) encoding = Charset.defaultCharset().name();
-               return encoding;
-       }
-
-       /**
-        * Creates temp file if necessary to check system encoding
-        * @return true if system uses UTF-8 by default
-        */
-       private static boolean isSystemUtf8()
-       {
-               if (_systemEncoding == null) _systemEncoding = getSystemEncoding();
-               return (_systemEncoding != null && _systemEncoding.toUpperCase().equals("UTF-8"));
-       }
-
        /**
         * Get the header string for the gpx tag
         * @param inCachers cacher list to ask for headers, if available
@@ -709,13 +670,11 @@ public class GpxExporter extends GenericFunction implements Runnable
         * Export the specified waypoint into the file
         * @param inPoint waypoint to export
         * @param inWriter writer object
-        * @param inTimestamps true to export timestamps too
-        * @param inPhoto true to export link to photo
-        * @param inAudio true to export link to audio
+        * @param inSettings export settings
         * @throws IOException on write failure
         */
-       private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps,
-               boolean inPhoto, boolean inAudio)
+       private static void exportWaypoint(DataPoint inPoint, Writer inWriter,
+               SettingsForExport inSettings)
                throws IOException
        {
                inWriter.write("\t<wpt lat=\"");
@@ -724,39 +683,39 @@ public class GpxExporter extends GenericFunction implements Runnable
                inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
                inWriter.write("\">\n");
                // altitude if available
-               if (inPoint.hasAltitude())
+               if (inPoint.hasAltitude() || inSettings.getExportMissingAltitudesAsZero())
                {
                        inWriter.write("\t\t<ele>");
-                       inWriter.write("" + inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES));
+                       inWriter.write(inPoint.hasAltitude() ? inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES) : "0");
                        inWriter.write("</ele>\n");
                }
-               // timestamp if available (point might have timestamp and then be turned into a waypoint)
-               if (inPoint.hasTimestamp() && inTimestamps)
+               // timestamp if available (some waypoints have timestamps, some not)
+               if (inPoint.hasTimestamp() && inSettings.getExportTimestamps())
                {
                        inWriter.write("\t\t<time>");
-                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
+                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601, null));
                        inWriter.write("</time>\n");
                }
                // write waypoint name after elevation and time
                inWriter.write("\t\t<name>");
-               inWriter.write(inPoint.getWaypointName().trim());
+               inWriter.write(XmlUtils.fixCdata(inPoint.getWaypointName().trim()));
                inWriter.write("</name>\n");
                // description, if any
                String desc = XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION));
                if (desc != null && !desc.equals(""))
                {
-                       inWriter.write("\t\t<description>");
+                       inWriter.write("\t\t<desc>");
                        inWriter.write(desc);
-                       inWriter.write("</description>\n");
+                       inWriter.write("</desc>\n");
                }
                // Media links, if any
-               if (inPhoto && inPoint.getPhoto() != null)
+               if (inSettings.getExportPhotoPoints() && inPoint.getPhoto() != null)
                {
                        inWriter.write("\t\t");
                        inWriter.write(makeMediaLink(inPoint.getPhoto()));
                        inWriter.write('\n');
                }
-               if (inAudio && inPoint.getAudio() != null)
+               if (inSettings.getExportAudioPoints() && inPoint.getAudio() != null)
                {
                        inWriter.write("\t\t");
                        inWriter.write(makeMediaLink(inPoint.getAudio()));
@@ -782,41 +741,38 @@ public class GpxExporter extends GenericFunction implements Runnable
         * Export the specified trackpoint into the file
         * @param inPoint trackpoint to export
         * @param inWriter writer object
-        * @param inTimestamps true to export timestamps too
-        * @param inExportPhoto true to export photo link
-        * @param inExportAudio true to export audio link
+        * @param inSettings export settings
         */
-       private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps,
-               boolean inExportPhoto, boolean inExportAudio)
+       private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, SettingsForExport inSettings)
                throws IOException
        {
-               inWriter.write("\t\t<trkpt lat=\"");
+               inWriter.write("\t\t\t<trkpt lat=\"");
                inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
                inWriter.write("\" lon=\"");
                inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
-               inWriter.write("\">");
+               inWriter.write("\">\n");
                // altitude
-               if (inPoint.hasAltitude())
+               if (inPoint.hasAltitude() || inSettings.getExportMissingAltitudesAsZero())
                {
-                       inWriter.write("<ele>");
-                       inWriter.write("" + inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES));
-                       inWriter.write("</ele>");
+                       inWriter.write("\t\t\t\t<ele>");
+                       inWriter.write(inPoint.hasAltitude() ? inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES) : "0");
+                       inWriter.write("</ele>\n");
                }
                // timestamp if available (and selected)
-               if (inPoint.hasTimestamp() && inTimestamps)
+               if (inPoint.hasTimestamp() && inSettings.getExportTimestamps())
                {
-                       inWriter.write("<time>");
-                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
-                       inWriter.write("</time>");
+                       inWriter.write("\t\t\t\t<time>");
+                       inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601, null));
+                       inWriter.write("</time>\n");
                }
                // photo, audio
-               if (inPoint.getPhoto() != null && inExportPhoto) {
+               if (inPoint.getPhoto() != null && inSettings.getExportPhotoPoints()) {
                        inWriter.write(makeMediaLink(inPoint.getPhoto()));
                }
-               if (inPoint.getAudio() != null && inExportAudio) {
+               if (inPoint.getAudio() != null && inSettings.getExportAudioPoints()) {
                        inWriter.write(makeMediaLink(inPoint.getAudio()));
                }
-               inWriter.write("</trkpt>\n");
+               inWriter.write("\t\t\t</trkpt>\n");
        }
 
 
@@ -867,6 +823,6 @@ public class GpxExporter extends GenericFunction implements Runnable
         */
        private static String stripTime(String inPointSource)
        {
-               return inPointSource.replaceAll("<time>.*?</time>", "");
+               return inPointSource.replaceAll("[ \t]*<time>.*?</time>", "");
        }
 }