Java tutorial
/******************************************************************************* * Copyright (C) 2005, 2016 Wolfgang Schramm and Contributors * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA *******************************************************************************/ package net.tourbook.tour.photo; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Set; import net.tourbook.Messages; import net.tourbook.application.TourbookPlugin; import net.tourbook.common.UI; import net.tourbook.common.util.PostSelectionProvider; import net.tourbook.common.util.SQL; import net.tourbook.common.util.StatusUtil; import net.tourbook.common.util.Util; import net.tourbook.data.TourData; import net.tourbook.data.TourPhoto; import net.tourbook.database.TourDatabase; import net.tourbook.photo.Camera; import net.tourbook.photo.IPhotoServiceProvider; import net.tourbook.photo.ImagePathReplacement; import net.tourbook.photo.Photo; import net.tourbook.photo.PhotoCache; import net.tourbook.photo.PhotoEventId; import net.tourbook.photo.PhotoImageMetadata; import net.tourbook.photo.PhotoManager; import net.tourbook.photo.PhotosWithExifSelection; import net.tourbook.photo.TourPhotoReference; import net.tourbook.preferences.ITourbookPreferences; import net.tourbook.tour.SelectionTourId; import net.tourbook.tour.TourManager; import net.tourbook.ui.SQLFilter; import org.apache.commons.imaging.ImageReadException; import org.apache.commons.imaging.ImageWriteException; import org.apache.commons.imaging.Imaging; import org.apache.commons.imaging.common.IImageMetadata; import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; public class TourPhotoManager implements IPhotoServiceProvider { private static final String STATE_CAMERA_ADJUSTMENT_NAME = "STATE_CAMERA_ADJUSTMENT_NAME"; //$NON-NLS-1$ private static final String STATE_CAMERA_ADJUSTMENT_TIME = "STATE_CAMERA_ADJUSTMENT_TIME"; //$NON-NLS-1$ private static final String STATE_REPLACE_IMAGE_FOLDER = "STATE_REPLACE_IMAGE_FOLDER"; //$NON-NLS-1$ private static final String CAMERA_UNKNOWN_KEY = "CAMERA_UNKNOWN_KEY"; //$NON-NLS-1$ private final IPreferenceStore _prefStore = TourbookPlugin.getDefault() // .getPreferenceStore(); private static final IDialogSettings _state = TourbookPlugin.getDefault() // .getDialogSettingsSection("PhotoManager"); //$NON-NLS-1$ private static TourPhotoManager _instance; /** * Contains all cameras which are every used, key is the camera name. */ private static HashMap<String, Camera> _allAvailableCameras = new HashMap<String, Camera>(); private Connection _sqlConnection; private PreparedStatement _sqlStatement; private long _sqlTourStart = Long.MAX_VALUE; private long _sqlTourEnd = Long.MIN_VALUE; private ArrayList<TourPhotoLink> _allDbTourPhotoLinks = new ArrayList<TourPhotoLink>(); private ArrayList<TourPhotoLink> _dbTourPhotoLinks = new ArrayList<TourPhotoLink>(); private static String _replaceImageFolder; /** * Compares 2 photos by the adjusted time. */ public static final Comparator<? super Photo> AdjustTimeComparatorLink; public static final Comparator<? super Photo> AdjustTimeComparatorTour; static { AdjustTimeComparatorLink = new Comparator<Photo>() { @Override public int compare(final Photo photo1, final Photo photo2) { final long diff = photo1.adjustedTimeLink - photo2.adjustedTimeLink; return diff < 0 ? -1 : diff > 0 ? 1 : 0; } }; AdjustTimeComparatorTour = new Comparator<Photo>() { @Override public int compare(final Photo photo1, final Photo photo2) { final long diff = photo1.adjustedTimeTour - photo2.adjustedTimeTour; return diff < 0 ? -1 : diff > 0 ? 1 : 0; } }; } private TourPhotoManager() { // set photo service provider into the Photo plugin Photo.setPhotoServiceProvider(this); } public static TourPhotoManager getInstance() { if (_instance == null) { _instance = new TourPhotoManager(); } return _instance; } public static TourPhotoLinkView openLinkView() { // final IWorkbench wb = PlatformUI.getWorkbench(); final IWorkbenchWindow wbWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); TourPhotoLinkView linkView = null; if (wbWindow != null) { try { final IWorkbenchPage activePage = wbWindow.getActivePage(); final IViewPart linkViewPart = activePage.findView(TourPhotoLinkView.ID); if (linkViewPart instanceof TourPhotoLinkView) { // link view is available in the current perspective linkView = (TourPhotoLinkView) linkViewPart; } else { // final String currentPerspectiveId = activePage.getPerspective().getId(); // // if (currentPerspectiveId.equals(TourPhotoLinkView.ID)) { // // // open link view in current perspective // // } else { // // // open link perspective // // wb.showPerspective(PerspectiveFactoryPhoto.PERSPECTIVE_ID, wbWindow); // } linkView = (TourPhotoLinkView) Util.showView(TourPhotoLinkView.ID, false); } // ensure link view is visible if (linkView != null) { // show view but do not make it active if (activePage.isPartVisible(linkViewPart) == false) { activePage.showView(TourPhotoLinkView.ID, null, IWorkbenchPage.VIEW_VISIBLE); } } } catch (final PartInitException e) { StatusUtil.showStatus(e); // } catch (final WorkbenchException e) { // StatusUtil.showStatus(e); } } return linkView; } public static void restoreState() { // ensure photo service provider is set in the photo getInstance(); /* * cameras + time adjustment */ final String[] cameraNames = _state.getArray(STATE_CAMERA_ADJUSTMENT_NAME); final long[] adjustments = Util.getStateLongArray(_state, STATE_CAMERA_ADJUSTMENT_TIME, null); if (cameraNames != null && adjustments != null && cameraNames.length == adjustments.length) { // it seems that the values are OK, create cameras with time adjustmens for (int index = 0; index < cameraNames.length; index++) { final String cameraName = cameraNames[index]; final Camera camera = new Camera(cameraName); camera.timeAdjustment = adjustments[index]; _allAvailableCameras.put(cameraName, camera); } } /* * replace image folder */ _replaceImageFolder = _state.get(STATE_REPLACE_IMAGE_FOLDER); } public static void saveState() { /* * camera time adjustment */ final int size = _allAvailableCameras.size(); final String[] cameras = new String[size]; final long[] adjustment = new long[size]; int index = 0; for (final Camera camera : _allAvailableCameras.values()) { cameras[index] = camera.cameraName; adjustment[index] = camera.timeAdjustment; index++; } _state.put(STATE_CAMERA_ADJUSTMENT_NAME, cameras); Util.setState(_state, STATE_CAMERA_ADJUSTMENT_TIME, adjustment); /* * replace image folder */ if (_replaceImageFolder != null) { _state.put(STATE_REPLACE_IMAGE_FOLDER, _replaceImageFolder); } } private static void setTourCameras(final HashMap<String, String> cameras, final TourPhotoLink historyTour) { final Collection<String> allCameras = cameras.values(); Collections.sort(new ArrayList<String>(allCameras)); final StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (final String camera : allCameras) { if (isFirst) { isFirst = false; sb.append(camera); } else { sb.append(UI.COMMA_SPACE); sb.append(camera); } } historyTour.tourCameras = sb.toString(); } @Override public boolean canSaveStarRating(final int selectedPhotos, final int ratingStars) { final int warningLevel = 5; if (selectedPhotos > warningLevel) { final boolean isShowWarning = _prefStore.getBoolean(// ITourbookPreferences.TOGGLE_STATE_SHOW_STAR_RATING_SAVE_WARNING) == false; if (isShowWarning) { final MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(// Display.getCurrent().getActiveShell(), Messages.Photo_TourPhotoMgr_Dialog_SaveStarRating_Title, NLS.bind(// Messages.Photo_TourPhotoMgr_Dialog_SaveStarRating_Message, new Object[] { ratingStars, selectedPhotos, warningLevel }), Messages.App_ToggleState_DoNotShowAgain, false, // toggle default state null, null); if (dialog.getReturnCode() == Window.OK) { // save toggle state only when OK is pressed _prefStore.setValue(ITourbookPreferences.TOGGLE_STATE_SHOW_STAR_RATING_SAVE_WARNING, dialog.getToggleState()); return true; } else { return false; } } } return true; } /** * create pseudo tours for photos which are not contained in a tour and remove all tours which * do not contain any photos * * @param allPhotos * @param visibleTourPhotoLinks * @param isShowToursOnlyWithPhotos * @param isShowToursWithoutSavedPhotos * @param allTourCameras */ void createTourPhotoLinks(final ArrayList<Photo> allPhotos, final ArrayList<TourPhotoLink> visibleTourPhotoLinks, final HashMap<String, Camera> allTourCameras, final boolean isShowToursOnlyWithPhotos, final boolean isShowToursWithoutSavedPhotos) { loadToursFromDb(allPhotos, true); TourPhotoLink currentTourPhotoLink = createTourPhotoLinks_10_GetFirstTour(allPhotos); final HashMap<String, String> tourCameras = new HashMap<String, String>(); final int numberOfRealTours = _dbTourPhotoLinks.size(); long nextDbTourStartTime = numberOfRealTours > 0 ? _dbTourPhotoLinks.get(0).tourStartTime : Long.MIN_VALUE; int tourIndex = 0; long photoTime = 0; // loop: all photos for (final Photo photo : allPhotos) { photoTime = photo.adjustedTimeLink; // check if current photo can be put into current tour photo link if (currentTourPhotoLink.isHistoryTour == false && photoTime <= currentTourPhotoLink.tourEndTime) { // current photo can be put into current real tour } else if (currentTourPhotoLink.isHistoryTour && photoTime < nextDbTourStartTime) { // current photo can be put into current history tour } else { // current photo do not fit into current photo link // finalize current tour photo link createTourPhotoLinks_30_FinalizeCurrentTourPhotoLink(currentTourPhotoLink, tourCameras, visibleTourPhotoLinks, isShowToursOnlyWithPhotos, isShowToursWithoutSavedPhotos); currentTourPhotoLink = null; tourCameras.clear(); /* * create/get new merge tour */ if (tourIndex >= numberOfRealTours) { /* * there are no further tours which can contain photos, put remaining photos * into a history tour */ nextDbTourStartTime = Long.MAX_VALUE; } else { for (; tourIndex < numberOfRealTours; tourIndex++) { final TourPhotoLink dbTourPhotoLink = _dbTourPhotoLinks.get(tourIndex); final long dbTourStart = dbTourPhotoLink.tourStartTime; final long dbTourEnd = dbTourPhotoLink.tourEndTime; if (photoTime < dbTourStart) { // image time is before the next tour start, create history tour nextDbTourStartTime = dbTourStart; break; } if (photoTime >= dbTourStart && photoTime <= dbTourEnd) { // current photo can be put into current tour currentTourPhotoLink = dbTourPhotoLink; break; } // current tour do not contain any images if (isShowToursOnlyWithPhotos == false && isShowToursWithoutSavedPhotos == false) { // tours without photos are displayed createTourPhotoLinks_40_AddTour(dbTourPhotoLink, visibleTourPhotoLinks); } // get start time for the next tour if (tourIndex + 1 < numberOfRealTours) { nextDbTourStartTime = _dbTourPhotoLinks.get(tourIndex + 1).tourStartTime; } else { nextDbTourStartTime = Long.MAX_VALUE; } } } if (currentTourPhotoLink == null) { // create history tour currentTourPhotoLink = new TourPhotoLink(photoTime); } } currentTourPhotoLink.linkPhotos.add(photo); // set camera into the photo final Camera camera = setCamera(photo, allTourCameras); tourCameras.put(camera.cameraName, camera.cameraName); // set number of GPS/No GPS photos final double latitude = photo.getLinkLatitude(); if (latitude == 0) { currentTourPhotoLink.numberOfNoGPSPhotos++; } else { currentTourPhotoLink.numberOfGPSPhotos++; } } createTourPhotoLinks_30_FinalizeCurrentTourPhotoLink(currentTourPhotoLink, tourCameras, visibleTourPhotoLinks, isShowToursOnlyWithPhotos, isShowToursWithoutSavedPhotos); createTourPhotoLinks_60_MergeHistoryTours(visibleTourPhotoLinks); /* * set tour GPS into photo */ final List<TourPhotoLink> tourPhotoLinksWithGps = new ArrayList<TourPhotoLink>(); for (final TourPhotoLink tourPhotoLink : visibleTourPhotoLinks) { if (tourPhotoLink.tourId != Long.MIN_VALUE) { tourPhotoLinksWithGps.add(tourPhotoLink); } } if (tourPhotoLinksWithGps.size() > 0) { setTourGpsIntoPhotos(tourPhotoLinksWithGps); } } void createTourPhotoLinks_01_OneHistoryTour(final ArrayList<Photo> allPhotos, final ArrayList<TourPhotoLink> visibleTourPhotoLinks, final HashMap<String, Camera> allTourCameras) { loadToursFromDb(allPhotos, false); final HashMap<String, String> tourCameras = new HashMap<String, String>(); final TourPhotoLink historyTour = new TourPhotoLink(allPhotos.get(0).adjustedTimeLink); historyTour.linkPhotos.addAll(allPhotos); for (final Photo photo : allPhotos) { // set camera into the photo final Camera camera = setCamera(photo, allTourCameras); tourCameras.put(camera.cameraName, camera.cameraName); // set number of GPS/No GPS photos final double latitude = photo.getLinkLatitude(); if (latitude == 0) { historyTour.numberOfNoGPSPhotos++; } else { historyTour.numberOfGPSPhotos++; } } setTourCameras(tourCameras, historyTour); // finalize history tour historyTour.setTourEndTime(Long.MAX_VALUE); visibleTourPhotoLinks.add(historyTour); } /** * Get/Create first tour photo link * * @param allPhotos */ private TourPhotoLink createTourPhotoLinks_10_GetFirstTour(final ArrayList<Photo> allPhotos) { TourPhotoLink currentTourPhotoLink = null; if (_dbTourPhotoLinks.size() > 0) { // real tours are available final TourPhotoLink firstTour = _dbTourPhotoLinks.get(0); final Photo firstPhoto = allPhotos.get(0); if (firstPhoto.adjustedTimeLink < firstTour.tourStartTime) { // first photo is before the first tour, create dummy tour } else { // first tour starts before the first photo currentTourPhotoLink = firstTour; } } else { // there are no real tours, create dummy tour } if (currentTourPhotoLink == null) { // 1st tour is a history tour final long tourStart = allPhotos.get(0).adjustedTimeLink; currentTourPhotoLink = new TourPhotoLink(tourStart); } return currentTourPhotoLink; } /** * Keep current tour when it contains photos. * * @param currentTourPhotoLink * @param tourCameras * @param allTourPhotoLinks * @param isShowToursOnlyWithPhotos * @param isShowToursWithoutSavedPhotos */ private void createTourPhotoLinks_30_FinalizeCurrentTourPhotoLink(final TourPhotoLink currentTourPhotoLink, final HashMap<String, String> tourCameras, final ArrayList<TourPhotoLink> allTourPhotoLinks, final boolean isShowToursOnlyWithPhotos, final boolean isShowToursWithoutSavedPhotos) { // keep only tours which contain photos final boolean isNoPhotos = currentTourPhotoLink.linkPhotos.size() == 0; final boolean isTourPhotos = currentTourPhotoLink.numberOfTourPhotos > 0; if (// // // exclude history tour without photos (isNoPhotos && currentTourPhotoLink.isHistoryTour) // // // exclude real tours without photos || (isNoPhotos && isShowToursOnlyWithPhotos) // // exclude real tours with saved photos || (isTourPhotos && isShowToursWithoutSavedPhotos) // ) { return; } // set tour end time if (currentTourPhotoLink.isHistoryTour) { currentTourPhotoLink.setTourEndTime(Long.MAX_VALUE); } setTourCameras(tourCameras, currentTourPhotoLink); createTourPhotoLinks_40_AddTour(currentTourPhotoLink, allTourPhotoLinks); } private void createTourPhotoLinks_40_AddTour(final TourPhotoLink tourPhotoLink, final ArrayList<TourPhotoLink> allTourPhotoLinks) { boolean isAddLink = true; final int numberOfLinks = allTourPhotoLinks.size(); if (numberOfLinks > 0) { // check if this tour is already added, this algorithm to add tours is a little bit complex final TourPhotoLink prevTour = allTourPhotoLinks.get(numberOfLinks - 1); if (prevTour.equals(tourPhotoLink)) { isAddLink = false; } } if (isAddLink) { allTourPhotoLinks.add(tourPhotoLink); } } /** * History tours can occure multiple times in sequence, when tours between history tours do not * contain photos. This will merge multiple history tours into one. * * @param allTourPhotoLinks */ private void createTourPhotoLinks_60_MergeHistoryTours(final ArrayList<TourPhotoLink> allTourPhotoLinks) { if (allTourPhotoLinks.size() == 0) { return; } boolean isSubsequentHistory = false; boolean isHistory = false; for (final TourPhotoLink tourPhotoLink : allTourPhotoLinks) { if (isHistory && tourPhotoLink.isHistoryTour == isHistory) { // 2 subsequent tours contains the same tour type isSubsequentHistory = true; break; } isHistory = tourPhotoLink.isHistoryTour; } if (isSubsequentHistory == false) { // there is nothing to merge return; } final ArrayList<TourPhotoLink> mergedLinks = new ArrayList<TourPhotoLink>(); TourPhotoLink prevHistoryTour = null; for (final TourPhotoLink tourPhotoLink : allTourPhotoLinks) { final boolean isHistoryTour = tourPhotoLink.isHistoryTour; if (isHistoryTour && prevHistoryTour == null) { // first history tour prevHistoryTour = tourPhotoLink; continue; } if (isHistoryTour && prevHistoryTour != null) { // this is a subsequent history tour, it is merged into previous history tour prevHistoryTour.linkPhotos.addAll(tourPhotoLink.linkPhotos); prevHistoryTour.numberOfGPSPhotos += tourPhotoLink.numberOfGPSPhotos; prevHistoryTour.numberOfNoGPSPhotos += tourPhotoLink.numberOfNoGPSPhotos; continue; } if (isHistoryTour == false && prevHistoryTour != null) { // this is a real tour, finalize previous history tour prevHistoryTour.setTourEndTime(Long.MAX_VALUE); mergedLinks.add(prevHistoryTour); } prevHistoryTour = null; // this is a real tour mergedLinks.add(tourPhotoLink); } if (prevHistoryTour != null) { // finalize previous history tour prevHistoryTour.setTourEndTime(Long.MAX_VALUE); mergedLinks.add(prevHistoryTour); } allTourPhotoLinks.clear(); allTourPhotoLinks.addAll(mergedLinks); } /** * @param imageFolder * @return Returns number of photos which set in {@link TourPhoto}s for a given folder. */ private ArrayList<String> getTourPhotos(final String imageFolder) { final ArrayList<String> tourPhotoImages = new ArrayList<String>(); Connection conn = null; try { conn = TourDatabase.getInstance().getConnection(); final String sql = "SELECT imageFileName" // //$NON-NLS-1$ + " FROM " + TourDatabase.TABLE_TOUR_PHOTO // //$NON-NLS-1$ + " WHERE imageFilePath=?"; // //$NON-NLS-1$ final PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, imageFolder); final ResultSet result = stmt.executeQuery(); while (result.next()) { tourPhotoImages.add(result.getString(1)); } } catch (final SQLException e) { SQL.showException(e); } finally { Util.closeSql(conn); } return tourPhotoImages; } private int getTourPhotoTours(final String imagePath) { int numberOfTours = 0; Connection conn = null; try { conn = TourDatabase.getInstance().getConnection(); final String sql = "" // //$NON-NLS-1$ // get number of tours + " SELECT COUNT(*)" // //$NON-NLS-1$ + " FROM" // //$NON-NLS-1$ // get all tours which contain the image folder + " (" // //$NON-NLS-1$ // + (" SELECT DISTINCT " + TourDatabase.TABLE_TOUR_DATA + "_tourId") //$NON-NLS-1$ //$NON-NLS-2$ + (" FROM " + TourDatabase.TABLE_TOUR_PHOTO) // //$NON-NLS-1$ + " WHERE imageFilePath=?" // //$NON-NLS-1$ // + " ) TourId"; // //$NON-NLS-1$ final PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, imagePath); final ResultSet result = stmt.executeQuery(); // get first result result.next(); // get first value numberOfTours = result.getInt(1); } catch (final SQLException e) { SQL.showException(e); } finally { Util.closeSql(conn); } return numberOfTours; } void linkPhotosWithTours(final PhotosWithExifSelection selectedPhotosWithExif) { final TourPhotoLinkView linkView = openLinkView(); if (linkView != null) { linkView.showPhotosAndTours(selectedPhotosWithExif.photos); } } /** * Loads tours from the database for all photos. * * @param allPhotos * @param isResetGeoPosition * @return Returns <code>true</code> when tours are loaded from the database, <code>false</code> * is returned when all photo time stamps are within the previously loaded tours. */ private void loadToursFromDb(final ArrayList<Photo> allPhotos, final boolean isResetGeoPosition) { /* * get date for 1st and last photo */ long firstPhotoTime = allPhotos.get(0).adjustedTimeLink; long lastPhotoTime = firstPhotoTime; for (final Photo photo : allPhotos) { final long imageTime = photo.adjustedTimeLink; if (imageTime < firstPhotoTime) { firstPhotoTime = imageTime; } else if (imageTime > lastPhotoTime) { lastPhotoTime = imageTime; } /* * the adjusted time can set a new position, remove old positions which are not covered * by a tour anymore */ if (isResetGeoPosition) { photo.resetLinkGeoPositions(); } } // check if tours are already loaded if (firstPhotoTime >= _sqlTourStart && lastPhotoTime <= _sqlTourEnd) { // photos are contained in the already loaded tours, data for the 'old' links will be reset } else { // adjust by 5 days that time adjustments are covered final long tourStartDate = firstPhotoTime - 5 * UI.DAY_IN_SECONDS * 1000; final long tourEndDate = lastPhotoTime + 5 * UI.DAY_IN_SECONDS * 1000; BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { @Override public void run() { loadToursFromDb_Runnable(tourStartDate, tourEndDate); } }); } _dbTourPhotoLinks.clear(); boolean isFirstTour = true; for (final TourPhotoLink tourPhotoLink : _allDbTourPhotoLinks) { final long tourStart = tourPhotoLink.tourStartTime; final long tourEnd = tourPhotoLink.tourEndTime; if (isFirstTour) { // check if this is the first tour if (firstPhotoTime > tourEnd) { continue; } else { // first tour is found isFirstTour = false; } } else { // subsequent tour if (tourStart > lastPhotoTime) { break; } } tourPhotoLink.linkPhotos.clear(); tourPhotoLink.numberOfGPSPhotos = 0; tourPhotoLink.numberOfNoGPSPhotos = 0; tourPhotoLink.tourCameras = UI.EMPTY_STRING; _dbTourPhotoLinks.add(tourPhotoLink); } return; } private void loadToursFromDb_Runnable(final long dbStartDate, final long dbEndDate) { // final long start = System.currentTimeMillis(); _allDbTourPhotoLinks.clear(); try { if (_sqlConnection == null) { final SQLFilter sqlFilter = new SQLFilter(false); final String sql = UI.EMPTY_STRING // + "SELECT " //$NON-NLS-1$ + " TourId, " // 1 //$NON-NLS-1$ + " TourStartTime, " // 2 //$NON-NLS-1$ + " TourEndTime, " // 3 //$NON-NLS-1$ + " TourType_TypeId, " // 4 //$NON-NLS-1$ + " numberOfPhotos, " // 5 //$NON-NLS-1$ + " photoTimeAdjustment " // 6 //$NON-NLS-1$ + UI.NEW_LINE + (" FROM " + TourDatabase.TABLE_TOUR_DATA + UI.NEW_LINE) //$NON-NLS-1$ + " WHERE" //$NON-NLS-1$ + (" TourStartTime >= ?") //$NON-NLS-1$ + (" AND TourEndTime <= ?") //$NON-NLS-1$ + sqlFilter.getWhereClause() + UI.NEW_LINE + (" ORDER BY TourStartTime"); //$NON-NLS-1$ _sqlConnection = TourDatabase.getInstance().getConnection(); _sqlStatement = _sqlConnection.prepareStatement(sql); sqlFilter.setParameters(_sqlStatement, 3); } _sqlStatement.setLong(1, dbStartDate); _sqlStatement.setLong(2, dbEndDate); _sqlTourStart = Long.MAX_VALUE; _sqlTourEnd = Long.MIN_VALUE; final ResultSet result = _sqlStatement.executeQuery(); while (result.next()) { final long dbTourId = result.getLong(1); final long dbTourStart = result.getLong(2); final long dbTourEnd = result.getLong(3); final Object dbTourTypeId = result.getObject(4); final int dbNumberOfPhotos = result.getInt(5); final int dbPhotoTimeAdjustment = result.getInt(6); final TourPhotoLink dbTourPhotoLink = new TourPhotoLink(dbTourId, dbTourStart, dbTourEnd, dbNumberOfPhotos, dbPhotoTimeAdjustment); dbTourPhotoLink.tourTypeId = (dbTourTypeId == null ? // TourDatabase.ENTITY_IS_NOT_SAVED : (Long) dbTourTypeId); _allDbTourPhotoLinks.add(dbTourPhotoLink); // get range of all tour start/end if (dbTourStart < _sqlTourStart) { _sqlTourStart = dbTourStart; } if (dbTourEnd > _sqlTourEnd) { _sqlTourEnd = dbTourEnd; } } } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } // System.out.println("loadToursFromDb_Runnable()\t" // + (System.currentTimeMillis() - start) // + " ms\t" // + (new DateTime(_sqlTourStart)) // + "\t" // + new DateTime(_sqlTourEnd)); // // TODO remove SYSTEM.OUT.PRINTLN } @Override public void openTour(final HashMap<Long, TourPhotoReference> tourPhotoReferences) { for (final TourPhotoReference ref : tourPhotoReferences.values()) { // fire a selection for the first tour final long tourId = ref.tourId; final SelectionTourId selection = new SelectionTourId(tourId); PostSelectionProvider.fireSelection(selection); break; } } @Override public ArrayList<ImagePathReplacement> replaceImageFilePath(final Photo sourcePhoto) { final Display display = Display.getDefault(); final Shell shell = display.getActiveShell(); final String newImageFolder[] = new String[1]; final String oldImageFolder = sourcePhoto.imagePathName; final ArrayList<String> tourPhotoImageNames = getTourPhotos(oldImageFolder); /* * show info when no images are found, this case should not happen because this method is * called with a tour photo and only when the photo image is not found */ if (tourPhotoImageNames.size() == 0) { MessageDialog.openInformation(shell, // Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Title, NLS.bind(// Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_NoImage_Message, oldImageFolder)); return null; } final ArrayList<IPath> validImages = new ArrayList<IPath>(); final ArrayList<String> inValidImageNames = new ArrayList<String>(); final int numberOfTourPhotoTours = getTourPhotoTours(oldImageFolder); final ArrayList<IPath> modifiedImages = new ArrayList<IPath>(); if (MessageDialog.openQuestion(shell, // Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Title, NLS.bind(// Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Message, new Object[] { numberOfTourPhotoTours, // tourPhotoImageNames.size(), oldImageFolder }))) { final DirectoryDialog dialog = new DirectoryDialog(shell, SWT.SAVE); dialog.setText(Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Title); dialog.setMessage(NLS.bind(Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_SelectFolder_Message, sourcePhoto.imageFileName, oldImageFolder)); if (_replaceImageFolder != null) { dialog.setFilterPath(_replaceImageFolder); } newImageFolder[0] = dialog.open(); if (newImageFolder[0] != null) { // a folder is selected _replaceImageFolder = newImageFolder[0]; // check which images are available at the new location BusyIndicator.showWhile(display, new Runnable() { @Override public void run() { final IPath folderPath = new Path(newImageFolder[0]).addTrailingSeparator(); for (final String imageName : tourPhotoImageNames) { final IPath imagePathName = folderPath.append(imageName); if (imagePathName.toFile().exists()) { validImages.add(imagePathName); } else { inValidImageNames.add(imageName); } } } }); if (validImages.size() == 0) { // there are no images in the new selected folder MessageDialog.openInformation(shell, // Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Title, NLS.bind(// Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_NoValidImages_Message, newImageFolder[0], oldImageFolder)); } else { // there are images in the new selected folder if (inValidImageNames.size() == 0) { // all images can be replaced if (MessageDialog.openQuestion(shell, // Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Title, NLS.bind(// Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_ReplaceAll_Message, new Object[] { validImages.size(), // oldImageFolder, newImageFolder[0] }))) { modifiedImages.addAll(validImages); } } else { // some images are not available if (MessageDialog.openQuestion(shell, // Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_Title, NLS.bind(// Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_ReplacePartly_Message, new Object[] { validImages.size(), inValidImageNames.size(), oldImageFolder, newImageFolder[0], tourPhotoImageNames.size() }))) { modifiedImages.addAll(validImages); } } } } } final ArrayList<ImagePathReplacement> replacedImages = new ArrayList<ImagePathReplacement>(); if (modifiedImages.size() > 0) { BusyIndicator.showWhile(display, new Runnable() { @Override public void run() { final ArrayList<ImagePathReplacement> replacedImagesInDb = replaceImageFilePath_InSQLDb( oldImageFolder, modifiedImages); replacedImages.addAll(replacedImagesInDb); } }); } /* * show error message with all invalid image names */ if (validImages.size() > 0 && inValidImageNames.size() > 0) { // sort names Collections.sort(inValidImageNames); final StringBuilder sb = new StringBuilder(); sb.append(NLS.bind(Messages.Photo_TourPhotoMgr_Dialog_ReplacePhotoImage_NoValidImageNames, newImageFolder[0])); for (final String invalidName : inValidImageNames) { sb.append(UI.NEW_LINE + invalidName); } StatusUtil.showStatus(sb.toString()); } return replacedImages; } /** * Replace image file path in the tour photo table. * * @param oldImageFolder * @param modifiedImages * @return */ private ArrayList<ImagePathReplacement> replaceImageFilePath_InSQLDb(final String oldImageFolder, final ArrayList<IPath> modifiedImages) { final ArrayList<ImagePathReplacement> replacedImages = new ArrayList<ImagePathReplacement>(); Connection conn = null; try { conn = TourDatabase.getInstance().getConnection(); final String sql = "UPDATE " + TourDatabase.TABLE_TOUR_PHOTO // //$NON-NLS-1$ + " SET" // //$NON-NLS-1$ + " imageFilePath=?, " // 1 //$NON-NLS-1$ + " imageFilePathName=? " // 2 //$NON-NLS-1$ + " WHERE imageFilePathName=?"; // 3 //$NON-NLS-1$ final PreparedStatement sqlUpdate = conn.prepareStatement(sql); final IPath oldImagePath = new Path(oldImageFolder); for (final IPath imagePath : modifiedImages) { final String imageFilePath = imagePath.removeLastSegments(1).toOSString(); final String imageFilePathName = imagePath.toOSString(); final String imageFileName = imagePath.lastSegment(); final String oldImageFilePathName = oldImagePath.append(imageFileName).toOSString(); // if (imageFileName.equals("P1000699.JPG")) { // int a = 0; // a++; // } // update photo in db sqlUpdate.setString(1, imageFilePath); sqlUpdate.setString(2, imageFilePathName); sqlUpdate.setString(3, oldImageFilePathName); sqlUpdate.executeUpdate(); replacedImages.add(new ImagePathReplacement(oldImageFilePathName, imagePath)); } } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } finally { if (conn != null) { try { conn.close(); } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } } } return replacedImages; } void resetTourStartEnd() { if (_sqlConnection != null) { Util.closeSql(_sqlStatement); Util.closeSql(_sqlConnection); _sqlStatement = null; _sqlConnection = null; // force reloading cached start/end _sqlTourStart = Long.MAX_VALUE; _sqlTourEnd = Long.MIN_VALUE; } } @Override public void saveStarRating(final ArrayList<Photo> photos) { // final long start = System.nanoTime(); Connection conn = null; try { conn = TourDatabase.getInstance().getConnection(); final PreparedStatement sqlUpdate = conn.prepareStatement(// "UPDATE " + TourDatabase.TABLE_TOUR_PHOTO // //$NON-NLS-1$ + " SET" // //$NON-NLS-1$ + " ratingStars=? " // //$NON-NLS-1$ + " WHERE photoId=?"); // //$NON-NLS-1$ final ArrayList<Photo> updatedPhotos = new ArrayList<Photo>(); for (final Photo photo : photos) { final int ratingStars = photo.ratingStars; final Collection<TourPhotoReference> photoRefs = photo.getTourPhotoReferences().values(); if (photoRefs.size() > 0) { for (final TourPhotoReference photoRef : photoRefs) { // update db sqlUpdate.setInt(1, ratingStars); sqlUpdate.setLong(2, photoRef.photoId); sqlUpdate.executeUpdate(); // update tour photo final TourData tourData = TourManager.getInstance().getTourData(photoRef.tourId); final Set<TourPhoto> tourPhotos = tourData.getTourPhotos(); for (final TourPhoto tourPhoto : tourPhotos) { if (tourPhoto.getPhotoId() == photoRef.photoId) { tourPhoto.setRatingStars(ratingStars); break; } } } updatedPhotos.add(photo); } } if (updatedPhotos.size() > 0) { // fire notification to update all galleries with the modified rating stars PhotoManager.firePhotoEvent(null, PhotoEventId.PHOTO_ATTRIBUTES_ARE_MODIFIED, photos); } } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } finally { if (conn != null) { try { conn.close(); } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } } } // System.out.println(net.tourbook.common.UI.timeStampNano() // + " save photo rating\t" // + ((float) (System.nanoTime() - start) / 1000000) // + " ms"); // // TODO remove SYSTEM.OUT.PRINTLN } /** * Creates a camera when not yet created and sets it into the photo. * * @param photo * @param allTourCameras * @return Returns camera which is set into the photo. */ Camera setCamera(final Photo photo, final HashMap<String, Camera> allTourCameras) { // get camera String photoCameraName = null; final PhotoImageMetadata metaData = photo.getImageMetaDataRaw(); if (metaData != null) { photoCameraName = metaData.model; } Camera camera = null; if (photoCameraName == null || photoCameraName.length() == 0) { // camera is not set in the photo camera = _allAvailableCameras.get(CAMERA_UNKNOWN_KEY); if (camera == null) { camera = new Camera(Messages.Photos_AndTours_Label_NoCamera); _allAvailableCameras.put(CAMERA_UNKNOWN_KEY, camera); } } else { // camera is set in the photo camera = _allAvailableCameras.get(photoCameraName); if (camera == null) { camera = new Camera(photoCameraName); _allAvailableCameras.put(photoCameraName, camera); } } allTourCameras.put(camera.cameraName, camera); photo.camera = camera; return camera; } /** * @param originalJpegImageFile * @param latitude * @param longitude * @return Returns * * <pre> * -1 when <b>SERIOUS</b> error occured * 0 when image file is read only * 1 when geo coordinates are written into the image file * </pre> */ private int setExifGPSTag_IntoImageFile(final File originalJpegImageFile, final double latitude, final double longitude, final boolean[] isReadOnlyMessageDisplayed) { // final Shell activeShell = Display.getCurrent().getActiveShell(); // // if (originalJpegImageFile.canWrite() == false) { // // if (isReadOnlyMessageDisplayed[0] == false) { // // isReadOnlyMessageDisplayed[0] = true; // // MessageDialog // .openError(activeShell, // // "Messages.Photos_AndTours_Dialog_ImageIsReadOnly_Title Set Geo Coordinates", // NLS // .bind( // "Messages.Photos_AndTours_Dialog_ImageIsReadOnly_Message The geo coordinates cannot be set into the image file\n\n{0}\n\nbecause the image file is readonly.\n\nFor subsequent image files which are readonly, this message will not be displayed.", // originalJpegImageFile.getAbsolutePath())); // } // // return 0; // } // // File gpsTempFile = null; // // final IPath originalFilePathName = new Path(originalJpegImageFile.getAbsolutePath()); // final String originalFileNameWithoutExt = originalFilePathName.removeFileExtension().lastSegment(); // // final File originalFilePath = originalFilePathName.removeLastSegments(1).toFile(); // File renamedOriginalFile = null; // // try { // // boolean returnState = false; // // try { // // gpsTempFile = File.createTempFile(// // originalFileNameWithoutExt + UI.SYMBOL_UNDERSCORE, // UI.SYMBOL_DOT + originalFilePathName.getFileExtension(), // originalFilePath); // // setExifGPSTag_IntoImageFile_WithExifRewriter(originalJpegImageFile, gpsTempFile, latitude, longitude); // // returnState = true; // // } catch (final ImageReadException e) { // StatusUtil.log(e); // } catch (final ImageWriteException e) { // StatusUtil.log(e); // } catch (final IOException e) { // StatusUtil.log(e); // } // // if (returnState == false) { // return -1; // } // // /* // * replace original file with gps file // */ // // try { // // /* // * rename original file into a temp file // */ // final String nanoString = Long.toString(System.nanoTime()); // final String nanoTime = nanoString.substring(nanoString.length() - 4); // // renamedOriginalFile = File.createTempFile(// // originalFileNameWithoutExt + TEMP_FILE_PREFIX_ORIG + nanoTime, // UI.SYMBOL_DOT + originalFilePathName.getFileExtension(), // originalFilePath); // // final String renamedOriginalFileName = renamedOriginalFile.getAbsolutePath(); // // Util.deleteTempFile(renamedOriginalFile); // // boolean isRenamed = originalJpegImageFile.renameTo(new File(renamedOriginalFileName)); // // if (isRenamed == false) { // // // original file cannot be renamed // MessageDialog.openError(activeShell, // // "Messages.Photos_AndTours_ErrorDialog_Title", //$NON-NLS-1$ // NLS.bind("The image file:\n\n{0}\n\ncannot be renamed into\n\n{1}", //$NON-NLS-1$ // originalFilePathName.toOSString(), // renamedOriginalFileName)); // return -1; // } // // /* // * rename gps temp file into original file // */ // isRenamed = gpsTempFile.renameTo(originalFilePathName.toFile()); // // if (isRenamed == false) { // // // gps file cannot be renamed to original file // MessageDialog // .openError(activeShell, // // "Messages.Photos_AndTours_ErrorDialog_Title", //$NON-NLS-1$ // NLS // .bind( // "THERE IS A SERIOUS PROBLEM\n\nThe image file\n\n{0}\n\nwas renamed to\n\n{1}\n\nbut the task of setting the geo\n\n coordinates cannot be\n\n finished.", //$NON-NLS-1$ // originalFilePathName.toOSString(), // renamedOriginalFile.getAbsolutePath())); // // /* // * prevent of deleting renamed original file because the original file is // * renamed into this // */ // renamedOriginalFile = null; // // return -1; // } // // if (renamedOriginalFile.delete() == false) { // // MessageDialog.openError(activeShell, // // "Messages.Photos_AndTours_ErrorDialog_Title", //$NON-NLS-1$ // NLS.bind("The image file:\n\n{0}\n\nwhich was renamed into\n\n{1}\n\ncannot be deleted.", //$NON-NLS-1$ // originalFilePathName.toOSString(), // renamedOriginalFile.getAbsolutePath())); // } // // } catch (final IOException e) { // StatusUtil.log(e); // } // // } finally { // // Util.deleteTempFile(gpsTempFile); // } return 1; } /** * This example illustrates how to set the GPS values in JPEG EXIF metadata. * * @param jpegImageFile * A source image file. * @param destinationFile * The output file. * @param latitude * @param longitude * @throws IOException * @throws ImageReadException * @throws ImageWriteException */ private void setExifGPSTag_IntoImageFile_WithExifRewriter(final File jpegImageFile, final File destinationFile, final double latitude, final double longitude) throws IOException, ImageReadException, ImageWriteException { OutputStream os = null; try { TiffOutputSet outputSet = null; // note that metadata might be null if no metadata is found. final IImageMetadata metadata = Imaging.getMetadata(jpegImageFile); final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; if (null != jpegMetadata) { // note that exif might be null if no Exif metadata is found. final TiffImageMetadata exif = jpegMetadata.getExif(); if (null != exif) { // TiffImageMetadata class is immutable (read-only). // TiffOutputSet class represents the Exif data to write. // // Usually, we want to update existing Exif metadata by // changing // the values of a few fields, or adding a field. // In these cases, it is easiest to use getOutputSet() to // start with a "copy" of the fields read from the image. outputSet = exif.getOutputSet(); } } // if file does not contain any exif metadata, we create an empty // set of exif metadata. Otherwise, we keep all of the other // existing tags. if (null == outputSet) { outputSet = new TiffOutputSet(); } { // Example of how to add/update GPS info to output set. // New York City // final double longitude = -74.0; // 74 degrees W (in Degrees East) // final double latitude = 40 + 43 / 60.0; // 40 degrees N (in Degrees // North) outputSet.setGPSInDegrees(longitude, latitude); } os = new FileOutputStream(destinationFile); os = new BufferedOutputStream(os); /** * the lossless method causes an exception after 3 times writing the image file, * therefore the lossy method is used * * <pre> * * org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter$ExifOverflowException: APP1 Segment is too long: 65564 * at org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter.writeSegmentsReplacingExif(ExifRewriter.java:552) * at org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter.updateExifMetadataLossless(ExifRewriter.java:393) * at org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter.updateExifMetadataLossless(ExifRewriter.java:293) * at net.tourbook.photo.PhotosAndToursView.setExifGPSTag_IntoPhoto(PhotosAndToursView.java:2309) * at net.tourbook.photo.PhotosAndToursView.setExifGPSTag(PhotosAndToursView.java:2141) * * </pre> */ // new ExifRewriter().updateExifMetadataLossless(jpegImageFile, os, outputSet); // new ExifRewriter().updateExifMetadataLossy(jpegImageFile, os, outputSet); os.close(); os = null; } finally { if (os != null) { try { os.close(); } catch (final IOException e) { } } } } private void setTourGpsIntoPhotos(final List<TourPhotoLink> tourPhotoLinksWithGps) { for (final TourPhotoLink tourPhotoLink : tourPhotoLinksWithGps) { // set tour gps into photos setTourGPSIntoPhotos_10(tourPhotoLink); /* * update number of photos */ tourPhotoLink.numberOfGPSPhotos = 0; tourPhotoLink.numberOfNoGPSPhotos = 0; for (final Photo photo : tourPhotoLink.linkPhotos) { // set number of GPS/No GPS photos final double latitude = photo.getLinkLatitude(); if (latitude == 0) { tourPhotoLink.numberOfNoGPSPhotos++; } else { tourPhotoLink.numberOfGPSPhotos++; } } } } private void setTourGPSIntoPhotos_10(final TourPhotoLink tourPhotoLink) { final ArrayList<Photo> allPhotos = tourPhotoLink.linkPhotos; final int numberOfPhotos = allPhotos.size(); if (numberOfPhotos == 0) { // no photos are available for this tour return; } final TourData tourData = TourManager.getInstance().getTourData(tourPhotoLink.tourId); final double[] latitudeSerie = tourData.latitudeSerie; final double[] longitudeSerie = tourData.longitudeSerie; if (latitudeSerie == null) { // no geo positions return; } final int[] timeSerie = tourData.timeSerie; final int numberOfTimeSlices = timeSerie.length; final long tourStartSeconds = tourData.getTourStartTime().toInstant().getEpochSecond(); long timeSliceEnd; if (numberOfTimeSlices > 1) { timeSliceEnd = tourStartSeconds + (long) (timeSerie[1] / 2.0); } else { // tour contains only 1 time slice timeSliceEnd = tourStartSeconds; } int timeIndex = 0; int photoIndex = 0; // get first photo Photo photo = allPhotos.get(photoIndex); // loop: time serie while (true) { // loop: photo serie, check if a photo is in the current time slice while (true) { final long imageAdjustedTime = photo.adjustedTimeLink; long imageTime = 0; if (imageAdjustedTime != Long.MIN_VALUE) { imageTime = imageAdjustedTime; } else { imageTime = photo.imageExifTime; } final long photoTime = imageTime / 1000; if (photoTime <= timeSliceEnd) { // photo is contained within the current time slice final double tourLatitude = latitudeSerie[timeIndex]; final double tourLongitude = longitudeSerie[timeIndex]; setTourGPSIntoPhotos_20(tourData, photo, tourLatitude, tourLongitude); photoIndex++; } else { // advance to the next time slice break; } if (photoIndex < numberOfPhotos) { photo = allPhotos.get(photoIndex); } else { break; } } if (photoIndex >= numberOfPhotos) { // no more photos break; } /* * photos are still available */ // advance to the next time slice on the x-axis timeIndex++; if (timeIndex >= numberOfTimeSlices - 1) { /* * end of tour is reached but there are still photos available, set remaining photos * at the end of the tour */ while (true) { final double tourLatitude = latitudeSerie[timeIndex]; final double tourLongitude = longitudeSerie[timeIndex]; setTourGPSIntoPhotos_20(tourData, photo, tourLatitude, tourLongitude); photoIndex++; if (photoIndex < numberOfPhotos) { photo = allPhotos.get(photoIndex); } else { break; } } } else { final long valuePointTime = timeSerie[timeIndex]; final long sliceDuration = timeSerie[timeIndex + 1] - valuePointTime; timeSliceEnd = tourStartSeconds + valuePointTime + (sliceDuration / 2); } } } private void setTourGPSIntoPhotos_20(final TourData tourData, final Photo photo, final double tourLatitude, final double tourLongitude) { // if (photo.isGeoFromExif) { // // // photo contains already EXIF GPS // // // don't overwrite geo from EXIF, use GPS geo from photo // // } else { // // // set gps from tour into the photo // // photo.setLinkGeoPosition(tourLatitude, tourLongitude); // } /* * Tour GPS is more accurate than EXIF GPS, the best way to handle this problem is by * specifiying an preference but because my camera has written the wrong GPS into the * photos, this is now (13.4.3) the new behaviour. */ photo.setLinkGeoPosition(tourLatitude, tourLongitude); } @Override public void setTourReference(final Photo photo) { // final long start = System.nanoTime(); Connection conn = null; try { conn = TourDatabase.getInstance().getConnection(); final String sql = "SELECT " // //$NON-NLS-1$ // + " photoId, " // 1 //$NON-NLS-1$ + (" " + TourDatabase.TABLE_TOUR_DATA + "_tourId, ") // 2 //$NON-NLS-1$ //$NON-NLS-2$ // + " adjustedTime, " // 3 //$NON-NLS-1$ + " imageExifTime, " // 4 //$NON-NLS-1$ + " latitude, " // 5 //$NON-NLS-1$ + " longitude, " // 6 //$NON-NLS-1$ + " isGeoFromPhoto, " // 7 //$NON-NLS-1$ + " ratingStars " // 8 //$NON-NLS-1$ // + " FROM " + TourDatabase.TABLE_TOUR_PHOTO // //$NON-NLS-1$ // + " WHERE imageFilePathName=?"; // //$NON-NLS-1$ final PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, photo.imageFilePathName); final ResultSet result = stmt.executeQuery(); while (result.next()) { final long dbPhotoId = result.getLong(1); final long dbTourId = result.getLong(2); final long dbAdjustedTime = result.getLong(3); final long dbImageExifTime = result.getLong(4); final double dbLatitude = result.getDouble(5); final double dbLongitude = result.getDouble(6); final int dbIsGeoFromExif = result.getInt(7); final int dbRatingStars = result.getInt(8); photo.addTour(dbTourId, dbPhotoId); /* * when a photo is in the photo cache it is possible that the tour is from the file * system, update tour relevant fields */ photo.isSavedInTour = true; photo.adjustedTimeTour = dbAdjustedTime; photo.imageExifTime = dbImageExifTime; photo.isGeoFromExif = dbIsGeoFromExif == 1; photo.isTourPhotoWithGps = dbLatitude != 0; photo.ratingStars = dbRatingStars; if (photo.getTourLatitude() == 0 && dbLatitude != 0) { photo.setTourGeoPosition(dbLatitude, dbLongitude); } PhotoCache.setPhoto(photo); } } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } finally { if (conn != null) { try { conn.close(); } catch (final SQLException e) { net.tourbook.ui.UI.showSQLException(e); } } } // System.out.println(net.tourbook.common.UI.timeStampNano() // + " load sql tourId from photo\t" // + ((float) (System.nanoTime() - start) / 1000000) // + " ms"); // TODO remove SYSTEM.OUT.PRINTLN } }