X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fsave%2FGpxExporter.java;h=0f038b77ce60f473a50a64b068c68b698f5b2050;hb=a6197ddcaac11c0b943183da7d46169742d024af;hp=a7f2823c42a2d5dfbd6b5766a4ec5fd619ed3262;hpb=649c5da6ee1bbc590699e11a92316ece2ea8512d;p=GpsPrune.git diff --git a/tim/prune/save/GpxExporter.java b/tim/prune/save/GpxExporter.java index a7f2823..0f038b7 100644 --- a/tim/prune/save/GpxExporter.java +++ b/tim/prune/save/GpxExporter.java @@ -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; @@ -35,7 +34,6 @@ import tim.prune.GpsPrune; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; -import tim.prune.data.Altitude; import tim.prune.data.AudioClip; import tim.prune.data.Coordinate; import tim.prune.data.DataPoint; @@ -45,6 +43,7 @@ import tim.prune.data.Photo; import tim.prune.data.RecentFile; import tim.prune.data.Timestamp; import tim.prune.data.TrackInfo; +import tim.prune.data.UnitSetLibrary; import tim.prune.gui.DialogCloser; import tim.prune.load.GenericFileFilter; import tim.prune.save.xml.GpxCacherList; @@ -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( @@ -278,6 +277,11 @@ public class GpxExporter extends GenericFunction implements Runnable */ public void run() { + // Instantiate source file cachers in case we want to copy output + GpxCacherList gpxCachers = null; + if (_copySourceCheckbox.isSelected()) { + gpxCachers = new GpxCacherList(_trackInfo.getFileInfo()); + } OutputStreamWriter writer = null; try { @@ -291,7 +295,7 @@ public class GpxExporter extends GenericFunction implements Runnable _pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()}; // write file final int numPoints = exportData(writer, _trackInfo, _nameField.getText(), - _descriptionField.getText(), saveFlags, _copySourceCheckbox.isSelected()); + _descriptionField.getText(), saveFlags, gpxCachers); // close file writer.close(); @@ -331,32 +335,21 @@ public class GpxExporter extends GenericFunction implements Runnable * @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 inUseCopy true to copy source if available + * @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, boolean inUseCopy) throws IOException + String inDesc, boolean[] inSaveFlags, GpxCacherList inGpxCachers) throws IOException { - // Instantiate source file cachers in case we want to copy output - GpxCacherList gpxCachers = null; - if (inUseCopy) gpxCachers = new GpxCacherList(inInfo.getFileInfo()); // Write or copy headers inWriter.write(getXmlHeaderString(inWriter)); - inWriter.write(getGpxHeaderString(gpxCachers)); + final String gpxHeader = getGpxHeaderString(inGpxCachers); + final boolean isVersion1_1 = (gpxHeader.toUpperCase().indexOf("GPX/1/1") > 0); + inWriter.write(gpxHeader); // Name field - String trackName = "GpsPruneTrack"; - if (inName != null && !inName.equals("")) - { - trackName = inName; - inWriter.write("\t"); - inWriter.write(trackName); - inWriter.write("\n"); - } - // Description field - inWriter.write("\t"); - inWriter.write((inDesc != null && !inDesc.equals(""))?inDesc:"Export from GpsPrune"); - inWriter.write("\n"); + String trackName = (inName != null && !inName.equals("")) ? inName : "GpsPruneTrack"; + writeNameAndDescription(inWriter, inName, inDesc, isVersion1_1); int i = 0; DataPoint point = null; @@ -383,8 +376,14 @@ public class GpxExporter extends GenericFunction implements Runnable // Make a wpt element for each waypoint if (point.isWaypoint() && exportWaypoints) { - String pointSource = (inUseCopy?getPointSource(gpxCachers, point):null); - if (pointSource != null) { + String pointSource = (inGpxCachers == null? null : getPointSource(inGpxCachers, point)); + if (pointSource != null) + { + // If timestamp checkbox is off, strip time + if (!exportTimestamps) { + pointSource = stripTime(pointSource); + } + inWriter.write('\t'); inWriter.write(pointSource); inWriter.write('\n'); } @@ -400,13 +399,13 @@ public class GpxExporter extends GenericFunction implements Runnable { // Output all route points (if any) numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos, - exportAudios, exportTimestamps, true, gpxCachers, "1\n", + exportAudios, exportTimestamps, true, inGpxCachers, "1\n", null, "\t\n"); // Output all track points, if any - String trackStart = "\t" + trackName + "1\n"; + String trackStart = "\t\n\t\t" + trackName + "\n\t\t1\n\t\t\n"; numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos, - exportAudios, exportTimestamps, false, gpxCachers, "\n\t\n", "\t\n"); + exportAudios, exportTimestamps, false, inGpxCachers, "\n\t\n", "\t\t\n\t\n"); } inWriter.write("\n"); @@ -414,6 +413,40 @@ public class GpxExporter extends GenericFunction implements Runnable } + /** + * Write the name and description according to the GPX version number + * @param inWriter writer object + * @param inName name, or null if none supplied + * @param inDesc description, or null if none supplied + * @param inIsVersion1_1 true if gpx version 1.1, false for version 1.0 + */ + 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) + { + // GPX 1.1 has the name and description inside a metadata tag + inWriter.write("\t\n"); + } + if (inName != null && !inName.equals("")) + { + if (inIsVersion1_1) {inWriter.write('\t');} + inWriter.write("\t"); + inWriter.write(inName); + inWriter.write("\n"); + } + if (inIsVersion1_1) {inWriter.write('\t');} + inWriter.write("\t"); + inWriter.write(desc); + inWriter.write("\n"); + if (inIsVersion1_1) + { + inWriter.write("\t\n"); + } + } + /** * Loop through the track outputting the relevant track points * @param inWriter writer object for output @@ -455,7 +488,9 @@ public class GpxExporter extends GenericFunction implements Runnable // 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 @@ -463,7 +498,12 @@ public class GpxExporter extends GenericFunction implements Runnable inWriter.write(inSegmentTag); } if (numSaved == 0) {inWriter.write(inStartTag);} - if (pointSource != null) { + if (pointSource != null) + { + // If timestamps checkbox is off, strip the time + if (!exportTimestamps) { + pointSource = stripTime(pointSource); + } inWriter.write(pointSource); inWriter.write('\n'); } @@ -494,8 +534,8 @@ public class GpxExporter extends GenericFunction implements Runnable // Point has been modified - maybe it's possible to modify the source 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, "", "", inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); - source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601)); + source = replaceGpxTags(source, "", "", inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); + source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.Format.ISO8601)); if (inPoint.isWaypoint()) { source = replaceGpxTags(source, "", "", inPoint.getWaypointName()); @@ -590,67 +630,10 @@ public class GpxExporter extends GenericFunction implements Runnable */ private static String getXmlHeaderString(OutputStreamWriter inWriter) { - return "\n"; + return "\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 - // Hack to fix bugs with Mac OSX (which reports MacRoman but is actually UTF-8) - if (encoding == null || encoding.toLowerCase().startsWith("macroman")) { - encoding = "UTF-8"; - } - return encoding; - } - - - /** - * 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 @@ -662,6 +645,7 @@ public class GpxExporter extends GenericFunction implements Runnable if (inCachers != null) {gpxHeader = inCachers.getFirstHeader();} if (gpxHeader == null || gpxHeader.length() < 5) { + // TODO: Consider changing this to default to GPX 1.1 // Create default (1.0) header gpxHeader = ""); - inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); + inWriter.write("" + inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); inWriter.write("\n"); } // timestamp if available (point might have timestamp and then be turned into a waypoint) if (inPoint.hasTimestamp() && inTimestamps) { inWriter.write("\t\t\n"); } // write waypoint name after elevation and time @@ -757,24 +741,24 @@ public class GpxExporter extends GenericFunction implements Runnable boolean inExportPhoto, boolean inExportAudio) throws IOException { - inWriter.write("\t\t"); + inWriter.write("\">\n"); // altitude if (inPoint.hasAltitude()) { - inWriter.write(""); - inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); - inWriter.write(""); + inWriter.write("\t\t\t\t"); + inWriter.write("" + inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); + inWriter.write("\n"); } // timestamp if available (and selected) if (inPoint.hasTimestamp() && inTimestamps) { - inWriter.write(""); + inWriter.write("\t\t\t\t\n"); } // photo, audio if (inPoint.getPhoto() != null && inExportPhoto) { @@ -783,7 +767,7 @@ public class GpxExporter extends GenericFunction implements Runnable if (inPoint.getAudio() != null && inExportAudio) { inWriter.write(makeMediaLink(inPoint.getAudio())); } - inWriter.write("\n"); + inWriter.write("\t\t\t\n"); } @@ -825,4 +809,15 @@ public class GpxExporter extends GenericFunction implements Runnable // No link available, must have been loaded from zip file - no link possible return ""; } + + + /** + * Strip the time from a GPX point source string + * @param inPointSource point source to copy + * @return point source with timestamp removed + */ + private static String stripTime(String inPointSource) + { + return inPointSource.replaceAll("[ \t]*", ""); + } }