]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/correlate/PhotoCorrelator.java
Version 11, August 2010
[GpsPrune.git] / tim / prune / correlate / PhotoCorrelator.java
index f2c1fc8961ecdc412a5a91495129911bd6f5ea4d..84c179ec3d3a23576d9e6ee83cdf1133758e20af 100644 (file)
@@ -17,7 +17,6 @@ import javax.swing.ButtonGroup;
 import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JDialog;
-import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
@@ -27,6 +26,7 @@ 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;
@@ -37,15 +37,14 @@ 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
+public class PhotoCorrelator extends GenericFunction
 {
-       private App _app;
-       private JFrame _parentFrame;
        private JDialog _dialog;
        private JButton _nextButton = null, _backButton = null;
        private JButton _okButton = null;
@@ -66,29 +65,36 @@ public class PhotoCorrelator
        /**
         * Constructor
         * @param inApp App object to report actions to
-        * @param inFrame parent frame for dialogs
         */
-       public PhotoCorrelator(App inApp, JFrame inFrame)
+       public PhotoCorrelator(App inApp)
        {
-               _app = inApp;
-               _parentFrame = inFrame;
-               _dialog = new JDialog(inFrame, I18nManager.getText("dialog.correlate.title"), true);
-               _dialog.setLocationRelativeTo(inFrame);
-               _dialog.getContentPane().add(makeDialogContents());
-               _dialog.pack();
+               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("dialog.correlate.title"), JOptionPane.INFORMATION_MESSAGE);
+                               I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
                        return;
                }
                // Check for any non-correlated photos, show warning continue/cancel
@@ -96,7 +102,7 @@ public class PhotoCorrelator
                {
                        Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")};
                        if (JOptionPane.showOptionDialog(_parentFrame, I18nManager.getText("dialog.correlate.nouncorrelatedphotos"),
-                                       I18nManager.getText("dialog.correlate.title"), JOptionPane.YES_NO_OPTION,
+                                       I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION,
                                        JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
                                == JOptionPane.NO_OPTION)
                        {
@@ -127,7 +133,7 @@ public class PhotoCorrelator
                        _tipLabel.setVisible(true);
                        setupSecondCard(null);
                }
-               _dialog.show();
+               _dialog.setVisible(true);
        }
 
 
@@ -160,8 +166,11 @@ public class PhotoCorrelator
                JPanel card2Top = new JPanel();
                card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS));
                _tipLabel = new JLabel(I18nManager.getText("dialog.correlate.options.tip"));
+               _tipLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
                card2Top.add(_tipLabel);
-               card2Top.add(new JLabel(I18nManager.getText("dialog.correlate.options.intro")));
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro"));
+               introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
+               card2Top.add(introLabel);
                // time offset section
                JPanel offsetPanel = new JPanel();
                offsetPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.offsetpanel")));
@@ -256,7 +265,7 @@ public class PhotoCorrelator
                card2Top.add(previewButton);
                card2.add(card2Top, BorderLayout.NORTH);
                // preview
-               _previewTable = new JTable();
+               _previewTable = new JTable(new PhotoPreviewTableModel());
                JScrollPane previewScrollPane = new JScrollPane(_previewTable);
                previewScrollPane.setPreferredSize(new Dimension(300, 100));
                card2.add(previewScrollPane, BorderLayout.CENTER);
@@ -298,7 +307,7 @@ public class PhotoCorrelator
                        {
                                public void actionPerformed(ActionEvent e)
                                {
-                                       _app.finishCorrelatePhotos(getPointPairs());
+                                       finishCorrelation();
                                        _dialog.dispose();
                                }
                        });
