X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fsave%2FKmlExporter.java;h=7f90dbc8cca16e897ee70072aabc1e88c8f82b10;hb=f35b6d628f68e3b5ef19965ad8988d0dd1eb8efa;hp=16624ad1495dd26c549a0af26a3c087fbb0364c7;hpb=d3679d647d57c2ee7376ddbf6def2d5b23c04307;p=GpsPrune.git
diff --git a/tim/prune/save/KmlExporter.java b/tim/prune/save/KmlExporter.java
index 16624ad..7f90dbc 100644
--- a/tim/prune/save/KmlExporter.java
+++ b/tim/prune/save/KmlExporter.java
@@ -1,182 +1,390 @@
package tim.prune.save;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.image.BufferedImage;
import java.io.File;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.Writer;
+import java.util.Iterator;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriter;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
import javax.swing.JFileChooser;
-import javax.swing.JFrame;
+import javax.swing.JLabel;
import javax.swing.JOptionPane;
-import javax.swing.filechooser.FileFilter;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
import tim.prune.App;
+import tim.prune.GenericFunction;
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.Track;
+import tim.prune.data.TrackInfo;
+import tim.prune.gui.ColourChooser;
+import tim.prune.gui.ColourPatch;
+import tim.prune.gui.DialogCloser;
+import tim.prune.gui.ImageUtils;
+import tim.prune.load.GenericFileFilter;
/**
* Class to export track information
- * into a specified Kml file
+ * into a specified Kml or Kmz file
*/
-public class KmlExporter
+public class KmlExporter extends GenericFunction implements Runnable
{
- private App _app = null;
- private JFrame _parentFrame = null;
+ private TrackInfo _trackInfo = null;
private Track _track = null;
+ private JDialog _dialog = null;
+ private JTextField _descriptionField = null;
+ private PointTypeSelector _pointTypeSelector = null;
+ private JCheckBox _altitudesCheckbox = null;
+ private JCheckBox _kmzCheckbox = null;
+ private JCheckBox _exportImagesCheckbox = null;
+ private ColourPatch _colourPatch = null;
+ private JLabel _progressLabel = null;
+ private JProgressBar _progressBar = null;
+ private Dimension[] _imageDimensions = null;
private JFileChooser _fileChooser = null;
+ private File _exportFile = null;
+ private JButton _okButton = null;
+ private boolean _cancelPressed = false;
+ private ColourChooser _colourChooser = null;
+
+ // Filename of Kml file within zip archive
+ 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;
+ // Default track colour
+ private static final Color DEFAULT_TRACK_COLOUR = new Color(204, 0, 0); // red
/**
- * Constructor giving App object, frame and track
- * @param inApp application object to inform of success
- * @param inParentFrame parent frame
- * @param inTrack track object to save
+ * Constructor
+ * @param inApp app object
*/
- public KmlExporter(App inApp, JFrame inParentFrame, Track inTrack)
+ public KmlExporter(App inApp)
{
- _app = inApp;
- _parentFrame = inParentFrame;
- _track = inTrack;
+ super(inApp);
+ _trackInfo = inApp.getTrackInfo();
+ _track = _trackInfo.getTrack();
}
+ /** Get name key */
+ public String getNameKey() {
+ return "function.exportkml";
+ }
/**
* Show the dialog to select options and export file
*/
- public boolean showDialog()
+ public void begin()
{
- boolean fileSaved = false;
- Object description = JOptionPane.showInputDialog(_parentFrame,
- I18nManager.getText("dialog.exportkml.text"),
- I18nManager.getText("dialog.exportkml.title"),
- JOptionPane.QUESTION_MESSAGE, null, null, "");
- // TODO: Make dialog window including colour selection, line width, track description
- if (description != null)
+ // Make dialog window including whether to compress to kmz (and include pictures) or not
+ if (_dialog == null)
{
- // OK pressed, so choose output file
- if (_fileChooser == null)
- _fileChooser = new JFileChooser();
- _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
- _fileChooser.setFileFilter(new FileFilter() {
- public boolean accept(File f)
- {
- return (f != null && (f.isDirectory() || f.getName().toLowerCase().endsWith(".kml")));
- }
- public String getDescription()
- {
- return I18nManager.getText("dialog.exportkml.filetype");
- }
- });
- _fileChooser.setAcceptAllFileFilterUsed(false);
- // Allow choose again if an existing file is selected
- boolean chooseAgain = false;
- do
- {
- chooseAgain = false;
- if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
- {
- // OK pressed and file chosen
- File file = _fileChooser.getSelectedFile();
- if (!file.getName().toLowerCase().endsWith(".kml"))
- {
- file = new File(file.getAbsolutePath() + ".kml");
- }
- // Check if file exists and if necessary prompt for overwrite
- Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
- if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
- I18nManager.getText("dialog.save.overwrite.text"),
- I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
- JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
- == JOptionPane.YES_OPTION)
- {
- if (exportFile(file, description.toString()))
- {
- fileSaved = true;
- }
- else
- {
- chooseAgain = true;
- }
- }
- else
- {
- chooseAgain = true;
- }
- }
- } while (chooseAgain);
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ _colourChooser = new ColourChooser(_dialog);
}
- return fileSaved;
+ enableCheckboxes();
+ _descriptionField.setEnabled(true);
+ _okButton.setEnabled(true);
+ _progressLabel.setText("");
+ _progressBar.setVisible(false);
+ _dialog.setVisible(true);
}
/**
- * Export the track data to the specified file with description
- * @param inFile File object to save to
- * @param inDescription description to use, if any
+ * Create dialog components
+ * @return Panel containing all gui elements in dialog
*/
- private boolean exportFile(File inFile, String inDescription)
+ private Component makeDialogComponents()
{
- FileWriter writer = null;
- try
+ JPanel dialogPanel = new JPanel();
+ dialogPanel.setLayout(new BorderLayout(0, 5));
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+ // Make a central panel with the text box and checkboxes
+ JPanel descPanel = new JPanel();
+ 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);
+ dialogPanel.add(mainPanel, BorderLayout.CENTER);
+ // point type selection
+ _pointTypeSelector = new PointTypeSelector();
+ _pointTypeSelector.setAlignmentX(Component.CENTER_ALIGNMENT);
+ mainPanel.add(_pointTypeSelector);
+ // Colour definition
+ Color trackColour = ColourUtils.colourFromHex(Config.getConfigString(Config.KEY_KML_TRACK_COLOUR));
+ if (trackColour == null) {
+ trackColour = DEFAULT_TRACK_COLOUR;
+ }
+ _colourPatch = new ColourPatch(trackColour);
+ _colourPatch.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ _colourChooser.showDialog(_colourPatch.getBackground());
+ Color colour = _colourChooser.getChosenColour();
+ if (colour != null) _colourPatch.setColour(colour);
+ }
+ });
+ JPanel colourPanel = new JPanel();
+ colourPanel.add(new JLabel(I18nManager.getText("dialog.exportkml.trackcolour")));
+ colourPanel.add(_colourPatch);
+ mainPanel.add(colourPanel);
+ // 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);
+ _kmzCheckbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ // enable image checkbox if kmz activated
+ enableCheckboxes();
+ }
+ });
+ mainPanel.add(_kmzCheckbox);
+ _exportImagesCheckbox = new JCheckBox(I18nManager.getText("dialog.exportkml.exportimages"));
+ _exportImagesCheckbox.setHorizontalTextPosition(SwingConstants.LEFT);
+ _exportImagesCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
+ mainPanel.add(_exportImagesCheckbox);
+ mainPanel.add(Box.createVerticalStrut(10));
+ _progressLabel = new JLabel("...");
+ _progressLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ mainPanel.add(_progressLabel);
+ _progressBar = new JProgressBar(0, 100);
+ _progressBar.setVisible(false);
+ _progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
+ mainPanel.add(_progressBar);
+ mainPanel.add(Box.createVerticalStrut(10));
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ _okButton = new JButton(I18nManager.getText("button.ok"));
+ ActionListener okListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ startExport();
+ }
+ };
+ _okButton.addActionListener(okListener);
+ _descriptionField.addActionListener(okListener);
+ buttonPanel.add(_okButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _cancelPressed = true;
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+ return dialogPanel;
+ }
+
+
+ /**
+ * Enable the checkboxes according to data
+ */
+ private void enableCheckboxes()
+ {
+ _pointTypeSelector.init(_trackInfo);
+ boolean hasAltitudes = _track.hasData(Field.ALTITUDE);
+ if (!hasAltitudes) {_altitudesCheckbox.setSelected(false);}
+ boolean hasPhotos = _trackInfo.getPhotoList() != null && _trackInfo.getPhotoList().getNumPhotos() > 0;
+ _exportImagesCheckbox.setSelected(hasPhotos && _kmzCheckbox.isSelected());
+ _exportImagesCheckbox.setEnabled(hasPhotos && _kmzCheckbox.isSelected());
+ }
+
+
+ /**
+ * Start the export process based on the input parameters
+ */
+ private void startExport()
+ {
+ // OK pressed, now validate selection checkboxes
+ if (!_pointTypeSelector.getAnythingSelected()) {
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
+ I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+ // Choose output file
+ if (_fileChooser == null)
{
- writer = new FileWriter(inFile);
- writer.write("\n\n\n");
- writer.write("\t");
- writer.write(inDescription);
- writer.write("\n");
-
- int i = 0;
- DataPoint point = null;
- boolean hasTrackpoints = false;
- // Loop over waypoints
- boolean writtenPhotoHeader = false;
- int numPoints = _track.getNumPoints();
- for (i=0; ihttp://maps.google.com/mapfiles/kml/pal4/icon46.png");
- writtenPhotoHeader = true;
- }
- exportPhotoPoint(point, writer);
+ file = new File(file.getAbsolutePath() + requiredExtension);
+ }
+ // Check if file exists and if necessary prompt for overwrite
+ Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
+ if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame,
+ I18nManager.getText("dialog.save.overwrite.text"),
+ I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
+ == JOptionPane.YES_OPTION)
+ {
+ // New file or overwrite confirmed, so initiate export in separate thread
+ _exportFile = file;
+ _cancelPressed = false;
+ new Thread(this).start();
}
else
{
- hasTrackpoints = true;
+ chooseAgain = true;
}
}
- if (hasTrackpoints)
+ } while (chooseAgain);
+ }
+
+
+ /**
+ * Run method for controlling separate thread for exporting
+ */
+ public void run()
+ {
+ // Disable ok button to stop second go
+ _okButton.setEnabled(false);
+ _descriptionField.setEnabled(false);
+ // Initialise progress indicators
+ _progressLabel.setText(I18nManager.getText("confirm.running"));
+ _progressBar.setVisible(true);
+ _progressBar.setValue(0);
+ boolean exportToKmz = _kmzCheckbox.isSelected();
+ boolean exportImages = exportToKmz && _exportImagesCheckbox.isSelected();
+ _progressBar.setMaximum(exportImages?getNumPhotosToExport():1);
+
+ // Determine photo thumbnail size from config
+ int thumbWidth = Config.getConfigInt(Config.KEY_KMZ_IMAGE_WIDTH);
+ if (thumbWidth < DEFAULT_THUMBNAIL_WIDTH) {thumbWidth = DEFAULT_THUMBNAIL_WIDTH;}
+ int thumbHeight = Config.getConfigInt(Config.KEY_KMZ_IMAGE_HEIGHT);
+ if (thumbHeight < DEFAULT_THUMBNAIL_HEIGHT) {thumbHeight = DEFAULT_THUMBNAIL_HEIGHT;}
+ // Create array for image dimensions in case it's required
+ _imageDimensions = new Dimension[_track.getNumPoints()];
+
+ OutputStreamWriter writer = null;
+ ZipOutputStream zipOutputStream = null;
+ try
+ {
+ // Select writer according to whether kmz requested or not
+ if (!_kmzCheckbox.isSelected())
+ {
+ // normal writing to file
+ writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
+ }
+ else
{
- writer.write("\t\n\t\ttrack\n\t\t\n\t\t\n\t\t\t");
- // Loop over track points
- for (i=0; i.jpg
+ // This is done first so that photo sizes are known for later
+ exportThumbnails(zipOutputStream, thumbWidth, thumbHeight);
}
- writer.write("\t\t\t\n\t\t\n\t");
+ writer = new OutputStreamWriter(zipOutputStream);
+ // Make an entry in the zip file for the kml file
+ ZipEntry kmlEntry = new ZipEntry(KML_FILENAME_IN_KMZ);
+ zipOutputStream.putNextEntry(kmlEntry);
+ }
+ // write file
+ final int numPoints = exportData(writer, exportImages);
+ // update config with selected track colour
+ Config.setConfigString(Config.KEY_KML_TRACK_COLOUR, ColourUtils.makeHexCode(_colourPatch.getBackground()));
+ // update progress bar
+ _progressBar.setValue(1);
+
+ // close zip entry if necessary
+ if (zipOutputStream != null)
+ {
+ // Make sure all buffered data in writer is flushed
+ writer.flush();
+ // Close off this entry in the zip file
+ zipOutputStream.closeEntry();
}
- writer.write("\n");
+
+ // close file
writer.close();
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.ok1")
- + " " + numPoints + " " + I18nManager.getText("dialog.save.ok2")
- + " " + inFile.getAbsolutePath(),
- I18nManager.getText("dialog.save.oktitle"), JOptionPane.INFORMATION_MESSAGE);
- return true;
+ _imageDimensions = null;
+ // Store directory in config for later
+ Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
+ // show confirmation
+ UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+ + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
+ + " " + _exportFile.getAbsolutePath());
+ // export successful so need to close dialog and return
+ _dialog.dispose();
+ return;
}
catch (IOException ioe)
{
@@ -184,29 +392,182 @@ public class KmlExporter
if (writer != null) writer.close();
}
catch (IOException ioe2) {}
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.failed") + ioe.getMessage(),
+ JOptionPane.showMessageDialog(_parentFrame,
+ I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(),
I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
}
- return false;
+ // if not returned already, export failed so need to recall the file selection
+ startExport();
+ }
+
+
+ /**
+ * Export the information to the given writer
+ * @param inWriter writer object
+ * @param inExportImages true if image thumbnails are to be referenced
+ * @return number of points written
+ */
+ private int exportData(OutputStreamWriter inWriter, boolean inExportImages)
+ throws IOException
+ {
+ 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");
+ if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
+ {
+ inWriter.write(_descriptionField.getText());
+ }
+ else {
+ inWriter.write("Export from Prune");
+ }
+ inWriter.write("\n");
+
+ // Examine selection if required
+ int selStart = -1, selEnd = -1;
+ if (justSelection) {
+ selStart = _trackInfo.getSelection().getStart();
+ selEnd = _trackInfo.getSelection().getEnd();
+ }
+
+ boolean absoluteAltitudes = _altitudesCheckbox.isSelected();
+ int i = 0;
+ DataPoint point = null;
+ boolean hasTrackpoints = false;
+ boolean writtenPhotoHeader = false, writtenAudioHeader = false;
+ final int numPoints = _track.getNumPoints();
+ int numSaved = 0;
+ int photoNum = 0;
+ // Loop over waypoints
+ for (i=0; i=selStart && i<=selEnd);
+ // Make a blob for each waypoint
+ if (point.isWaypoint())
+ {
+ if (writeWaypoints && writeCurrentPoint)
+ {
+ exportWaypoint(point, inWriter, absoluteAltitudes);
+ numSaved++;
+ }
+ }
+ 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 (!writtenPhotoHeader)
+ {
+ inWriter.write("");
+ writtenPhotoHeader = true;
+ }
+ photoNum++;
+ exportPhotoPoint(point, inWriter, inExportImages, i, photoNum, absoluteAltitudes);
+ numSaved++;
+ }
+ // Make a blob with description for each audio file
+ 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";
+ }
+ else {
+ trackStart += "\t\t\tclampToGround\n";
+ }
+ 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; i=selStart && i<=selEnd);
+ 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;
+ }
+ }
+ }
+ // end segment
+ inWriter.write(trackEnd);
+ }
+ inWriter.write("\n");
+ return numSaved;
}
+ /**
+ * Reverse the hex code for the colours for KML's stupid backwards format
+ * @param inCode colour code rrggbb
+ * @return kml code bbggrr
+ */
+ private static String reverse(String inCode)
+ {
+ return inCode.substring(4, 6) + inCode.substring(2, 4) + inCode.substring(0, 2);
+ }
/**
* Export the specified waypoint into the file
* @param inPoint waypoint to export
* @param inWriter writer object
+ * @param inAbsoluteAltitude true for absolute altitude
* @throws IOException on write failure
*/
- private void exportWaypoint(DataPoint inPoint, Writer inWriter) throws IOException
+ 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\t\t\t");
- inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
- inWriter.write(',');
- inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
- inWriter.write(",0\n\t\t\n\t\n");
+ String name = inPoint.getWaypointName().trim();
+ exportNamedPoint(inPoint, inWriter, name, null, 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().getFile().getName();
+ String desc = inPoint.getAudio().getFile().getAbsolutePath();
+ exportNamedPoint(inPoint, inWriter, name, desc, "audio_icon", inAbsoluteAltitude);
}
@@ -214,20 +575,81 @@ public class KmlExporter
* Export the specified photo into the file
* @param inPoint data point including photo
* @param inWriter writer object
+ * @param inImageLink flag to set whether to export image links or not
+ * @param inPointNumber number of point for accessing dimensions
+ * @param inImageNumber number of image for filename
+ * @param inAbsoluteAltitude true for absolute altitudes
* @throws IOException on write failure
*/
- private void exportPhotoPoint(DataPoint inPoint, Writer inWriter) throws IOException
+ private void exportPhotoPoint(DataPoint inPoint, Writer inWriter, boolean inImageLink,
+ int inPointNumber, int inImageNumber, boolean inAbsoluteAltitude)
+ throws IOException
+ {
+ String name = inPoint.getPhoto().getFile().getName();
+ String desc = null;
+ if (inImageLink)
+ {
+ Dimension imageSize = _imageDimensions[inPointNumber];
+ // Create html for the thumbnail images
+ desc = "
|
"
+ + "" + inPoint.getPhoto().getFile().getName() + " |
]]>";
+ }
+ // 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
{
- // TODO: Export photos to KML too - for photos need kmz!
inWriter.write("\t\n\t\t");
- inWriter.write(inPoint.getPhoto().getFile().getName());
+ inWriter.write(inName);
inWriter.write("\n");
- inWriter.write("#camera_icon\n");
- inWriter.write("\t\t\n\t\t\t");
- inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
+ if (inDesc != null)
+ {
+ // Write out description
+ inWriter.write("");
+ inWriter.write(inDesc);
+ inWriter.write("");
+ }
+ if (inStyle != null)
+ {
+ inWriter.write("#");
+ inWriter.write(inStyle);
+ 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_DEG_WITHOUT_CARDINAL));
- inWriter.write(",0\n\t\t\n\t\n");
+ inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
+ inWriter.write(',');
+ // Altitude if point has one
+ if (inPoint.hasAltitude()) {
+ inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
+ }
+ else {
+ inWriter.write('0');
+ }
+ inWriter.write("\n\t\t\n\t\n");
}
@@ -238,10 +660,95 @@ public class KmlExporter
*/
private void exportTrackpoint(DataPoint inPoint, Writer inWriter) throws IOException
{
- inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
+ inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
inWriter.write(',');
- inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DEG_WITHOUT_CARDINAL));
- // Altitude not exported, locked to ground by Google Earth
- inWriter.write(",0\n");
+ inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
+ // Altitude if point has one
+ inWriter.write(',');
+ if (inPoint.hasAltitude()) {
+ inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
+ }
+ else {
+ inWriter.write('0');
+ }
+ inWriter.write('\n');
+ }
+
+
+ /**
+ * Loop through the photos and create thumbnails
+ * @param inZipStream zip stream to save image files to
+ * @param inThumbWidth thumbnail width
+ * @param inThumbHeight thumbnail height
+ */
+ private void exportThumbnails(ZipOutputStream inZipStream, int inThumbWidth, int inThumbHeight)
+ throws IOException
+ {
+ // set up image writer
+ Iterator writers = ImageIO.getImageWritersByFormatName("jpg");
+ if (writers == null || !writers.hasNext())
+ {
+ throw new IOException("no JPEG writer found");
+ }
+ ImageWriter imageWriter = writers.next();
+
+ // Check selection checkbox
+ boolean justSelection = _pointTypeSelector.getJustSelection();
+ int selStart = -1, selEnd = -1;
+ if (justSelection) {
+ selStart = _trackInfo.getSelection().getStart();
+ selEnd = _trackInfo.getSelection().getEnd();
+ }
+
+ int numPoints = _track.getNumPoints();
+ DataPoint point = null;
+ int photoNum = 0;
+ // Loop over all points in track
+ for (int i=0; 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());
+
+ // Scale and smooth image to required size
+ BufferedImage bufferedImage = ImageUtils.rotateImage(icon.getImage(),
+ inThumbWidth, inThumbHeight, 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());
+
+ imageWriter.setOutput(ImageIO.createImageOutputStream(inZipStream));
+ imageWriter.write(bufferedImage);
+ // Close zip file entry
+ inZipStream.closeEntry();
+ // Update progress bar
+ _progressBar.setValue(photoNum+1);
+ }
+ }
+ }
+
+
+ /**
+ * @return number of correlated photos in the track
+ */
+ private int getNumPhotosToExport()
+ {
+ int numPoints = _track.getNumPoints();
+ int numPhotos = 0;
+ DataPoint point = null;
+ // Loop over all points in track
+ for (int i=0; i