Index: trunk/src/com/drew/metadata/exif/ExifReader.java
===================================================================
--- trunk/src/com/drew/metadata/exif/ExifReader.java	(revision 6207)
+++ trunk/src/com/drew/metadata/exif/ExifReader.java	(revision 6209)
@@ -21,4 +21,7 @@
 package com.drew.metadata.exif;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import com.drew.lang.BufferBoundsException;
 import com.drew.lang.BufferReader;
@@ -28,7 +31,4 @@
 import com.drew.metadata.Metadata;
 import com.drew.metadata.MetadataReader;
-
-import java.util.HashSet;
-import java.util.Set;
 
 /**
@@ -234,5 +234,5 @@
                 // rubbish until we go out of bounds (which may be a while).  Exit now.
                 directory.addError("Invalid TIFF tag format code: " + formatCode);
-                return;
+                continue; // JOSM patch to fix #9030
             }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 6207)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 6209)
@@ -65,5 +65,4 @@
 import com.drew.imaging.jpeg.JpegMetadataReader;
 import com.drew.lang.CompoundException;
-import com.drew.lang.Rational;
 import com.drew.metadata.Directory;
 import com.drew.metadata.Metadata;
@@ -72,4 +71,7 @@
 import com.drew.metadata.exif.GpsDirectory;
 
+/**
+ * Layer displaying geottaged pictures.
+ */
 public class GeoImageLayer extends Layer implements PropertyChangeListener, JumpToMarkerLayer {
 
@@ -263,6 +265,10 @@
     }
 
+    /**
+     * Constructs a new {@code GeoImageLayer}.
+     * @param data The list of images to display
+     * @param gpxLayer The associated GPX layer
+     */
     public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer) {
-
         super(tr("Geotagged Images"));
 
@@ -499,16 +505,11 @@
     }
 
-    /*
-     * Extract gps from image exif
+    /**
+     * Extract GPS metadata from image EXIF
      *
-     * If successful, fills in the LatLon and EastNorth attributes of passed in
-     * image;
+     * If successful, fills in the LatLon and EastNorth attributes of passed in image
      */