@@ -330,7 +339,9 @@ public class PhotoCorrelator
                for (int i=0; i<numPhotos; i++)
                {
                        Photo photo = inTrackInfo.getPhotoList().getPhoto(i);
-                       if (photo.getDataPoint() != null && photo.getDataPoint().hasTimestamp())
+                       // For working out time differences, can't use photos which already had point information
+                       if (photo.getDataPoint() != null && photo.getDataPoint().hasTimestamp()
+                               && photo.getOriginalStatus() == Photo.Status.NOT_CONNECTED)
                        {
                                // Calculate time difference, add to table model
                                long timeDiff = photo.getTimestamp().getSecondsSince(photo.getDataPoint().getTimestamp());
@@ -376,7 +387,7 @@ public class PhotoCorrelator
                _pointLaterOption.setSelected(!inTimeDiff.getIsPositive());
                createPreview(inTimeDiff, true);
                CardLayout cl = (CardLayout) _cards.getLayout();
-               cl.next(_cards);
+               cl.last(_cards);
                _backButton.setEnabled(hasTimeDiff);
                _nextButton.setEnabled(false);
                // enable ok button if any photos have been selected
@@ -422,6 +433,8 @@ public class PhotoCorrelator
                        PhotoPreviewTableRow row = new PhotoPreviewTableRow(pair);
                        // Don't try to correlate photos which don't have points either side
                        boolean correlatePhoto = pair.isValid();
+                       // Don't select photos which already have a point
+                       if (photo.getCurrentStatus() != Photo.Status.NOT_CONNECTED) {correlatePhoto = false;}
                        // Check time limits, distance limits
                        if (timeLimit != null && correlatePhoto) {
                                long numSecs = pair.getMinSeconds();
@@ -436,8 +449,7 @@ public class PhotoCorrelator
                                correlatePhoto = (angDistPhoto < angDistLimit);
                        }
                        // Don't select photos which are already correlated to the same point
-                       if (pair.getSecondsBefore() == 0L && pair.getPointBefore().getPhoto() != null
-                               && pair.getPointBefore().getPhoto().equals(photo)) {
+                       if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(photo.getDataPoint())) {
                                correlatePhoto = false;
                        }
                        row.setCorrelateFlag(correlatePhoto);
@@ -457,7 +469,7 @@ public class PhotoCorrelator
                if (inShowWarning && !model.hasPhotosSelected())
                {
                        JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.correlate.alloutsiderange"),
-                               I18nManager.getText("dialog.correlate.title"), JOptionPane.ERROR_MESSAGE);
+                               I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
                }
        }
 
@@ -503,9 +515,9 @@ public class PhotoCorrelator
        /**
         * @return the selected distance units from the dropdown
         */
-       private int getSelectedDistanceUnits()
+       private Distance.Units getSelectedDistanceUnits()
        {
-               final int[] distUnits = {Distance.UNITS_KILOMETRES, Distance.UNITS_METRES, Distance.UNITS_MILES};
+               final Distance.Units[] distUnits = {Distance.Units.KILOMETRES, Distance.Units.METRES, Distance.Units.MILES};
                return distUnits[_distUnitsDropdown.getSelectedIndex()];
        }
 
