Geotagging JPEGs with pyexiv2

4.2k views Asked by At

I am geotagging JPEGs using the pyexiv2 Python module using code I found in another SO answer (see: What is the best way to geotag jpeg-images using Python?) and I have a question about the GPSTag value.

The code given in the answer has the following lines:

exiv_image["Exif.Image.GPSTag"] = 654
exiv_image["Exif.GPSInfo.GPSMapDatum"] = "WGS-84"
exiv_image["Exif.GPSInfo.GPSVersionID"] = '2 0 0 0'

I have looked at the Exiv2 documentation and found descriptions of GPSTag, GPSMapDatum, and GPSVersionID but am still confused about the value for GPSTag.

From the documentation it says:

A pointer to the GPS Info IFD. The Interoperability structure of the GPS Info IFD, like that of Exif IFD, has no image data.

This description does not really explain how to determine what value to use and I have not been able to find a better description of GPSTag online.

So my questions are:

  1. Given a new image, how do you determine the value of Exif.Image.GPSTag?
  2. Why is the code sample using a value of 654 (this may be answered by question one)?

Thanks for your help.

2

There are 2 answers

0
robru On

If you're wanting to access GPS data from pyexiv2, that code looks like this:

    GPS = 'Exif.GPSInfo.GPS'
    try:
        self.latitude = dms_to_decimal(
            *self.exif[GPS + 'Latitude'].value +
            [self.exif[GPS + 'LatitudeRef'].value]
        )
        self.longitude = dms_to_decimal(
            *self.exif[GPS + 'Longitude'].value +
            [self.exif[GPS + 'LongitudeRef'].value]
        )
    except KeyError:
        pass
    try:
        self.altitude = float(self.exif[GPS + 'Altitude'].value)
        if int(self.exif[GPS + 'AltitudeRef'].value) > 0:
            self.altitude *= -1
    except KeyError:
        pass

And writing looks like this:

    self.exif[GPS + 'AltitudeRef']  = '0' if self.altitude >= 0 else '1'
    self.exif[GPS + 'Altitude']     = Fraction(self.altitude)
    self.exif[GPS + 'Latitude']     = decimal_to_dms(self.latitude)
    self.exif[GPS + 'LatitudeRef']  = 'N' if self.latitude >= 0 else 'S'
    self.exif[GPS + 'Longitude']    = decimal_to_dms(self.longitude)
    self.exif[GPS + 'LongitudeRef'] = 'E' if self.longitude >= 0 else 'W'
    self.exif[GPS + 'MapDatum']     = 'WGS-84'

With these support functions:

class Fraction(fractions.Fraction):
    """Only create Fractions from floats.
    
    >>> Fraction(0.3)
    Fraction(3, 10)
    >>> Fraction(1.1)
    Fraction(11, 10)
    """
    
    def __new__(cls, value, ignore=None):
        """Should be compatible with Python 2.6, though untested."""
        return fractions.Fraction.from_float(value).limit_denominator(99999)

def dms_to_decimal(degrees, minutes, seconds, sign=' '):
    """Convert degrees, minutes, seconds into decimal degrees.
    
    >>> dms_to_decimal(10, 10, 10)
    10.169444444444444
    >>> dms_to_decimal(8, 9, 10, 'S')
    -8.152777777777779
    """
    return (-1 if sign[0] in 'SWsw' else 1) * (
        float(degrees)        +
        float(minutes) / 60   +
        float(seconds) / 3600
    )


def decimal_to_dms(decimal):
    """Convert decimal degrees into degrees, minutes, seconds.
    
    >>> decimal_to_dms(50.445891)
    [Fraction(50, 1), Fraction(26, 1), Fraction(113019, 2500)]
    >>> decimal_to_dms(-125.976893)
    [Fraction(125, 1), Fraction(58, 1), Fraction(92037, 2500)]
    """
    remainder, degrees = math.modf(abs(decimal))
    remainder, minutes = math.modf(remainder * 60)
    return [Fraction(n) for n in (degrees, minutes, remainder * 60)]

Hope this helps!

* pyexiv2's author, Olivier Tilloy, has asked me for help with maintainership as he no longer has much time
0
ATOzTOA On

My version, a little lengthy...

from fractions import Fraction
import pyexiv2

try:
    metadata = pyexiv2.metadata.ImageMetadata(image_file)
    metadata.read();
    thumb = metadata.exif_thumbnail

    try:
        latitude = metadata.__getitem__("Exif.GPSInfo.GPSLatitude")
        latitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef")
        longitude = metadata.__getitem__("Exif.GPSInfo.GPSLongitude")
        longitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef")

        latitude = str(latitude).split("=")[1][1:-1].split(" ");
        latitude = map(lambda f: str(float(Fraction(f))), latitude)
        latitude = latitude[0] + u"\u00b0" + latitude[1] + "'" + latitude[2] + '"' + " " + str(latitudeRef).split("=")[1][1:-1]

        longitude = str(longitude).split("=")[1][1:-1].split(" ");
        longitude = map(lambda f: str(float(Fraction(f))), longitude)
        longitude = longitude[0] + u"\u00b0" + longitude[1] + "'" + longitude[2] + '"' + " " + str(longitudeRef).split("=")[1][1:-1]

        latitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLatitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef").value]);
        longitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLongitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef").value]);

        print "--- GPS ---"
        print "Coordinates: " + latitude + ", " + longitude
        print "Coordinates: " + str(latitude_value) + ", " + str(longitude_value)
        print "--- GPS ---"
    except Exception, e:
        print "No GPS Information!"
        #print e

    # Check for thumbnail
    if(thumb.data == ""):
        print "No thumbnail!"
except Exception, e:
    print "Error processing image..."
    print e;