-
     private static void extractExif(ImageEntry e) {
 
-        double deg;
-        double min, sec;
-        double lon, lat;
         Metadata metadata;
         Directory dirExif;
@@ -542,5 +543,5 @@
 
         try {
-            double ele=dirGps.getDouble(GpsDirectory.TAG_GPS_ALTITUDE);
+            double ele = dirGps.getDouble(GpsDirectory.TAG_GPS_ALTITUDE);
             int d = dirGps.getInt(GpsDirectory.TAG_GPS_ALTITUDE_REF);
             if (d == 1) {
@@ -552,51 +553,6 @@
 
         try {
-            // longitude
-
-            Rational[] components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
-            if (components != null) {
-                deg = components[0].doubleValue();
-                min = components[1].doubleValue();
-                sec = components[2].doubleValue();
-
-                if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
-                    throw new IllegalArgumentException();
-
-                lon = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
-
-                if (dirGps.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') {
-                    lon = -lon;
-                }
-            } else {
-                // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)
-                lon = dirGps.getDouble(GpsDirectory.TAG_GPS_LONGITUDE);
-            }
-
-            // latitude
-
-            components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
-            if (components != null) {
-                deg = components[0].doubleValue();
-                min = components[1].doubleValue();
-                sec = components[2].doubleValue();
-
-                if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
-                    throw new IllegalArgumentException();
-
-                lat = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
-
-                if (Double.isNaN(lat))
-                    throw new IllegalArgumentException();
-
-                if (dirGps.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S') {
-                    lat = -lat;
-                }
-            } else {
-                lat = dirGps.getDouble(GpsDirectory.TAG_GPS_LATITUDE);
-            }
-
-            // Store values
-
-            e.setExifCoor(new LatLon(lat, lon));
+            LatLon latlon = ExifReader.readLatLon(dirGps);
+            e.setExifCoor(latlon);
             e.setPos(e.getExifCoor());
 
@@ -607,10 +563,6 @@
         }
 
-        // compass direction value
-
-        Rational direction = null;
-
         try {
-            direction = dirGps.getRational(GpsDirectory.TAG_GPS_IMG_DIRECTION);
+            Double direction = ExifReader.readDirection(dirGps);
             if (direction != null) {
                 e.setExifImgDir(direction.doubleValue());
@@ -846,4 +798,8 @@
     }
 
+    /**
+     * Returns the associated GPX layer.
+     * @return The associated GPX layer
+     */
     public GpxLayer getGpxLayer() {
         return gpxLayer;
Index: trunk/src/org/openstreetmap/josm/tools/ExifReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ExifReader.java	(revision 6207)
+++ trunk/src/org/openstreetmap/josm/tools/ExifReader.java	(revision 6209)
@@ -7,6 +7,9 @@
 import java.util.Date;
 
+import org.openstreetmap.josm.data.coor.LatLon;
+
 import com.drew.imaging.jpeg.JpegMetadataReader;
 import com.drew.imaging.jpeg.JpegProcessingException;
+import com.drew.lang.Rational;
 import com.drew.metadata.Directory;
 import com.drew.metadata.Metadata;
@@ -15,12 +18,20 @@
 import com.drew.metadata.exif.ExifIFD0Directory;
 import com.drew.metadata.exif.ExifSubIFDDirectory;
+import com.drew.metadata.exif.GpsDirectory;
 
 /**
- * Read out exif file information from a jpeg file
+ * Read out EXIF information from a JPEG file
  * @author Imi
+ * @since 99
  */
 public class ExifReader {
 
-    @SuppressWarnings("unchecked") public static Date readTime(File filename) throws ParseException {
+    /**
+     * Returns the date/time from the given JPEG file.
+     * @param filename The JPEG file to read
+     * @return The date/time read in the EXIF section, or {@code null} if not found
+     * @throws ParseException if {@link DateParser#parse} fails to parse date/time
+     */
+    public static Date readTime(File filename) throws ParseException {
         try {
             Metadata metadata = JpegMetadataReader.readMetadata(filename);
@@ -51,10 +62,24 @@
     }
 
-    public static Integer readOrientation(File filename) throws ParseException {
-        Integer orientation = null;
+    /**
+     * Returns the image orientation of the given JPEG file.
+     * @param filename The JPEG file to read
+     * @return The image orientation as an {@code int}. Default value is 1. Possible values are listed in EXIF spec as follows:<br>
+     * <ul>1. The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.</ul>
+     * <ul>2. The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.</ul>
+     * <ul>3. The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.</ul>
+     * <ul>4. The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.</ul>
+     * <ul>5. The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.</ul>
+     * <ul>6. The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.</ul>
+     * <ul>7. The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.</ul>
+     * <ul>8. The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.</ul>
+     * @see <a href="http://www.impulseadventure.com/photo/exif-orientation.html">http://www.impulseadventure.com/photo/exif-orientation.html</a>
+     * @see <a href="http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto">http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto</a>
+     */
+    public static Integer readOrientation(File filename) {
         try {
             final Metadata metadata = JpegMetadataReader.readMetadata(filename);
             final Directory dir = metadata.getDirectory(ExifIFD0Directory.class);
-            orientation = dir.getInt(ExifIFD0Directory.TAG_ORIENTATION);
+            return dir.getInt(ExifIFD0Directory.TAG_ORIENTATION);
         } catch (JpegProcessingException e) {
             e.printStackTrace();
@@ -64,6 +89,100 @@
             e.printStackTrace();
         }
-        return orientation;
+        return null;
     }
 
+    /**
+     * Returns the geolocation of the given JPEG file.
+     * @param filename The JPEG file to read
+     * @return The lat/lon read in the EXIF section, or {@code null} if not found
+     * @since 6209
+     */
+    public static LatLon readLatLon(File filename) {
+        try {
+            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
+            final GpsDirectory dirGps = metadata.getDirectory(GpsDirectory.class);
+            return readLatLon(dirGps);
+        } catch (JpegProcessingException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (MetadataException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the geolocation of the given EXIF GPS directory.
+     * @param dirGps The EXIF GPS directory
+     * @return The lat/lon read in the EXIF section, or {@code null} if {@code dirGps} is null
+     * @throws MetadataException 
+     * @since 6209
+     */
+    public static LatLon readLatLon(GpsDirectory dirGps) throws MetadataException {
+        if (dirGps != null) {
+            double lat = readAxis(dirGps, GpsDirectory.TAG_GPS_LATITUDE, GpsDirectory.TAG_GPS_LATITUDE_REF, 'S');
+            double lon = readAxis(dirGps, GpsDirectory.TAG_GPS_LONGITUDE, GpsDirectory.TAG_GPS_LONGITUDE_REF, 'W');
+            return new LatLon(lat, lon);
+        }
+        return null;
+    }
+    
+    /**
+     * 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
+     * @since 6209
+     */
+    public static Double readDirection(File filename) {
+        try {
+            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
+            final GpsDirectory dirGps = metadata.getDirectory(GpsDirectory.class);
+            return readDirection(dirGps);
+        } catch (JpegProcessingException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    
+    /**
+     * 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), or {@code null} if missing or if {@code dirGps} is null
+     * @since 6209
+     */
+    public static Double readDirection(GpsDirectory dirGps) {
+        if (dirGps != null) {
+            Rational direction = dirGps.getRational(GpsDirectory.TAG_GPS_IMG_DIRECTION);
+            if (direction != null) {
+                return direction.doubleValue();
+            }
+        }
+        return null;
+    }
+
+    private static double readAxis(GpsDirectory dirGps, int gpsTag, int gpsTagRef, char cRef) throws MetadataException  {
+        double value;
+        Rational[] components = dirGps.getRationalArray(gpsTag);
+        if (components != null) {
+            double deg = components[0].doubleValue();
+            double min = components[1].doubleValue();
+            double sec = components[2].doubleValue();
+   
+            if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
+                throw new IllegalArgumentException();
+   
+            value = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
+   
+            if (dirGps.getString(gpsTagRef).charAt(0) == cRef) {
+                value = -value;
+            }
+        } else {
+            // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)
+            value = dirGps.getDouble(gpsTag);
+        }
+        return value;
+    }
 }
