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