Ticket #12255: GeoImageLayer_ImageDialog.patch

File GeoImageLayer_ImageDialog.patch, 19.9 KB (added by holgermappt, 10 years ago)
  • 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
     
    6763import org.openstreetmap.josm.gui.layer.Layer;
    6864import org.openstreetmap.josm.gui.util.GuiHelper;
    6965import org.openstreetmap.josm.io.JpgImporter;
    70 import org.openstreetmap.josm.tools.ExifReader;
    7166import org.openstreetmap.josm.tools.ImageProvider;
    7267import org.openstreetmap.josm.tools.Utils;
    7368
    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 
    8269/**
    8370 * Layer displaying geottaged pictures.
    8471 */
     
    157144                progressMonitor.subTask(tr("Reading {0}...", f.getName()));
    158145                progressMonitor.worked(1);
    159146
    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);
     147                ImageEntry e = new ImageEntry(f);
     148                e.extractExif();
    172149                data.add(e);
    173150            }
    174151            layer = new GeoImageLayer(data, gpxLayer);
     
    593570        }
    594571    }
    595572
    596     /**
    597      * Extract GPS metadata from image EXIF
    598      *
    599      * If successful, fills in the LatLon and EastNorth attributes of passed in image
    600      */
    601     private static void extractExif(ImageEntry e) {
    602 
    603         Metadata metadata;
    604         Directory dirExif;
    605         GpsDirectory dirGps;
    606 
    607         try {
    608             metadata = JpegMetadataReader.readMetadata(e.getFile());
    609             dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    610             dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    611         } catch (CompoundException | IOException p) {
    612             e.setExifCoor(null);
    613             e.setPos(null);
    614             return;
    615         }
    616 
    617         try {
    618             if (dirExif != null) {
    619                 int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    620                 e.setExifOrientation(orientation);
    621             }
    622         } catch (MetadataException ex) {
    623             Main.debug(ex.getMessage());
    624         }
    625 
    626         if (dirGps == null) {
    627             e.setExifCoor(null);
    628             e.setPos(null);
    629             return;
    630         }
    631 
    632         try {
    633             double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);
    634             String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
    635             if ("M".equalsIgnoreCase(speedRef)) {
    636                 // miles per hour
    637                 speed *= 1.609344;
    638             } else if ("N".equalsIgnoreCase(speedRef)) {
    639                 // knots == nautical miles per hour
    640                 speed *= 1.852;
    641             }
    642             // default is K (km/h)
    643             e.setSpeed(speed);
    644         } catch (Exception ex) {
    645             Main.debug(ex.getMessage());
    646         }
    647 
    648         try {
    649             double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);
    650             int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);
    651             if (d == 1) {
    652                 ele *= -1;
    653             }
    654             e.setElevation(ele);
    655         } catch (MetadataException ex) {
    656             Main.debug(ex.getMessage());
    657         }
    658 
    659         try {
    660             LatLon latlon = ExifReader.readLatLon(dirGps);
    661             e.setExifCoor(latlon);
    662             e.setPos(e.getExifCoor());
    663 
    664         } catch (Exception ex) { // (other exceptions, e.g. #5271)
    665             Main.error("Error reading EXIF from file: "+ex);
    666             e.setExifCoor(null);
    667             e.setPos(null);
    668         }
    669 
    670         try {
    671             Double direction = ExifReader.readDirection(dirGps);
    672             if (direction != null) {
    673                 e.setExifImgDir(direction.doubleValue());
    674             }
    675         } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271)
    676             Main.debug(ex.getMessage());
    677         }
    678 
    679         // Time and date. We can have these cases:
    680         // 1) GPS_TIME_STAMP not set -> date/time will be null
    681         // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default
    682         // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set
    683         int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_TIME_STAMP);
    684         if (timeStampComps != null) {
    685             int gpsHour = timeStampComps[0];
    686             int gpsMin = timeStampComps[1];
    687             int gpsSec = timeStampComps[2];
    688             Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    689 
    690             // We have the time. Next step is to check if the GPS date stamp is set.
    691             // dirGps.getString() always succeeds, but the return value might be null.
    692             String dateStampStr = dirGps.getString(GpsDirectory.TAG_DATE_STAMP);
    693             if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {
    694                 String[] dateStampComps = dateStampStr.split(":");
    695                 cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));
    696                 cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);
    697                 cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));
    698             } else {
    699                 // No GPS date stamp in EXIF data. Copy it from EXIF time.
    700                 // Date is not set if EXIF time is not available.
    701                 if (e.hasExifTime()) {
    702                     // Time not set yet, so we can copy everything, not just date.
    703                     cal.setTime(e.getExifTime());
    704                 }
    705             }
    706 
    707             cal.set(Calendar.HOUR_OF_DAY, gpsHour);
    708             cal.set(Calendar.MINUTE, gpsMin);
    709             cal.set(Calendar.SECOND, gpsSec);
    710 
    711             e.setExifGpsTime(cal.getTime());
    712         }
    713     }
    714 
    715573    public void showNextPhoto() {
    716574        if (data != null && !data.isEmpty()) {
    717575            currentPhoto++;
  • 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 org.openstreetmap.josm.Main;
    814import org.openstreetmap.josm.data.coor.CachedLatLon;
    915import org.openstreetmap.josm.data.coor.LatLon;
     16import org.openstreetmap.josm.tools.ExifReader;
    1017
     18import com.drew.imaging.jpeg.JpegMetadataReader;
     19import com.drew.lang.CompoundException;
     20import com.drew.metadata.Directory;
     21import com.drew.metadata.Metadata;
     22import com.drew.metadata.MetadataException;
     23import com.drew.metadata.exif.ExifIFD0Directory;
     24import com.drew.metadata.exif.GpsDirectory;
     25
    1126/**
    1227 * Stores info about each image
    1328 */
     
    5065    ImageEntry tmp;
    5166
    5267    /**
     68     * Constructs a new {@code ImageEntry}.
     69     */
     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    /**
    5381     * getter methods that refer to the temporary value
    5482     */
    5583    public CachedLatLon getPos() {
     
    85113        return (tmp != null && tmp.gpsTime != null) || gpsTime != null;
    86114    }
    87115
     116    public Double getExifImgDir() {
     117        if (tmp != null)
     118            return tmp.exifImgDir;
     119        return exifImgDir;
     120    }
     121
    88122    /**
    89123     * other getter methods
    90124     */
     
    137171        return exifCoor;
    138172    }
    139173
    140     public Double getExifImgDir() {
    141         return exifImgDir;
    142     }
    143 
    144174    public boolean hasThumbnail() {
    145175        return thumbnail != null;
    146176    }
    147177
     178    public Image getThumbnail() {
     179        return thumbnail;
     180    }
     181
    148182    /**
    149183     * setter methods
    150184     */
     
    153187    }
    154188
    155189    public void setPos(LatLon pos) {
    156         this.pos = new CachedLatLon(pos);
     190        if (pos == null) {
     191            this.pos = null;
     192        } else {
     193            this.pos = new CachedLatLon(pos);
     194        }
    157195    }
    158196
    159197    public void setSpeed(Double speed) {
     
    193231        this.exifCoor = exifCoor;
    194232    }
    195233
    196     public void setExifImgDir(double exifDir) {
     234    public void setExifImgDir(Double exifDir) {
    197235        this.exifImgDir = exifDir;
    198236    }
    199237
     238    //public void setExifImgDir(double exifDir) {
     239    //    this.exifImgDir = exifDir;
     240    //}
     241
    200242    @Override
    201243    public ImageEntry clone() {
    202244        Object c;
     
    230272    }
    231273
    232274    /**
    233      * Copy the values from the temporary variable to the main instance.
     275     * Get temporary variable that is used for real time parameter
     276     * adjustments. The temporary variable is created if it does not exist
     277     * yet. Use applyTmp() or discardTmp() if the temporary variable is not
     278     * needed anymore.
    234279     */
     280    public ImageEntry getTmp() {
     281        if (tmp == null) {
     282            tmp = clone();
     283            tmp.tmp = null;
     284        }
     285        return tmp;
     286    }
     287
     288    /**
     289     * Copy the values from the temporary variable to the main instance. The
     290     * temporary variable is deleted.
     291     */
    235292    public void applyTmp() {
    236293        if (tmp != null) {
    237294            pos = tmp.pos;
     
    238295            speed = tmp.speed;
    239296            elevation = tmp.elevation;
    240297            gpsTime = tmp.gpsTime;
     298            exifImgDir = tmp.exifImgDir;
    241299            tmp = null;
    242300        }
    243301    }
    244302
    245303    /**
     304     * Delete the temporary variable. Temporary modifications are lost.
     305     */
     306    public void discardTmp() {
     307        tmp = null;
     308    }
     309
     310    /**
    246311     * If it has been tagged i.e. matched to a gpx track or retrieved lat/lon from exif
    247312     */
    248313    public boolean isTagged() {
     
    287352    public boolean hasNewGpsData() {
    288353        return isNewGpsData;
    289354    }
     355
     356    /**
     357     * Extract GPS metadata from image EXIF. Has no effect if the image file is not set
     358     *
     359     * If successful, fills in the LatLon, speed, elevation, image direction, and other attributes
     360     */
     361    public void extractExif() {
     362
     363        Metadata metadata;
     364        Directory dirExif;
     365        GpsDirectory dirGps;
     366
     367        if (file == null) {
     368            return;
     369        }
     370
     371        try {
     372            metadata = JpegMetadataReader.readMetadata(file);
     373            dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
     374            dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
     375        } catch (CompoundException | IOException p) {
     376            setExifCoor(null);
     377            setPos(null);
     378            return;
     379        }
     380
     381        try {
     382            if (dirExif != null) {
     383                int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
     384                setExifOrientation(orientation);
     385            }
     386        } catch (MetadataException ex) {
     387            Main.debug(ex.getMessage());
     388        }
     389
     390        if (dirGps == null) {
     391            setExifCoor(null);
     392            setPos(null);
     393            return;
     394        }
     395
     396        try {
     397            double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);
     398            String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
     399            if ("M".equalsIgnoreCase(speedRef)) {
     400                // miles per hour
     401                speed *= 1.609344;
     402            } else if ("N".equalsIgnoreCase(speedRef)) {
     403                // knots == nautical miles per hour
     404                speed *= 1.852;
     405            }
     406            // default is K (km/h)
     407            setSpeed(speed);
     408        } catch (Exception ex) {
     409            Main.debug(ex.getMessage());
     410        }
     411
     412        try {
     413            double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);
     414            int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);
     415            if (d == 1) {
     416                ele *= -1;
     417            }
     418            setElevation(ele);
     419        } catch (MetadataException ex) {
     420            Main.debug(ex.getMessage());
     421        }
     422
     423        try {
     424            LatLon latlon = ExifReader.readLatLon(dirGps);
     425            setExifCoor(latlon);
     426            setPos(getExifCoor());
     427
     428        } catch (Exception ex) { // (other exceptions, e.g. #5271)
     429            Main.error("Error reading EXIF from file: "+ex);
     430            setExifCoor(null);
     431            setPos(null);
     432        }
     433
     434        try {
     435            Double direction = ExifReader.readDirection(dirGps);
     436            if (direction != null) {
     437                setExifImgDir(direction);
     438            }
     439        } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271)
     440            Main.debug(ex.getMessage());
     441        }
     442
     443        // Changed to silently cope with no time info in exif. One case
     444        // of person having time that couldn't be parsed, but valid GPS info
     445        try {
     446            setExifTime(ExifReader.readTime(file));
     447        } catch (ParseException ex) {
     448            setExifTime(null);
     449        }
     450
     451        // Time and date. We can have these cases:
     452        // 1) GPS_TIME_STAMP not set -> date/time will be null
     453        // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default
     454        // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set
     455        int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_TIME_STAMP);
     456        if (timeStampComps != null) {
     457            int gpsHour = timeStampComps[0];
     458            int gpsMin = timeStampComps[1];
     459            int gpsSec = timeStampComps[2];
     460            Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
     461
     462            // We have the time. Next step is to check if the GPS date stamp is set.
     463            // dirGps.getString() always succeeds, but the return value might be null.
     464            String dateStampStr = dirGps.getString(GpsDirectory.TAG_DATE_STAMP);
     465            if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {
     466                String[] dateStampComps = dateStampStr.split(":");
     467                cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));
     468                cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);
     469                cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));
     470            } else {
     471                // No GPS date stamp in EXIF data. Copy it from EXIF time.
     472                // Date is not set if EXIF time is not available.
     473                if (hasExifTime()) {
     474                    // Time not set yet, so we can copy everything, not just date.
     475                    cal.setTime(getExifTime());
     476                }
     477            }
     478
     479            cal.set(Calendar.HOUR_OF_DAY, gpsHour);
     480            cal.set(Calendar.MINUTE, gpsMin);
     481            cal.set(Calendar.SECOND, gpsSec);
     482
     483            setExifGpsTime(cal.getTime());
     484        }
     485    }
    290486}
  • 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
     
    275276        getInstance().btnNext.setEnabled(value);
    276277    }
    277278
     279    /**
     280     * Enables (or disables) the "Center view" button.
     281     * @param value {@code true} to enable the button, {@code false} otherwise
     282     * @return the old enabled value. Can be used to restore the original enable state
     283     */
     284    public static boolean setCentreEnabled(boolean value) {
     285        final ImageViewerDialog instance = getInstance();
     286        boolean wasEnabled;
     287        synchronized (instance) {
     288            wasEnabled = instance.tbCentre.isEnabled();
     289            instance.tbCentre.setEnabled(value);
     290            if (value) {
     291                boolean selected = instance.tbCentre.isSelected();
     292                instance.centerView = selected;
     293                if (selected) {
     294                    // Trigger map centering in case the image position was
     295                    // modified while the center view was disabled.
     296                    if (Main.isDisplayingMapView()
     297                        && instance.currentEntry != null
     298                        && instance.currentEntry.getPos() != null) {
     299                        Main.map.mapView.zoomTo(instance.currentEntry.getPos());
     300                    }
     301                }
     302            } else {
     303                instance.centerView = false;
     304            }
     305        }
     306        return wasEnabled;
     307    }
     308
    278309    private transient GeoImageLayer currentLayer;
    279310    private transient ImageEntry currentEntry;
    280311
     
    303334            setTitle(tr("Geotagged Images") + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
    304335            StringBuilder osd = new StringBuilder(entry.getFile() != null ? entry.getFile().getName() : "");
    305336            if (entry.getElevation() != null) {
    306                 osd.append(tr("\nAltitude: {0} m", entry.getElevation().longValue()));
     337                osd.append(tr("\nAltitude: {0} m", Math.round(entry.getElevation())));
    307338            }
    308339            if (entry.getSpeed() != null) {
    309340                osd.append(tr("\nSpeed: {0} km/h", Math.round(entry.getSpeed())));
  • src/org/openstreetmap/josm/io/session/GeoImageSessionImporter.java

     
    7979                                            Double.parseDouble(attrElem.getAttribute("lon"))));
    8080                                    break;
    8181                                case "exif-image-direction":
    82                                     entry.setExifImgDir(Double.parseDouble(attrElem.getTextContent()));
     82                                    entry.setExifImgDir(Double.valueOf(attrElem.getTextContent()));
    8383                                    break;
    8484                                case "is-new-gps-data":
    8585                                    if (Boolean.parseBoolean(attrElem.getTextContent())) {