30

I am writing GPS coordinates to my JPEG image, and the coordinates are correct (as demonstrated by my logcat output) but it appears that it's being corrupted somehow. Reading the exif data results in either null values or, in the case of my GPS: 512.976698 degrees, 512.976698 degrees. Can anyone shed some light on this problem?

writing it:

 try { ExifInterface exif = new ExifInterface(filename); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, latitude); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, longitude); exif.saveAttributes(); Log.e("LATITUDE: ", latitude); Log.e("LONGITUDE: ", longitude); } catch (IOException e) { e.printStackTrace(); } 

and reading it:

 try { ExifInterface exif = new ExifInterface("/sdcard/globetrotter/mytags/"+ TAGS[position]); Log.e("LATITUDE EXTRACTED", exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE)); Log.e("LONGITUDE EXTRACTED", exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE)); } catch (IOException e) { e.printStackTrace(); } 

It goes in (for example) 37.715183, -117.260489 and comes out 33619970/65540, 14811136/3368550, 33619970/65540, 14811136/3368550. Am I doing it wrong?

EDIT:

So, the problem is I am not encoding it in the properly defined format, which is something like you see here:

enter image description here

Can anyone explain what this format is? Obviously the first number is 22/1 = 22 degrees, but I can't figure out how to compute the decimal there.

1

6 Answers 6

27

GPSLatitude

Indicates the latitude. The latitude is expressed as three RATIONAL values giving the degrees, minutes, and seconds, respectively. If latitude is expressed as degrees, minutes and seconds, a typical format would be dd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two decimal places, the format would be dd/1,mmmm/100,0/1.

https://docs.google.com/viewer?url=http%3A%2F%2Fwww.exif.org%2FExif2-2.PDF

The Android docs specify this without explanation: http://developer.android.com/reference/android/media/ExifInterface.html#TAG_GPS_LATITUDE

Exif data is standardized, and GPS data must be encoded using geographical coordinates (minutes, seconds, etc) described above instead of a fraction. Unless it's encoded in that format in the exif tag, it won't stick.

How to encode: http://en.wikipedia.org/wiki/Geographic_coordinate_conversion

How to decode: http://android-er.blogspot.com/2010/01/convert-exif-gps-info-to-degree-format.html

Sign up to request clarification or add additional context in comments.

2 Comments

It's been a long time since Android decide to support this lat/lon to degree transform. Now you can just call ExifInterface(file).setGpsInfo(location) and saveAttributes() to simply do the job. And be sure to create ExifInterface from File instead of Uri or you'll get IOException("ExifInterface does not support saving attributes for the current input."). developer.android.com/reference/android/support/media/…
to update on the previous comment, that library is now deprecated, the replacement can be found here: developer.android.com/reference/androidx/exifinterface/media/…
21

Here is some code I've done to geotag my pictures. It's not heavily tested yet, but it seems to be ok (JOSM editor and exiftool read location).

ExifInterface exif = new ExifInterface(filePath.getAbsolutePath()); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, GPS.convert(latitude)); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, GPS.latitudeRef(latitude)); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, GPS.convert(longitude)); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, GPS.longitudeRef(longitude)); exif.saveAttributes(); 

