Changeset 2566 in josm for trunk/src/org
- Timestamp:
- 2009-12-03T22:15:17+01:00 (15 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/gui
- Files:
-
- 5 added
- 4 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/MapFrame.java
r2391 r2566 80 80 */ 81 81 private List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>(); 82 private DialogsPanel dialogsPanel;82 private final DialogsPanel dialogsPanel; 83 83 84 84 public final ButtonGroup toolGroup = new ButtonGroup(); … … 219 219 toolBarToggle.add(button); 220 220 allDialogs.add(dlg); 221 if (dialogsPanel.initialized) { 222 dialogsPanel.add(dlg); 223 } 221 224 return button; 222 225 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java
-
Property svn:eol-style
set to
native
r2525 r2566 32 32 } 33 33 34 private boolean initialized = false; 35 public void initialize(List<ToggleDialog> allDialogs) { 34 public boolean initialized = false; // read only from outside 35 36 public void initialize(List<ToggleDialog> pAllDialogs) { 36 37 if (initialized) 37 38 throw new IllegalStateException(); 38 39 initialized = true; 39 this.allDialogs = allDialogs; 40 41 for (Integer i=0; i < allDialogs.size(); ++i) { 42 final ToggleDialog dlg = allDialogs.get(i); 43 dlg.setDialogsPanel(this); 44 dlg.setVisible(false); 45 } 46 for (int i=0; i < allDialogs.size() + 1; ++i) { 47 final JPanel p = new JPanel() { 48 /** 49 * Honoured by the MultiSplitPaneLayout when the 50 * entire Window is resized. 51 */ 52 @Override 53 public Dimension getMinimumSize() { 54 return new Dimension(0, 40); 55 } 56 }; 57 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 58 p.setVisible(false); 59 60 mSpltPane.add(p, "L"+i); 61 panels.add(p); 62 } 63 64 for (Integer i=0; i < allDialogs.size(); ++i) { 65 final ToggleDialog dlg = allDialogs.get(i); 66 if (dlg.isDialogShowing()) { 67 dlg.showDialog(); 68 if (dlg.isDialogInCollapsedView()) { 69 dlg.isCollapsed = false; // pretend to be in Default view, this will be set back by collapse() 70 dlg.collapse(); 71 } 72 } else { 73 dlg.hideDialog(); 74 } 75 } 40 allDialogs = new ArrayList<ToggleDialog>(); 41 42 for (Integer i=0; i < pAllDialogs.size(); ++i) { 43 add(pAllDialogs.get(i), false); 44 } 45 76 46 this.add(mSpltPane); 77 47 reconstruct(Action.ELEMENT_SHRINKS, null); 48 } 49 50 public void add(ToggleDialog dlg) { 51 add(dlg, true); 52 } 53 54 public void add(ToggleDialog dlg, boolean doReconstruct) { 55 allDialogs.add(dlg); 56 int i = allDialogs.size() - 1; 57 dlg.setDialogsPanel(this); 58 dlg.setVisible(false); 59 final JPanel p = new JPanel() { 60 /** 61 * Honoured by the MultiSplitPaneLayout when the 62 * entire Window is resized. 63 */ 64 @Override 65 public Dimension getMinimumSize() { 66 return new Dimension(0, 40); 67 } 68 }; 69 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 70 p.setVisible(false); 71 72 mSpltPane.add(p, "L"+i); 73 panels.add(p); 74 75 if (dlg.isDialogShowing()) { 76 dlg.showDialog(); 77 if (dlg.isDialogInCollapsedView()) { 78 dlg.isCollapsed = false; // pretend to be in Default view, this will be set back by collapse() 79 dlg.collapse(); 80 } 81 if (doReconstruct) { 82 reconstruct(Action.INVISIBLE_TO_DEFAULT, dlg); 83 } 84 } else { 85 dlg.hideDialog(); 86 } 78 87 } 79 88 -
Property svn:eol-style
set to
-
trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java
r2525 r2566 1 // License: GPL. See LICENSE file for details. 1 2 package org.openstreetmap.josm.gui.dialogs; 2 3 -
trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
r2549 r2566 64 64 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 65 65 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 66 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer; 66 67 import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker; 67 68 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; … … 298 299 }); 299 300 300 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get(" tagimages"));301 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("dialogs/geoimage")); 301 302 tagimage.putClientProperty("help", ht("/Action/ImportImages")); 302 303 tagimage.addActionListener(new ActionListener() { -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
r2564 r2566 1 //License: GPL. Copyright 2007 by Immanuel Scholz and others 2 package org.openstreetmap.josm.gui.layer; 1 // License: GPL. See LICENSE file for details. 2 // Copyright 2007 by Christian Gallioz (aka khris78) 3 // Parts of code from Geotagged plugin (by Rob Neild) 4 // and the core JOSM source code (by Immanuel Scholz and others) 5 6 package org.openstreetmap.josm.gui.layer.geoimage; 3 7 4 8 import static org.openstreetmap.josm.tools.I18n.tr; 5 9 import static org.openstreetmap.josm.tools.I18n.trn; 6 10 7 import java.awt.BorderLayout;8 import java.awt.Color;9 11 import java.awt.Component; 10 import java.awt.Cursor;11 12 import java.awt.Graphics2D; 12 import java.awt.GridBagLayout;13 13 import java.awt.Image; 14 import java.awt. Insets;14 import java.awt.MediaTracker; 15 15 import java.awt.Point; 16 16 import java.awt.Rectangle; 17 17 import java.awt.Toolkit; 18 import java.awt.event.ActionEvent;19 import java.awt.event.ActionListener;20 import java.awt.event.ComponentEvent;21 import java.awt.event.ComponentListener;22 import java.awt.event.KeyEvent;23 18 import java.awt.event.MouseAdapter; 24 19 import java.awt.event.MouseEvent; 25 20 import java.awt.image.BufferedImage; 26 import java.awt.image.ImageObserver;27 21 import java.io.File; 28 22 import java.io.IOException; 29 import java.lang.ref.SoftReference;30 23 import java.text.ParseException; 31 import java.text.SimpleDateFormat;32 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.Collections; 33 27 import java.util.Collection; 34 import java.util.Collections;35 28 import java.util.Date; 36 import java.util.LinkedList; 29 import java.util.LinkedHashSet; 30 import java.util.HashSet; 37 31 import java.util.List; 38 import java.util.WeakHashMap; 39 40 import javax.swing.BorderFactory; 41 import javax.swing.DefaultListCellRenderer; 32 42 33 import javax.swing.Icon; 43 import javax.swing.ImageIcon;44 import javax.swing.JButton;45 import javax.swing.JDialog;46 import javax.swing.JFileChooser;47 import javax.swing.JLabel;48 import javax.swing.JList;49 34 import javax.swing.JMenuItem; 50 35 import javax.swing.JOptionPane; 51 import javax.swing.JPanel;52 import javax.swing.JScrollPane;53 36 import javax.swing.JSeparator; 54 import javax.swing.JTextField;55 import javax.swing.JToggleButton;56 import javax.swing.JViewport;57 import javax.swing.ScrollPaneConstants;58 import javax.swing.border.BevelBorder;59 import javax.swing.border.Border;60 import javax.swing.filechooser.FileFilter;61 37 62 38 import org.openstreetmap.josm.Main; … … 65 41 import org.openstreetmap.josm.data.coor.CachedLatLon; 66 42 import org.openstreetmap.josm.data.coor.LatLon; 67 import org.openstreetmap.josm.data.gpx.GpxTrack;68 import org.openstreetmap.josm.data.gpx.WayPoint;69 43 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 70 import org.openstreetmap.josm.gui.MapFrame;71 44 import org.openstreetmap.josm.gui.MapView; 72 45 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 73 46 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 74 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 75 import org.openstreetmap.josm.gui.layer.GeoImageLayer.ImageLoader.ImageLoadedListener; 76 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 77 import org.openstreetmap.josm.tools.DateParser; 47 import org.openstreetmap.josm.gui.layer.GpxLayer; 48 import org.openstreetmap.josm.gui.layer.Layer; 78 49 import org.openstreetmap.josm.tools.ExifReader; 79 import org.openstreetmap.josm.tools.GBC;80 50 import org.openstreetmap.josm.tools.ImageProvider; 81 51 82 /** 83 * A layer which imports several photos from disk and read EXIF time information from them. 84 * 85 * @author Imi 86 */ 52 import com.drew.imaging.jpeg.JpegMetadataReader; 53 import com.drew.lang.Rational; 54 import com.drew.metadata.Directory; 55 import com.drew.metadata.Metadata; 56 import com.drew.metadata.exif.GpsDirectory; 57 87 58 public class GeoImageLayer extends Layer { 88 59 89 /** 90 * Allows to load and scale images. Loaded images are kept in cache (using soft reference). Both 91 * synchronous and asynchronous loading is supported 92 * 60 List<ImageEntry> data; 61 62 private Icon icon = ImageProvider.get("dialogs/geoimage/photo-marker"); 63 private Icon selectedIcon = ImageProvider.get("dialogs/geoimage/photo-marker-selected"); 64 65 private int currentPhoto = -1; 66 67 // These are used by the auto-guess function to store the result, 68 // so when the dialig is re-opened the users modifications don't 69 // get overwritten 70 public boolean hasTimeoffset = false; 71 public long timeoffset = 0; 72 73 /* 74 * Stores info about each image 93 75 */ 94 public static class ImageLoader implements ImageObserver { 95 96 public static class Entry { 97 final File file; 98 final int width; 99 final int height; 100 final int maxSize; 101 private final ImageLoadedListener listener; 102 103 volatile Image scaledImage; 104 105 public Entry(File file, int width, int height, int maxSize, ImageLoadedListener listener) { 106 this.file = file; 107 this.height = height; 108 this.width = width; 109 this.maxSize = maxSize; 110 this.listener = listener; 111 } 112 } 113 114 public interface ImageLoadedListener { 115 void imageLoaded(); 116 } 117 118 private final List<ImageLoader.Entry> queue = new ArrayList<ImageLoader.Entry>(); 119 private final WeakHashMap<File, SoftReference<Image>> loadedImageCache = new WeakHashMap<File, SoftReference<Image>>(); 120 private ImageLoader.Entry currentEntry; 121 122 private Image getOrLoadImage(File file) { 123 SoftReference<Image> cachedImageRef = loadedImageCache.get(file); 124 if (cachedImageRef != null) { 125 Image cachedImage = cachedImageRef.get(); 126 if (cachedImage != null) 127 return cachedImage; 128 } 129 return Toolkit.getDefaultToolkit().createImage(currentEntry.file.getAbsolutePath()); 130 } 131 132 private BufferedImage createResizedCopy(Image originalImage, 133 int scaledWidth, int scaledHeight) 76 77 static final class ImageEntry implements Comparable<ImageEntry> { 78 File file; 79 Date time; 80 LatLon exifCoor; 81 CachedLatLon pos; 82 Image thumbnail; 83 /** Speed in kilometer per second */ 84 Double speed; 85 /** Elevation (altitude) in meters */ 86 Double elevation; 87 88 public void setCoor(LatLon latlon) 134 89 { 135 BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB); 136 Graphics2D g = scaledBI.createGraphics(); 137 while (!g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null)) 138 { 139 try { 140 Thread.sleep(10); 141 } catch(InterruptedException ie) {} 142 } 143 g.dispose(); 144 return scaledBI; 145 } 146 private void loadImage() { 147 if (currentEntry != null) 90 pos = new CachedLatLon(latlon); 91 } 92 public int compareTo(ImageEntry image) { 93 if (time != null && image.time != null) { 94 return time.compareTo(image.time); 95 } else if (time == null && image.time == null) { 96 return 0; 97 } else if (time == null) { 98 return -1; 99 } else { 100 return 1; 101 } 102 } 103 } 104 105 /** Loads a set of images, while displaying a dialog that indicates what the plugin is currently doing. 106 * In facts, this object is instantiated with a list of files. These files may be JPEG files or 107 * directories. In case of directories, they are scanned to find all the images they contain. 108 * Then all the images that have be found are loaded as ImageEntry instances. 109 */ 110 private static final class Loader extends PleaseWaitRunnable { 111 112 private boolean cancelled = false; 113 private GeoImageLayer layer; 114 private Collection<File> selection; 115 private HashSet<String> loadedDirectories = new HashSet<String>(); 116 private LinkedHashSet<String> errorMessages; 117 118 protected void rememberError(String message) { 119 this.errorMessages.add(message); 120 } 121 122 public Loader(Collection<File> selection, GpxLayer gpxLayer) { 123 super(tr("Extracting GPS locations from EXIF")); 124 this.selection = selection; 125 errorMessages = new LinkedHashSet<String>(); 126 } 127 128 @Override protected void realRun() throws IOException { 129 130 progressMonitor.subTask(tr("Starting directory scan")); 131 Collection<File> files = new ArrayList<File>(); 132 try { 133 addRecursiveFiles(files, selection); 134 } catch(NullPointerException npe) { 135 rememberError(tr("One of the selected files was null")); 136 } 137 138 if (cancelled) { 148 139 return; 149 while (!queue.isEmpty()) { 150 currentEntry = queue.get(0); 151 queue.remove(0); 152 153 Image newImage = getOrLoadImage(currentEntry.file); 154 if (newImage.getWidth(this) == -1) { 140 } 141 progressMonitor.subTask(tr("Read photos...")); 142 progressMonitor.setTicksCount(files.size()); 143 144 progressMonitor.subTask(tr("Read photos...")); 145 progressMonitor.setTicksCount(files.size()); 146 147 // read the image files 148 List<ImageEntry> data = new ArrayList<ImageEntry>(files.size()); 149 150 for (File f : files) { 151 152 if (cancelled) { 155 153 break; 156 } else { 157 finishImage(newImage, currentEntry); 158 currentEntry = null; 159 } 160 } 161 } 162 163 private void finishImage(Image img, ImageLoader.Entry entry) { 164 loadedImageCache.put(entry.file, new SoftReference<Image>(img)); 165 if (entry.maxSize != -1) { 166 int w = img.getWidth(null); 167 int h = img.getHeight(null); 168 if (w>h) { 169 h = Math.round(entry.maxSize*((float)h/w)); 170 w = entry.maxSize; 171 } else { 172 w = Math.round(entry.maxSize*((float)w/h)); 173 h = entry.maxSize; 174 } 175 entry.scaledImage = createResizedCopy(img, w, h); 176 } else if (entry.width != -1 && entry.height != -1) { 177 entry.scaledImage = createResizedCopy(img, entry.width, entry.height); 178 } else { 179 entry.scaledImage = img; 180 } 181 if (entry.listener != null) { 182 entry.listener.imageLoaded(); 183 } 184 } 185 186 public synchronized ImageLoader.Entry loadImage(File file, int width, int height, int maxSize, ImageLoadedListener listener) { 187 ImageLoader.Entry e = new Entry(file, width, height, maxSize, listener); 188 queue.add(e); 189 loadImage(); 190 return e; 191 } 192 193 public Image waitForImage(File file, int width, int height) { 194 return waitForImage(file, width, height, -1); 195 } 196 197 public Image waitForImage(File file, int maxSize) { 198 return waitForImage(file, -1, -1, maxSize); 199 } 200 201 public Image waitForImage(File file) { 202 return waitForImage(file, -1, -1, -1); 203 } 204 205 private synchronized Image waitForImage(File file, int width, int height, int maxSize) { 206 ImageLoader.Entry entry; 207 if (currentEntry != null && currentEntry.file.equals(file)) { 208 entry = currentEntry; 209 } else { 210 entry = new Entry(file, width, height, maxSize, null); 211 queue.add(0, entry); 212 } 213 loadImage(); 214 215 while (true) { 216 if (entry.scaledImage != null) 217 return entry.scaledImage; 218 try { 219 wait(); 220 } catch (InterruptedException e) {} 221 } 222 } 223 224 public synchronized boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { 225 if ((infoflags & ImageObserver.ALLBITS) != 0) { 226 finishImage(img, currentEntry); 227 currentEntry = null; 228 loadImage(); 229 notifyAll(); 230 } else if ((infoflags & ImageObserver.ERROR) != 0) { 231 currentEntry.scaledImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY); 232 currentEntry = null; 233 } 234 return true; 235 } 236 } 237 238 private static final int ICON_SIZE = 16; 239 private static ImageLoader imageLoader = new ImageLoader(); 240 241 private static final class ImageEntry implements Comparable<ImageEntry>, ImageLoadedListener { 242 243 private static final Image EMPTY_IMAGE = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY); 244 245 final File image; 246 ImageLoader.Entry icon; 247 Date time; 248 CachedLatLon pos; 249 250 public ImageEntry(File image) { 251 this.image = image; 252 icon = imageLoader.loadImage(image, ICON_SIZE, ICON_SIZE, -1, this); 253 } 254 255 public int compareTo(ImageEntry image) { 256 return time.compareTo(image.time); 257 } 258 259 public Image getIcon() { 260 if (icon.scaledImage == null) 261 return EMPTY_IMAGE; 262 else 263 return icon.scaledImage; 264 } 265 266 public void imageLoaded() { 267 MapFrame frame = Main.map; 268 if (frame != null) { 269 frame.mapView.repaint(); 270 } 271 } 272 } 273 274 private static final class Loader extends PleaseWaitRunnable { 275 private GeoImageLayer layer; 276 private final Collection<File> files; 277 private final GpxLayer gpxLayer; 278 private LinkedList<TimedPoint> gps; 279 280 public Loader(Collection<File> files, GpxLayer gpxLayer) { 281 super(tr("Images for {0}", gpxLayer.getName())); 282 this.files = files; 283 this.gpxLayer = gpxLayer; 284 } 285 @Override protected void realRun() throws IOException { 286 progressMonitor.subTask(tr("Read GPX...")); 287 progressMonitor.setTicksCount(10 + files.size()); 288 gps = new LinkedList<TimedPoint>(); 289 290 // Extract dates and locations from GPX input 291 292 ProgressMonitor gpxSubTask = progressMonitor.createSubTaskMonitor(10, true); 293 int size = 0; 294 for (GpxTrack trk:gpxLayer.data.tracks) { 295 for (Collection<WayPoint> segment : trk.trackSegs) { 296 size += segment.size(); 297 } 298 } 299 gpxSubTask.beginTask(null, size); 300 301 try { 302 for (GpxTrack trk : gpxLayer.data.tracks) { 303 for (Collection<WayPoint> segment : trk.trackSegs) { 304 for (WayPoint p : segment) { 305 LatLon c = p.getCoor(); 306 if (!p.attr.containsKey("time")) 307 throw new IOException(tr("No time for point {0} x {1}",c.lat(),c.lon())); 308 Date d = null; 309 try { 310 d = DateParser.parse((String) p.attr.get("time")); 311 } catch (ParseException e) { 312 throw new IOException(tr("Cannot read time \"{0}\" from point {1} x {2}",p.attr.get("time"),c.lat(),c.lon())); 313 } 314 gps.add(new TimedPoint(d, c)); 315 gpxSubTask.worked(1); 316 } 317 } 318 } 319 } finally { 320 gpxSubTask.finishTask(); 321 } 322 323 if (gps.isEmpty()) 324 return; 325 326 // read the image files 327 ArrayList<ImageEntry> data = new ArrayList<ImageEntry>(files.size()); 328 for (File f : files) { 329 if (progressMonitor.isCancelled()) { 330 break; 331 } 332 progressMonitor.subTask(tr("Reading {0}...",f.getName())); 333 334 ImageEntry e = new ImageEntry(f); 154 } 155 156 progressMonitor.subTask(tr("Reading {0}...", f.getName())); 157 progressMonitor.worked(1); 158 159 ImageEntry e = new ImageEntry(); 160 161 // Changed to silently cope with no time info in exif. One case 162 // of person having time that couldn't be parsed, but valid GPS info 163 335 164 try { 336 165 e.time = ExifReader.readTime(f); 337 progressMonitor.worked(1);338 166 } catch (ParseException e1) { 339 continue; 340 } 341 if (e.time == null) { 342 continue; 343 } 344 167 e.time = null; 168 } 169 e.file = f; 170 extractExif(e); 345 171 data.add(e); 346 172 } 347 layer = new GeoImageLayer(data, gps); 348 layer.calculatePosition(); 349 } 173 layer = new GeoImageLayer(data); 174 files.clear(); 175 Thread thumbsloader = new Thread(new Thumbsloader()); 176 thumbsloader.setPriority(Thread.MIN_PRIORITY); 177 thumbsloader.start(); 178 } 179 180 private void addRecursiveFiles(Collection<File> files, Collection<File> sel) { 181 boolean nullFile = false; 182 183 for (File f : sel) { 184 185 if(cancelled) { 186 break; 187 } 188 189 if (f == null) { 190 nullFile = true; 191 192 } else if (f.isDirectory()) { 193 String canonical = null; 194 try { 195 canonical = f.getCanonicalPath(); 196 } catch (IOException e) { 197 e.printStackTrace(); 198 rememberError(tr("Unable to get canonical path for directory {0}\n", 199 f.getAbsolutePath())); 200 } 201 202 if (canonical == null || loadedDirectories.contains(canonical)) { 203 continue; 204 } else { 205 loadedDirectories.add(canonical); 206 } 207 208 Collection<File> children = Arrays.asList(f.listFiles(JpegFileFilter.getInstance())); 209 if (children != null) { 210 progressMonitor.subTask(tr("Scanning directory {0}", f.getPath())); 211 try { 212 addRecursiveFiles(files, children); 213 } catch(NullPointerException npe) { 214 npe.printStackTrace(); 215 rememberError(tr("Found null file in directory {0}\n", f.getPath())); 216 } 217 } else { 218 rememberError(tr("Error while getting files from directory {0}\n", f.getPath())); 219 } 220 221 } else { 222 files.add(f); 223 } 224 } 225 226 if (nullFile) { 227 throw new NullPointerException(); 228 } 229 } 230 231 protected String formatErrorMessages() { 232 StringBuffer sb = new StringBuffer(); 233 sb.append("<html>"); 234 if (errorMessages.size() == 1) { 235 sb.append(errorMessages.iterator().next()); 236 } else { 237 sb.append("<ul>"); 238 for (String msg: errorMessages) { 239 sb.append("<li>").append(msg).append("</li>"); 240 } 241 sb.append("/ul>"); 242 } 243 sb.append("</html>"); 244 return sb.toString(); 245 } 246 350 247 @Override protected void finish() { 351 if (gps.isEmpty()) { 352 JOptionPane.showMessageDialog( 353 Main.parent, 354 tr("No images with readable timestamps found."), 355 tr("Warning"), 356 JOptionPane.WARNING_MESSAGE 357 ); 358 return; 359 } 248 if (!errorMessages.isEmpty()) { 249 JOptionPane.showMessageDialog( 250 Main.parent, 251 formatErrorMessages(), 252 tr("Error"), 253 JOptionPane.ERROR_MESSAGE 254 ); 255 } 360 256 if (layer != null) { 361 257 Main.main.addLayer(layer); 362 } 363 } 364 365 @Override 366 protected void cancel() { 367 368 } 369 } 370 371 public ArrayList<ImageEntry> data; 372 private LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>(); 373 374 /** 375 * The delta added to all timestamps in files from the camera 376 * to match to the timestamp from the gps receivers tracklog. 377 */ 378 private long delta = Long.parseLong(Main.pref.get("tagimages.delta", "0")); 379 private long gpstimezone = Long.parseLong(Main.pref.get("tagimages.gpstimezone", "0"))*60*60*1000; 380 private boolean mousePressed = false; 381 private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 382 private MouseAdapter mouseAdapter; 383 private ImageViewerDialog imageViewerDialog; 384 385 public static final class GpsTimeIncorrect extends Exception { 386 public GpsTimeIncorrect(String message, Throwable cause) { 387 super(message, cause); 388 } 389 public GpsTimeIncorrect(String message) { 390 super(message); 391 } 392 } 393 394 private static final class TimedPoint implements Comparable<TimedPoint> { 395 Date time; 396 CachedLatLon pos; 397 398 public TimedPoint(Date time, LatLon pos) { 399 this.time = time; 400 this.pos = new CachedLatLon(pos); 401 } 402 public int compareTo(TimedPoint point) { 403 return time.compareTo(point.time); 404 } 405 } 258 layer.hook_up_mouse_events(); // Main.map.mapView should exist 259 // now. Can add mouse listener 260 261 if (! cancelled && layer.data.size() > 0) { 262 boolean noGeotagFound = true; 263 for (ImageEntry e : layer.data) { 264 if (e.pos != null) { 265 noGeotagFound = false; 266 } 267 } 268 if (noGeotagFound) { 269 new CorrelateGpxWithImages(layer).actionPerformed(null); 270 } 271 } 272 } 273 } 274 275 @Override protected void cancel() { 276 cancelled = true; 277 } 278 279 class Thumbsloader implements Runnable { 280 public void run() { 281 System.err.println("Load Thumbnails"); 282 MediaTracker tracker = new MediaTracker(Main.map.mapView); 283 for (int i = 0; i < layer.data.size(); i++) { 284 System.err.println("getImg "+i); 285 Image img = Toolkit.getDefaultToolkit().createImage(layer.data.get(i).file.getPath()); 286 tracker.addImage(img, 0); 287 try { 288 tracker.waitForID(0); 289 } catch (InterruptedException e) { 290 System.err.println("InterruptedException"); 291 return; // FIXME 292 } 293 BufferedImage scaledBI = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB); 294 Graphics2D g = scaledBI.createGraphics(); 295 while (!g.drawImage(img, 0, 0, 16, 16, null)) 296 { 297 try { 298 Thread.sleep(10); 299 } catch(InterruptedException ie) {} 300 } 301 g.dispose(); 302 tracker.removeImage(img); 303 layer.data.get(i).thumbnail = scaledBI; 304 if (Main.map != null && Main.map.mapView != null) { 305 Main.map.mapView.repaint(); 306 } 307 } 308 309 // boolean error = tracker.isErrorID(1); 310 // if (img != null && (img.getWidth(null) == 0 || img.getHeight(null) == 0)) { 311 // error = true; 312 // } 313 314 315 } 316 } 317 } 318 319 private static boolean addedToggleDialog = false; 406 320 407 321 public static void create(Collection<File> files, GpxLayer gpxLayer) { 408 322 Loader loader = new Loader(files, gpxLayer); 409 323 Main.worker.execute(loader); 410 } 411 412 private GeoImageLayer(final ArrayList<ImageEntry> data, LinkedList<TimedPoint> gps) { 324 if (!addedToggleDialog) { 325 Main.map.addToggleDialog(ImageViewerDialog.getInstance()); 326 addedToggleDialog = true; 327 } 328 } 329 330 private GeoImageLayer(final List<ImageEntry> data) { 331 413 332 super(tr("Geotagged Images")); 333 414 334 Collections.sort(data); 415 Collections.sort(gps);416 335 this.data = data; 417 this.gps = gps; 418 final Layer self = this; 419 mouseAdapter = new MouseAdapter(){ 336 } 337 338 @Override 339 public Icon getIcon() { 340 return ImageProvider.get("dialogs/geoimage"); 341 } 342 343 @Override 344 public Object getInfoComponent() { 345 // TODO Auto-generated method stub 346 return null; 347 } 348 349 @Override 350 public Component[] getMenuEntries() { 351 352 JMenuItem correlateItem = new JMenuItem(tr("Correlate to GPX"), ImageProvider.get("dialogs/geoimage/gpx2img")); 353 correlateItem.addActionListener(new CorrelateGpxWithImages(this)); 354 355 return new Component[] { 356 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)), 357 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)), 358 new JMenuItem(new RenameLayerAction(null, this)), 359 new JSeparator(), 360 correlateItem 361 }; 362 } 363 364 @Override 365 public String getToolTipText() { 366 int i = 0; 367 for (ImageEntry e : data) 368 if (e.pos != null) 369 i++; 370 return data.size() + " " + trn("image", "images", data.size()) 371 + " loaded. " + tr("{0} were found to be gps tagged.", i); 372 } 373 374 @Override 375 public boolean isMergable(Layer other) { 376 return other instanceof GeoImageLayer; 377 } 378 379 @Override 380 public void mergeFrom(Layer from) { 381 GeoImageLayer l = (GeoImageLayer) from; 382 383 ImageEntry selected = null; 384 if (l.currentPhoto >= 0) { 385 selected = l.data.get(l.currentPhoto); 386 } 387 388 data.addAll(l.data); 389 Collections.sort(data); 390 391 // Supress the double photos. 392 if (data.size() > 1) { 393 ImageEntry cur; 394 ImageEntry prev = data.get(data.size() - 1); 395 for (int i = data.size() - 2; i >= 0; i--) { 396 cur = data.get(i); 397 if (cur.file.equals(prev.file)) { 398 data.remove(i); 399 } else { 400 prev = cur; 401 } 402 } 403 } 404 405 if (selected != null) { 406 for (int i = 0; i < data.size() ; i++) { 407 if (data.get(i) == selected) { 408 currentPhoto = i; 409 ImageViewerDialog.showImage(GeoImageLayer.this, data.get(i)); 410 break; 411 } 412 } 413 } 414 415 setName(l.getName()); 416 417 } 418 419 @Override 420 public void paint(Graphics2D g, MapView mv, Bounds bounds) { 421 422 for (ImageEntry e : data) { 423 if (e.pos != null) { 424 Point p = mv.getPoint(e.pos); 425 if (e.thumbnail != null && e.thumbnail.getWidth(null) > 0 && e.thumbnail.getHeight(null) > 0) { 426 g.drawImage(e.thumbnail, 427 p.x - e.thumbnail.getWidth(null) / 2, 428 p.y - e.thumbnail.getHeight(null) / 2, null); 429 } 430 else { 431 icon.paintIcon(mv, g, 432 p.x - icon.getIconWidth() / 2, 433 p.y - icon.getIconHeight() / 2); 434 } 435 } 436 } 437 438 // Draw the selection on top of the other pictures. 439 if (currentPhoto >= 0 && currentPhoto < data.size()) { 440 ImageEntry e = data.get(currentPhoto); 441 442 if (e.pos != null) { 443 Point p = mv.getPoint(e.pos); 444 445 Rectangle r = new Rectangle(p.x - selectedIcon.getIconWidth() / 2, 446 p.y - selectedIcon.getIconHeight() / 2, 447 selectedIcon.getIconWidth(), 448 selectedIcon.getIconHeight()); 449 selectedIcon.paintIcon(mv, g, r.x, r.y); 450 } 451 } 452 } 453 454 @Override 455 public void visitBoundingBox(BoundingXYVisitor v) { 456 for (ImageEntry e : data) 457 v.visit(e.pos); 458 } 459 460 /* 461 * Extract gps from image exif 462 * 463 * If successful, fills in the LatLon and EastNorth attributes of passed in 464 * image; 465 */ 466 467 private static void extractExif(ImageEntry e) { 468 469 try { 470 int deg; 471 float min, sec; 472 double lon, lat; 473 474 Metadata metadata = JpegMetadataReader.readMetadata(e.file); 475 Directory dir = metadata.getDirectory(GpsDirectory.class); 476 477 // longitude 478 479 Rational[] components = dir 480 .getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE); 481 482 deg = components[0].intValue(); 483 min = components[1].floatValue(); 484 sec = components[2].floatValue(); 485 486 lon = (deg + (min / 60) + (sec / 3600)); 487 488 if (dir.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') 489 lon = -lon; 490 491 // latitude 492 493 components = dir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE); 494 495 deg = components[0].intValue(); 496 min = components[1].floatValue(); 497 sec = components[2].floatValue(); 498 499 lat = (deg + (min / 60) + (sec / 3600)); 500 501 if (dir.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S') 502 lat = -lat; 503 504 // Store values 505 506 e.setCoor(new LatLon(lat, lon)); 507 e.exifCoor = e.pos; 508 509 } catch (Exception p) { 510 e.pos = null; 511 } 512 } 513 514 public void showNextPhoto() { 515 if (data != null && data.size() > 0) { 516 currentPhoto++; 517 if (currentPhoto >= data.size()) { 518 currentPhoto = data.size() - 1; 519 } 520 ImageViewerDialog.showImage(this, data.get(currentPhoto)); 521 } else { 522 currentPhoto = -1; 523 } 524 Main.main.map.repaint(); 525 } 526 527 public void showPreviousPhoto() { 528 if (data != null && data.size() > 0) { 529 currentPhoto--; 530 if (currentPhoto < 0) { 531 currentPhoto = 0; 532 } 533 ImageViewerDialog.showImage(this, data.get(currentPhoto)); 534 } else { 535 currentPhoto = -1; 536 } 537 Main.main.map.repaint(); 538 } 539 540 public void checkPreviousNextButtons() { 541 System.err.println("check: " + currentPhoto); 542 ImageViewerDialog.setNextEnabled(currentPhoto < data.size() - 1); 543 ImageViewerDialog.setPreviousEnabled(currentPhoto > 0); 544 } 545 546 public void removeCurrentPhoto() { 547 if (data != null && data.size() > 0 && currentPhoto >= 0 && currentPhoto < data.size()) { 548 data.remove(currentPhoto); 549 if (currentPhoto >= data.size()) { 550 currentPhoto = data.size() - 1; 551 } 552 if (currentPhoto >= 0) { 553 ImageViewerDialog.showImage(this, data.get(currentPhoto)); 554 } else { 555 ImageViewerDialog.showImage(this, null); 556 } 557 } 558 Main.main.map.repaint(); 559 } 560 561 private MouseAdapter mouseAdapter = null; 562 563 private void hook_up_mouse_events() { 564 mouseAdapter = new MouseAdapter() { 420 565 @Override public void mousePressed(MouseEvent e) { 421 if (e.getButton() != MouseEvent.BUTTON1) 566 567 if (e.getButton() != MouseEvent.BUTTON1) { 422 568 return; 423 mousePressed = true;424 if (isVisible()) {569 } 570 if (isVisible()) 425 571 Main.map.mapView.repaint(); 426 427 } 572 } 573 428 574 @Override public void mouseReleased(MouseEvent ev) { 429 if (ev.getButton() != MouseEvent.BUTTON1) 575 576 if (ev.getButton() != MouseEvent.BUTTON1) { 430 577 return; 431 mousePressed = false;432 if (!isVisible()) 578 } 579 if (!isVisible()) { 433 580 return; 434 for (int i = data.size(); i > 0; --i) { 435 ImageEntry e = data.get(i-1); 436 if (e.pos == null) { 581 } 582 583 ImageViewerDialog d = ImageViewerDialog.getInstance(); 584 // System.err.println(d.isDialogShowing()); 585 586 587 for (int i = data.size() - 1; i >= 0; --i) { 588 ImageEntry e = data.get(i); 589 if (e.pos == null) 437 590 continue; 438 }439 591 Point p = Main.map.mapView.getPoint(e.pos); 440 Rectangle r = new Rectangle(p.x-ICON_SIZE/2, p.y-ICON_SIZE/2, ICON_SIZE, ICON_SIZE); 592 Rectangle r = new Rectangle(p.x - icon.getIconWidth() / 2, 593 p.y - icon.getIconHeight() / 2, 594 icon.getIconWidth(), 595 icon.getIconHeight()); 441 596 if (r.contains(ev.getPoint())) { 442 showImage(i-1); 597 currentPhoto = i; 598 ImageViewerDialog.showImage(GeoImageLayer.this, e); 599 Main.main.map.repaint(); 600 601 443 602 break; 444 603 } … … 448 607 }; 449 608 Main.map.mapView.addMouseListener(mouseAdapter); 450 Layer.listeners.add(new LayerChangeListener(){ 451 public void activeLayerChange(Layer oldLayer, Layer newLayer) {} 452 public void layerAdded(Layer newLayer) {} 609 Layer.listeners.add(new LayerChangeListener() { 610 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 611 if (newLayer == GeoImageLayer.this && currentPhoto >= 0) { 612 Main.main.map.repaint(); 613 ImageViewerDialog.showImage(GeoImageLayer.this, data.get(currentPhoto)); 614 } 615 } 616 617 public void layerAdded(Layer newLayer) { 618 } 619 453 620 public void layerRemoved(Layer oldLayer) { 454 if (oldLayer == self) {621 if (oldLayer == GeoImageLayer.this) { 455 622 Main.map.mapView.removeMouseListener(mouseAdapter); 623 currentPhoto = -1; 624 data.clear(); 625 data = null; 456 626 } 457 627 } … … 459 629 } 460 630 461 private class ImageViewerDialog {462 463 private int currentImage;464 private ImageEntry currentImageEntry;465 466 private final JDialog dlg;467 private final JButton nextButton;468 private final JButton prevButton;469 private final JToggleButton scaleToggle;470 private final JToggleButton centerToggle;471 private final JViewport imageViewport;472 private final JLabel imageLabel;473 474 private class ImageAction implements ActionListener {475 476 private final int offset;477 478 public ImageAction(int offset) {479 this.offset = offset;480 }481 482 public void actionPerformed(ActionEvent e) {483 showImage(currentImage + offset);484 }485 486 }487 488 public ImageViewerDialog(ImageEntry firstImage) {489 final JPanel p = new JPanel(new BorderLayout());490 imageLabel = new JLabel(new ImageIcon(imageLoader.waitForImage(firstImage.image, 580)));491 final JScrollPane scroll = new JScrollPane(imageLabel);492 scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);493 scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);494 imageViewport = scroll.getViewport();495 p.add(scroll, BorderLayout.CENTER);496 497 scaleToggle = new JToggleButton(ImageProvider.get("dialogs", "zoom-best-fit"));498 nextButton = new JButton(ImageProvider.get("dialogs", "next"));499 prevButton = new JButton(ImageProvider.get("dialogs", "previous"));500 centerToggle = new JToggleButton(ImageProvider.get("dialogs", "centreview"));501 502 JPanel p2 = new JPanel();503 p2.add(prevButton);504 p2.add(scaleToggle);505 p2.add(centerToggle);506 p2.add(nextButton);507 p.add(p2, BorderLayout.SOUTH);508 final JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE);509 dlg = pane.createDialog(Main.parent, "");510 scaleToggle.addActionListener(new ImageAction(0));511 scaleToggle.setSelected(true);512 centerToggle.addActionListener(new ImageAction(0));513 514 nextButton.setActionCommand("Next");515 prevButton.setActionCommand("Previous");516 nextButton.setMnemonic(KeyEvent.VK_RIGHT);517 prevButton.setMnemonic(KeyEvent.VK_LEFT);518 scaleToggle.setMnemonic(KeyEvent.VK_F);519 centerToggle.setMnemonic(KeyEvent.VK_C);520 nextButton.setToolTipText("Show next image");521 prevButton.setToolTipText("Show previous image");522 centerToggle.setToolTipText("Centre image location in main display");523 scaleToggle.setToolTipText("Scale image to fit");524 525 prevButton.addActionListener(new ImageAction(-1));526 nextButton.addActionListener(new ImageAction(1));527 centerToggle.setSelected(false);528 529 dlg.addComponentListener(new ComponentListener() {530 boolean ignoreEvent = true;531 public void componentHidden(ComponentEvent e) {}532 public void componentMoved(ComponentEvent e) {}533 public void componentResized(ComponentEvent ev) {534 // we ignore the first resize event, as the picture is scaled already on load:535 if (scaleToggle.getModel().isSelected() && !ignoreEvent) {536 imageLabel.setIcon(new ImageIcon(imageLoader.waitForImage(currentImageEntry.image,537 Math.max(imageViewport.getWidth(), imageViewport.getHeight()))));538 }539 ignoreEvent = false;540 }541 public void componentShown(ComponentEvent e) {}542 543 });544 dlg.setModal(false);545 dlg.setResizable(true);546 dlg.pack();547 }548 549 public void showImage(int index) {550 dlg.setVisible(true);551 dlg.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));552 553 if (index < 0) {554 index = 0;555 } else if (index >= data.size() - 1) {556 index = data.size() - 1;557 }558 559 currentImage = index;560 currentImageEntry = data.get(currentImage);561 562 prevButton.setEnabled(currentImage > 0);563 nextButton.setEnabled(currentImage < data.size() - 1);564 565 if (scaleToggle.getModel().isSelected()) {566 imageLabel.setIcon(new ImageIcon(imageLoader.waitForImage(currentImageEntry.image,567 Math.max(imageViewport.getWidth(), imageViewport.getHeight()))));568 } else {569 imageLabel.setIcon(new ImageIcon(imageLoader.waitForImage(currentImageEntry.image)));570 }571 572 if (centerToggle.getModel().isSelected()) {573 Main.map.mapView.zoomTo(currentImageEntry.pos);574 }575 576 dlg.setTitle(currentImageEntry.image +577 " (" + currentImageEntry.pos.toDisplayString() + ")");578 dlg.setCursor(Cursor.getDefaultCursor());579 }580 581 }582 583 private void showImage(int i) {584 if (imageViewerDialog == null) {585 imageViewerDialog = new ImageViewerDialog(data.get(i));586 }587 imageViewerDialog.showImage(i);588 }589 590 @Override public Icon getIcon() {591 return ImageProvider.get("layer", "tagimages_small");592 }593 594 @Override public Object getInfoComponent() {595 JPanel p = new JPanel(new GridBagLayout());596 p.add(new JLabel(getToolTipText()), GBC.eop());597 598 p.add(new JLabel(tr("GPS start: {0}",dateFormat.format(gps.getFirst().time))), GBC.eol());599 p.add(new JLabel(tr("GPS end: {0}",dateFormat.format(gps.getLast().time))), GBC.eop());600 601 p.add(new JLabel(tr("current delta: {0}s",(delta/1000.0))), GBC.eol());602 p.add(new JLabel(tr("timezone difference: ")+(gpstimezone>0?"+":"")+(gpstimezone/1000/60/60)), GBC.eop());603 604 JList img = new JList(data.toArray());605 img.setCellRenderer(new DefaultListCellRenderer(){606 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {607 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);608 ImageEntry e = (ImageEntry)value;609 setIcon(new ImageIcon(e.getIcon()));610 setText(e.image.getName()+" ("+dateFormat.format(new Date(e.time.getTime()+(delta+gpstimezone)))+")");611 if (e.pos == null) {612 setForeground(Color.red);613 }614 return this;615 }616 });617 img.setVisibleRowCount(5);618 p.add(new JScrollPane(img), GBC.eop().fill(GBC.BOTH));619 return p;620 }621 622 @Override public String getToolTipText() {623 int i = 0;624 for (ImageEntry e : data)625 if (e.pos != null) {626 i++;627 }628 return data.size()+" "+trn("image","images",data.size())+". "+tr("{0} within the track.",i);629 }630 631 @Override public boolean isMergable(Layer other) {632 return other instanceof GeoImageLayer;633 }634 635 @Override public void mergeFrom(Layer from) {636 GeoImageLayer l = (GeoImageLayer)from;637 data.addAll(l.data);638 }639 640 @Override public void paint(Graphics2D g, MapView mv, Bounds box) {641 int clickedIndex = -1;642 643 // First select beveled icon (for cases where are more icons on the same spot)644 Point mousePosition = mv.getMousePosition();645 if (mousePosition != null && mousePressed) {646 for (int i = data.size() - 1; i >= 0; i--) {647 ImageEntry e = data.get(i);648 if (e.pos == null) {649 continue;650 }651 652 Point p = mv.getPoint(e.pos);653 Rectangle r = new Rectangle(p.x-ICON_SIZE / 2, p.y-ICON_SIZE / 2, ICON_SIZE, ICON_SIZE);654 if (r.contains(mousePosition)) {655 clickedIndex = i;656 break;657 }658 }659 }660 661 for (int i = 0; i < data.size(); i++) {662 ImageEntry e = data.get(i);663 if (e.pos != null) {664 Point p = mv.getPoint(e.pos);665 Rectangle r = new Rectangle(p.x-ICON_SIZE / 2, p.y-ICON_SIZE / 2, ICON_SIZE, ICON_SIZE);666 g.drawImage(e.getIcon(), r.x, r.y, null);667 Border b = null;668 if (i == clickedIndex) {669 b = BorderFactory.createBevelBorder(BevelBorder.LOWERED);670 } else {671 b = BorderFactory.createBevelBorder(BevelBorder.RAISED);672 }673 Insets inset = b.getBorderInsets(mv);674 r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2);675 b.paintBorder(mv, g, r.x, r.y, r.width, r.height);676 }677 }678 }679 680 @Override public void visitBoundingBox(BoundingXYVisitor v) {681 for (ImageEntry e : data) {682 v.visit(e.pos);683 }684 }685 686 @Override public Component[] getMenuEntries() {687 JMenuItem sync = new JMenuItem(tr("Sync clock"), ImageProvider.get("clock"));688 sync.addActionListener(new ActionListener(){689 public void actionPerformed(ActionEvent e) {690 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));691 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);692 fc.setAcceptAllFileFilterUsed(false);693 fc.setFileFilter(new FileFilter(){694 @Override public boolean accept(File f) {695 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");696 }697 @Override public String getDescription() {698 return tr("JPEG images (*.jpg)");699 }700 });701 fc.showOpenDialog(Main.parent);702 File sel = fc.getSelectedFile();703 if (sel == null)704 return;705 Main.pref.put("tagimages.lastdirectory", sel.getPath());706 sync(sel);707 Main.map.repaint();708 }709 });710 return new Component[]{711 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)),712 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)),713 new JSeparator(),714 sync,715 new JSeparator(),716 new JMenuItem(new RenameLayerAction(null, this)),717 new JSeparator(),718 new JMenuItem(new LayerListPopup.InfoAction(this))};719 }720 721 private void calculatePosition() {722 for (ImageEntry e : data) {723 TimedPoint lastTP = null;724 for (TimedPoint tp : gps) {725 Date time = new Date(tp.time.getTime() - (delta+gpstimezone));726 if (time.after(e.time) && lastTP != null) {727 e.pos = new CachedLatLon(lastTP.pos.getCenter(tp.pos));728 break;729 }730 lastTP = tp;731 }732 if (e.pos == null) {733 e.pos = gps.getLast().pos;734 }735 }736 }737 738 private void sync(File f) {739 Date exifDate;740 try {741 exifDate = ExifReader.readTime(f);742 } catch (ParseException e) {743 JOptionPane.showMessageDialog(744 Main.parent,745 tr("The date in file \"{0}\" could not be parsed.", f.getName()),746 tr("Error"),747 JOptionPane.ERROR_MESSAGE748 );749 return;750 }751 if (exifDate == null) {752 JOptionPane.showMessageDialog(753 Main.parent,754 tr("There is no EXIF time within the file \"{0}\".", f.getName()),755 tr("Error"),756 JOptionPane.ERROR_MESSAGE757 );758 return;759 }760 JPanel p = new JPanel(new GridBagLayout());761 p.add(new JLabel(tr("Image")), GBC.eol());762 p.add(new JLabel(new ImageIcon(imageLoader.waitForImage(f, 300))), GBC.eop());763 p.add(new JLabel(tr("Enter shown date (mm/dd/yyyy HH:MM:SS)")), GBC.eol());764 JTextField gpsText = new JTextField(dateFormat.format(new Date(exifDate.getTime()+delta)));765 p.add(gpsText, GBC.eol().fill(GBC.HORIZONTAL));766 p.add(new JLabel(tr("GPS unit timezone (difference to photo)")), GBC.eol());767 String t = Main.pref.get("tagimages.gpstimezone", "0");768 if (t.charAt(0) != '-') {769 t = "+"+t;770 }771 JTextField gpsTimezone = new JTextField(t);772 p.add(gpsTimezone, GBC.eol().fill(GBC.HORIZONTAL));773 774 while (true) {775 int answer = JOptionPane.showConfirmDialog(776 Main.parent,777 p,778 tr("Synchronize Time with GPS Unit"),779 JOptionPane.OK_CANCEL_OPTION,780 JOptionPane.QUESTION_MESSAGE781 );782 if (answer != JOptionPane.OK_OPTION || gpsText.getText().equals(""))783 return;784 try {785 delta = DateParser.parse(gpsText.getText()).getTime() - exifDate.getTime();786 String time = gpsTimezone.getText();787 if (!time.equals("") && time.charAt(0) == '+') {788 time = time.substring(1);789 }790 if (time.equals("")) {791 time = "0";792 }793 gpstimezone = Long.valueOf(time)*60*60*1000;794 Main.pref.put("tagimages.delta", ""+delta);795 Main.pref.put("tagimages.gpstimezone", time);796 calculatePosition();797 return;798 } catch (NumberFormatException x) {799 JOptionPane.showMessageDialog(800 Main.parent,801 tr("Time entered could not be parsed."),802 tr("Error"),803 JOptionPane.ERROR_MESSAGE804 );805 } catch (ParseException x) {806 JOptionPane.showMessageDialog(807 Main.parent,808 tr("Time entered could not be parsed."),809 tr("Error"),810 JOptionPane.ERROR_MESSAGE811 );812 }813 }814 }815 816 631 }
Note:
See TracChangeset
for help on using the changeset viewer.