X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=src%2Ftim%2Fprune%2Fsave%2FExifSaver.java;fp=src%2Ftim%2Fprune%2Fsave%2FExifSaver.java;h=d17c5d48fe4ca951ae58518b408c3724d7fbd5e5;hp=0000000000000000000000000000000000000000;hb=ce6f2161b8596f7018d6a76bff79bc9e571f35fd;hpb=2d8cb72e84d5cc1089ce77baf1e34ea3ea2f8465 diff --git a/src/tim/prune/save/ExifSaver.java b/src/tim/prune/save/ExifSaver.java new file mode 100644 index 0000000..d17c5d4 --- /dev/null +++ b/src/tim/prune/save/ExifSaver.java @@ -0,0 +1,420 @@ +package tim.prune.save; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import tim.prune.ExternalTools; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; +import tim.prune.data.Coordinate; +import tim.prune.data.DataPoint; +import tim.prune.data.Photo; +import tim.prune.data.PhotoList; + +/** + * Class to call Exiftool to save coordinate information in jpg files + */ +public class ExifSaver implements Runnable +{ + private Frame _parentFrame = null; + private JDialog _dialog = null; + private JButton _okButton = null; + private JCheckBox _overwriteCheckbox = null; + private JCheckBox _forceCheckbox = null; + private JProgressBar _progressBar = null; + private PhotoTableModel _photoTableModel = null; + private boolean _saveCancelled = false; + + + // To preserve timestamps of file use parameter -P + // To overwrite file (careful!) use parameter -overwrite_original_in_place + + // To read all GPS tags, use -GPS:All + // To delete all GPS tags, use -GPS:All= + + // To set Altitude, use -GPSAltitude= and -GPSAltitudeRef= + // To set Latitude, use -GPSLatitude= and -GPSLatitudeRef= + + // To delete all tags with overwrite: exiftool -P -overwrite_original_in_place -GPS:All= + + // To set altitude with overwrite: exiftool -P -overwrite_original_in_place -GPSAltitude=1234 -GPSAltitudeRef='Above Sea Level' + // (setting altitude ref to 0 doesn't work) + // To set latitude with overwrite: exiftool -P -overwrite_original_in_place -GPSLatitude='12 34 56.78' -GPSLatitudeRef=N + // (latitude as space-separated deg min sec, reference as either N or S) + // Same for longitude, reference E or W + + + /** + * Constructor + * @param inParentFrame parent frame + */ + public ExifSaver(Frame inParentFrame) + { + _parentFrame = inParentFrame; + } + + + /** + * Save exif information to all photos in the list + * whose coordinate information has changed since loading + * @param inPhotoList list of photos to save + * @return true if saved + */ + public boolean saveExifInformation(PhotoList inPhotoList) + { + // Check if external exif tool can be called + boolean exifToolInstalled = ExternalTools.isToolInstalled(ExternalTools.TOOL_EXIFTOOL); + if (!exifToolInstalled) + { + // show warning + int answer = JOptionPane.showConfirmDialog(_dialog, I18nManager.getText("dialog.saveexif.noexiftool"), + I18nManager.getText("dialog.saveexif.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + if (answer == JOptionPane.NO_OPTION || answer == JOptionPane.CLOSED_OPTION) + { + return false; + } + } + // Make model and add all photos to it + _photoTableModel = new PhotoTableModel(inPhotoList.getNumPhotos()); + for (int i=0; i 0) + { + JOptionPane.showMessageDialog(_parentFrame, + I18nManager.getTextWithNumber("error.saveexif.failed", numFailed), + I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE); + } + if (numForced > 0) + { + JOptionPane.showMessageDialog(_parentFrame, + I18nManager.getTextWithNumber("error.saveexif.forced", numForced), + I18nManager.getText("dialog.saveexif.title"), JOptionPane.WARNING_MESSAGE); + } + // close dialog, all finished + _dialog.dispose(); + } + + + /** + * Save the details for the given photo + * @param inPhoto Photo object + * @param inOverwriteFlag true to overwrite file, false otherwise + * @param inForceFlag true to force write, ignoring minor errors + * @return true if details saved ok + */ + private boolean savePhoto(Photo inPhoto, boolean inOverwriteFlag, boolean inForceFlag) + { + // If photos don't have a file, then can't save them + if (inPhoto.getFile() == null) { + return false; + } + // Check whether photo file still exists + if (!inPhoto.getFile().exists()) + { + // photo file doesn't exist any more + JOptionPane.showMessageDialog(_parentFrame, + I18nManager.getText("error.saveexif.filenotfound") + " : " + inPhoto.getFile().getAbsolutePath(), + I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE); + return false; + } + // Warn if file read-only and selected to overwrite + if (inOverwriteFlag && !inPhoto.getFile().canWrite()) + { + // eek, can't overwrite file + int answer = JOptionPane.showConfirmDialog(_parentFrame, + I18nManager.getText("error.saveexif.cannotoverwrite1") + " " + inPhoto.getFile().getAbsolutePath() + + " " + I18nManager.getText("error.saveexif.cannotoverwrite2"), + I18nManager.getText("dialog.saveexif.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE); + if (answer == JOptionPane.YES_OPTION) + { + // don't overwrite this image but write to copy + inOverwriteFlag = false; + } + else + { + // don't do anything with this file + return false; + } + } + String[] command = null; + if (inPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED) + { + // Photo is no longer connected, so delete gps tags + command = getDeleteGpsExifTagsCommand(inPhoto.getFile(), inOverwriteFlag); + } + else + { + // Photo is now connected, so write new gps tags + command = getWriteGpsExifTagsCommand(inPhoto.getFile(), inPhoto.getDataPoint(), inOverwriteFlag, inForceFlag); + } + // Execute exif command + boolean saved = false; + try + { + Process process = Runtime.getRuntime().exec(command); + // Wait for process to finish so not too many run in parallel + try { + process.waitFor(); + } + catch (InterruptedException ie) {} + saved = (process.exitValue() == 0); + } + catch (Exception e) + { + // show error message + JOptionPane.showMessageDialog(_parentFrame, "Exception: '" + e.getClass().getName() + "' : " + + e.getMessage(), I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE); + } + return saved; + } + + + /** + * Create the command to delete the gps exif tags from the specified file + * @param inFile file from which to delete tags + * @param inOverwrite true to overwrite file, false to create copy + * @return external command to delete gps tags + */ + private static String[] getDeleteGpsExifTagsCommand(File inFile, boolean inOverwrite) + { + // Make a string array to construct the command and its parameters + String[] result = new String[inOverwrite?5:4]; + result[0] = Config.getConfigString(Config.KEY_EXIFTOOL_PATH); + result[1] = "-P"; + if (inOverwrite) {result[2] = " -overwrite_original_in_place";} + // remove all gps tags + int paramOffset = inOverwrite?3:2; + result[paramOffset] = "-GPS:All="; + result[paramOffset + 1] = inFile.getAbsolutePath(); + return result; + } + + + /** + * Create the comand to write the gps exif tags to the specified file + * @param inFile file to which to write the tags + * @param inPoint DataPoint object containing coordinate information + * @param inOverwrite true to overwrite file, false to create copy + * @param inForce true to force write, ignoring minor errors + * @return external command to write gps tags + */ + private static String[] getWriteGpsExifTagsCommand(File inFile, DataPoint inPoint, + boolean inOverwrite, boolean inForce) + { + // Make a string array to construct the command and its parameters + String[] result = new String[(inOverwrite?10:9) + (inForce?1:0)]; + result[0] = Config.getConfigString(Config.KEY_EXIFTOOL_PATH); + result[1] = "-P"; + if (inOverwrite) {result[2] = "-overwrite_original_in_place";} + int paramOffset = inOverwrite?3:2; + if (inForce) { + result[paramOffset] = "-m"; + paramOffset++; + } + // To set latitude : -GPSLatitude='12 34 56.78' -GPSLatitudeRef='N' + // (latitude as space-separated deg min sec, reference as either N or S) + result[paramOffset] = "-GPSLatitude='" + inPoint.getLatitude().output(Coordinate.FORMAT_DEG_MIN_SEC_WITH_SPACES) + + "'"; + result[paramOffset + 1] = "-GPSLatitudeRef=" + inPoint.getLatitude().output(Coordinate.FORMAT_CARDINAL); + // same for longitude with space-separated deg min sec, reference as either E or W + result[paramOffset + 2] = "-GPSLongitude='" + inPoint.getLongitude().output(Coordinate.FORMAT_DEG_MIN_SEC_WITH_SPACES) + + "'"; + result[paramOffset + 3] = "-GPSLongitudeRef=" + inPoint.getLongitude().output(Coordinate.FORMAT_CARDINAL); + // add altitude if it has it + result[paramOffset + 4] = "-GPSAltitude=" + + (inPoint.hasAltitude()?inPoint.getAltitude().getMetricValue():0); + result[paramOffset + 5] = "-GPSAltitudeRef='Above Sea Level'"; + // add the filename to modify + result[paramOffset + 6] = inFile.getAbsolutePath(); + return result; + } +}