Project

General

Profile

Format of Exif.GPSInfo.GPSLatitude

Added by Paul Mybofy over 3 years ago

Bonjour

With iPone 5s the Exif.GPSInfo.GPSLongitude is a string like "2/1 49/1 1775/100", with a format I can see as a regular expression "[0-9]+/[0-9]+ [0-9]+/[0-9]+ [0-9]+/[0-9]+"

Exiv2 does use strictly that format whichever is the source of metadatas, like : smartphone IOS, Android, ... ; APN Canon eos 06d, Nikon D5300, ... ; Garmin GPS Navigator for cars ; etc. ?

The decimal value is always? :
float long = 2/1 + 49/(60*1) + 1775/(3600*100);
That works OK for me.

Merci


Replies (2)

RE: Format of Exif.GPSInfo.GPSLatitude - Added by Robin Mills over 3 years ago

Paul

I think it's OK to parse Exif.GPSInfo.GPSLongitude with that regular expression. I will give you a more detailed explanation later this evening.

Robin

RE: Format of Exif.GPSInfo.GPSLatitude - Added by Robin Mills over 3 years ago

Paul

Exif metadata format is defined by the following standard: http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf I believe all "common" cameras such a iPhone, Canon, Sony and many others write metadata to the standard.

Every Exif tag has a count and a typeID. Download my favourite test file: http://clanmills.com/Stonehenge.jpg

You can see the tag Exif.GPSInfo.GPSLongitude:

606 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ bin/exiv2 -pa --grep GPSLong Stonehenge.jpg 
Exif.GPSInfo.GPSLongitudeRef                 Ascii       2  West
Exif.GPSInfo.GPSLongitude                    Rational    3  1deg 49.59840' 
607 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ 
This says that the "value" of GPSLongitude is an array of 3 rationals. A rational is a std::pair of integers of which the first is the enumerator and the second is the denominator. The Exiv2 option -pa calls the toString() function which formats the Longitude into "human" format. The values stored can be seen with the -pv option:
607 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ bin/exiv2 -pv --grep GPSLong Stonehenge.jpg 
0x0003 GPSInfo      GPSLongitudeRef             Ascii       2  W
0x0004 GPSInfo      GPSLongitude                Rational    3  1/1 495984/10000 0/1
608 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ 
Conventionally, rational values are written first/second, however they are stored as unsigned in C++ in the Rational : std::pair<unsigned>

I've modified samples/exifvalue.cpp and put the modified code below. When you run that on Stonehenge.jpg, you'll see:

608 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ bin/exifvalue Stonehenge.jpg Exif.GPSInfo.GPSLongitude
1deg 49.59840' 
Longitude: count = 3 type = 5 Exiv2::unsignedRational 1/1 495984/10000 0/1 decimal = '1.826640' string = '1/1 495984/10000 0/1' 
609 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ 
I hope you find the code in std::string gpsToString(Exiv2::Metadatum& value) easy to follow and you can modify to your exact purposes. You'll notice that I've calculated the decimal = 1.826640 = 1 degree, (0.626640*60) = 49.5984 minutes (as reported by Exiv2). If you want seconds, that'll be .5984*60 = 35.904 seconds.

You asked a couple of days ago about the function: void Converter::cnvExifGPSVersion(const char* from, const char* to) That function converts Exif metadata (in the rational array described above) into Xmp (an ascii string). If you've understood my explanation above, I think you'll easily read and understand cnvExifGPSVersion().

There's another matter to understand. The Exif standard requires the key GPSLongitudeRef to be defined to say if the Longitude is East or West. Latitude and Altitude also require this.

616 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ exiv2 -pa --grep GPSInfo/i Stonehenge.jpg 
Exif.GPSInfo.GPSVersionID                    Byte        4  2.3.0.0
Exif.GPSInfo.GPSLatitudeRef                  Ascii       2  North
Exif.GPSInfo.GPSLatitude                     Rational    3  51deg 10.69690' 
Exif.GPSInfo.GPSLongitudeRef                 Ascii       2  West
Exif.GPSInfo.GPSLongitude                    Rational    3  1deg 49.59840' 
Exif.GPSInfo.GPSAltitudeRef                  Byte        1  Above sea level
Exif.GPSInfo.GPSAltitude                     Rational    1  97 m
Exif.GPSInfo.GPSTimeStamp                    Rational    3  14:38:55.9
Exif.GPSInfo.GPSSatellites                   Ascii       3  09
Exif.GPSInfo.GPSMapDatum                     Ascii      17  WGS-84          
Exif.GPSInfo.GPSDateStamp                    Ascii      11  2015:07:16
617 rmills@rmillsmbp:~/gnu/github/exiv2/exiv2/build $ 

Here's my modified samples/exifvalue.cpp (which I won't be submitting to the Exiv2 code base). Good Luck. Let me know how this works out for you.

// ***************************************************************** -*- C++ -*-
// exifvalue.cpp
// Sample program to print value of an exif key in an image

#include <exiv2/exiv2.hpp>

#include <iostream>
#include <iomanip>
#include <cassert>
#include <string>

std::string gpsToString(Exiv2::Metadatum& value)
{
    char r[500];
    int l = 0;
    l += sprintf(r+l,"count = %ld ",value.count());
    l += sprintf(r+l,"type = %d ",value.typeId());
    switch ( value.typeId() ) {
        case Exiv2::unsignedRational:
        {
            l += sprintf(r+l,"Exiv2::unsignedRational ");
            double decimal = 0 ;
            double denom   = 1 ;
            for ( int i = 0 ; i < value.count() ; i++) {
                Exiv2::Rational rational = value.toRational(i);
                l += sprintf(r+l,"%d/%d ",rational.first,rational.second);
                decimal += value.toFloat(i) / denom;
                denom   *= 60;
            }
            l+= sprintf(r+l,"decimal = '%f' ",decimal);
            l+= sprintf(r+l,"string = '%s' ",value.toString().c_str());
        } break;

        default:
        break;
    }

    return std::string(r);
}

int main(int argc, char* const argv[])
{
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " file key\n";
        return 1;
    }

    const char* file = argv[1];
    const char* key  = argv[2];

    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
    assert(image.get() != 0);
    image->readMetadata();
    Exiv2::ExifData &exifData = image->exifData();

    if ( exifData.empty()) {
        std::cerr << "no metadata found in file " << file << std::endl;
        exit(2);
    }

    try {
         std::cout << exifData[key] << std::endl;
         if ( ::strcmp(key,"Exif.GPSInfo.GPSLongitude") == 0 ) {
             std::cout << "Longitude: " << gpsToString(exifData[key]) << std::endl;
         }
    } catch (Exiv2::AnyError& e) {
        std::cerr << "Caught Exiv2 exception '" << e << "'" << std::endl;
        exit(3);
    } catch ( ... ) {
        std::cerr << "Caught a cold!" << std::endl;
        exit(4);
    }

    return 0;
}
    (1-2/2)