KExiv2

kexiv2gps.cpp
Go to the documentation of this file.
1 
30 #include "kexiv2.h"
31 #include "kexiv2_p.h"
32 
33 // C ANSI includes
34 
35 #include <math.h>
36 
37 // C++ includes
38 
39 #include <climits>
40 #include <cmath>
41 
42 // Local includes
43 
44 #include "libkexiv2_debug.h"
45 
46 namespace KExiv2Iface
47 {
48 
49 bool KExiv2::getGPSInfo(double& altitude, double& latitude, double& longitude) const
50 {
51  // Some GPS device do not set Altitude. So a valid GPS position can be with a zero value.
52  // No need to check return value.
53  getGPSAltitude(&altitude);
54 
55  if (!getGPSLatitudeNumber(&latitude))
56  return false;
57 
58  if (!getGPSLongitudeNumber(&longitude))
59  return false;
60 
61  return true;
62 }
63 
64 bool KExiv2::getGPSLatitudeNumber(double* const latitude) const
65 {
66  try
67  {
68  *latitude=0.0;
69 
70  // Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
71  if ( convertFromGPSCoordinateString(getXmpTagString("Xmp.exif.GPSLatitude"), latitude) )
72  return true;
73 
74  // Now try to get the reference from Exif.
75  const QByteArray latRef = getExifTagData("Exif.GPSInfo.GPSLatitudeRef");
76 
77  if (!latRef.isEmpty())
78  {
79  Exiv2::ExifKey exifKey("Exif.GPSInfo.GPSLatitude");
80  Exiv2::ExifData exifData(d->exifMetadata());
81  Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
82 
83  if (it != exifData.end() && (*it).count() == 3)
84  {
85  // Latitude decoding from Exif.
86  double num, den, min, sec;
87 
88  num = (double)((*it).toRational(0).first);
89  den = (double)((*it).toRational(0).second);
90 
91  if (den == 0)
92  return false;
93 
94  *latitude = num/den;
95 
96  num = (double)((*it).toRational(1).first);
97  den = (double)((*it).toRational(1).second);
98 
99  if (den == 0)
100  return false;
101 
102  min = num/den;
103 
104  if (min != -1.0)
105  *latitude = *latitude + min/60.0;
106 
107  num = (double)((*it).toRational(2).first);
108  den = (double)((*it).toRational(2).second);
109 
110  if (den == 0)
111  {
112  // be relaxed and accept 0/0 seconds. See #246077.
113  if (num == 0)
114  den = 1;
115  else
116  return false;
117  }
118 
119  sec = num/den;
120 
121  if (sec != -1.0)
122  *latitude = *latitude + sec/3600.0;
123  }
124  else
125  {
126  return false;
127  }
128 
129  if (latRef[0] == 'S')
130  *latitude *= -1.0;
131 
132  return true;
133  }
134  }
135  catch( Exiv2::Error& e )
136  {
137  d->printExiv2ExceptionError(QString::fromLatin1("Cannot get GPS tag using Exiv2 "), e);
138  }
139  catch(...)
140  {
141  qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
142  }
143 
144  return false;
145 }
146 
147 bool KExiv2::getGPSLongitudeNumber(double* const longitude) const
148 {
149  try
150  {
151  *longitude=0.0;
152 
153  // Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
154  if ( convertFromGPSCoordinateString(getXmpTagString("Xmp.exif.GPSLongitude"), longitude) )
155  return true;
156 
157  // Now try to get the reference from Exif.
158  const QByteArray lngRef = getExifTagData("Exif.GPSInfo.GPSLongitudeRef");
159 
160  if (!lngRef.isEmpty())
161  {
162  // Longitude decoding from Exif.
163 
164  Exiv2::ExifKey exifKey2("Exif.GPSInfo.GPSLongitude");
165  Exiv2::ExifData exifData(d->exifMetadata());
166  Exiv2::ExifData::iterator it = exifData.findKey(exifKey2);
167 
168  if (it != exifData.end() && (*it).count() == 3)
169  {
172  double num, den;
173 
174  num = (double)((*it).toRational(0).first);
175  den = (double)((*it).toRational(0).second);
176 
177  if (den == 0)
178  {
179  return false;
180  }
181 
182  *longitude = num/den;
183 
184  num = (double)((*it).toRational(1).first);
185  den = (double)((*it).toRational(1).second);
186 
187  if (den == 0)
188  {
189  return false;
190  }
191 
192  const double min = num/den;
193 
194  if (min != -1.0)
195  {
196  *longitude = *longitude + min/60.0;
197  }
198 
199  num = (double)((*it).toRational(2).first);
200  den = (double)((*it).toRational(2).second);
201 
202  if (den == 0)
203  {
204  // be relaxed and accept 0/0 seconds. See #246077.
205  if (num == 0)
206  den = 1;
207  else
208  return false;
209  }
210 
211  const double sec = num/den;
212 
213  if (sec != -1.0)
214  {
215  *longitude = *longitude + sec/3600.0;
216  }
217  }
218  else
219  {
220  return false;
221  }
222 
223  if (lngRef[0] == 'W')
224  {
225  *longitude *= -1.0;
226  }
227 
228  return true;
229  }
230  }
231  catch( Exiv2::Error& e )
232  {
233  d->printExiv2ExceptionError(QString::fromLatin1("Cannot get GPS tag using Exiv2 "), e);
234  }
235  catch(...)
236  {
237  qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
238  }
239 
240  return false;
241 }
242 
243 bool KExiv2::getGPSAltitude(double* const altitude) const
244 {
245  try
246  {
247  double num, den;
248  *altitude=0.0;
249 
250  // Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
251  const QString altRefXmp = getXmpTagString("Xmp.exif.GPSAltitudeRef");
252 
253  if (!altRefXmp.isEmpty())
254  {
255  const QString altXmp = getXmpTagString("Xmp.exif.GPSAltitude");
256 
257  if (!altXmp.isEmpty())
258  {
259  num = altXmp.section(QString::fromLatin1("/"), 0, 0).toDouble();
260  den = altXmp.section(QString::fromLatin1("/"), 1, 1).toDouble();
261 
262  if (den == 0)
263  return false;
264 
265  *altitude = num/den;
266 
267  if (altRefXmp == QString::fromLatin1("1"))
268  *altitude *= -1.0;
269 
270  return true;
271  }
272  }
273 
274  // Get the reference from Exif (above/below sea level)
275  const QByteArray altRef = getExifTagData("Exif.GPSInfo.GPSAltitudeRef");
276 
277  if (!altRef.isEmpty())
278  {
279  // Altitude decoding from Exif.
280 
281  Exiv2::ExifKey exifKey3("Exif.GPSInfo.GPSAltitude");
282  Exiv2::ExifData exifData(d->exifMetadata());
283  Exiv2::ExifData::iterator it = exifData.findKey(exifKey3);
284  if (it != exifData.end() && (*it).count())
285  {
286  num = (double)((*it).toRational(0).first);
287  den = (double)((*it).toRational(0).second);
288 
289  if (den == 0)
290  return false;
291 
292  *altitude = num/den;
293  }
294  else
295  {
296  return false;
297  }
298 
299  if (altRef[0] == '1')
300  *altitude *= -1.0;
301 
302  return true;
303  }
304  }
305  catch( Exiv2::Error& e )
306  {
307  d->printExiv2ExceptionError(QString::fromLatin1("Cannot get GPS tag using Exiv2 "), e);
308  }
309  catch(...)
310  {
311  qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
312  }
313 
314  return false;
315 }
316 
318 {
319  double latitude;
320 
321  if (!getGPSLatitudeNumber(&latitude))
322  return QString();
323 
324  return convertToGPSCoordinateString(true, latitude);
325 }
326 
327 QString KExiv2::getGPSLongitudeString() const
328 {
329  double longitude;
330 
331  if (!getGPSLongitudeNumber(&longitude))
332  return QString();
333 
334  return convertToGPSCoordinateString(false, longitude);
335 }
336 
337 bool KExiv2::initializeGPSInfo(const bool setProgramName)
338 {
339  if (!setProgramId(setProgramName))
340  return false;
341 
342  try
343  {
344  // TODO: what happens if these already exist?
345 
346  // Do all the easy constant ones first.
347  // GPSVersionID tag: standard says is should be four bytes: 02 00 00 00
348  // (and, must be present).
349  Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::unsignedByte);
350  value->read("2 0 0 0");
351  d->exifMetadata().add(Exiv2::ExifKey("Exif.GPSInfo.GPSVersionID"), value.get());
352 
353  // Datum: the datum of the measured data. If not given, we insert WGS-84.
354  d->exifMetadata()["Exif.GPSInfo.GPSMapDatum"] = "WGS-84";
355 
356 #ifdef _XMP_SUPPORT_
357  setXmpTagString("Xmp.exif.GPSVersionID", QString::fromLatin1("2.0.0.0"), false);
358  setXmpTagString("Xmp.exif.GPSMapDatum", QString::fromLatin1("WGS-84"), false);
359 #endif // _XMP_SUPPORT_
360 
361  return true;
362  }
363  catch( Exiv2::Error& e )
364  {
365  d->printExiv2ExceptionError(QString::fromLatin1("Cannot initialize GPS data using Exiv2 "), e);
366  }
367  catch(...)
368  {
369  qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
370  }
371 
372  return false;
373 }
374 
375 bool KExiv2::setGPSInfo(const double altitude, const double latitude, const double longitude, const bool setProgramName)
376 {
377  return setGPSInfo(&altitude, latitude, longitude, setProgramName);
378 }
379 
380 bool KExiv2::setGPSInfo(const double* const altitude, const double latitude, const double longitude, const bool setProgramName)
381 {
382  if (!setProgramId(setProgramName))
383  return false;
384 
385  try
386  {
387  // In first, we need to clean up all existing GPS info.
388  removeGPSInfo();
389 
390  // now re-initialize the GPS info:
391  if (!initializeGPSInfo(setProgramName))
392  return false;
393 
394  char scratchBuf[100];
395  long int nom, denom;
396  long int deg, min;
397 
398  // Now start adding data.
399 
400  // ALTITUDE.
401  if (altitude)
402  {
403  // Altitude reference: byte "00" meaning "above sea level", "01" mening "behing sea level".
404  Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::unsignedByte);
405 
406  if ((*altitude) >= 0) value->read("0");
407  else value->read("1");
408 
409  d->exifMetadata().add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"), value.get());
410 
411  // And the actual altitude, as absolute value..
412  convertToRational(fabs(*altitude), &nom, &denom, 4);
413  snprintf(scratchBuf, 100, "%ld/%ld", nom, denom);
414  d->exifMetadata()["Exif.GPSInfo.GPSAltitude"] = scratchBuf;
415 
416 #ifdef _XMP_SUPPORT_
417  setXmpTagString("Xmp.exif.GPSAltitudeRef", ((*altitude) >= 0) ? QString::fromLatin1("0") : QString::fromLatin1("1"), false);
418  setXmpTagString("Xmp.exif.GPSAltitude", QString::fromLatin1(scratchBuf), false);
419 #endif // _XMP_SUPPORT_
420  }
421 
422  // LATITUDE
423  // Latitude reference:
424  // latitude < 0 : "S"
425  // latitude > 0 : "N"
426  //
427  d->exifMetadata()["Exif.GPSInfo.GPSLatitudeRef"] = (latitude < 0 ) ? "S" : "N";
428 
429  // Now the actual latitude itself.
430  // This is done as three rationals.
431  // I choose to do it as:
432  // dd/1 - degrees.
433  // mmmm/100 - minutes
434  // 0/1 - seconds
435  // Exif standard says you can do it with minutes
436  // as mm/1 and then seconds as ss/1, but its
437  // (slightly) more accurate to do it as
438  // mmmm/100 than to split it.
439  // We also absolute the value (with fabs())
440  // as the sign is encoded in LatRef.
441  // Further note: original code did not translate between
442  // dd.dddddd to dd mm.mm - that's why we now multiply
443  // by 6000 - x60 to get minutes, x1000000 to get to mmmm/1000000.
444  deg = (int)floor(fabs(latitude)); // Slice off after decimal.
445  min = (int)floor((fabs(latitude) - floor(fabs(latitude))) * 60000000);
446  snprintf(scratchBuf, 100, "%ld/1 %ld/1000000 0/1", deg, min);
447  d->exifMetadata()["Exif.GPSInfo.GPSLatitude"] = scratchBuf;
448 
449 #ifdef _XMP_SUPPORT_
450 
454  setXmpTagString("Xmp.exif.GPSLatitudeRef", (latitude < 0) ? QString::fromLatin1("S") : QString::fromLatin1("N"), false);
455  setXmpTagString("Xmp.exif.GPSLatitude", convertToGPSCoordinateString(true, latitude), false);
456 #endif // _XMP_SUPPORT_
457 
458  // LONGITUDE
459  // Longitude reference:
460  // longitude < 0 : "W"
461  // longitude > 0 : "E"
462  d->exifMetadata()["Exif.GPSInfo.GPSLongitudeRef"] = (longitude < 0 ) ? "W" : "E";
463 
464  // Now the actual longitude itself.
465  // This is done as three rationals.
466  // I choose to do it as:
467  // dd/1 - degrees.
468  // mmmm/100 - minutes
469  // 0/1 - seconds
470  // Exif standard says you can do it with minutes
471  // as mm/1 and then seconds as ss/1, but its
472  // (slightly) more accurate to do it as
473  // mmmm/100 than to split it.
474  // We also absolute the value (with fabs())
475  // as the sign is encoded in LongRef.
476  // Further note: original code did not translate between
477  // dd.dddddd to dd mm.mm - that's why we now multiply
478  // by 6000 - x60 to get minutes, x1000000 to get to mmmm/1000000.
479  deg = (int)floor(fabs(longitude)); // Slice off after decimal.
480  min = (int)floor((fabs(longitude) - floor(fabs(longitude))) * 60000000);
481  snprintf(scratchBuf, 100, "%ld/1 %ld/1000000 0/1", deg, min);
482  d->exifMetadata()["Exif.GPSInfo.GPSLongitude"] = scratchBuf;
483 
484 #ifdef _XMP_SUPPORT_
485 
489  setXmpTagString("Xmp.exif.GPSLongitudeRef", (longitude < 0) ? QString::fromLatin1("W") : QString::fromLatin1("E"), false);
490  setXmpTagString("Xmp.exif.GPSLongitude", convertToGPSCoordinateString(false, longitude), false);
491 #endif // _XMP_SUPPORT_
492 
493  return true;
494  }
495  catch( Exiv2::Error& e )
496  {
497  d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif GPS tag using Exiv2 "), e);
498  }
499  catch(...)
500  {
501  qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
502  }
503 
504  return false;
505 }
506 
507 bool KExiv2::setGPSInfo(const double altitude, const QString& latitude, const QString& longitude, const bool setProgramName)
508 {
509  double longitudeValue, latitudeValue;
510 
511  if (!convertFromGPSCoordinateString(latitude, &latitudeValue))
512  return false;
513 
514  if (!convertFromGPSCoordinateString(longitude, &longitudeValue))
515  return false;
516 
517  return setGPSInfo(&altitude, latitudeValue, longitudeValue, setProgramName);
518 }
519 
520 bool KExiv2::removeGPSInfo(const bool setProgramName)
521 {
522  if (!setProgramId(setProgramName))
523  return false;
524 
525  try
526  {
527  QStringList gpsTagsKeys;
528 
529  for (Exiv2::ExifData::iterator it = d->exifMetadata().begin();
530  it != d->exifMetadata().end(); ++it)
531  {
532  QString key = QString::fromLocal8Bit(it->key().c_str());
533 
534  if (key.section(QString::fromLatin1("."), 1, 1) == QString::fromLatin1("GPSInfo"))
535  gpsTagsKeys.append(key);
536  }
537 
538  for(QStringList::const_iterator it2 = gpsTagsKeys.constBegin(); it2 != gpsTagsKeys.constEnd(); ++it2)
539  {
540  Exiv2::ExifKey gpsKey((*it2).toLatin1().constData());
541  Exiv2::ExifData::iterator it3 = d->exifMetadata().findKey(gpsKey);
542 
543  if (it3 != d->exifMetadata().end())
544  d->exifMetadata().erase(it3);
545  }
546 
547 #ifdef _XMP_SUPPORT_
548 
552  removeXmpTag("Xmp.exif.GPSLatitudeRef", false);
553  removeXmpTag("Xmp.exif.GPSLongitudeRef", false);
554  removeXmpTag("Xmp.exif.GPSVersionID", false);
555  removeXmpTag("Xmp.exif.GPSLatitude", false);
556  removeXmpTag("Xmp.exif.GPSLongitude", false);
557  removeXmpTag("Xmp.exif.GPSAltitudeRef", false);
558  removeXmpTag("Xmp.exif.GPSAltitude", false);
559  removeXmpTag("Xmp.exif.GPSTimeStamp", false);
560  removeXmpTag("Xmp.exif.GPSSatellites", false);
561  removeXmpTag("Xmp.exif.GPSStatus", false);
562  removeXmpTag("Xmp.exif.GPSMeasureMode", false);
563  removeXmpTag("Xmp.exif.GPSDOP", false);
564  removeXmpTag("Xmp.exif.GPSSpeedRef", false);
565  removeXmpTag("Xmp.exif.GPSSpeed", false);
566  removeXmpTag("Xmp.exif.GPSTrackRef", false);
567  removeXmpTag("Xmp.exif.GPSTrack", false);
568  removeXmpTag("Xmp.exif.GPSImgDirectionRef", false);
569  removeXmpTag("Xmp.exif.GPSImgDirection", false);
570  removeXmpTag("Xmp.exif.GPSMapDatum", false);
571  removeXmpTag("Xmp.exif.GPSDestLatitude", false);
572  removeXmpTag("Xmp.exif.GPSDestLongitude", false);
573  removeXmpTag("Xmp.exif.GPSDestBearingRef", false);
574  removeXmpTag("Xmp.exif.GPSDestBearing", false);
575  removeXmpTag("Xmp.exif.GPSDestDistanceRef", false);
576  removeXmpTag("Xmp.exif.GPSDestDistance", false);
577  removeXmpTag("Xmp.exif.GPSProcessingMethod", false);
578  removeXmpTag("Xmp.exif.GPSAreaInformation", false);
579  removeXmpTag("Xmp.exif.GPSDifferential", false);
580 #endif // _XMP_SUPPORT_
581 
582  return true;
583  }
584  catch( Exiv2::Error& e )
585  {
586  d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif GPS tag using Exiv2 "), e);
587  }
588  catch(...)
589  {
590  qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
591  }
592 
593  return false;
594 }
595 
596 void KExiv2::convertToRational(const double number, long int* const numerator,
597  long int* const denominator, const int rounding)
598 {
599  // This function converts the given decimal number
600  // to a rational (fractional) number.
601  //
602  // Examples in comments use Number as 25.12345, Rounding as 4.
603 
604  // Split up the number.
605  double whole = trunc(number);
606  double fractional = number - whole;
607 
608  // Calculate the "number" used for rounding.
609  // This is 10^Digits - ie, 4 places gives us 10000.
610  double rounder = pow(10.0, rounding);
611 
612  // Round the fractional part, and leave the number
613  // as greater than 1.
614  // To do this we: (for example)
615  // 0.12345 * 10000 = 1234.5
616  // floor(1234.5) = 1234 - now bigger than 1 - ready...
617  fractional = round(fractional * rounder);
618 
619  // Convert the whole thing to a fraction.
620  // Fraction is:
621  // (25 * 10000) + 1234 251234
622  // ------------------- = ------ = 25.1234
623  // 10000 10000
624  double numTemp = (whole * rounder) + fractional;
625  double denTemp = rounder;
626 
627  // Now we should reduce until we can reduce no more.
628 
629  // Try simple reduction...
630  // if Num
631  // ----- = integer out then....
632  // Den
633  if (trunc(numTemp / denTemp) == (numTemp / denTemp))
634  {
635  // Divide both by Denominator.
636  numTemp /= denTemp;
637  denTemp /= denTemp;
638  }
639 
640  // And, if that fails, brute force it.
641  while (1)
642  {
643  // Jump out if we can't integer divide one.
644  if ((numTemp / 2) != trunc(numTemp / 2)) break;
645  if ((denTemp / 2) != trunc(denTemp / 2)) break;
646  // Otherwise, divide away.
647  numTemp /= 2;
648  denTemp /= 2;
649  }
650 
651  // Copy out the numbers.
652  *numerator = (int)numTemp;
653  *denominator = (int)denTemp;
654 }
655 
656 void KExiv2::convertToRationalSmallDenominator(const double number, long int* const numerator, long int* const denominator)
657 {
658  // This function converts the given decimal number
659  // to a rational (fractional) number.
660  //
661  // This method, in contrast to the method above, will retrieve the smallest possible
662  // denominator. It is tested to retrieve the correct value for 1/x, with 0 < x <= 1000000.
663  // Note: This requires double precision, storing in float breaks some numbers (49, 59, 86,...)
664 
665  // Split up the number.
666  double whole = trunc(number);
667  double fractional = number - whole;
668 
669  /*
670  * Find best rational approximation to a double
671  * by C.B. Falconer, 2006-09-07. Released to public domain.
672  *
673  * Newsgroups: comp.lang.c, comp.programming
674  * From: CBFalconer <[email protected]>
675  * Date: Thu, 07 Sep 2006 17:35:30 -0400
676  * Subject: Rational approximations
677  */
678  int lastnum = 500; // this is _not_ the largest possible denominator
679  long int num, approx, bestnum=0, bestdenom=1;
680  double value, error, leasterr, criterion;
681 
682  value = fractional;
683 
684  if (value == 0.0)
685  {
686  *numerator = (long int)whole;
687  *denominator = 1;
688  return;
689  }
690 
691  criterion = 2 * value * DBL_EPSILON;
692 
693  for (leasterr = value, num = 1; num < lastnum; ++num)
694  {
695  approx = (int)(num / value + 0.5);
696  error = fabs((double)num / approx - value);
697 
698  if (error < leasterr)
699  {
700  bestnum = num;
701  bestdenom = approx;
702  leasterr = error;
703 
704  if (leasterr <= criterion) break;
705  }
706  }
707 
708  // add whole number part
709  if (bestdenom * whole > (double)INT_MAX)
710  {
711  // In some cases, we would generate an integer overflow.
712  // Fall back to Gilles's code which is better suited for such numbers.
713  convertToRational(number, numerator, denominator, 5);
714  }
715  else
716  {
717  bestnum += bestdenom * (long int)whole;
718  *numerator = bestnum;
719  *denominator = bestdenom;
720  }
721 }
722 
723 QString KExiv2::convertToGPSCoordinateString(const long int numeratorDegrees, const long int denominatorDegrees,
724  const long int numeratorMinutes, const long int denominatorMinutes,
725  const long int numeratorSeconds, long int denominatorSeconds,
726  const char directionReference)
727 {
736  QString coordinate;
737 
738  // be relaxed with seconds of 0/0
739  if (denominatorSeconds == 0 && numeratorSeconds == 0)
740  denominatorSeconds = 1;
741 
742  if (denominatorDegrees == 1 &&
743  denominatorMinutes == 1 &&
744  denominatorSeconds == 1)
745  {
746  // use form DDD,MM,SSk
747  coordinate = QString::fromLatin1("%1,%2,%3%4");
748  coordinate = coordinate.arg(numeratorDegrees).arg(numeratorMinutes).arg(numeratorSeconds).arg(directionReference);
749  }
750  else if (denominatorDegrees == 1 &&
751  denominatorMinutes == 100 &&
752  denominatorSeconds == 1)
753  {
754  // use form DDD,MM.mmk
755  coordinate = QString::fromLatin1("%1,%2%3");
756  double minutes = (double)numeratorMinutes / (double)denominatorMinutes;
757  minutes += (double)numeratorSeconds / 60.0;
758  QString minutesString = QString::number(minutes, 'f', 8);
759 
760  while (minutesString.endsWith(QString::fromLatin1("0")) && !minutesString.endsWith(QString::fromLatin1(".0")))
761  {
762  minutesString.chop(1);
763  }
764 
765  coordinate = coordinate.arg(numeratorDegrees).arg(minutesString).arg(directionReference);
766  }
767  else if (denominatorDegrees == 0 ||
768  denominatorMinutes == 0 ||
769  denominatorSeconds == 0)
770  {
771  // Invalid. 1/0 is everything but 0. As is 0/0.
772  return QString();
773  }
774  else
775  {
776  // use form DDD,MM.mmk
777  coordinate = QString::fromLatin1("%1,%2%3");
778  double degrees = (double)numeratorDegrees / (double)denominatorDegrees;
779  double wholeDegrees = trunc(degrees);
780  double minutes = (double)numeratorMinutes / (double)denominatorMinutes;
781  minutes += (degrees - wholeDegrees) * 60.0;
782  minutes += ((double)numeratorSeconds / (double)denominatorSeconds) / 60.0;
783  QString minutesString = QString::number(minutes, 'f', 8);
784 
785  while (minutesString.endsWith(QString::fromLatin1("0")) && !minutesString.endsWith(QString::fromLatin1(".0")))
786  {
787  minutesString.chop(1);
788  }
789 
790  coordinate = coordinate.arg((int)wholeDegrees).arg(minutesString).arg(directionReference);
791  }
792 
793  return coordinate;
794 }
795 
796 QString KExiv2::convertToGPSCoordinateString(const bool isLatitude, double coordinate)
797 {
798  if (coordinate < -360.0 || coordinate > 360.0)
799  return QString();
800 
801  QString coordinateString;
802 
803  char directionReference;
804 
805  if (isLatitude)
806  {
807  if (coordinate < 0)
808  directionReference = 'S';
809  else
810  directionReference = 'N';
811  }
812  else
813  {
814  if (coordinate < 0)
815  directionReference = 'W';
816  else
817  directionReference = 'E';
818  }
819 
820  // remove sign
821  coordinate = fabs(coordinate);
822 
823  int degrees = (int)floor(coordinate);
824  // get fractional part
825  coordinate = coordinate - (double)(degrees);
826  // To minutes
827  double minutes = coordinate * 60.0;
828 
829  // use form DDD,MM.mmk
830  coordinateString = QString::fromLatin1("%1,%2%3");
831  coordinateString = coordinateString.arg(degrees);
832  coordinateString = coordinateString.arg(minutes, 0, 'f', 8).arg(directionReference);
833 
834  return coordinateString;
835 }
836 
838  long int* const numeratorDegrees, long int* const denominatorDegrees,
839  long int* const numeratorMinutes, long int* const denominatorMinutes,
840  long int* const numeratorSeconds, long int* const denominatorSeconds,
841  char* const directionReference)
842 {
843  if (gpsString.isEmpty())
844  return false;
845 
846  *directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
847  QString coordinate = gpsString.left(gpsString.length() - 1);
848  QStringList parts = coordinate.split(QString::fromLatin1(","));
849 
850  if (parts.size() == 2)
851  {
852  // form DDD,MM.mmk
853  *denominatorDegrees = 1;
854  *denominatorMinutes = 1000000;
855  *denominatorSeconds = 1;
856 
857  *numeratorDegrees = parts[0].toLong();
858 
859  double minutes = parts[1].toDouble();
860  minutes *= 1000000;
861 
862  *numeratorMinutes = (long)round(minutes);
863  *numeratorSeconds = 0;
864 
865  return true;
866  }
867  else if (parts.size() == 3)
868  {
869  // use form DDD,MM,SSk
870  *denominatorDegrees = 1;
871  *denominatorMinutes = 1;
872  *denominatorSeconds = 1;
873 
874  *numeratorDegrees = parts[0].toLong();
875  *numeratorMinutes = parts[1].toLong();
876  *numeratorSeconds = parts[2].toLong();
877 
878  return true;
879  }
880  else
881  {
882  return false;
883  }
884 }
885 
886 bool KExiv2::convertFromGPSCoordinateString(const QString& gpsString, double* const degrees)
887 {
888  if (gpsString.isEmpty())
889  return false;
890 
891  char directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
892  QString coordinate = gpsString.left(gpsString.length() - 1);
893  QStringList parts = coordinate.split(QString::fromLatin1(","));
894 
895  if (parts.size() == 2)
896  {
897  // form DDD,MM.mmk
898  *degrees = parts[0].toLong();
899  *degrees += parts[1].toDouble() / 60.0;
900 
901  if (directionReference == 'W' || directionReference == 'S')
902  *degrees *= -1.0;
903 
904  return true;
905  }
906  else if (parts.size() == 3)
907  {
908  // use form DDD,MM,SSk
909 
910  *degrees = parts[0].toLong();
911  *degrees += parts[1].toLong() / 60.0;
912  *degrees += parts[2].toLong() / 3600.0;
913 
914  if (directionReference == 'W' || directionReference == 'S')
915  *degrees *= -1.0;
916 
917  return true;
918  }
919  else
920  {
921  return false;
922  }
923 }
924 
926  int* const degrees, int* const minutes,
927  double* const seconds, char* const directionReference)
928 {
929  if (gpsString.isEmpty())
930  return false;
931 
932  *directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
933  QString coordinate = gpsString.left(gpsString.length() - 1);
934  QStringList parts = coordinate.split(QString::fromLatin1(","));
935 
936  if (parts.size() == 2)
937  {
938  // form DDD,MM.mmk
939  *degrees = parts[0].toInt();
940  double fractionalMinutes = parts[1].toDouble();
941  *minutes = (int)trunc(fractionalMinutes);
942  *seconds = (fractionalMinutes - (double)(*minutes)) * 60.0;
943 
944  return true;
945  }
946  else if (parts.size() == 3)
947  {
948  // use form DDD,MM,SSk
949  *degrees = parts[0].toInt();
950  *minutes = parts[1].toInt();
951  *seconds = (double)parts[2].toInt();
952 
953  return true;
954  }
955  else
956  {
957  return false;
958  }
959 }
960 
961 void KExiv2::convertToUserPresentableNumbers(const bool isLatitude, double coordinate,
962  int* const degrees, int* const minutes,
963  double* const seconds, char* const directionReference)
964 {
965  if (isLatitude)
966  {
967  if (coordinate < 0)
968  *directionReference = 'S';
969  else
970  *directionReference = 'N';
971  }
972  else
973  {
974  if (coordinate < 0)
975  *directionReference = 'W';
976  else
977  *directionReference = 'E';
978  }
979 
980  // remove sign
981  coordinate = fabs(coordinate);
982  *degrees = (int)floor(coordinate);
983  // get fractional part
984  coordinate = coordinate - (double)(*degrees);
985  // To minutes
986  coordinate *= 60.0;
987  *minutes = (int)floor(coordinate);
988  // get fractional part
989  coordinate = coordinate - (double)(*minutes);
990  // To seconds
991  coordinate *= 60.0;
992  *seconds = coordinate;
993 }
994 
995 } // NameSpace KExiv2Iface
KExiv2Iface.
Definition: kexiv2.cpp:36
bool getGPSLatitudeNumber(double *const latitude) const
Get GPS location information set in the image, as a double floating point number as in degrees where ...
Definition: kexiv2gps.cpp:64
bool setXmpTagString(const char *xmpTagName, const QString &value, bool setProgramName=true) const
Set a Xmp tag content using a string.
Definition: kexiv2xmp.cpp:387
static void convertToRationalSmallDenominator(const double number, long int *const numerator, long int *const denominator)
This method convert a &#39;number&#39; to a rational value, returned in &#39;numerator&#39; and &#39;denominator&#39; paramet...
Definition: kexiv2gps.cpp:656
bool removeGPSInfo(const bool setProgramName=true)
Remove all Exif tags relevant of GPS location information.
Definition: kexiv2gps.cpp:520
bool isEmpty() const const
void chop(int n)
double toDouble(bool *ok) const const
int size() const const
QString number(int n, int base)
QString fromLocal8Bit(const char *str, int size)
void append(const T &value)
bool getGPSInfo(double &altitude, double &latitude, double &longitude) const
Get all GPS location information set in image.
Definition: kexiv2gps.cpp:49
static QString convertToGPSCoordinateString(const long int numeratorDegrees, const long int denominatorDegrees, const long int numeratorMinutes, const long int denominatorMinutes, const long int numeratorSeconds, long int denominatorSeconds, const char directionReference)
Converts a GPS position stored as rationals in Exif to the form described as GPSCoordinate in the XMP...
Definition: kexiv2gps.cpp:723
bool isEmpty() const const
static void convertToRational(const double number, long int *const numerator, long int *const denominator, const int rounding)
This method converts &#39;number&#39; to a rational value, returned in the &#39;numerator&#39; and &#39;denominator&#39; para...
Definition: kexiv2gps.cpp:596
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool removeXmpTag(const char *xmpTagName, bool setProgramName=true) const
Remove the Xmp tag &#39;xmpTagName&#39; from Xmp metadata.
Definition: kexiv2xmp.cpp:1121
QString getGPSLatitudeString() const
Get GPS location information set in the image, in the GPSCoordinate format as described in the XMP sp...
Definition: kexiv2gps.cpp:317
bool getGPSAltitude(double *const altitude) const
Get GPS altitude information, in meters, relative to sea level (positive sign above sea level) ...
Definition: kexiv2gps.cpp:243
virtual bool setProgramId(bool on=true) const
Re-implement this method to set automatically the Program Name and Program Version information in Exi...
Definition: kexiv2.cpp:529
char toLatin1() const const
bool setGPSInfo(const double altitude, const double latitude, const double longitude, const bool setProgramName=true)
Set all GPS location information into image.
Definition: kexiv2gps.cpp:375
QString getXmpTagString(const char *xmpTagName, bool escapeCR=true) const
Get a Xmp tag content like a string.
Definition: kexiv2xmp.cpp:346
static bool convertFromGPSCoordinateString(const QString &coordinate, long int *const numeratorDegrees, long int *const denominatorDegrees, long int *const numeratorMinutes, long int *const denominatorMinutes, long int *const numeratorSeconds, long int *const denominatorSeconds, char *const directionReference)
Converts a GPSCoordinate string as defined by XMP to three rationals and the direction reference...
Definition: kexiv2gps.cpp:837
QByteArray getExifTagData(const char *exifTagName) const
Get an Exif tag content like a bytes array.
Definition: kexiv2exif.cpp:714
bool getGPSLongitudeNumber(double *const longitude) const
Definition: kexiv2gps.cpp:147
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
int length() const const
static bool convertToUserPresentableNumbers(const QString &coordinate, int *const degrees, int *const minutes, double *const seconds, char *const directionReference)
Converts a GPSCoordinate string to user presentable numbers, integer degrees and minutes and double f...
Definition: kexiv2gps.cpp:925
QString section(QChar sep, int start, int end, QString::SectionFlags flags) const const
QString left(int n) const const
QString fromLatin1(const char *str, int size)
bool initializeGPSInfo(const bool setProgramName)
Make sure all static required GPS EXIF and XMP tags exist.
Definition: kexiv2gps.cpp:337
QList::const_iterator constEnd() const const
QList::const_iterator constBegin() const const
===========================================================This file is a part of KDE project ...
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Nov 26 2020 22:35:13 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.