• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdegraphics API Reference
  • KDE Home
  • Contact Us
 

libs/libkexiv2/libkexiv2

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

KDE's Doxygen guidelines are available online.

libs/libkexiv2/libkexiv2

Skip menu "libs/libkexiv2/libkexiv2"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdegraphics API Reference

Skip menu "kdegraphics API Reference"
  •     libkdcraw
  •     libkexiv2
  •     libkipi
  •     libksane
  • okular

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal