Ticket #14536: GeoImageLayer_ExifReader_refactoring.patch

File GeoImageLayer_ExifReader_refactoring.patch, 14.6 KB (added by holgermappt, 4 years ago)

Patch file.

  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java

     
    88import java.util.Date;
    99
    1010import org.openstreetmap.josm.Main;
    11 import org.openstreetmap.josm.data.SystemOfMeasurement;
    1211import org.openstreetmap.josm.data.coor.CachedLatLon;
    1312import org.openstreetmap.josm.data.coor.LatLon;
    1413import org.openstreetmap.josm.tools.ExifReader;
     
    431430    public void extractExif() {
    432431
    433432        Metadata metadata;
    434         Directory dirExif;
    435         GpsDirectory dirGps;
    436433
    437434        if (file == null) {
    438435            return;
    439436        }
    440437
     438        try {
     439            metadata = JpegMetadataReader.readMetadata(file);
     440        } catch (CompoundException | IOException ex) {
     441            Main.error(ex);
     442            setExifTime(null);
     443            setExifCoor(null);
     444            setPos(null);
     445            return;
     446        }
     447
    441448        // Changed to silently cope with no time info in exif. One case
    442449        // of person having time that couldn't be parsed, but valid GPS info
    443450        try {
    444             setExifTime(ExifReader.readTime(file));
     451            setExifTime(ExifReader.readTime(metadata));
    445452        } catch (RuntimeException ex) {
    446453            Main.warn(ex);
    447454            setExifTime(null);
    448455        }
    449456
    450         try {
    451             metadata = JpegMetadataReader.readMetadata(file);
    452             dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    453             dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    454         } catch (CompoundException | IOException ex) {
    455             Main.warn(ex);
    456             setExifCoor(null);
    457             setPos(null);
    458             return;
    459         }
     457        final Directory dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
     458        final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    460459
    461460        try {
    462461            if (dirExif != null) {
     
    473472            return;
    474473        }
    475474
    476         try {
    477             double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);
    478             String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
    479             if ("M".equalsIgnoreCase(speedRef)) {
    480                 // miles per hour
    481                 speed *= SystemOfMeasurement.IMPERIAL.bValue / 1000;
    482             } else if ("N".equalsIgnoreCase(speedRef)) {
    483                 // knots == nautical miles per hour
    484                 speed *= SystemOfMeasurement.NAUTICAL_MILE.bValue / 1000;
    485             }
    486             // default is K (km/h)
     475        final Double speed = ExifReader.readSpeed(dirGps);
     476        if (speed != null) {
    487477            setSpeed(speed);
    488         } catch (MetadataException ex) {
    489             Main.debug(ex);
    490478        }
    491479
    492         try {
    493             double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);
    494             int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);
    495             if (d == 1) {
    496                 ele *= -1;
    497             }
     480        final Double ele = ExifReader.readElevation(dirGps);
     481        if (ele != null) {
    498482            setElevation(ele);
    499         } catch (MetadataException ex) {
    500             Main.debug(ex);
    501483        }
    502484
    503485        try {
    504             LatLon latlon = ExifReader.readLatLon(dirGps);
     486            final LatLon latlon = ExifReader.readLatLon(dirGps);
    505487            setExifCoor(latlon);
    506488            setPos(getExifCoor());
    507 
    508489        } catch (MetadataException | IndexOutOfBoundsException ex) { // (other exceptions, e.g. #5271)
    509490            Main.error("Error reading EXIF from file: " + ex);
    510491            setExifCoor(null);
     
    512493        }
    513494
    514495        try {
    515             Double direction = ExifReader.readDirection(dirGps);
     496            final Double direction = ExifReader.readDirection(dirGps);
    516497            if (direction != null) {
    517498                setExifImgDir(direction);
    518499            }
  • src/org/openstreetmap/josm/tools/ExifReader.java

     
    88import java.util.concurrent.TimeUnit;
    99
    1010import org.openstreetmap.josm.Main;
     11import org.openstreetmap.josm.data.SystemOfMeasurement;
    1112import org.openstreetmap.josm.data.coor.LatLon;
    1213import org.openstreetmap.josm.tools.date.DateUtils;
    1314
     
    4142     */
    4243    public static Date readTime(File filename) {
    4344        try {
    44             Metadata metadata = JpegMetadataReader.readMetadata(filename);
    45             String dateStr = null;
     45            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
     46            return readTime(metadata);
     47        } catch (JpegProcessingException | IOException e) {
     48            Main.error(e);
     49        }
     50        return null;
     51    }
     52
     53    /**
     54     * Returns the date/time from the given JPEG file.
     55     * @param metadata The EXIF metadata
     56     * @return The date/time read in the EXIF section, or {@code null} if not found
     57     */
     58    public static Date readTime(Metadata metadata) {
     59        try {
     60            String dateTimeOrig = null;
    4661            String dateTime = null;
    47             String subSeconds = null;
     62            String dateTimeDig = null;
     63            String subSecOrig = null;
     64            String subSec = null;
     65            String subSecDig = null;
     66            // The date fields are preferred in this order: DATETIME_ORIGINAL
     67            // (0x9003), DATETIME (0x0132), DATETIME_DIGITIZED (0x9004).  Some
     68            // cameras store the fields in the wrong directory, so all
     69            // directories are searched.  Assume that the order of the fields
     70            // in the directories is random.
    4871            for (Directory dirIt : metadata.getDirectories()) {
    4972                if (!(dirIt instanceof ExifDirectoryBase)) {
    5073                    continue;
     
    5275                for (Tag tag : dirIt.getTags()) {
    5376                    if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL /* 0x9003 */ &&
    5477                            !tag.getDescription().matches("\\[[0-9]+ .+\\]")) {
    55                         // prefer DATETIME_ORIGINAL
    56                         dateStr = tag.getDescription();
    57                     }
    58                     if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */) {
    59                         // prefer DATETIME over DATETIME_DIGITIZED
     78                        dateTimeOrig = tag.getDescription();
     79                    } else if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */) {
    6080                        dateTime = tag.getDescription();
     81                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */) {
     82                        dateTimeDig = tag.getDescription();
     83                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME_ORIGINAL /* 0x9291 */) {
     84                        subSecOrig = tag.getDescription();
     85                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME /* 0x9290 */) {
     86                        subSec = tag.getDescription();
     87                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME_DIGITIZED /* 0x9292 */) {
     88                        subSecDig = tag.getDescription();
    6189                    }
    62                     if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */ && dateTime == null) {
    63                         dateTime = tag.getDescription();
    64                     }
    65                     if (tag.getTagType() == ExifIFD0Directory.TAG_SUBSECOND_TIME_ORIGINAL) {
    66                         subSeconds = tag.getDescription();
    67                     }
    6890                }
    6991            }
    70             if (dateStr == null) {
     92            String dateStr = null;
     93            String subSeconds = null;
     94            if (dateTimeOrig != null) {
     95                // prefer TAG_DATETIME_ORIGINAL
     96                dateStr = dateTimeOrig;
     97                subSeconds = subSecOrig;
     98            } else if (dateTime != null) {
     99                // TAG_DATETIME is second choice, see #14209
    71100                dateStr = dateTime;
     101                subSeconds = subSec;
     102            } else if (dateTimeDig != null) {
     103                dateStr = dateTimeDig;
     104                subSeconds = subSecDig;
    72105            }
    73106            if (dateStr != null) {
    74107                dateStr = dateStr.replace('/', ':'); // workaround for HTC Sensation bug, see #7228
     
    83116                }
    84117                return date;
    85118            }
    86         } catch (UncheckedParseException | JpegProcessingException | IOException e) {
     119        } catch (UncheckedParseException e) {
    87120            Main.error(e);
    88121        }
    89122        return null;
     
    153186     * Returns the direction of the given JPEG file.
    154187     * @param filename The JPEG file to read
    155188     * @return The direction of the image when it was captures (in degrees between 0.0 and 359.99),
    156      * or {@code null} if missing or if {@code dirGps} is null
     189     * or {@code null} if not found
    157190     * @since 6209
    158191     */
    159192    public static Double readDirection(File filename) {
     
    170203    /**
    171204     * Returns the direction of the given EXIF GPS directory.
    172205     * @param dirGps The EXIF GPS directory
    173      * @return The direction of the image when it was captures (in degrees between 0.0 and 359.99),
     206     * @return The direction of the image when it was captured (in degrees between 0.0 and 359.99),
    174207     * or {@code null} if missing or if {@code dirGps} is null
    175208     * @since 6209
    176209     */
     
    208241    }
    209242
    210243    /**
     244     * Returns the speed of the given JPEG file.
     245     * @param filename The JPEG file to read
     246     * @return The speed of the camera when the image was captured (in km/h),
     247     *         or {@code null} if not found
     248     */
     249    public static Double readSpeed(File filename) {
     250        try {
     251            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
     252            final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
     253            return readSpeed(dirGps);
     254        } catch (JpegProcessingException | IOException e) {
     255            Main.error(e);
     256        }
     257        return null;
     258    }
     259
     260    /**
     261     * Returns the speed of the given EXIF GPS directory.
     262     * @param dirGps The EXIF GPS directory
     263     * @return The speed of the camera when the image was captured (in km/h),
     264     *         or {@code null} if missing or if {@code dirGps} is null
     265     */
     266    public static Double readSpeed(GpsDirectory dirGps) {
     267        if (dirGps != null) {
     268            Double speed = dirGps.getDoubleObject(GpsDirectory.TAG_SPEED);
     269            if (speed != null) {
     270                final String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
     271                if ("M".equalsIgnoreCase(speedRef)) {
     272                    // miles per hour
     273                    speed *= SystemOfMeasurement.IMPERIAL.bValue / 1000;
     274                } else if ("N".equalsIgnoreCase(speedRef)) {
     275                    // knots == nautical miles per hour
     276                    speed *= SystemOfMeasurement.NAUTICAL_MILE.bValue / 1000;
     277                }
     278                // default is K (km/h)
     279                return speed;
     280            }
     281        }
     282        return null;
     283    }
     284
     285    /**
     286     * Returns the elevation of the given JPEG file.
     287     * @param filename The JPEG file to read
     288     * @return The elevation of the camera when the image was captured (in m),
     289     *         or {@code null} if not found
     290     */
     291    public static Double readElevation(File filename) {
     292        try {
     293            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
     294            final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
     295            return readElevation(dirGps);
     296        } catch (JpegProcessingException | IOException e) {
     297            Main.error(e);
     298        }
     299        return null;
     300    }
     301
     302    /**
     303     * Returns the elevation of the given EXIF GPS directory.
     304     * @param dirGps The EXIF GPS directory
     305     * @return The elevation of the camera when the image was captured (in m),
     306     *         or {@code null} if missing or if {@code dirGps} is null
     307     */
     308    public static Double readElevation(GpsDirectory dirGps) {
     309        if (dirGps != null) {
     310            Double ele = dirGps.getDoubleObject(GpsDirectory.TAG_ALTITUDE);
     311            if (ele != null) {
     312                final Integer d = dirGps.getInteger(GpsDirectory.TAG_ALTITUDE_REF);
     313                if (d != null && d.intValue() == 1) {
     314                    ele *= -1;
     315                }
     316                return ele;
     317            }
     318        }
     319        return null;
     320    }
     321
     322    /**
    211323     * Returns a Transform that fixes the image orientation.
    212324     *
    213325     * Only orientation 1, 3, 6 and 8 are supported. Everything else is treated as 1.
  • test/unit/org/openstreetmap/josm/tools/ExifReaderTest.java

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
     
    116116    }
    117117
    118118    /**
     119     * Test speed extraction
     120     */
     121    @Test
     122    public void testReadSpeed() {
     123        assertEquals(Double.valueOf(12.3), ExifReader.readSpeed(new File("data_nodist/exif-example_speed_ele.jpg")));
     124    }
     125
     126    /**
     127     * Test elevation extraction
     128     */
     129    @Test
     130    public void testReadElevation() {
     131        assertEquals(Double.valueOf(23.4), ExifReader.readElevation(new File("data_nodist/exif-example_speed_ele.jpg")));
     132    }
     133
     134    /**
    119135     * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/11685">#11685</a>
    120136     * @throws IOException if an error occurs during reading
    121137     */
  • data_nodist/exif-example_speed_ele.jpg

    Cannot display: file marked as a binary type.
    svn:mime-type = image/jpeg