开发者

How to save GPS coordinates in exif data on Android?

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(ExifInterfac开发者_开发问答e.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:

How to save GPS coordinates in exif data on Android?

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.


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


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();
    }
}


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


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;
}


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