1 package tim.prune.correlate;
3 import javax.swing.JOptionPane;
4 import javax.swing.JTable;
7 import tim.prune.DataSubscriber;
8 import tim.prune.I18nManager;
9 import tim.prune.UpdateMessageBroker;
10 import tim.prune.data.DataPoint;
11 import tim.prune.data.MediaList;
12 import tim.prune.data.Photo;
13 import tim.prune.data.PhotoList;
14 import tim.prune.data.TimeDifference;
15 import tim.prune.undo.UndoCorrelatePhotos;
18 * Class to manage the automatic correlation of photos to points
19 * including the GUI stuff to control the correlation options
21 public class PhotoCorrelator extends Correlator
25 * @param inApp App object to report actions to
27 public PhotoCorrelator(App inApp) {
32 /** Get the name key */
33 public String getNameKey() {
34 return "function.correlatephotos";
37 /** @return type key */
38 protected String getMediaTypeKey() {
42 /** @return photo list*/
43 protected MediaList getMediaList() {
44 return _app.getTrackInfo().getPhotoList();
48 * Create a preview of the correlate action using the selected time difference
49 * @param inTimeDiff TimeDifference to use for preview
50 * @param inShowWarning true to show warning if all points out of range
52 protected void createPreview(TimeDifference inTimeDiff, boolean inShowWarning)
54 TimeDifference timeLimit = parseTimeLimit();
55 double angDistLimit = parseDistanceLimit();
56 MediaPreviewTableModel model = new MediaPreviewTableModel("dialog.correlate.select.photoname");
57 PhotoList photos = _app.getTrackInfo().getPhotoList();
58 // Loop through photos deciding whether to set correlate flag or not
59 int numPhotos = photos.getNumPhotos();
60 for (int i=0; i<numPhotos; i++)
62 Photo photo = photos.getPhoto(i);
63 PointMediaPair pair = getPointPairForMedia(_app.getTrackInfo().getTrack(), photo, inTimeDiff);
64 MediaPreviewTableRow row = new MediaPreviewTableRow(pair);
65 // Don't try to correlate photos which don't have points either side
66 boolean correlatePhoto = pair.isValid();
67 // Don't select photos which already have a point
68 if (photo.getCurrentStatus() != Photo.Status.NOT_CONNECTED) {correlatePhoto = false;}
69 // Check time limits, distance limits
70 if (timeLimit != null && correlatePhoto) {
71 long numSecs = pair.getMinSeconds();
72 correlatePhoto = (numSecs <= timeLimit.getTotalSeconds());
74 if (angDistLimit > 0.0 && correlatePhoto)
76 final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter());
77 double frac = pair.getFraction();
78 if (frac > 0.5) {frac = 1 - frac;}
79 final double angDistPhoto = angDistPair * frac;
80 correlatePhoto = (angDistPhoto < angDistLimit);
82 // Don't select photos which are already correlated to the same point
83 if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(photo.getDataPoint())) {
84 correlatePhoto = false;
86 row.setCorrelateFlag(correlatePhoto);
89 _previewTable.setModel(model);
91 model.setDistanceUnits(getSelectedDistanceUnits());
93 _previewTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
94 final int[] colWidths = {150, 160, 100, 100, 50};
95 for (int i=0; i<model.getColumnCount(); i++) {
96 _previewTable.getColumnModel().getColumn(i).setPreferredWidth(colWidths[i]);
98 // check if any photos found
99 _okButton.setEnabled(model.hasAnySelected());
100 if (inShowWarning && !model.hasAnySelected())
102 JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.correlate.alloutsiderange"),
103 I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
109 * Finish the correlation by modifying the track
110 * and passing the Undo information back to the App
112 protected void finishCorrelation()
114 PointMediaPair[] pointPairs = getPointPairs();
115 if (pointPairs == null || pointPairs.length <= 0) {return;}
117 // begin to construct undo information
118 UndoCorrelatePhotos undo = new UndoCorrelatePhotos(_app.getTrackInfo());
120 int arraySize = pointPairs.length;
121 int i = 0, numPhotos = 0;
122 int numPointsToCreate = 0;
123 PointMediaPair pair = null;
124 for (i=0; i<arraySize; i++)
126 pair = pointPairs[i];
127 if (pair != null && pair.isValid())
129 if (pair.getMinSeconds() == 0L)
132 Photo pointPhoto = pair.getPointBefore().getPhoto();
133 if (pointPhoto == null)
135 // photo coincides with photoless point so connect the two
136 pair.getPointBefore().setPhoto((Photo) pair.getMedia());
137 pair.getMedia().setDataPoint(pair.getPointBefore());
139 else if (pointPhoto.equals(pair.getMedia())) {
140 // photo is already connected, nothing to do
143 // point is already connected to a different photo, so need to clone point
149 // photo time falls between two points, so need to interpolate new one
155 // Second loop, to create points if necessary
156 if (numPointsToCreate > 0)
158 // make new array for added points
159 DataPoint[] addedPoints = new DataPoint[numPointsToCreate];
161 DataPoint pointToAdd = null;
162 for (i=0; i<arraySize; i++)
164 pair = pointPairs[i];
165 if (pair != null && pair.isValid())
168 if (pair.getMinSeconds() == 0L && pair.getPointBefore().getPhoto() != null
169 && !pair.getPointBefore().getPhoto().equals(pair.getMedia()))
172 pointToAdd = pair.getPointBefore().clonePoint();
174 else if (pair.getMinSeconds() > 0L)
177 pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction());
179 if (pointToAdd != null)
181 // link photo to point
182 pointToAdd.setPhoto((Photo) pair.getMedia());
183 pair.getMedia().setDataPoint(pointToAdd);
184 // set to start of segment so not joined in track
185 pointToAdd.setSegmentStart(true);
186 // add to point array
187 addedPoints[pointNum] = pointToAdd;
193 _app.getTrackInfo().getTrack().appendPoints(addedPoints);
196 // send undo information back to controlling app
197 undo.setNumPhotosCorrelated(numPhotos);
198 _app.completeFunction(undo, ("" + numPhotos + " "
199 + (numPhotos==1?I18nManager.getText("confirm.correlatephotos.single"):I18nManager.getText("confirm.correlatephotos.multi"))));
200 // observers already informed by track update if new points created
201 if (numPointsToCreate == 0) {
202 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);