package tim.prune.correlate; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Calendar; import java.util.Iterator; import java.util.TreeSet; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import tim.prune.App; import tim.prune.GenericFunction; import tim.prune.I18nManager; import tim.prune.data.DataPoint; import tim.prune.data.Distance; import tim.prune.data.Field; import tim.prune.data.Photo; import tim.prune.data.PhotoList; import tim.prune.data.TimeDifference; import tim.prune.data.Timestamp; import tim.prune.data.Track; import tim.prune.data.TrackInfo; import tim.prune.undo.UndoCorrelatePhotos; /** * Class to manage the automatic correlation of photos to points * including the GUI stuff to control the correlation options */ public class PhotoCorrelator extends GenericFunction { private JDialog _dialog; private JButton _nextButton = null, _backButton = null; private JButton _okButton = null; private JPanel _cards = null; private JTable _photoSelectionTable = null; private JLabel _tipLabel = null; private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null; private JRadioButton _photoLaterOption = null, _pointLaterOption = null; private JRadioButton _timeLimitRadio = null, _distLimitRadio = null; private JTextField _limitMinBox = null, _limitSecBox = null; private JTextField _limitDistBox = null; private JComboBox _distUnitsDropdown = null; private JTable _previewTable = null; private boolean _firstTabAvailable = false; private boolean _previewEnabled = false; // flag required to enable preview function on second panel /** * Constructor * @param inApp App object to report actions to */ public PhotoCorrelator(App inApp) { super(inApp); } /** Get the name key */ public String getNameKey() { return "function.correlatephotos"; } /** * Reset dialog and show it */ public void begin() { // First create dialog if necessary if (_dialog == null) { _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); _dialog.setLocationRelativeTo(_parentFrame); _dialog.getContentPane().add(makeDialogContents()); _dialog.pack(); } // Check whether track has timestamps, exit if not if (!_app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP)) { JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.correlate.notimestamps"), I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE); return; } // Check for any non-correlated photos, show warning continue/cancel if (!trackHasUncorrelatedPhotos()) { Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")}; if (JOptionPane.showOptionDialog(_parentFrame, I18nManager.getText("dialog.correlate.nouncorrelatedphotos"), I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) == JOptionPane.NO_OPTION) { return; } } PhotoSelectionTableModel model = makePhotoSelectionTableModel(_app.getTrackInfo()); _firstTabAvailable = model != null && model.getRowCount() > 0; CardLayout cl = (CardLayout) _cards.getLayout(); if (_firstTabAvailable) { cl.first(_cards); _nextButton.setEnabled(true); _backButton.setEnabled(false); _tipLabel.setVisible(false); _photoSelectionTable.setModel(model); _previewEnabled = false; for (int i=0; i 0.0 && correlatePhoto) { final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter()); double frac = pair.getFraction(); if (frac > 0.5) {frac = 1 - frac;} final double angDistPhoto = angDistPair * frac; correlatePhoto = (angDistPhoto < angDistLimit); } // Don't select photos which are already correlated to the same point if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(photo.getDataPoint())) { correlatePhoto = false; } row.setCorrelateFlag(correlatePhoto); model.addPhotoRow(row); } _previewTable.setModel(model); // Set distance units model.setDistanceUnits(getSelectedDistanceUnits()); // Set column widths _previewTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); final int[] colWidths = {150, 160, 100, 100, 50}; for (int i=0; i set = new TreeSet(); // loop through rows of table adding to list int numRows = inModel.getRowCount(); int i; for (i=0; i iterator = set.iterator(); for (i=0; i<(numRows+1)/2; i++) { pair = iterator.next(); } return pair.getIndex(); } /** * Disable the ok button */ public void disableOkButton() { if (_okButton != null) { _okButton.setEnabled(false); } } /** * Check if the track has any uncorrelated photos * @return true if there are any photos which are not connected to points */ private boolean trackHasUncorrelatedPhotos() { PhotoList photoList = _app.getTrackInfo().getPhotoList(); int numPhotos = photoList.getNumPhotos(); // loop over photos for (int i=0; i 0) { // make new array for added points DataPoint[] addedPoints = new DataPoint[numPointsToCreate]; int pointNum = 0; DataPoint pointToAdd = null; for (i=0; i 0L) { // interpolate point pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction()); } if (pointToAdd != null) { // link photo to point pointToAdd.setPhoto(pair.getPhoto()); pair.getPhoto().setDataPoint(pointToAdd); // set to start of segment so not joined in track pointToAdd.setSegmentStart(true); // add to point array addedPoints[pointNum] = pointToAdd; pointNum++; } } } // expand track _app.getTrackInfo().getTrack().appendPoints(addedPoints); } // send undo information back to controlling app undo.setNumPhotosCorrelated(numPhotos); _app.completeFunction(undo, ("" + numPhotos + " " + (numPhotos==1?I18nManager.getText("confirm.correlate.single"):I18nManager.getText("confirm.correlate.multi")))); // observers already informed by track update } }