Index: src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 11742)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(working copy)
@@ -8,7 +8,6 @@
 import java.util.Date;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.SystemOfMeasurement;
 import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.tools.ExifReader;
@@ -431,32 +430,32 @@
     public void extractExif() {
 
         Metadata metadata;
-        Directory dirExif;
-        GpsDirectory dirGps;
 
         if (file == null) {
             return;
         }
 
+        try {
+            metadata = JpegMetadataReader.readMetadata(file);
+        } catch (CompoundException | IOException ex) {
+            Main.error(ex);
+            setExifTime(null);
+            setExifCoor(null);
+            setPos(null);
+            return;
+        }
+
         // Changed to silently cope with no time info in exif. One case
         // of person having time that couldn't be parsed, but valid GPS info
         try {
-            setExifTime(ExifReader.readTime(file));
+            setExifTime(ExifReader.readTime(metadata));
         } catch (RuntimeException ex) {
             Main.warn(ex);
             setExifTime(null);
         }
 
-        try {
-            metadata = JpegMetadataReader.readMetadata(file);
-            dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
-            dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
-        } catch (CompoundException | IOException ex) {
-            Main.warn(ex);
-            setExifCoor(null);
-            setPos(null);
-            return;
-        }
+        final Directory dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+        final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
 
         try {
             if (dirExif != null) {
@@ -473,38 +472,20 @@
             return;
         }
 
-        try {
-            double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);
-            String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
-            if ("M".equalsIgnoreCase(speedRef)) {
-                // miles per hour
-                speed *= SystemOfMeasurement.IMPERIAL.bValue / 1000;
-            } else if ("N".equalsIgnoreCase(speedRef)) {
-                // knots == nautical miles per hour
-                speed *= SystemOfMeasurement.NAUTICAL_MILE.bValue / 1000;
-            }
-            // default is K (km/h)
+        final Double speed = ExifReader.readSpeed(dirGps);
+        if (speed != null) {
             setSpeed(speed);
-        } catch (MetadataException ex) {
-            Main.debug(ex);
         }
 
-        try {
-            double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);
-            int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);
-            if (d == 1) {
-                ele *= -1;
-            }
+        final Double ele = ExifReader.readElevation(dirGps);
+        if (ele != null) {
             setElevation(ele);
-        } catch (MetadataException ex) {
-            Main.debug(ex);
         }
 
         try {
-            LatLon latlon = ExifReader.readLatLon(dirGps);
+            final LatLon latlon = ExifReader.readLatLon(dirGps);
             setExifCoor(latlon);
             setPos(getExifCoor());
-
         } catch (MetadataException | IndexOutOfBoundsException ex) { // (other exceptions, e.g. #5271)
             Main.error("Error reading EXIF from file: " + ex);
             setExifCoor(null);
@@ -512,7 +493,7 @@
         }
 
         try {
-            Double direction = ExifReader.readDirection(dirGps);
+            final Double direction = ExifReader.readDirection(dirGps);
             if (direction != null) {
                 setExifImgDir(direction);
             }
Index: src/org/openstreetmap/josm/tools/ExifReader.java
===================================================================
--- src/org/openstreetmap/josm/tools/ExifReader.java	(revision 11742)
+++ src/org/openstreetmap/josm/tools/ExifReader.java	(working copy)
@@ -8,6 +8,7 @@
 import java.util.concurrent.TimeUnit;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.SystemOfMeasurement;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
@@ -41,10 +42,32 @@
      */
     public static Date readTime(File filename) {
         try {
-            Metadata metadata = JpegMetadataReader.readMetadata(filename);
-            String dateStr = null;
+            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
+            return readTime(metadata);
+        } catch (JpegProcessingException | IOException e) {
+            Main.error(e);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the date/time from the given JPEG file.
+     * @param metadata The EXIF metadata
+     * @return The date/time read in the EXIF section, or {@code null} if not found
+     */
+    public static Date readTime(Metadata metadata) {
+        try {
+            String dateTimeOrig = null;
             String dateTime = null;
-            String subSeconds = null;
+            String dateTimeDig = null;
+            String subSecOrig = null;
+            String subSec = null;
+            String subSecDig = null;
+            // The date fields are preferred in this order: DATETIME_ORIGINAL
+            // (0x9003), DATETIME (0x0132), DATETIME_DIGITIZED (0x9004).  Some
+            // cameras store the fields in the wrong directory, so all
+            // directories are searched.  Assume that the order of the fields
+            // in the directories is random.
             for (Directory dirIt : metadata.getDirectories()) {
                 if (!(dirIt instanceof ExifDirectoryBase)) {
                     continue;
@@ -52,23 +75,33 @@
                 for (Tag tag : dirIt.getTags()) {
                     if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL /* 0x9003 */ &&
                             !tag.getDescription().matches("\\[[0-9]+ .+\\]")) {
-                        // prefer DATETIME_ORIGINAL
-                        dateStr = tag.getDescription();
-                    }
-                    if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */) {
-                        // prefer DATETIME over DATETIME_DIGITIZED
+                        dateTimeOrig = tag.getDescription();
+                    } else if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */) {
                         dateTime = tag.getDescription();
+                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */) {
+                        dateTimeDig = tag.getDescription();
+                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME_ORIGINAL /* 0x9291 */) {
+                        subSecOrig = tag.getDescription();
+                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME /* 0x9290 */) {
+                        subSec = tag.getDescription();
+                    } else if (tag.getTagType() == ExifSubIFDDirectory.TAG_SUBSECOND_TIME_DIGITIZED /* 0x9292 */) {
+                        subSecDig = tag.getDescription();
                     }
-                    if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */ && dateTime == null) {
-                        dateTime = tag.getDescription();
-                    }
-                    if (tag.getTagType() == ExifIFD0Directory.TAG_SUBSECOND_TIME_ORIGINAL) {
-                        subSeconds = tag.getDescription();
-                    }
                 }
             }
-            if (dateStr == null) {
+            String dateStr = null;
+            String subSeconds = null;
+            if (dateTimeOrig != null) {
+                // prefer TAG_DATETIME_ORIGINAL
+                dateStr = dateTimeOrig;
+                subSeconds = subSecOrig;
+            } else if (dateTime != null) {
+                // TAG_DATETIME is second choice, see #14209
                 dateStr = dateTime;
+                subSeconds = subSec;
+            } else if (dateTimeDig != null) {
+                dateStr = dateTimeDig;
+                subSeconds = subSecDig;
             }
             if (dateStr != null) {
                 dateStr = dateStr.replace('/', ':'); // workaround for HTC Sensation bug, see #7228
@@ -83,7 +116,7 @@
                 }
                 return date;
             }
-        } catch (UncheckedParseException | JpegProcessingException | IOException e) {
+        } catch (UncheckedParseException e) {
             Main.error(e);
         }
         return null;
@@ -153,7 +186,7 @@
      * Returns the direction of the given JPEG file.
      * @param filename The JPEG file to read
      * @return The direction of the image when it was captures (in degrees between 0.0 and 359.99),
-     * or {@code null} if missing or if {@code dirGps} is null
+     * or {@code null} if not found
      * @since 6209
      */
     public static Double readDirection(File filename) {
@@ -170,7 +203,7 @@
     /**
      * Returns the direction of the given EXIF GPS directory.
      * @param dirGps The EXIF GPS directory
-     * @return The direction of the image when it was captures (in degrees between 0.0 and 359.99),
+     * @return The direction of the image when it was captured (in degrees between 0.0 and 359.99),
      * or {@code null} if missing or if {@code dirGps} is null
      * @since 6209
      */
@@ -208,6 +241,85 @@
     }
 
     /**
+     * Returns the speed of the given JPEG file.
+     * @param filename The JPEG file to read
+     * @return The speed of the camera when the image was captured (in km/h),
+     *         or {@code null} if not found
+     */
+    public static Double readSpeed(File filename) {
+        try {
+            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
+            final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
+            return readSpeed(dirGps);
+        } catch (JpegProcessingException | IOException e) {
+            Main.error(e);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the speed of the given EXIF GPS directory.
+     * @param dirGps The EXIF GPS directory
+     * @return The speed of the camera when the image was captured (in km/h),
+     *         or {@code null} if missing or if {@code dirGps} is null
+     */
+    public static Double readSpeed(GpsDirectory dirGps) {
+        if (dirGps != null) {
+            Double speed = dirGps.getDoubleObject(GpsDirectory.TAG_SPEED);
+            if (speed != null) {
+                final String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);
+                if ("M".equalsIgnoreCase(speedRef)) {
+                    // miles per hour
+                    speed *= SystemOfMeasurement.IMPERIAL.bValue / 1000;
+                } else if ("N".equalsIgnoreCase(speedRef)) {
+                    // knots == nautical miles per hour
+                    speed *= SystemOfMeasurement.NAUTICAL_MILE.bValue / 1000;
+                }
+                // default is K (km/h)
+                return speed;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the elevation of the given JPEG file.
+     * @param filename The JPEG file to read
+     * @return The elevation of the camera when the image was captured (in m),
+     *         or {@code null} if not found
+     */
+    public static Double readElevation(File filename) {
+        try {
+            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
+            final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
+            return readElevation(dirGps);
+        } catch (JpegProcessingException | IOException e) {
+            Main.error(e);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the elevation of the given EXIF GPS directory.
+     * @param dirGps The EXIF GPS directory
+     * @return The elevation of the camera when the image was captured (in m),
+     *         or {@code null} if missing or if {@code dirGps} is null
+     */
+    public static Double readElevation(GpsDirectory dirGps) {
+        if (dirGps != null) {
+            Double ele = dirGps.getDoubleObject(GpsDirectory.TAG_ALTITUDE);
+            if (ele != null) {
+                final Integer d = dirGps.getInteger(GpsDirectory.TAG_ALTITUDE_REF);
+                if (d != null && d.intValue() == 1) {
+                    ele *= -1;
+                }
+                return ele;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns a Transform that fixes the image orientation.
      *
      * Only orientation 1, 3, 6 and 8 are supported. Everything else is treated as 1.
Index: test/data/regress/14209/7VWFOryj--.1.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: test/unit/org/openstreetmap/josm/tools/ExifReaderTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/ExifReaderTest.java	(revision 11742)
+++ test/unit/org/openstreetmap/josm/tools/ExifReaderTest.java	(working copy)
@@ -116,6 +116,22 @@
     }
 
     /**
+     * Test speed extraction
+     */
+    @Test
+    public void testReadSpeed() {
+        assertEquals(Double.valueOf(12.3), ExifReader.readSpeed(new File("data_nodist/exif-example_speed_ele.jpg")));
+    }
+
+    /**
+     * Test elevation extraction
+     */
+    @Test
+    public void testReadElevation() {
+        assertEquals(Double.valueOf(23.4), ExifReader.readElevation(new File("data_nodist/exif-example_speed_ele.jpg")));
+    }
+
+    /**
      * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/11685">#11685</a>
      * @throws IOException if an error occurs during reading
      */
Index: data_nodist/exif-example_speed_ele.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = image/jpeg
Index: data_nodist/exif-example_speed_ele.jpg
===================================================================
--- data_nodist/exif-example_speed_ele.jpg	(nonexistent)
+++ data_nodist/exif-example_speed_ele.jpg	(working copy)

Property changes on: data_nodist/exif-example_speed_ele.jpg
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+image/jpeg
\ No newline at end of property