@@ -536,17 +548,20 @@ public class PhotoCorrelator
        private static PointPair getPointPairForPhoto(Track inTrack, Photo inPhoto, TimeDifference inOffset)
        {
                PointPair pair = new PointPair(inPhoto);
-               // Add offet to photo timestamp
-               Timestamp photoStamp = inPhoto.getTimestamp().subtractOffset(inOffset);
+               // Add/subtract offet to photo timestamp
+               Timestamp photoStamp = inPhoto.getTimestamp().createMinusOffset(inOffset);
                int numPoints = inTrack.getNumPoints();
                for (int i=0; i<numPoints; i++)
                {
                        DataPoint point = inTrack.getPoint(i);
-                       Timestamp pointStamp = point.getTimestamp();
-                       if (pointStamp != null && pointStamp.isValid())
+                       if (point.getPhoto() == null || point.getPhoto().getCurrentStatus() != Photo.Status.TAGGED)
                        {
-                               long numSeconds = pointStamp.getSecondsSince(photoStamp);
-                               pair.addPoint(point, numSeconds);
+                               Timestamp pointStamp = point.getTimestamp();
+                               if (pointStamp != null && pointStamp.isValid())
+                               {
+                                       long numSeconds = pointStamp.getSecondsSince(photoStamp);
+                                       pair.addPoint(point, numSeconds);
+                               }
                        }
                }
                return pair;
@@ -604,7 +619,7 @@ public class PhotoCorrelator
        private static int getMedianIndex(PhotoSelectionTableModel inModel)
        {
                // make sortable list
-               TreeSet set = new TreeSet();
+               TreeSet<TimeIndexPair> set = new TreeSet<TimeIndexPair>();
                // loop through rows of table adding to list
                int numRows = inModel.getRowCount();
                int i;
@@ -615,10 +630,10 @@ public class PhotoCorrelator
                }
                // pull out middle entry and return index
                TimeIndexPair pair = null;
-               Iterator iterator = set.iterator();
+               Iterator<TimeIndexPair> iterator = set.iterator();
                for (i=0; i<(numRows+1)/2; i++)
                {
-                       pair = (TimeIndexPair) iterator.next();
+                       pair = iterator.next();
                }
                return pair.getIndex();
        }
@@ -654,4 +669,99 @@ public class PhotoCorrelator
                // no uncorrelated photos found
                return false;
        }
+
+       /**
+        * Finish the correlation by modifying the track
+        * and passing the Undo information back to the App
+        */
+       private void finishCorrelation()
+       {
+               PointPair[] pointPairs = getPointPairs();
+               if (pointPairs == null || pointPairs.length <= 0) {return;}
+
+               // begin to construct undo information
+               UndoCorrelatePhotos undo = new UndoCorrelatePhotos(_app.getTrackInfo());
+               // loop over Photos
+               int arraySize = pointPairs.length;
+               int i = 0, numPhotos = 0;
+               int numPointsToCreate = 0;
+               PointPair pair = null;
+               for (i=0; i<arraySize; i++)
+               {
+                       pair = pointPairs[i];
+                       if (pair != null && pair.isValid())
+                       {
+                               if (pair.getMinSeconds() == 0L)
+                               {
+                                       // exact match
+                                       Photo pointPhoto = pair.getPointBefore().getPhoto();
+                                       if (pointPhoto == null)
+                                       {
+                                               // photo coincides with photoless point so connect the two
+                                               pair.getPointBefore().setPhoto(pair.getPhoto());
+                                               pair.getPhoto().setDataPoint(pair.getPointBefore());
+                                       }
+                                       else if (pointPhoto.equals(pair.getPhoto())) {
+                                               // photo is already connected, nothing to do
+                                       }
+                                       else {
+                                               // point is already connected to a different photo, so need to clone point
+                                               numPointsToCreate++;
+                                       }
+                               }
+                               else
+                               {
+                                       // photo time falls between two points, so need to interpolate new one
+                                       numPointsToCreate++;
+                               }
+                               numPhotos++;
+                       }
+               }
+               // Second loop, to create points if necessary
+               if (numPointsToCreate > 0)
+               {
+                       // make new array for added points
+                       DataPoint[] addedPoints = new DataPoint[numPointsToCreate];
+                       int pointNum = 0;
+                       DataPoint pointToAdd = null;
+                       for (i=0; i<arraySize; i++)
+                       {
+                               pair = pointPairs[i];
+                               if (pair != null && pair.isValid())
+                               {
+                                       pointToAdd = null;
+                                       if (pair.getMinSeconds() == 0L && pair.getPointBefore().getPhoto() != null
+                                        && !pair.getPointBefore().getPhoto().equals(pair.getPhoto()))
+                                       {
+                                               // clone point
+                                               pointToAdd = pair.getPointBefore().clonePoint();
+                                       }
+                                       else if (pair.getMinSeconds() > 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
+       }
 }