Ticket #12255: 12255-v4.patch

File 12255-v4.patch, 28.7 KB (added by holgermappt, 8 years ago)
  • src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java

     
    156156                if (yLayer != null) {
    157157                    if (yLayer.data != null) {
    158158                        for (ImageEntry ie : yLayer.data) {
    159                             ie.tmp = null;
     159                            ie.discardTmp();
    160160                        }
    161161                    }
    162162                    yLayer.updateBufferAndRepaint();
     
    825825            // So reset all images.
    826826            if (yLayer.data != null) {
    827827                for (ImageEntry ie: yLayer.data) {
    828                     ie.tmp = null;
     828                    ie.discardTmp();
    829829                }
    830830            }
    831831
     
    833833            List<ImageEntry> dateImgLst = getSortedImgList();
    834834            // Create a temporary copy for each image
    835835            for (ImageEntry ie : dateImgLst) {
    836                 ie.cleanTmp();
     836                ie.createTmp();
     837                ie.tmp.setPos(null);
    837838            }
    838839
    839840            GpxDataWrapper selGpx = selectedGPX(false);
  • src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

     
    2121import java.beans.PropertyChangeListener;
    2222import java.io.File;
    2323import java.io.IOException;
    24 import java.text.ParseException;
    2524import java.util.ArrayList;
    2625import java.util.Arrays;
    27 import java.util.Calendar;
    2826import java.util.Collection;
    2927import java.util.Collections;
    30 import java.util.GregorianCalendar;
    3128import java.util.HashSet;
    3229import java.util.LinkedHashSet;
    3330import java.util.LinkedList;
    3431import java.util.List;
    3532import java.util.Set;
    36 import java.util.TimeZone;
    3733import java.util.concurrent.ExecutorService;
    3834import java.util.concurrent.Executors;
    3935
     
    4945import org.openstreetmap.josm.actions.mapmode.MapMode;
    5046import org.openstreetmap.josm.actions.mapmode.SelectAction;
    5147import org.openstreetmap.josm.data.Bounds;
    52 import org.openstreetmap.josm.data.coor.LatLon;
    5348import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    5449import org.openstreetmap.josm.gui.ExtendedDialog;
    5550import org.openstreetmap.josm.gui.MapFrame;
     
    6762import org.openstreetmap.josm.gui.layer.Layer;
    6863import org.openstreetmap.josm.gui.util.GuiHelper;
    6964import org.openstreetmap.josm.io.JpgImporter;
    70 import org.openstreetmap.josm.tools.ExifReader;
    7165import org.openstreetmap.josm.tools.ImageProvider;
    7266import org.openstreetmap.josm.tools.Utils;
    7367
    74 import com.drew.imaging.jpeg.JpegMetadataReader;
    75 import com.drew.lang.CompoundException;
    76 import com.drew.metadata.Directory;
    77 import com.drew.metadata.Metadata;
    78 import com.drew.metadata.MetadataException;
    79 import com.drew.metadata.exif.ExifIFD0Directory;
    80 import com.drew.metadata.exif.GpsDirectory;
    81 
    8268/**
    8369 * Layer displaying geottaged pictures.
    8470 */
     
    157143                progressMonitor.subTask(tr("Reading {0}...", f.getName()));
    158144                progressMonitor.worked(1);
    159145
    160                 ImageEntry e = new ImageEntry();
    161 
    162                 // Changed to silently cope with no time info in exif. One case
    163                 // of person having time that couldn't be parsed, but valid GPS info
    164 
    165                 try {
    166                     e.setExifTime(ExifReader.readTime(f));
    167                 } catch (ParseException ex) {
    168                     e.setExifTime(null);
    169                 }
    170                 e.setFile(f);
    171                 extractExif(e);
     146                ImageEntry e = new ImageEntry(f);
     147                e.extractExif();
    172148                data.add(e);
    173149            }
    174150            layer = new GeoImageLayer(data, gpxLayer);
     
    498474                            continue;
    499475                        }
    500476                        Point p = mv.getPoint(e.getPos());
    501                         if (e.thumbnail != null) {
    502                             Dimension d = scaledDimension(e.thumbnail);
     477                        if (e.hasThumbnail()) {
     478                            Dimension d = scaledDimension(e.getThumbnail());
    503479                            Rectangle target = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
    504480                            if (clip.intersects(target)) {
    505                                 tempG.drawImage(e.thumbnail, target.x, target.y, target.width, target.height, null);
     481                                tempG.drawImage(e.getThumbnail(), target.x, target.y, target.width, target.height, null);
    506482                            }
    507483                        } else { // thumbnail not loaded yet
    508484                            icon.paintIcon(mv, tempG,
     
    534510
    535511                int imgWidth = 100;
    536512                int imgHeight = 100;
    537                 if (useThumbs && e.thumbnail != null) {
    538                     Dimension d = scaledDimension(e.thumbnail);
     513                if (useThumbs && e.hasThumbnail()) {
     514                    Dimension d = scaledDimension(e.getThumbnail());
    539515                    imgWidth = d.width;
    540516                    imgHeight = d.height;
    541517                } else {
     
    573549                    g.drawPolyline(xar, yar, 3);
    574550                }
    575551
    576                 if (useThumbs && e.thumbnail != null) {
     552                if (useThumbs && e.hasThumbnail()) {
    577553                    g.setColor(new Color(128, 0, 0, 122));
    578554                    g.fillRect(p.x - imgWidth / 2, p.y - imgHeight / 2, imgWidth, imgHeight);
    579555                } else {
     
    593569        }
    594570    }
    595571
    596     /**
    597      * Extract GPS metadata from image EXIF
    598      *
    599      * If successful, fills in the LatLon and EastNorth attributes of passed in image
    600      * @param e image entry
    601      */
    602     private static void extractExif(ImageEntry e) {
    603 
    604         Metadata metadata;
    605         Directory dirExif;
    606         GpsDirectory dirGps;
    607 
    608         try {
    609             metadata = JpegMetadataReader.readMetadata(e.getFile());
    610             dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    611             dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    612         } catch (CompoundException | IOException p) {
    613             e.setExifCoor(null);
    614             e.setPos(null);
    615             return;
    616         }
    617 
    618         try {
    619             if (dirExif != null) {
    620                 int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    621                 e.setExifOrientation(orientation);
    622             }
    623         } catch (MetadataException ex) {
    624             Main.debug(ex.getMessage());
    625         }
    626 
    627         if (dirGps == null) {
    628             e.setExifCoor(null);
    629             e.setPos(null);
    630             return;
    631         }
    632 
    633         try {
    634             double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);
    635             String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
    636             if ("M".equalsIgnoreCase(speedRef)) {
    637                 // miles per hour
    638                 speed *= 1.609344;
    639             } else if ("N".equalsIgnoreCase(speedRef)) {
    640                 // knots == nautical miles per hour
    641                 speed *= 1.852;
    642             }
    643             // default is K (km/h)
    644             e.setSpeed(speed);
    645         } catch (Exception ex) {
    646             Main.debug(ex.getMessage());
    647         }
    648 
    649         try {
    650             double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);
    651             int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);
    652             if (d == 1) {
    653                 ele *= -1;
    654             }
    655             e.setElevation(ele);
    656         } catch (MetadataException ex) {
    657             Main.debug(ex.getMessage());
    658         }
    659 
    660         try {
    661             LatLon latlon = ExifReader.readLatLon(dirGps);
    662             e.setExifCoor(latlon);
    663             e.setPos(e.getExifCoor());
    664 
    665         } catch (Exception ex) { // (other exceptions, e.g. #5271)
    666             Main.error("Error reading EXIF from file: "+ex);
    667             e.setExifCoor(null);
    668             e.setPos(null);
    669         }
    670 
    671         try {
    672             Double direction = ExifReader.readDirection(dirGps);
    673             if (direction != null) {
    674                 e.setExifImgDir(direction.doubleValue());
    675             }
    676         } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271)
    677             Main.debug(ex.getMessage());
    678         }
    679 
    680         // Time and date. We can have these cases:
    681         // 1) GPS_TIME_STAMP not set -> date/time will be null
    682         // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default
    683         // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set
    684         int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_TIME_STAMP);
    685         if (timeStampComps != null) {
    686             int gpsHour = timeStampComps[0];
    687             int gpsMin = timeStampComps[1];
    688             int gpsSec = timeStampComps[2];
    689             Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    690 
    691             // We have the time. Next step is to check if the GPS date stamp is set.
    692             // dirGps.getString() always succeeds, but the return value might be null.
    693             String dateStampStr = dirGps.getString(GpsDirectory.TAG_DATE_STAMP);
    694             if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {
    695                 String[] dateStampComps = dateStampStr.split(":");
    696                 cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));
    697                 cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);
    698                 cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));
    699             } else {
    700                 // No GPS date stamp in EXIF data. Copy it from EXIF time.
    701                 // Date is not set if EXIF time is not available.
    702                 if (e.hasExifTime()) {
    703                     // Time not set yet, so we can copy everything, not just date.
    704                     cal.setTime(e.getExifTime());
    705                 }
    706             }
    707 
    708             cal.set(Calendar.HOUR_OF_DAY, gpsHour);
    709             cal.set(Calendar.MINUTE, gpsMin);
    710             cal.set(Calendar.SECOND, gpsSec);
    711 
    712             e.setExifGpsTime(cal.getTime());
    713         }
    714     }
    715 
    716572    public void showNextPhoto() {
    717573        if (data != null && !data.isEmpty()) {
    718574            currentPhoto++;
     
    861717                }
    862718                Point p = Main.map.mapView.getPoint(img.getPos());
    863719                Rectangle r;
    864                 if (useThumbs && img.thumbnail != null) {
    865                     Dimension d = scaledDimension(img.thumbnail);
     720                if (useThumbs && img.hasThumbnail()) {
     721                    Dimension d = scaledDimension(img.getThumbnail());
    866722                    r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
    867723                } else {
    868724                    r = new Rectangle(p.x - icon.getIconWidth() / 2,
     
    971827                    }
    972828                    Point p = Main.map.mapView.getPoint(e.getPos());
    973829                    Rectangle r;
    974                     if (useThumbs && e.thumbnail != null) {
    975                         Dimension d = scaledDimension(e.thumbnail);
     830                    if (useThumbs && e.hasThumbnail()) {
     831                        Dimension d = scaledDimension(e.getThumbnail());
    976832                        r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
    977833                    } else {
    978834                        r = new Rectangle(p.x - icon.getIconWidth() / 2,
  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java

     
    33
    44import java.awt.Image;
    55import java.io.File;
     6import java.io.IOException;
     7import java.text.ParseException;
     8import java.util.Calendar;
    69import java.util.Date;
     10import java.util.GregorianCalendar;
     11import java.util.TimeZone;
    712
     13import com.drew.imaging.jpeg.JpegMetadataReader;
     14import com.drew.lang.CompoundException;
     15import com.drew.metadata.Directory;
     16import com.drew.metadata.Metadata;
     17import com.drew.metadata.MetadataException;
     18import com.drew.metadata.exif.ExifIFD0Directory;
     19import com.drew.metadata.exif.GpsDirectory;
     20import org.openstreetmap.josm.Main;
     21import org.openstreetmap.josm.data.SystemOfMeasurement;
    822import org.openstreetmap.josm.data.coor.CachedLatLon;
    923import org.openstreetmap.josm.data.coor.LatLon;
     24import org.openstreetmap.josm.tools.ExifReader;
    1025
    1126/**
    1227 * Stores info about each image
     
    2540    private boolean isNewGpsData;
    2641    /** Temporary source of GPS time if not correlated with GPX track. */
    2742    private Date exifGpsTime;
    28     Image thumbnail;
     43    private Image thumbnail;
    2944
    3045    /**
    3146     * The following values are computed from the correlation with the gpx track
     
    4358     * When the correlation dialog is open, we like to show the image position
    4459     * for the current time offset on the map in real time.
    4560     * On the other hand, when the user aborts this operation, the old values
    46      * should be restored. We have a temprary copy, that overrides
     61     * should be restored. We have a temporary copy, that overrides
    4762     * the normal values if it is not null. (This may be not the most elegant
    4863     * solution for this, but it works.)
    4964     */
     
    5065    ImageEntry tmp;
    5166
    5267    /**
    53      * Returns the cached temporary position value.
    54      * @return the cached temporary position value
     68     * Constructs a new {@code ImageEntry}.
    5569     */
     70    public ImageEntry() {}
     71
     72    /**
     73     * Constructs a new {@code ImageEntry}.
     74     * @param file Path to image file on disk
     75     */
     76    public ImageEntry(File file) {
     77        setFile(file);
     78    }
     79
     80    /**
     81     * Returns the position value. The position value from the temporary copy
     82     * is returned if that copy exists.
     83     * @return the position value
     84     */
    5685    public CachedLatLon getPos() {
    5786        if (tmp != null)
    5887            return tmp.pos;
     
    6089    }
    6190
    6291    /**
    63      * Returns the cached temporary speed value.
    64      * @return the cached temporary speed value
     92     * Returns the speed value. The speed value from the temporary copy is
     93     * returned if that copy exists.
     94     * @return the speed value
    6595     */
    6696    public Double getSpeed() {
    6797        if (tmp != null)
     
    70100    }
    71101
    72102    /**
    73      * Returns the cached temporary elevation value.
    74      * @return the cached temporary elevation value
     103     * Returns the elevation value. The elevation value from the temporary
     104     * copy is returned if that copy exists.
     105     * @return the elevation value
    75106     */
    76107    public Double getElevation() {
    77108        if (tmp != null)
     
    80111    }
    81112
    82113    /**
    83      * Returns the cached temporary GPS time value.
    84      * @return the cached temporary GPS time value
     114     * Returns the GPS time value. The GPS time value from the temporary copy
     115     * is returned if that copy exists.
     116     * @return the GPS time value
    85117     */
    86118    public Date getGpsTime() {
    87119        if (tmp != null)
     
    98130        return (tmp != null && tmp.gpsTime != null) || gpsTime != null;
    99131    }
    100132
     133    public Double getExifImgDir() {
     134        if (tmp != null)
     135            return tmp.exifImgDir;
     136        return exifImgDir;
     137    }
     138
    101139    /**
    102140     * Returns associated file.
    103141     * @return associated file
     
    159197        return exifCoor;
    160198    }
    161199
    162     public Double getExifImgDir() {
    163         return exifImgDir;
    164     }
    165 
    166200    public boolean hasThumbnail() {
    167201        return thumbnail != null;
    168202    }
    169203
     204    public Image getThumbnail() {
     205        return thumbnail;
     206    }
     207
    170208    /**
     209     * Sets the thumbnail.
     210     * @param thumbnail thumbnail
     211     */
     212    public void setThumbnail(Image thumbnail) {
     213        this.thumbnail = thumbnail;
     214    }
     215
     216    /**
     217     * Loads the thumbnail if it was not loaded yet.
     218     */
     219    public void loadThumbnail() {
     220        if (thumbnail == null) {
     221            ThumbsLoader thumbsloader = new ThumbsLoader(this);
     222            thumbsloader.run();
     223        }
     224    }
     225
     226    /**
    171227     * Sets the position.
    172228     * @param pos cached position
    173229     */
     
    180236     * @param pos position (will be cached)
    181237     */
    182238    public void setPos(LatLon pos) {
    183         setPos(new CachedLatLon(pos));
     239        setPos(pos != null ? new CachedLatLon(pos) : null);
    184240    }
    185241
    186242    /**
     
    240296        this.exifCoor = exifCoor;
    241297    }
    242298
    243     public void setExifImgDir(double exifDir) {
     299    public void setExifImgDir(Double exifDir) {
    244300        this.exifImgDir = exifDir;
    245301    }
    246302
     
    268324    }
    269325
    270326    /**
    271      * Make a fresh copy and save it in the temporary variable.
     327     * Make a fresh copy and save it in the temporary variable. Use
     328     * {@link #applyTmp()} or {@link #discardTmp()} if the temporary variable
     329     * is not needed anymore.
    272330     */
    273     public void cleanTmp() {
     331    public void createTmp() {
    274332        tmp = clone();
    275         tmp.setPos(null);
    276333        tmp.tmp = null;
    277334    }
    278335
    279336    /**
    280      * Copy the values from the temporary variable to the main instance.
     337     * Get temporary variable that is used for real time parameter
     338     * adjustments. The temporary variable is created if it does not exist
     339     * yet. Use {@link #applyTmp()} or {@link #discardTmp()} if the temporary
     340     * variable is not needed anymore.
     341     * @return temporary variable
    281342     */
     343    public ImageEntry getTmp() {
     344        if (tmp == null) {
     345            createTmp();
     346        }
     347        return tmp;
     348    }
     349
     350    /**
     351     * Copy the values from the temporary variable to the main instance. The
     352     * temporary variable is deleted.
     353     * @see #discardTmp()
     354     */
    282355    public void applyTmp() {
    283356        if (tmp != null) {
    284357            pos = tmp.pos;
     
    285358            speed = tmp.speed;
    286359            elevation = tmp.elevation;
    287360            gpsTime = tmp.gpsTime;
     361            exifImgDir = tmp.exifImgDir;
    288362            tmp = null;
    289363        }
    290364    }
    291365
    292366    /**
     367     * Delete the temporary variable. Temporary modifications are lost.
     368     * @see #applyTmp()
     369     */
     370    public void discardTmp() {
     371        tmp = null;
     372    }
     373
     374    /**
    293375     * If it has been tagged i.e. matched to a gpx track or retrieved lat/lon from exif
    294376     * @return {@code true} if it has been tagged
    295377     */
     
    335417    public boolean hasNewGpsData() {
    336418        return isNewGpsData;
    337419    }
     420
     421    /**
     422     * Extract GPS metadata from image EXIF. Has no effect if the image file is not set
     423     *
     424     * If successful, fills in the LatLon, speed, elevation, image direction, and other attributes
     425     */
     426    public void extractExif() {
     427
     428        Metadata metadata;
     429        Directory dirExif;
     430        GpsDirectory dirGps;
     431
     432        if (file == null) {
     433            return;
     434        }
     435
     436        try {
     437            metadata = JpegMetadataReader.readMetadata(file);
     438            dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
     439            dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
     440        } catch (CompoundException | IOException p) {
     441            setExifCoor(null);
     442            setPos(null);
     443            return;
     444        }
     445
     446        try {
     447            if (dirExif != null) {
     448                int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
     449                setExifOrientation(orientation);
     450            }
     451        } catch (MetadataException ex) {
     452            Main.debug(ex.getMessage());
     453        }
     454
     455        if (dirGps == null) {
     456            setExifCoor(null);
     457            setPos(null);
     458            return;
     459        }
     460
     461        try {
     462            double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);
     463            String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
     464            if ("M".equalsIgnoreCase(speedRef)) {
     465                // miles per hour
     466                speed *= SystemOfMeasurement.IMPERIAL.bValue / 1000;
     467            } else if ("N".equalsIgnoreCase(speedRef)) {
     468                // knots == nautical miles per hour
     469                speed *= SystemOfMeasurement.NAUTICAL_MILE.bValue / 1000;
     470            }
     471            // default is K (km/h)
     472            setSpeed(speed);
     473        } catch (Exception ex) {
     474            Main.debug(ex.getMessage());
     475        }
     476
     477        try {
     478            double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);
     479            int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);
     480            if (d == 1) {
     481                ele *= -1;
     482            }
     483            setElevation(ele);
     484        } catch (MetadataException ex) {
     485            Main.debug(ex.getMessage());
     486        }
     487
     488        try {
     489            LatLon latlon = ExifReader.readLatLon(dirGps);
     490            setExifCoor(latlon);
     491            setPos(getExifCoor());
     492
     493        } catch (Exception ex) { // (other exceptions, e.g. #5271)
     494            Main.error("Error reading EXIF from file: " + ex);
     495            setExifCoor(null);
     496            setPos(null);
     497        }
     498
     499        try {
     500            Double direction = ExifReader.readDirection(dirGps);
     501            if (direction != null) {
     502                setExifImgDir(direction);
     503            }
     504        } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271)
     505            Main.debug(ex.getMessage());
     506        }
     507
     508        // Changed to silently cope with no time info in exif. One case
     509        // of person having time that couldn't be parsed, but valid GPS info
     510        try {
     511            setExifTime(ExifReader.readTime(file));
     512        } catch (ParseException ex) {
     513            setExifTime(null);
     514        }
     515
     516        // Time and date. We can have these cases:
     517        // 1) GPS_TIME_STAMP not set -> date/time will be null
     518        // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default
     519        // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set
     520        int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_TIME_STAMP);
     521        if (timeStampComps != null) {
     522            int gpsHour = timeStampComps[0];
     523            int gpsMin = timeStampComps[1];
     524            int gpsSec = timeStampComps[2];
     525            Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
     526
     527            // We have the time. Next step is to check if the GPS date stamp is set.
     528            // dirGps.getString() always succeeds, but the return value might be null.
     529            String dateStampStr = dirGps.getString(GpsDirectory.TAG_DATE_STAMP);
     530            if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {
     531                String[] dateStampComps = dateStampStr.split(":");
     532                cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));
     533                cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);
     534                cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));
     535            } else {
     536                // No GPS date stamp in EXIF data. Copy it from EXIF time.
     537                // Date is not set if EXIF time is not available.
     538                if (hasExifTime()) {
     539                    // Time not set yet, so we can copy everything, not just date.
     540                    cal.setTime(getExifTime());
     541                }
     542            }
     543
     544            cal.set(Calendar.HOUR_OF_DAY, gpsHour);
     545            cal.set(Calendar.MINUTE, gpsMin);
     546            cal.set(Calendar.SECOND, gpsSec);
     547
     548            setExifGpsTime(cal.getTime());
     549        }
     550    }
    338551}
  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

     
    6969    private JButton btnNext;
    7070    private JButton btnPrevious;
    7171    private JButton btnCollapse;
     72    private JToggleButton tbCentre;
    7273
    7374    private ImageViewerDialog() {
    7475        super(tr("Geotagged Images"), "geoimage", tr("Display geotagged images"), Shortcut.registerShortcut("tools:geotagged",
     
    149150                        "geoimage:last", tr("Geoimage: {0}", tr("Show last Image")), KeyEvent.VK_END, Shortcut.DIRECT)
    150151        );
    151152
    152         JToggleButton tbCentre = new JToggleButton(new ImageAction(COMMAND_CENTERVIEW,
     153        tbCentre = new JToggleButton(new ImageAction(COMMAND_CENTERVIEW,
    153154                ImageProvider.get("dialogs", "centreview"), tr("Center view")));
    154155        tbCentre.setPreferredSize(buttonDim);
    155156
     
    224225            } else if (COMMAND_LAST.equals(action) && currentLayer != null) {
    225226                currentLayer.showLastPhoto();
    226227            } else if (COMMAND_CENTERVIEW.equals(action)) {
    227                 centerView = ((JToggleButton) e.getSource()).isSelected();
     228                final JToggleButton button = (JToggleButton) e.getSource();
     229                centerView = button.isEnabled() && button.isSelected();
    228230                if (centerView && currentEntry != null && currentEntry.getPos() != null) {
    229231                    Main.map.mapView.zoomTo(currentEntry.getPos());
    230232                }
     
    275277        getInstance().btnNext.setEnabled(value);
    276278    }
    277279
     280    /**
     281     * Enables (or disables) the "Center view" button.
     282     * @param value {@code true} to enable the button, {@code false} otherwise
     283     * @return the old enabled value. Can be used to restore the original enable state
     284     */
     285    public static synchronized boolean setCentreEnabled(boolean value) {
     286        final ImageViewerDialog instance = getInstance();
     287        final boolean wasEnabled = instance.tbCentre.isEnabled();
     288        instance.tbCentre.setEnabled(value);
     289        instance.tbCentre.getAction().actionPerformed(new ActionEvent(instance.tbCentre, 0, null));
     290        return wasEnabled;
     291    }
     292
    278293    private transient GeoImageLayer currentLayer;
    279294    private transient ImageEntry currentEntry;
    280295
     
    303318            setTitle(tr("Geotagged Images") + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
    304319            StringBuilder osd = new StringBuilder(entry.getFile() != null ? entry.getFile().getName() : "");
    305320            if (entry.getElevation() != null) {
    306                 osd.append(tr("\nAltitude: {0} m", entry.getElevation().longValue()));
     321                osd.append(tr("\nAltitude: {0} m", Math.round(entry.getElevation())));
    307322            }
    308323            if (entry.getSpeed() != null) {
    309324                osd.append(tr("\nSpeed: {0} km/h", Math.round(entry.getSpeed())));
  • src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java

     
    3232    private ICacheAccess<String, BufferedImageCacheEntry> cache;
    3333    private final boolean cacheOff = Main.pref.getBoolean("geoimage.noThumbnailCache", false);
    3434
     35    /**
     36     * Constructs a new thumbnail loader that operates on a geoimage layer.
     37     * @param layer geoimage layer
     38     */
    3539    public ThumbsLoader(GeoImageLayer layer) {
    3640        this.layer = layer;
    3741        this.data = new ArrayList<>(layer.data);
     42        initCache();
     43    }
     44
     45    /**
     46     * Constructs a new thumbnail loader that operates on a single image entry.
     47     * @param entry image entry
     48     */
     49    public ThumbsLoader(ImageEntry entry) {
     50        layer = null;
     51        data = new ArrayList<>(1);
     52        data.add(entry);
     53        initCache();
     54    }
     55
     56    /**
     57     * Initialize the thumbnail cache.
     58     */
     59    private void initCache() {
    3860        if (!cacheOff) {
    3961            try {
    4062                cache = JCSCacheManager.getCache("geoimage-thumbnails", 0, 120,
     
    5476            if (stop) return;
    5577
    5678            // Do not load thumbnails that were loaded before.
    57             if (entry.thumbnail == null) {
    58                 entry.thumbnail = loadThumb(entry);
     79            if (!entry.hasThumbnail()) {
     80                entry.setThumbnail(loadThumb(entry));
    5981
    60                 if (Main.isDisplayingMapView()) {
     82                if (layer != null && Main.isDisplayingMapView()) {
    6183                    layer.updateOffscreenBuffer = true;
    6284                    Main.map.mapView.repaint();
    6385                }
    6486            }
    6587        }
    66         layer.thumbsLoaded();
    67         layer.updateOffscreenBuffer = true;
    68         Main.map.mapView.repaint();
     88        if (layer != null) {
     89            layer.thumbsLoaded();
     90            layer.updateOffscreenBuffer = true;
     91            Main.map.mapView.repaint();
     92        }
    6993    }
    7094
    7195    private BufferedImage loadThumb(ImageEntry entry) {