And class GPS is here. (method could be shorter, but it's readable at least)

/* * @author fabien */ public class GPS { private static StringBuilder sb = new StringBuilder(20); /** * returns ref for latitude which is S or N. * @param latitude * @return S or N */ public static String latitudeRef(double latitude) { return latitude<0.0d?"S":"N"; } /** * returns ref for latitude which is S or N. * @param latitude * @return S or N */ public static String longitudeRef(double longitude) { return longitude<0.0d?"W":"E"; } /** * convert latitude into DMS (degree minute second) format. For instance<br/> * -79.948862 becomes<br/> * 79/1,56/1,55903/1000<br/> * It works for latitude and longitude<br/> * @param latitude could be longitude. * @return */ synchronized public static final String convert(double latitude) { latitude=Math.abs(latitude); int degree = (int) latitude; latitude *= 60; latitude -= (degree * 60.0d); int minute = (int) latitude; latitude *= 60; latitude -= (minute * 60.0d); int second = (int) (latitude*1000.0d); sb.setLength(0); sb.append(degree); sb.append("/1,"); sb.append(minute); sb.append("/1,"); sb.append(second); sb.append("/1000"); return sb.toString(); } } 

2 Comments

I'm using geo tagging in my application, how did you implement the above solution? I've added the Gps class and the pasted the first block of code where I'm saving the photo but,latitude and longtitude cannot be resolved to a variable, did you set these up as Geo coordinate variables somewhere in your main class?
@BrianJ Take a look at LocationManager, LocationListener to begin with. You can get the latitude and longitude via the LocationManager.
6

Other answers delivered nice background info and even an example. This is not a direct answer to the question but I would like to add an even simpler example without the need to do any math. The Location class delivers a nice convert function:

public String getLonGeoCoordinates(Location location) { if (location == null) return "0/1,0/1,0/1000"; // You can adapt this to latitude very easily by passing location.getLatitude() String[] degMinSec = Location.convert(location.getLongitude(), Location.FORMAT_SECONDS).split(":"); return degMinSec[0] + "/1," + degMinSec[1] + "/1," + degMinSec[2] + "/1000"; } 

I stored the return value in my image and the tag is parsed fine. You can check your image and the geocoordinates inside here: http://regex.info/exif.cgi

Edit

@ratanas comment translated to code:

public boolean storeGeoCoordsToImage(File imagePath, Location location) { // Avoid NullPointer if (imagePath == null || location == null) return false; // If we use Location.convert(), we do not have to worry about absolute values. try { // c&p and adapted from @Fabyen (sorry for being lazy) ExifInterface exif = new ExifInterface(imagePath.getAbsolutePath()); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, getLatGeoCoordinates(location)); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, location.getLatitude() < 0 ? "S" : "N"); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, getLonGeoCoordinates(location)); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, location.getLongitude() < 0 ? "W" : "E"); exif.saveAttributes(); } catch (IOException e) { // do something return false; } // Data was likely written. For sure no NullPointer. return true; } 

Here are some nice LatLong converter: latlong.net

1 Comment

This is indeed helpful, but it should also be noted that to use this for Exif, one must use the absolute value of latitude and longitude, and make sure to also provide the TAG_GPS_*_REF Exif attributes in the form of N,S,W,E according to the sign of the latitude and longitude.
2
ExifInterface exif = new ExifInterface(compressedImage.getPath()); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE,gpsTracker.dec2DMS(gpsTracker.getLatitude())); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE,gpsTracker.dec2DMS(gpsTracker.getLongitude())); 

Convertor double to String

 String dec2DMS(double coord) { coord = coord > 0 ? coord : -coord; String sOut = Integer.toString((int)coord) + "/1,"; coord = (coord % 1) * 60; sOut = sOut + Integer.toString((int)coord) + "/1,"; coord = (coord % 1) * 60000; sOut = sOut + Integer.toString((int)coord) + "/1000"; return sOut; } 

Comments

1

The most modern and shortest solution (with AndroidX) is using ExifInterface.setGpsInfo(Location), for example:

ExifInterface exif = new ExifInterface(filename); Location location = new Location(""); //may be empty location.setLatitude(latitude); //double value location.setLongitude(longitude); //double value exif.setGpsInfo(location) exif.saveAttributes(); 

Sources: one and two

3 Comments

There is no such function.
Well OK, little bit struggle to get this class instead of the standard but you are right ! Only thing is that the function don't wrote the other parameters like speed and bearing wrong, so I back to do it myself with setAttributs.
-1

check android source code: https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r2/core/java/android/hardware/Camera.java

/** * Sets GPS longitude coordinate. This will be stored in JPEG EXIF * header. * * @param longitude GPS longitude coordinate. */ public void setGpsLongitude(double longitude) { set(KEY_GPS_LONGITUDE, Double.toString(longitude)); }

So it's a direct print, my log supports it as well: ExifInterface.TAG_GPS_LONGITUDE : -121.0553966

My conclusion is setting it as direct print is fine.

3 Comments

If you examine the source code of ExifInterface, you'll easily recognize that getAttribute and getLatLong functions both call convertRationalLatLonToFloat which does the conversion from degrees to a floating point number. If you bypass those functions, sure I guess a string is a string... you could store the GPS coordinates to be "my house" if you wanted to. We should really follow the defined API, though, and save this in degrees instead.
Oh and by the way, if we dig deeper, we begin to understand that Camera.java is just an object that ferries data around for the Camera service and a driver. The driver ultimately ultimately saves this value in rational format. ExifInterface magically converts it back to decimal for you when you fetch it, which is what you saw in your logs.
One more thought. You aren't wrong. Setting through the Camera class with a direct floating point number will work. However, the original problem was really around setting data through ExifInterface and reading it back, only to discover it was all wrong. The answer I provided explains why.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.