X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fsave%2FKmlExporter.java;h=9d1c0789b4f4d9a3e6820f5883b7f7235bac74ab;hb=92dad5df664287acb51728e9ea599f150765d34a;hp=a5a4ee26a87e0bc7d383acf70d24fbc42434f270;hpb=1ee49ae3c8ef3aa2e63eadd458531e5f8bd4f92c;p=GpsPrune.git diff --git a/tim/prune/save/KmlExporter.java b/tim/prune/save/KmlExporter.java index a5a4ee2..9d1c078 100644 --- a/tim/prune/save/KmlExporter.java +++ b/tim/prune/save/KmlExporter.java @@ -23,6 +23,7 @@ import javax.imageio.ImageIO; import javax.imageio.ImageWriter; import javax.swing.Box; import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -32,6 +33,7 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; +import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.SwingConstants; @@ -41,16 +43,21 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.ColourUtils; import tim.prune.config.Config; -import tim.prune.data.Altitude; import tim.prune.data.Coordinate; import tim.prune.data.DataPoint; import tim.prune.data.Field; +import tim.prune.data.RecentFile; +import tim.prune.data.Timestamp; import tim.prune.data.Track; import tim.prune.data.TrackInfo; -import tim.prune.gui.ColourChooser; -import tim.prune.gui.ColourPatch; +import tim.prune.data.UnitSetLibrary; +import tim.prune.gui.DialogCloser; import tim.prune.gui.ImageUtils; +import tim.prune.gui.WholeNumberField; +import tim.prune.gui.colour.ColourChooser; +import tim.prune.gui.colour.ColourPatch; import tim.prune.load.GenericFileFilter; +import tim.prune.save.xml.XmlUtils; /** * Class to export track information @@ -63,9 +70,12 @@ public class KmlExporter extends GenericFunction implements Runnable private JDialog _dialog = null; private JTextField _descriptionField = null; private PointTypeSelector _pointTypeSelector = null; + private JRadioButton _gxExtensionsRadio = null; private JCheckBox _altitudesCheckbox = null; private JCheckBox _kmzCheckbox = null; private JCheckBox _exportImagesCheckbox = null; + private JLabel _imageSizeLabel = null; + private WholeNumberField _imageSizeField = null; private ColourPatch _colourPatch = null; private JLabel _progressLabel = null; private JProgressBar _progressBar = null; @@ -80,10 +90,6 @@ public class KmlExporter extends GenericFunction implements Runnable private static final String KML_FILENAME_IN_KMZ = "doc.kml"; // Default width and height of thumbnail images in Kmz private static final int DEFAULT_THUMBNAIL_WIDTH = 240; - private static final int DEFAULT_THUMBNAIL_HEIGHT = 240; - // Actual selected width and height of thumbnail images in Kmz - private static int THUMBNAIL_WIDTH = 0; - private static int THUMBNAIL_HEIGHT = 0; // Default track colour private static final Color DEFAULT_TRACK_COLOUR = new Color(204, 0, 0); // red @@ -119,6 +125,8 @@ public class KmlExporter extends GenericFunction implements Runnable _dialog.pack(); _colourChooser = new ColourChooser(_dialog); } + // Fill in image size from config + _imageSizeField.setValue(Config.getConfigInt(Config.KEY_KMZ_IMAGE_SIZE)); enableCheckboxes(); _descriptionField.setEnabled(true); _okButton.setEnabled(true); @@ -143,6 +151,7 @@ public class KmlExporter extends GenericFunction implements Runnable descPanel.setLayout(new FlowLayout()); descPanel.add(new JLabel(I18nManager.getText("dialog.exportkml.text"))); _descriptionField = new JTextField(20); + _descriptionField.addKeyListener(new DialogCloser(_dialog)); descPanel.add(_descriptionField); descPanel.setAlignmentX(Component.CENTER_ALIGNMENT); mainPanel.add(descPanel); @@ -168,19 +177,31 @@ public class KmlExporter extends GenericFunction implements Runnable colourPanel.add(new JLabel(I18nManager.getText("dialog.exportkml.trackcolour"))); colourPanel.add(_colourPatch); mainPanel.add(colourPanel); + // Pair of radio buttons for standard/extended KML + JRadioButton standardKmlRadio = new JRadioButton(I18nManager.getText("dialog.exportkml.standardkml")); + _gxExtensionsRadio = new JRadioButton(I18nManager.getText("dialog.exportkml.extendedkml")); + ButtonGroup bGroup = new ButtonGroup(); + bGroup.add(standardKmlRadio); bGroup.add(_gxExtensionsRadio); + JPanel radioPanel = new JPanel(); + radioPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 1)); + radioPanel.add(standardKmlRadio); + radioPanel.add(_gxExtensionsRadio); + standardKmlRadio.setSelected(true); + mainPanel.add(radioPanel); // Checkbox for altitude export _altitudesCheckbox = new JCheckBox(I18nManager.getText("dialog.exportkml.altitude")); _altitudesCheckbox.setHorizontalTextPosition(SwingConstants.LEFT); _altitudesCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT); mainPanel.add(_altitudesCheckbox); + // Checkboxes for kmz export and image export _kmzCheckbox = new JCheckBox(I18nManager.getText("dialog.exportkml.kmz")); _kmzCheckbox.setHorizontalTextPosition(SwingConstants.LEFT); _kmzCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT); + // enable image checkbox if kmz activated _kmzCheckbox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - // enable image checkbox if kmz activated enableCheckboxes(); } }); @@ -188,7 +209,23 @@ public class KmlExporter extends GenericFunction implements Runnable _exportImagesCheckbox = new JCheckBox(I18nManager.getText("dialog.exportkml.exportimages")); _exportImagesCheckbox.setHorizontalTextPosition(SwingConstants.LEFT); _exportImagesCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT); + // enable image size fields if image checkbox changes + _exportImagesCheckbox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + enableImageSizeFields(); + } + }); mainPanel.add(_exportImagesCheckbox); + // Panel for the image size + JPanel imageSizePanel = new JPanel(); + imageSizePanel.setLayout(new FlowLayout(FlowLayout.CENTER)); + _imageSizeLabel = new JLabel(I18nManager.getText("dialog.exportkml.imagesize")); + _imageSizeLabel.setAlignmentX(Component.RIGHT_ALIGNMENT); + imageSizePanel.add(_imageSizeLabel); + _imageSizeField = new WholeNumberField(4); + imageSizePanel.add(_imageSizeField); + mainPanel.add(imageSizePanel); + mainPanel.add(Box.createVerticalStrut(10)); _progressLabel = new JLabel("..."); _progressLabel.setAlignmentX(Component.CENTER_ALIGNMENT); @@ -236,9 +273,26 @@ public class KmlExporter extends GenericFunction implements Runnable boolean hasPhotos = _trackInfo.getPhotoList() != null && _trackInfo.getPhotoList().getNumPhotos() > 0; _exportImagesCheckbox.setSelected(hasPhotos && _kmzCheckbox.isSelected()); _exportImagesCheckbox.setEnabled(hasPhotos && _kmzCheckbox.isSelected()); + enableImageSizeFields(); + } + + /** + * Enable and disable the image size fields according to the checkboxes + */ + private void enableImageSizeFields() + { + boolean exportImages = _exportImagesCheckbox.isEnabled() && _exportImagesCheckbox.isSelected(); + _imageSizeField.setEnabled(exportImages); + _imageSizeLabel.setEnabled(exportImages); } + /** + * @return true if using gx extensions for kml export + */ + private boolean useGxExtensions() { + return _gxExtensionsRadio.isSelected(); + } /** * Start the export process based on the input parameters */ @@ -324,11 +378,6 @@ public class KmlExporter extends GenericFunction implements Runnable boolean exportImages = exportToKmz && _exportImagesCheckbox.isSelected(); _progressBar.setMaximum(exportImages?getNumPhotosToExport():1); - // Determine photo thumbnail size from config - THUMBNAIL_WIDTH = Config.getConfigInt(Config.KEY_KMZ_IMAGE_WIDTH); - if (THUMBNAIL_WIDTH < DEFAULT_THUMBNAIL_WIDTH) {THUMBNAIL_WIDTH = DEFAULT_THUMBNAIL_WIDTH;} - THUMBNAIL_HEIGHT = Config.getConfigInt(Config.KEY_KMZ_IMAGE_HEIGHT); - if (THUMBNAIL_HEIGHT < DEFAULT_THUMBNAIL_HEIGHT) {THUMBNAIL_HEIGHT = DEFAULT_THUMBNAIL_HEIGHT;} // Create array for image dimensions in case it's required _imageDimensions = new Dimension[_track.getNumPoints()]; @@ -349,9 +398,14 @@ public class KmlExporter extends GenericFunction implements Runnable // Export images into zip file too if requested if (exportImages) { + // Get entered value for image size, store in config + int thumbSize = _imageSizeField.getValue(); + if (thumbSize < DEFAULT_THUMBNAIL_WIDTH) {thumbSize = DEFAULT_THUMBNAIL_WIDTH;} + Config.setConfigInt(Config.KEY_KMZ_IMAGE_SIZE, thumbSize); + // Create thumbnails of each photo in turn and add to zip as images/image.jpg // This is done first so that photo sizes are known for later - exportThumbnails(zipOutputStream); + exportThumbnails(zipOutputStream, thumbSize); } writer = new OutputStreamWriter(zipOutputStream); // Make an entry in the zip file for the kml file @@ -379,7 +433,10 @@ public class KmlExporter extends GenericFunction implements Runnable _imageDimensions = null; // Store directory in config for later Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath()); + // Add to recent file list + Config.getRecentFileList().addFile(new RecentFile(_exportFile, true)); // show confirmation + UpdateMessageBroker.informSubscribers(); UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1") + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2") + " " + _exportFile.getAbsolutePath()); @@ -414,16 +471,22 @@ public class KmlExporter extends GenericFunction implements Runnable boolean writeTrack = _pointTypeSelector.getTrackpointsSelected(); boolean writeWaypoints = _pointTypeSelector.getWaypointsSelected(); boolean writePhotos = _pointTypeSelector.getPhotopointsSelected(); + boolean writeAudios = _pointTypeSelector.getAudiopointsSelected(); boolean justSelection = _pointTypeSelector.getJustSelection(); - inWriter.write("\n\n\n"); - inWriter.write("\t"); + // Define xml header (depending on whether extensions are used or not) + if (useGxExtensions()) { + inWriter.write("\n\n"); + } + else { + inWriter.write("\n\n"); + } + inWriter.write("\n\t"); if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals("")) { - inWriter.write(_descriptionField.getText()); + inWriter.write(XmlUtils.fixCdata(_descriptionField.getText())); } - else - { - inWriter.write("Export from Prune"); + else { + inWriter.write("Export from GpsPrune"); } inWriter.write("\n"); @@ -438,7 +501,7 @@ public class KmlExporter extends GenericFunction implements Runnable int i = 0; DataPoint point = null; boolean hasTrackpoints = false; - boolean writtenPhotoHeader = false; + boolean writtenPhotoHeader = false, writtenAudioHeader = false; final int numPoints = _track.getNumPoints(); int numSaved = 0; int photoNum = 0; @@ -456,13 +519,13 @@ public class KmlExporter extends GenericFunction implements Runnable numSaved++; } } - else if (point.getPhoto() == null) + else if (!point.hasMedia()) { hasTrackpoints = true; } // Make a blob with description for each photo // Photos have already been written so picture sizes already known - if (point.getPhoto() != null && writePhotos && writeCurrentPoint) + if (point.getPhoto() != null && point.getPhoto().isValid() && writePhotos && writeCurrentPoint) { if (!writtenPhotoHeader) { @@ -473,55 +536,183 @@ public class KmlExporter extends GenericFunction implements Runnable exportPhotoPoint(point, inWriter, inExportImages, i, photoNum, absoluteAltitudes); numSaved++; } + // Make a blob with description for each audio clip + if (point.getAudio() != null && writeAudios && writeCurrentPoint) + { + if (!writtenAudioHeader) + { + inWriter.write(""); + writtenAudioHeader = true; + } + exportAudioPoint(point, inWriter, absoluteAltitudes); + numSaved++; + } } // Make a line for the track, if there is one if (hasTrackpoints && writeTrack) { - // Set up strings for start and end of track segment - String trackStart = "\t\n\t\ttrack\n\t\t\n\t\t\n"; - if (absoluteAltitudes) { - trackStart += "\t\t\t1\n\t\t\tabsolute\n"; + boolean useGxExtensions = _gxExtensionsRadio.isSelected(); + if (useGxExtensions) + { + // Write track using the Google Extensions to KML including gx:Track + numSaved += writeGxTrack(inWriter, absoluteAltitudes, selStart, selEnd); } else { - trackStart += "\t\t\tclampToGround\n"; + // Write track using standard KML + numSaved += writeStandardTrack(inWriter, absoluteAltitudes, selStart, selEnd); + } + } + inWriter.write("\n\n"); + return numSaved; + } + + + /** + * Write out the track using standard KML LineString tag + * @param inWriter writer object to write to + * @param inAbsoluteAltitudes true to use absolute altitudes, false to clamp to ground + * @param inSelStart start index of selection, or -1 if whole track + * @param inSelEnd end index of selection, or -1 if whole track + * @return number of track points written + */ + private int writeStandardTrack(OutputStreamWriter inWriter, boolean inAbsoluteAltitudes, int inSelStart, + int inSelEnd) + throws IOException + { + int numSaved = 0; + // Set up strings for start and end of track segment + String trackStart = "\t\n\t\ttrack\n\t\t\n\t\t\n"; + if (inAbsoluteAltitudes) { + trackStart += "\t\t\t1\n\t\t\tabsolute\n"; + } + else { + trackStart += "\t\t\tclampToGround\n"; + } + trackStart += "\t\t\t"; + String trackEnd = "\t\t\t\n\t\t\n\t"; + + boolean justSelection = _pointTypeSelector.getJustSelection(); + + // Start segment + inWriter.write(trackStart); + // Loop over track points + boolean firstTrackpoint = true; + final int numPoints = _track.getNumPoints(); + for (int i=0; i=inSelStart && i<=inSelEnd); + if (!point.isWaypoint() && writeCurrentPoint) + { + // start new track segment if necessary + if (point.getSegmentStart() && !firstTrackpoint) { + inWriter.write(trackEnd); + inWriter.write(trackStart); + } + if (point.getPhoto() == null) + { + exportTrackpoint(point, inWriter); + numSaved++; + firstTrackpoint = false; + } } - trackStart += "\t\t\t"; - String trackEnd = "\t\t\t\n\t\t\n\t"; - - // Start segment - inWriter.write(trackStart); - // Loop over track points - boolean firstTrackpoint = true; - for (i=0; icc" + reverse(ColourUtils.makeHexCode(_colourPatch.getBackground())) + "\n" + + "\t\t\t\t4\n\t\t\t\n" + + "\t\t\n\t\t\n"; + if (inAbsoluteAltitudes) { + trackStart += "\t\t\t1\n\t\t\tabsolute\n"; + } + else { + trackStart += "\t\t\tclampToGround\n"; + } + String trackEnd = "\n\t\t\n\t\n"; + + boolean justSelection = _pointTypeSelector.getJustSelection(); + + // Start segment + inWriter.write(trackStart); + StringBuilder whenList = new StringBuilder(); + StringBuilder coordList = new StringBuilder(); + + // Loop over track points + boolean firstTrackpoint = true; + final int numPoints = _track.getNumPoints(); + for (int i=0; i=inSelStart && i<=inSelEnd); + if (!point.isWaypoint() && writeCurrentPoint) { - point = _track.getPoint(i); - boolean writeCurrentPoint = !justSelection || (i>=selStart && i<=selEnd); - if (!point.isWaypoint() && writeCurrentPoint) + // start new track segment if necessary + if (point.getSegmentStart() && !firstTrackpoint) + { + inWriter.write(whenList.toString()); + inWriter.write('\n'); + inWriter.write(coordList.toString()); + inWriter.write('\n'); + inWriter.write(trackEnd); + whenList.setLength(0); coordList.setLength(0); + inWriter.write(trackStart); + } + if (point.getPhoto() == null) { - // start new track segment if necessary - if (point.getSegmentStart() && !firstTrackpoint) { - inWriter.write(trackEnd); - inWriter.write(trackStart); + // Add timestamp (if any) to the list + whenList.append(""); + if (point.hasTimestamp()) { + whenList.append(point.getTimestamp().getText(Timestamp.Format.ISO8601, null)); + } + whenList.append("\n"); + // Add coordinates to the list + coordList.append(""); + coordList.append(point.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)).append(' '); + coordList.append(point.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)).append(' '); + if (point.hasAltitude()) { + coordList.append("" + point.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); } - if (point.getPhoto() == null) - { - exportTrackpoint(point, inWriter); - numSaved++; - firstTrackpoint = false; + else { + coordList.append('0'); } + coordList.append("\n"); + numSaved++; + firstTrackpoint = false; } } - // end segment - inWriter.write(trackEnd); } - inWriter.write("\n"); + // end segment + inWriter.write(whenList.toString()); + inWriter.write('\n'); + inWriter.write(coordList.toString()); + inWriter.write('\n'); + inWriter.write(trackEnd); return numSaved; } + /** * Reverse the hex code for the colours for KML's stupid backwards format * @param inCode colour code rrggbb @@ -541,28 +732,26 @@ public class KmlExporter extends GenericFunction implements Runnable */ private void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException { - inWriter.write("\t\n\t\t"); - inWriter.write(inPoint.getWaypointName().trim()); - inWriter.write("\n"); - inWriter.write("\t\t\n"); - if (inAbsoluteAltitude && inPoint.hasAltitude()) { - inWriter.write("\t\t\tabsolute\n"); - } - else { - inWriter.write("\t\t\tclampToGround\n"); - } - inWriter.write("\t\t\t"); - inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); - inWriter.write(','); - inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); - inWriter.write(","); - if (inPoint.hasAltitude()) { - inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); - } - else { - inWriter.write("0"); + String name = inPoint.getWaypointName().trim(); + exportNamedPoint(inPoint, inWriter, name, inPoint.getFieldValue(Field.DESCRIPTION), null, inAbsoluteAltitude); + } + + + /** + * Export the specified audio point into the file + * @param inPoint audio point to export + * @param inWriter writer object + * @param inAbsoluteAltitude true for absolute altitude + * @throws IOException on write failure + */ + private void exportAudioPoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException + { + String name = inPoint.getAudio().getName(); + String desc = null; + if (inPoint.getAudio().getFile() != null) { + desc = inPoint.getAudio().getFile().getAbsolutePath(); } - inWriter.write("\n\t\t\n\t\n"); + exportNamedPoint(inPoint, inWriter, name, desc, "audio_icon", inAbsoluteAltitude); } @@ -580,18 +769,51 @@ public class KmlExporter extends GenericFunction implements Runnable int inPointNumber, int inImageNumber, boolean inAbsoluteAltitude) throws IOException { - inWriter.write("\t\n\t\t"); - inWriter.write(inPoint.getPhoto().getFile().getName()); - inWriter.write("\n"); + String name = inPoint.getPhoto().getName(); + String desc = null; if (inImageLink) { Dimension imageSize = _imageDimensions[inPointNumber]; - // Write out some html for the thumbnail images - inWriter.write("" - + "
" + inPoint.getPhoto().getFile().getName() + "
]]>
"); + + "
" + name + "
]]>"; + } + // Export point + exportNamedPoint(inPoint, inWriter, name, desc, "camera_icon", inAbsoluteAltitude); + } + + + /** + * Export the specified named point into the file, like waypoint or photo point + * @param inPoint data point + * @param inWriter writer object + * @param inName name of point + * @param inDesc description of point, or null + * @param inStyle style of point, or null + * @param inAbsoluteAltitude true for absolute altitudes + * @throws IOException on write failure + */ + private void exportNamedPoint(DataPoint inPoint, Writer inWriter, String inName, + String inDesc, String inStyle, boolean inAbsoluteAltitude) + throws IOException + { + inWriter.write("\t\n\t\t"); + inWriter.write(XmlUtils.fixCdata(inName)); + inWriter.write("\n"); + if (inDesc != null) + { + // Write out description + inWriter.write("\t\t"); + inWriter.write(XmlUtils.fixCdata(inDesc)); + inWriter.write("\n"); + } + if (inStyle != null) + { + inWriter.write("#"); + inWriter.write(inStyle); + inWriter.write("\n"); } - inWriter.write("#camera_icon\n"); inWriter.write("\t\t\n"); if (inAbsoluteAltitude && inPoint.hasAltitude()) { inWriter.write("\t\t\tabsolute\n"); @@ -603,13 +825,13 @@ public class KmlExporter extends GenericFunction implements Runnable inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); inWriter.write(','); inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); - inWriter.write(","); + inWriter.write(','); // Altitude if point has one if (inPoint.hasAltitude()) { - inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); + inWriter.write("" + inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); } else { - inWriter.write("0"); + inWriter.write('0'); } inWriter.write("\n\t\t\n\t\n"); } @@ -626,22 +848,24 @@ public class KmlExporter extends GenericFunction implements Runnable inWriter.write(','); inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); // Altitude if point has one - inWriter.write(","); + inWriter.write(','); if (inPoint.hasAltitude()) { - inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); + inWriter.write("" + inPoint.getAltitude().getStringValue(UnitSetLibrary.UNITS_METRES)); } else { - inWriter.write("0"); + inWriter.write('0'); } - inWriter.write("\n"); + inWriter.write('\n'); } /** * Loop through the photos and create thumbnails * @param inZipStream zip stream to save image files to + * @param inThumbSize thumbnail size */ - private void exportThumbnails(ZipOutputStream inZipStream) throws IOException + private void exportThumbnails(ZipOutputStream inZipStream, int inThumbSize) + throws IOException { // set up image writer Iterator writers = ImageIO.getImageWritersByFormatName("jpg"); @@ -652,32 +876,32 @@ public class KmlExporter extends GenericFunction implements Runnable ImageWriter imageWriter = writers.next(); // Check selection checkbox - boolean justSelection = _pointTypeSelector.getJustSelection(); + final boolean justSelection = _pointTypeSelector.getJustSelection(); int selStart = -1, selEnd = -1; if (justSelection) { selStart = _trackInfo.getSelection().getStart(); selEnd = _trackInfo.getSelection().getEnd(); } - int numPoints = _track.getNumPoints(); + final int numPoints = _track.getNumPoints(); DataPoint point = null; int photoNum = 0; // Loop over all points in track for (int i=0; i=selStart && i<=selEnd))) + if (point.getPhoto() != null && point.getPhoto().isValid() && (!justSelection || (i>=selStart && i<=selEnd))) { photoNum++; // Make a new entry in zip file ZipEntry entry = new ZipEntry("images/image" + photoNum + ".jpg"); inZipStream.putNextEntry(entry); // Load image and write to outstream - ImageIcon icon = new ImageIcon(point.getPhoto().getFile().getAbsolutePath()); + ImageIcon icon = point.getPhoto().createImageIcon(); - // Scale and smooth image to required size + // Scale image to required size (not smoothed) BufferedImage bufferedImage = ImageUtils.rotateImage(icon.getImage(), - THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, point.getPhoto().getRotationDegrees()); + inThumbSize, inThumbSize, point.getPhoto().getRotationDegrees()); // Store image dimensions so that it doesn't have to be calculated again for the points _imageDimensions[i] = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight());