X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fload%2FJpegLoader.java;h=672cd02d3cfe74fb32e537eb173d5377f5d239cf;hb=a6197ddcaac11c0b943183da7d46169742d024af;hp=e6917c1a48c2500f846e78cdd1338a97f000e3c0;hpb=5625a1abadb5f2ca5f017fe7dbda1d5141cb637b;p=GpsPrune.git diff --git a/tim/prune/load/JpegLoader.java b/tim/prune/load/JpegLoader.java index e6917c1..672cd02 100644 --- a/tim/prune/load/JpegLoader.java +++ b/tim/prune/load/JpegLoader.java @@ -1,51 +1,47 @@ package tim.prune.load; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.File; -import java.util.ArrayList; +import java.util.TreeSet; -import javax.swing.BorderFactory; import javax.swing.BoxLayout; -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.JPanel; -import javax.swing.JProgressBar; import tim.prune.App; import tim.prune.I18nManager; +import tim.prune.config.Config; import tim.prune.data.Altitude; import tim.prune.data.DataPoint; +import tim.prune.data.Field; +import tim.prune.data.LatLonRectangle; import tim.prune.data.Latitude; import tim.prune.data.Longitude; import tim.prune.data.Photo; -import tim.prune.data.PhotoStatus; import tim.prune.data.Timestamp; -import tim.prune.drew.jpeg.ExifReader; -import tim.prune.drew.jpeg.JpegData; -import tim.prune.drew.jpeg.JpegException; -import tim.prune.drew.jpeg.Rational; +import tim.prune.data.UnitSetLibrary; +import tim.prune.function.Cancellable; +import tim.prune.jpeg.ExifGateway; +import tim.prune.jpeg.JpegData; /** * Class to manage the loading of Jpegs and dealing with the GPS data from them */ -public class JpegLoader implements Runnable +public class JpegLoader implements Runnable, Cancellable { private App _app = null; private JFrame _parentFrame = null; private JFileChooser _fileChooser = null; + private GenericFileFilter _fileFilter = null; private JCheckBox _subdirCheckbox = null; private JCheckBox _noExifCheckbox = null; - private JDialog _progressDialog = null; - private JProgressBar _progressBar = null; + private JCheckBox _outsideAreaCheckbox = null; + private MediaLoadProgressDialog _progressDialog = null; private int[] _fileCounts = null; private boolean _cancelled = false; - private ArrayList _photos = null; + private LatLonRectangle _trackRectangle = null; + private TreeSet _photos = null; /** @@ -57,67 +53,58 @@ public class JpegLoader implements Runnable { _app = inApp; _parentFrame = inParentFrame; + _fileFilter = new JpegFileFilter(); } /** - * Select an input file and open the GUI frame - * to select load options + * Open the GUI to select options and start the load + * @param inRectangle track rectangle */ - public void openFile() + public void openDialog(LatLonRectangle inRectangle) { + // Create file chooser if necessary if (_fileChooser == null) { _fileChooser = new JFileChooser(); _fileChooser.setMultiSelectionEnabled(true); _fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + _fileChooser.setFileFilter(_fileFilter); _fileChooser.setDialogTitle(I18nManager.getText("menu.file.addphotos")); _subdirCheckbox = new JCheckBox(I18nManager.getText("dialog.jpegload.subdirectories")); _subdirCheckbox.setSelected(true); _noExifCheckbox = new JCheckBox(I18nManager.getText("dialog.jpegload.loadjpegswithoutcoords")); _noExifCheckbox.setSelected(true); + _outsideAreaCheckbox = new JCheckBox(I18nManager.getText("dialog.jpegload.loadjpegsoutsidearea")); + _outsideAreaCheckbox.setSelected(true); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.add(_subdirCheckbox); panel.add(_noExifCheckbox); + panel.add(_outsideAreaCheckbox); _fileChooser.setAccessory(panel); + // start from directory in config if already set by other operations + String configDir = Config.getConfigString(Config.KEY_PHOTO_DIR); + if (configDir == null) {configDir = Config.getConfigString(Config.KEY_TRACK_DIR);} + if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));} } + // enable/disable track checkbox + _trackRectangle = inRectangle; + _outsideAreaCheckbox.setEnabled(_trackRectangle != null && !_trackRectangle.isEmpty()); + // Show file dialog to choose file / directory(ies) if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION) { // Bring up dialog before starting - showDialog(); + _progressDialog = new MediaLoadProgressDialog(_parentFrame, this); + _progressDialog.show(); + // start thread for processing new Thread(this).start(); } } - - /** - * Show the main dialog - */ - private void showDialog() - { - _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title")); - _progressDialog.setLocationRelativeTo(_parentFrame); - _progressBar = new JProgressBar(0, 100); - _progressBar.setValue(0); - _progressBar.setStringPainted(true); - _progressBar.setString(""); - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); - panel.add(new JLabel(I18nManager.getText("dialog.jpegload.progress"))); - panel.add(_progressBar); - JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); - cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - _cancelled = true; - } - }); - panel.add(cancelButton); - _progressDialog.getContentPane().add(panel); - _progressDialog.pack(); - _progressDialog.show(); + /** Cancel */ + public void cancel() { + _cancelled = true; } @@ -127,52 +114,38 @@ public class JpegLoader implements Runnable public void run() { // Initialise arrays, errors, summaries - _fileCounts = new int[4]; // files, jpegs, exifs, gps - _photos = new ArrayList(); + _fileCounts = new int[3]; // files, jpegs, gps + _photos = new TreeSet(new MediaSorter()); File[] files = _fileChooser.getSelectedFiles(); // Loop recursively over selected files/directories to count files int numFiles = countFileList(files, true, _subdirCheckbox.isSelected()); // Set up the progress bar for this number of files - _progressBar.setMaximum(numFiles); - _progressBar.setValue(0); + _progressDialog.showProgress(0, numFiles); _cancelled = false; // Process the files recursively and build lists of photos processFileList(files, true, _subdirCheckbox.isSelected()); - _progressDialog.hide(); + _progressDialog.close(); if (_cancelled) {return;} - //System.out.println("Finished - counts are: " + _fileCounts[0] + ", " + _fileCounts[1] - // + ", " + _fileCounts[2] + ", " + _fileCounts[3]); if (_fileCounts[0] == 0) { // No files found at all - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.jpegload.nofilesfound"), - I18nManager.getText("error.jpegload.dialogtitle"), JOptionPane.ERROR_MESSAGE); + _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nofilesfound"); } else if (_fileCounts[1] == 0) { // No jpegs found - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.jpegload.nojpegsfound"), - I18nManager.getText("error.jpegload.dialogtitle"), JOptionPane.ERROR_MESSAGE); + _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nojpegsfound"); } else if (!_noExifCheckbox.isSelected() && _fileCounts[2] == 0) - { - // Need coordinates but no exif found - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.jpegload.noexiffound"), - I18nManager.getText("error.jpegload.dialogtitle"), JOptionPane.ERROR_MESSAGE); - } - else if (!_noExifCheckbox.isSelected() && _fileCounts[3] == 0) { // Need coordinates but no gps information found - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.jpegload.nogpsfound"), - I18nManager.getText("error.jpegload.dialogtitle"), JOptionPane.ERROR_MESSAGE); + _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nogpsfound"); } else { - // Found some photos to load - // TODO: Load jpeg information into dialog for confirmation? - // Pass information back to app + // Found some photos to load - pass information back to app _app.informPhotosLoaded(_photos); } } @@ -186,34 +159,27 @@ public class JpegLoader implements Runnable */ private void processFileList(File[] inFiles, boolean inFirstDir, boolean inDescend) { - if (inFiles != null) + if (inFiles == null) return; + // Loop over elements in array + for (int i=0; i " + photo.getTimestamp().getText()); - } - else - { - //System.out.println("timestamp from file = " + photo.getTimestamp().getText()); + if (timestamp == null) { + timestamp = new Timestamp(inFile.lastModified()); } - // Add the photo if it's got a point or if pointless photos should be added - if (photo.getDataPoint() != null || _noExifCheckbox.isSelected()) - { - _photos.add(photo); + // Apply timestamp to photo and its point (if any) + photo.setTimestamp(timestamp); + if (photo.getDataPoint() != null) { + photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.Format.ISO8601), false); } + return photo; } @@ -294,6 +282,11 @@ public class JpegLoader implements Runnable File file = inFiles[i]; if (file.exists() && file.canRead()) { + // Store first directory in config for later + if (i == 0 && inFirstDir) { + File workingDir = file.isDirectory()?file:file.getParentFile(); + Config.setConfigString(Config.KEY_PHOTO_DIR, workingDir.getAbsolutePath()); + } // Check whether it's a file or a directory if (file.isFile()) { @@ -325,26 +318,25 @@ public class JpegLoader implements Runnable inData.getLongitudeRef() == 'E' || inData.getLongitudeRef() == 'e'); Longitude longitude = new Longitude(lonval, Longitude.FORMAT_DEG_MIN_SEC); Altitude altitude = null; - if (inData.getAltitude() != null) - { - altitude = new Altitude(inData.getAltitude().intValue(), Altitude.FORMAT_METRES); + if (inData.hasAltitude()) { + altitude = new Altitude(inData.getAltitude(), UnitSetLibrary.UNITS_METRES); } return new DataPoint(latitude, longitude, altitude); } /** - * Convert an array of 3 Rational numbers into a double coordinate value - * @param inRationals array of three Rational objects + * Convert an array of 3 doubles (deg-min-sec) into a double coordinate value + * @param inValues array of three doubles for deg-min-sec * @param isPositive true for positive hemisphere, for positive double value * @return double value of coordinate, either positive or negative */ - private static double getCoordinateDoubleValue(Rational[] inRationals, boolean isPositive) + private static double getCoordinateDoubleValue(double[] inValues, boolean isPositive) { - if (inRationals == null || inRationals.length != 3) return 0.0; - double value = inRationals[0].doubleValue() // degrees - + inRationals[1].doubleValue() / 60.0 // minutes - + inRationals[2].doubleValue() / 60.0 / 60.0; // seconds + if (inValues == null || inValues.length != 3) return 0.0; + double value = inValues[0] // degrees + + inValues[1] / 60.0 // minutes + + inValues[2] / 60.0 / 60.0; // seconds // make sure it's the correct sign value = Math.abs(value); if (!isPositive) value = -value; @@ -353,46 +345,39 @@ public class JpegLoader implements Runnable /** - * Use the given Rational values to create a timestamp - * @param inDate rationals describing date - * @param inTime rationals describing time + * Use the given int values to create a timestamp + * @param inDate ints describing date + * @param inTime ints describing time * @return Timestamp object corresponding to inputs */ - private static Timestamp createTimestamp(Rational[] inDate, Rational[] inTime) + private static Timestamp createTimestamp(int[] inDate, int[] inTime) { - //System.out.println("Making timestamp for date (" + inDate[0].toString() + "," + inDate[1].toString() + "," + inDate[2].toString() + ") and time (" - // + inTime[0].toString() + "," + inTime[1].toString() + "," + inTime[2].toString() + ")"); - return new Timestamp(inDate[0].intValue(), inDate[1].intValue(), inDate[2].intValue(), - inTime[0].intValue(), inTime[1].intValue(), inTime[2].intValue()); + if (inDate == null || inTime == null || inDate.length != 3 || inTime.length != 3) { + return null; + } + return new Timestamp(inDate[0], inDate[1], inDate[2], + inTime[0], inTime[1], inTime[2]); } /** - * Check whether to accept the given filename - * @param inName name of file - * @return true if accepted, false otherwise + * Use the given String value to create a timestamp + * @param inStamp timestamp from exif + * @return Timestamp object corresponding to input */ - private static boolean acceptPhotoFilename(String inName) + private static Timestamp createTimestamp(String inStamp) { - if (inName != null && inName.length() > 4) + Timestamp stamp = null; + try { - // Check for three-character file extensions jpg and jpe - String lastFour = inName.substring(inName.length() - 4).toLowerCase(); - if (lastFour.equals(".jpg") || lastFour.equals(".jpe")) - { - return true; - } - // If not found, check for file extension jpeg - if (inName.length() > 5) - { - String lastFive = inName.substring(inName.length() - 5).toLowerCase(); - if (lastFive.equals(".jpeg")) - { - return true; - } - } + stamp = new Timestamp(Integer.parseInt(inStamp.substring(0, 4)), + Integer.parseInt(inStamp.substring(5, 7)), + Integer.parseInt(inStamp.substring(8, 10)), + Integer.parseInt(inStamp.substring(11, 13)), + Integer.parseInt(inStamp.substring(14, 16)), + Integer.parseInt(inStamp.substring(17))); } - // Not matched so don't accept - return false; + catch (NumberFormatException nfe) {} + return stamp; } }