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