source: josm/trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java@ 3662

Last change on this file since 3662 was 3662, checked in by bastiK, 14 years ago

cosmetics

  • Property svn:eol-style set to native
File size: 28.2 KB
Line 
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)
5package org.openstreetmap.josm.gui.layer.geoimage;
6
7import static org.openstreetmap.josm.tools.I18n.tr;
8import static org.openstreetmap.josm.tools.I18n.trn;
9
10import java.awt.AlphaComposite;
11import java.awt.Color;
12import java.awt.Composite;
13import java.awt.Dimension;
14import java.awt.Graphics2D;
15import java.awt.Image;
16import java.awt.Point;
17import java.awt.Rectangle;
18import java.awt.event.MouseAdapter;
19import java.awt.event.MouseEvent;
20import java.awt.image.BufferedImage;
21import java.beans.PropertyChangeEvent;
22import java.beans.PropertyChangeListener;
23import java.io.File;
24import java.io.IOException;
25import java.text.ParseException;
26import java.util.ArrayList;
27import java.util.Arrays;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.HashSet;
31import java.util.LinkedHashSet;
32import java.util.LinkedList;
33import java.util.List;
34
35import javax.swing.Action;
36import javax.swing.Icon;
37import javax.swing.JLabel;
38import javax.swing.JOptionPane;
39import javax.swing.SwingConstants;
40
41import org.openstreetmap.josm.Main;
42import org.openstreetmap.josm.actions.RenameLayerAction;
43import org.openstreetmap.josm.actions.mapmode.MapMode;
44import org.openstreetmap.josm.data.Bounds;
45import org.openstreetmap.josm.data.coor.LatLon;
46import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
47import org.openstreetmap.josm.gui.ExtendedDialog;
48import org.openstreetmap.josm.gui.MapFrame;
49import org.openstreetmap.josm.gui.MapView;
50import org.openstreetmap.josm.gui.PleaseWaitRunnable;
51import org.openstreetmap.josm.gui.MapFrame.MapModeChangeListener;
52import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
53import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
54import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
55import org.openstreetmap.josm.gui.layer.GpxLayer;
56import org.openstreetmap.josm.gui.layer.Layer;
57import org.openstreetmap.josm.tools.ExifReader;
58import org.openstreetmap.josm.tools.ImageProvider;
59
60import com.drew.imaging.jpeg.JpegMetadataReader;
61import com.drew.lang.CompoundException;
62import com.drew.lang.Rational;
63import com.drew.metadata.Directory;
64import com.drew.metadata.Metadata;
65import com.drew.metadata.exif.GpsDirectory;
66
67public class GeoImageLayer extends Layer implements PropertyChangeListener {
68
69 List<ImageEntry> data;
70 GpxLayer gpxLayer;
71
72 private Icon icon = ImageProvider.get("dialogs/geoimage/photo-marker");
73 private Icon selectedIcon = ImageProvider.get("dialogs/geoimage/photo-marker-selected");
74
75 private int currentPhoto = -1;
76
77 boolean useThumbs = false;
78 ThumbsLoader thumbsloader;
79 boolean thumbsLoaded = false;
80 private BufferedImage offscreenBuffer;
81 boolean updateOffscreenBuffer = true;
82
83 /** Loads a set of images, while displaying a dialog that indicates what the plugin is currently doing.
84 * In facts, this object is instantiated with a list of files. These files may be JPEG files or
85 * directories. In case of directories, they are scanned to find all the images they contain.
86 * Then all the images that have be found are loaded as ImageEntry instances.
87 */
88 private static final class Loader extends PleaseWaitRunnable {
89
90 private boolean cancelled = false;
91 private GeoImageLayer layer;
92 private Collection<File> selection;
93 private HashSet<String> loadedDirectories = new HashSet<String>();
94 private LinkedHashSet<String> errorMessages;
95 private GpxLayer gpxLayer;
96
97 protected void rememberError(String message) {
98 this.errorMessages.add(message);
99 }
100
101 public Loader(Collection<File> selection, GpxLayer gpxLayer) {
102 super(tr("Extracting GPS locations from EXIF"));
103 this.selection = selection;
104 this.gpxLayer = gpxLayer;
105 errorMessages = new LinkedHashSet<String>();
106 }
107
108 @Override protected void realRun() throws IOException {
109
110 progressMonitor.subTask(tr("Starting directory scan"));
111 Collection<File> files = new ArrayList<File>();
112 try {
113 addRecursiveFiles(files, selection);
114 } catch(NullPointerException npe) {
115 rememberError(tr("One of the selected files was null"));
116 }
117
118 if (cancelled)
119 return;
120 progressMonitor.subTask(tr("Read photos..."));
121 progressMonitor.setTicksCount(files.size());
122
123 progressMonitor.subTask(tr("Read photos..."));
124 progressMonitor.setTicksCount(files.size());
125
126 // read the image files
127 List<ImageEntry> data = new ArrayList<ImageEntry>(files.size());
128
129 for (File f : files) {
130
131 if (cancelled) {
132 break;
133 }
134
135 progressMonitor.subTask(tr("Reading {0}...", f.getName()));
136 progressMonitor.worked(1);
137
138 ImageEntry e = new ImageEntry();
139
140 // Changed to silently cope with no time info in exif. One case
141 // of person having time that couldn't be parsed, but valid GPS info
142
143 try {
144 e.setExifTime(ExifReader.readTime(f));
145 } catch (ParseException e1) {
146 e.setExifTime(null);
147 }
148 e.setFile(f);
149 extractExif(e);
150 data.add(e);
151 }
152 layer = new GeoImageLayer(data, gpxLayer);
153 files.clear();
154 }
155
156 private void addRecursiveFiles(Collection<File> files, Collection<File> sel) {
157 boolean nullFile = false;
158
159 for (File f : sel) {
160
161 if(cancelled) {
162 break;
163 }
164
165 if (f == null) {
166 nullFile = true;
167
168 } else if (f.isDirectory()) {
169 String canonical = null;
170 try {
171 canonical = f.getCanonicalPath();
172 } catch (IOException e) {
173 e.printStackTrace();
174 rememberError(tr("Unable to get canonical path for directory {0}\n",
175 f.getAbsolutePath()));
176 }
177
178 if (canonical == null || loadedDirectories.contains(canonical)) {
179 continue;
180 } else {
181 loadedDirectories.add(canonical);
182 }
183
184 Collection<File> children = Arrays.asList(f.listFiles(JpegFileFilter.getInstance()));
185 if (children != null) {
186 progressMonitor.subTask(tr("Scanning directory {0}", f.getPath()));
187 try {
188 addRecursiveFiles(files, children);
189 } catch(NullPointerException npe) {
190 npe.printStackTrace();
191 rememberError(tr("Found null file in directory {0}\n", f.getPath()));
192 }
193 } else {
194 rememberError(tr("Error while getting files from directory {0}\n", f.getPath()));
195 }
196
197 } else {
198 files.add(f);
199 }
200 }
201
202 if (nullFile)
203 throw new NullPointerException();
204 }
205
206 protected String formatErrorMessages() {
207 StringBuilder sb = new StringBuilder();
208 sb.append("<html>");
209 if (errorMessages.size() == 1) {
210 sb.append(errorMessages.iterator().next());
211 } else {
212 sb.append("<ul>");
213 for (String msg: errorMessages) {
214 sb.append("<li>").append(msg).append("</li>");
215 }
216 sb.append("/ul>");
217 }
218 sb.append("</html>");
219 return sb.toString();
220 }
221
222 @Override protected void finish() {
223 if (!errorMessages.isEmpty()) {
224 JOptionPane.showMessageDialog(
225 Main.parent,
226 formatErrorMessages(),
227 tr("Error"),
228 JOptionPane.ERROR_MESSAGE
229 );
230 }
231 if (layer != null) {
232 Main.main.addLayer(layer);
233 layer.hook_up_mouse_events(); // Main.map.mapView should exist
234 // now. Can add mouse listener
235 Main.map.mapView.addPropertyChangeListener(layer);
236 if (Main.map.getToggleDialog(ImageViewerDialog.class) == null) {
237 ImageViewerDialog.newInstance();
238 Main.map.addToggleDialog(ImageViewerDialog.getInstance());
239 }
240
241 if (! cancelled && layer.data.size() > 0) {
242 boolean noGeotagFound = true;
243 for (ImageEntry e : layer.data) {
244 if (e.getPos() != null) {
245 noGeotagFound = false;
246 }
247 }
248 if (noGeotagFound) {
249 new CorrelateGpxWithImages(layer).actionPerformed(null);
250 }
251 }
252 }
253 }
254
255 @Override protected void cancel() {
256 cancelled = true;
257 }
258 }
259
260 public static void create(Collection<File> files, GpxLayer gpxLayer) {
261 Loader loader = new Loader(files, gpxLayer);
262 Main.worker.execute(loader);
263 }
264
265 private GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer) {
266
267 super(tr("Geotagged Images"));
268
269 Collections.sort(data);
270 this.data = data;
271 this.gpxLayer = gpxLayer;
272 }
273
274 @Override
275 public Icon getIcon() {
276 return ImageProvider.get("dialogs/geoimage");
277 }
278
279 private static List<Action> menuAdditions = new LinkedList<Action>();
280 public static void registerMenuAddition(Action addition) {
281 menuAdditions.add(addition);
282 }
283
284 @Override
285 public Action[] getMenuEntries() {
286
287 List<Action> entries = new ArrayList<Action>();
288 entries.add(LayerListDialog.getInstance().createShowHideLayerAction());
289 entries.add(LayerListDialog.getInstance().createDeleteLayerAction());
290 entries.add(new RenameLayerAction(null, this));
291 entries.add(SeparatorLayerAction.INSTANCE);
292 entries.add(new CorrelateGpxWithImages(this));
293 if (!menuAdditions.isEmpty()) {
294 entries.add(SeparatorLayerAction.INSTANCE);
295 entries.addAll(menuAdditions);
296 }
297 entries.add(SeparatorLayerAction.INSTANCE);
298 entries.add(new LayerListPopup.InfoAction(this));
299
300 return entries.toArray(new Action[0]);
301
302 }
303
304 private String infoText() {
305 int i = 0;
306 for (ImageEntry e : data)
307 if (e.getPos() != null) {
308 i++;
309 }
310 return trn("{0} image loaded.", "{0} images loaded.", data.size(), data.size())
311 + " " + trn("{0} was found to be GPS tagged.", "{0} were found to be GPS tagged.", i, i);
312 }
313
314 @Override public Object getInfoComponent() {
315 return infoText();
316 }
317
318 @Override
319 public String getToolTipText() {
320 return infoText();
321 }
322
323 @Override
324 public boolean isMergable(Layer other) {
325 return other instanceof GeoImageLayer;
326 }
327
328 @Override
329 public void mergeFrom(Layer from) {
330 GeoImageLayer l = (GeoImageLayer) from;
331
332 ImageEntry selected = null;
333 if (l.currentPhoto >= 0) {
334 selected = l.data.get(l.currentPhoto);
335 }
336
337 data.addAll(l.data);
338 Collections.sort(data);
339
340 // Supress the double photos.
341 if (data.size() > 1) {
342 ImageEntry cur;
343 ImageEntry prev = data.get(data.size() - 1);
344 for (int i = data.size() - 2; i >= 0; i--) {
345 cur = data.get(i);
346 if (cur.getFile().equals(prev.getFile())) {
347 data.remove(i);
348 } else {
349 prev = cur;
350 }
351 }
352 }
353
354 if (selected != null) {
355 for (int i = 0; i < data.size() ; i++) {
356 if (data.get(i) == selected) {
357 currentPhoto = i;
358 ImageViewerDialog.showImage(GeoImageLayer.this, data.get(i));
359 break;
360 }
361 }
362 }
363
364 setName(l.getName());
365 }
366
367 private Dimension scaledDimension(Image thumb) {
368 final double d = Main.map.mapView.getDist100Pixel();
369 final double size = 10 /*meter*/; /* size of the photo on the map */
370 double s = size * 100 /*px*/ / d;
371
372 final double sMin = ThumbsLoader.minSize;
373 final double sMax = ThumbsLoader.maxSize;
374
375 if (s < sMin) {
376 s = sMin;
377 }
378 if (s > sMax) {
379 s = sMax;
380 }
381 final double f = s / sMax; /* scale factor */
382
383 if (thumb == null)
384 return null;
385
386 return new Dimension(
387 (int) Math.round(f * thumb.getWidth(null)),
388 (int) Math.round(f * thumb.getHeight(null)));
389 }
390
391 @Override
392 public void paint(Graphics2D g, MapView mv, Bounds bounds) {
393 int width = Main.map.mapView.getWidth();
394 int height = Main.map.mapView.getHeight();
395 Rectangle clip = g.getClipBounds();
396 if (useThumbs) {
397 if (null == offscreenBuffer || offscreenBuffer.getWidth() != width // reuse the old buffer if possible
398 || offscreenBuffer.getHeight() != height) {
399 offscreenBuffer = new BufferedImage(width, height,
400 BufferedImage.TYPE_INT_ARGB);
401 updateOffscreenBuffer = true;
402 }
403
404 if (updateOffscreenBuffer) {
405 Graphics2D tempG = offscreenBuffer.createGraphics();
406 tempG.setColor(new Color(0,0,0,0));
407 Composite saveComp = tempG.getComposite();
408 tempG.setComposite(AlphaComposite.Clear); // remove the old images
409 tempG.fillRect(0, 0, width, height);
410 tempG.setComposite(saveComp);
411
412 for (ImageEntry e : data) {
413 if (e.getPos() == null) {
414 continue;
415 }
416 Point p = mv.getPoint(e.getPos());
417 if (e.thumbnail != null) {
418 Dimension d = scaledDimension(e.thumbnail);
419 Rectangle target = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
420 if (clip.intersects(target)) {
421 tempG.drawImage(e.thumbnail, target.x, target.y, target.width, target.height, null);
422 }
423 }
424 else { // thumbnail not loaded yet
425 icon.paintIcon(mv, tempG,
426 p.x - icon.getIconWidth() / 2,
427 p.y - icon.getIconHeight() / 2);
428 }
429 }
430 updateOffscreenBuffer = false;
431 }
432 g.drawImage(offscreenBuffer, 0, 0, null);
433 }
434 else {
435 for (ImageEntry e : data) {
436 if (e.getPos() == null) {
437 continue;
438 }
439 Point p = mv.getPoint(e.getPos());
440 icon.paintIcon(mv, g,
441 p.x - icon.getIconWidth() / 2,
442 p.y - icon.getIconHeight() / 2);
443 }
444 }
445
446 if (currentPhoto >= 0 && currentPhoto < data.size()) {
447 ImageEntry e = data.get(currentPhoto);
448
449 if (e.getPos() != null) {
450 Point p = mv.getPoint(e.getPos());
451
452 if (e.thumbnail != null) {
453 Dimension d = scaledDimension(e.thumbnail);
454 g.setColor(new Color(128, 0, 0, 122));
455 g.fillRect(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
456 } else {
457 if (e.getExifImgDir() != null) {
458 double arrowlength = 25;
459 double arrowwidth = 18;
460
461 double dir = e.getExifImgDir();
462 // Rotate 90 degrees CCW
463 double headdir = ( dir < 90 ) ? dir + 270 : dir - 90;
464 double leftdir = ( headdir < 90 ) ? headdir + 270 : headdir - 90;
465 double rightdir = ( headdir > 270 ) ? headdir - 270 : headdir + 90;
466
467 double ptx = p.x + Math.cos(Math.toRadians(headdir)) * arrowlength;
468 double pty = p.y + Math.sin(Math.toRadians(headdir)) * arrowlength;
469
470 double ltx = p.x + Math.cos(Math.toRadians(leftdir)) * arrowwidth/2;
471 double lty = p.y + Math.sin(Math.toRadians(leftdir)) * arrowwidth/2;
472
473 double rtx = p.x + Math.cos(Math.toRadians(rightdir)) * arrowwidth/2;
474 double rty = p.y + Math.sin(Math.toRadians(rightdir)) * arrowwidth/2;
475
476 g.setColor(Color.white);
477 int[] xar = {(int) ltx, (int) ptx, (int) rtx, (int) ltx};
478 int[] yar = {(int) lty, (int) pty, (int) rty, (int) lty};
479 g.fillPolygon(xar, yar, 4);
480 }
481
482 selectedIcon.paintIcon(mv, g,
483 p.x - selectedIcon.getIconWidth() / 2,
484 p.y - selectedIcon.getIconHeight() / 2);
485
486 }
487 }
488 }
489 }
490
491 @Override
492 public void visitBoundingBox(BoundingXYVisitor v) {
493 for (ImageEntry e : data) {
494 v.visit(e.getPos());
495 }
496 }
497
498 /*
499 * Extract gps from image exif
500 *
501 * If successful, fills in the LatLon and EastNorth attributes of passed in
502 * image;
503 */
504
505 private static void extractExif(ImageEntry e) {
506
507 int deg;
508 double min, sec;
509 double lon, lat;
510 Metadata metadata = null;
511 Directory dir = null;
512
513 try {
514 metadata = JpegMetadataReader.readMetadata(e.getFile());
515 dir = metadata.getDirectory(GpsDirectory.class);
516 } catch (CompoundException p) {
517 e.setExifCoor(null);
518 e.setPos(null);
519 return;
520 }
521
522 try {
523 // longitude
524
525 Rational[] components = dir.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
526
527 deg = components[0].intValue();
528 min = components[1].floatValue();
529 sec = components[2].floatValue();
530
531 lon = (deg + (min / 60) + (sec / 3600));
532
533 if (dir.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') {
534 lon = -lon;
535 }
536
537 // latitude
538
539 components = dir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
540
541 deg = components[0].intValue();
542 min = components[1].floatValue();
543 sec = components[2].floatValue();
544
545 lat = (deg + (min / 60) + (sec / 3600));
546
547 if (dir.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S') {
548 lat = -lat;
549 }
550
551 // Store values
552
553 e.setExifCoor(new LatLon(lat, lon));
554 e.setPos(e.getExifCoor());
555
556 } catch (CompoundException p) {
557 // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)
558 try {
559 Double longitude = dir.getDouble(GpsDirectory.TAG_GPS_LONGITUDE);
560 Double latitude = dir.getDouble(GpsDirectory.TAG_GPS_LATITUDE);
561 if (longitude == null || latitude == null)
562 throw new CompoundException("");
563
564 // Store values
565
566 e.setExifCoor(new LatLon(latitude, longitude));
567 e.setPos(e.getExifCoor());
568 } catch (CompoundException ex) {
569 e.setExifCoor(null);
570 e.setPos(null);
571 }
572 } catch (Exception ex) { // (other exceptions, e.g. #5271)
573 System.err.println("Error when reading EXIF from file: "+ex);
574 e.setExifCoor(null);
575 e.setPos(null);
576 }
577
578 // compass direction value
579
580 Rational direction = null;
581
582 try {
583 direction = dir.getRational(GpsDirectory.TAG_GPS_IMG_DIRECTION);
584 if (direction != null) {
585 e.setExifImgDir(direction.doubleValue());
586 }
587 } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271)
588 // Do nothing
589 }
590 }
591
592 public void showNextPhoto() {
593 if (data != null && data.size() > 0) {
594 currentPhoto++;
595 if (currentPhoto >= data.size()) {
596 currentPhoto = data.size() - 1;
597 }
598 ImageViewerDialog.showImage(this, data.get(currentPhoto));
599 } else {
600 currentPhoto = -1;
601 }
602 Main.map.repaint();
603 }
604
605 public void showPreviousPhoto() {
606 if (data != null && data.size() > 0) {
607 currentPhoto--;
608 if (currentPhoto < 0) {
609 currentPhoto = 0;
610 }
611 ImageViewerDialog.showImage(this, data.get(currentPhoto));
612 } else {
613 currentPhoto = -1;
614 }
615 Main.map.repaint();
616 }
617
618 public void checkPreviousNextButtons() {
619 ImageViewerDialog.setNextEnabled(currentPhoto < data.size() - 1);
620 ImageViewerDialog.setPreviousEnabled(currentPhoto > 0);
621 }
622
623 public void removeCurrentPhoto() {
624 if (data != null && data.size() > 0 && currentPhoto >= 0 && currentPhoto < data.size()) {
625 data.remove(currentPhoto);
626 if (currentPhoto >= data.size()) {
627 currentPhoto = data.size() - 1;
628 }
629 if (currentPhoto >= 0) {
630 ImageViewerDialog.showImage(this, data.get(currentPhoto));
631 } else {
632 ImageViewerDialog.showImage(this, null);
633 }
634 updateOffscreenBuffer = true;
635 Main.map.repaint();
636 }
637 }
638
639 public void removeCurrentPhotoFromDisk() {
640 ImageEntry toDelete = null;
641 if (data != null && data.size() > 0 && currentPhoto >= 0 && currentPhoto < data.size()) {
642 toDelete = data.get(currentPhoto);
643
644 int result = new ExtendedDialog(
645 Main.parent,
646 tr("Delete image file from disk"),
647 new String[] {tr("Cancel"), tr("Delete")})
648 .setButtonIcons(new String[] {"cancel.png", "dialogs/delete.png"})
649 .setContent(new JLabel(tr("<html><h3>Delete the file {0} from disk?<p>The image file will be permanently lost!</h3></html>"
650 ,toDelete.getFile().getName()), ImageProvider.get("dialogs/geoimage/deletefromdisk"),SwingConstants.LEFT))
651 .toggleEnable("geoimage.deleteimagefromdisk")
652 .setCancelButton(1)
653 .setDefaultButton(2)
654 .showDialog()
655 .getValue();
656
657 if(result == 2)
658 {
659 data.remove(currentPhoto);
660 if (currentPhoto >= data.size()) {
661 currentPhoto = data.size() - 1;
662 }
663 if (currentPhoto >= 0) {
664 ImageViewerDialog.showImage(this, data.get(currentPhoto));
665 } else {
666 ImageViewerDialog.showImage(this, null);
667 }
668
669 if (toDelete.getFile().delete()) {
670 System.out.println("File "+toDelete.getFile().toString()+" deleted. ");
671 } else {
672 JOptionPane.showMessageDialog(
673 Main.parent,
674 tr("Image file could not be deleted."),
675 tr("Error"),
676 JOptionPane.ERROR_MESSAGE
677 );
678 }
679
680 updateOffscreenBuffer = true;
681 Main.map.repaint();
682 }
683 }
684 }
685
686 private MouseAdapter mouseAdapter = null;
687 private MapModeChangeListener mapModeListener = null;
688
689 private void hook_up_mouse_events() {
690 mouseAdapter = new MouseAdapter() {
691 @Override public void mousePressed(MouseEvent e) {
692
693 if (e.getButton() != MouseEvent.BUTTON1)
694 return;
695 if (isVisible()) {
696 Main.map.mapView.repaint();
697 }
698 }
699
700 @Override public void mouseReleased(MouseEvent ev) {
701 if (ev.getButton() != MouseEvent.BUTTON1)
702 return;
703 if (!isVisible())
704 return;
705
706 for (int i = data.size() - 1; i >= 0; --i) {
707 ImageEntry e = data.get(i);
708 if (e.getPos() == null) {
709 continue;
710 }
711 Point p = Main.map.mapView.getPoint(e.getPos());
712 Rectangle r;
713 if (e.thumbnail != null) {
714 Dimension d = scaledDimension(e.thumbnail);
715 r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
716 } else {
717 r = new Rectangle(p.x - icon.getIconWidth() / 2,
718 p.y - icon.getIconHeight() / 2,
719 icon.getIconWidth(),
720 icon.getIconHeight());
721 }
722 if (r.contains(ev.getPoint())) {
723 currentPhoto = i;
724 ImageViewerDialog.showImage(GeoImageLayer.this, e);
725 Main.map.repaint();
726 break;
727 }
728 }
729 }
730 };
731
732 mapModeListener = new MapModeChangeListener() {
733 public void mapModeChange(MapMode oldMapMode, MapMode newMapMode) {
734 if (newMapMode instanceof org.openstreetmap.josm.actions.mapmode.SelectAction) {
735 Main.map.mapView.addMouseListener(mouseAdapter);
736 } else {
737 Main.map.mapView.removeMouseListener(mouseAdapter);
738 }
739 }
740 };
741
742 MapFrame.addMapModeChangeListener(mapModeListener);
743 mapModeListener.mapModeChange(null, Main.map.mapMode);
744
745 MapView.addLayerChangeListener(new LayerChangeListener() {
746 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
747 if (newLayer == GeoImageLayer.this) {
748 // only in select mode it is possible to click the images
749 Main.map.selectSelectTool(false);
750 }
751 }
752
753 public void layerAdded(Layer newLayer) {
754 }
755
756 public void layerRemoved(Layer oldLayer) {
757 if (oldLayer == GeoImageLayer.this) {
758 if (thumbsloader != null) {
759 thumbsloader.stop = true;
760 }
761 Main.map.mapView.removeMouseListener(mouseAdapter);
762 MapFrame.removeMapModeChangeListener(mapModeListener);
763 currentPhoto = -1;
764 data.clear();
765 data = null;
766 // stop listening to layer change events
767 MapView.removeLayerChangeListener(this);
768 }
769 }
770 });
771 }
772
773 public void propertyChange(PropertyChangeEvent evt) {
774 if ("center".equals(evt.getPropertyName()) || "scale".equals(evt.getPropertyName())) {
775 updateOffscreenBuffer = true;
776 }
777 }
778
779 public void loadThumbs() {
780 if (useThumbs && !thumbsLoaded) {
781 thumbsLoaded = true;
782 thumbsloader = new ThumbsLoader(this);
783 Thread t = new Thread(thumbsloader);
784 t.setPriority(Thread.MIN_PRIORITY);
785 t.start();
786 }
787 }
788
789 public void updateBufferAndRepaint() {
790 updateOffscreenBuffer = true;
791 Main.map.mapView.repaint();
792 }
793
794 public List<ImageEntry> getImages() {
795 List<ImageEntry> copy = new ArrayList<ImageEntry>();
796 for (ImageEntry ie : data) {
797 copy.add(ie.clone());
798 }
799 return copy;
800 }
801}
Note: See TracBrowser for help on using the repository browser.