File indexing completed on 2024-04-14 14:45:56

0001 /*
0002     SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
0003     SPDX-FileCopyrightText: 2006-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0004     SPDX-FileCopyrightText: 2010-2012 Michael G. Hansen <mike at mghansen dot de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "kexiv2.h"
0010 #include "kexiv2_p.h"
0011 
0012 // C ANSI includes
0013 
0014 #include <math.h>
0015 
0016 // C++ includes
0017 
0018 #include <climits>
0019 #include <cmath>
0020 
0021 // Local includes
0022 
0023 #include "libkexiv2_debug.h"
0024 
0025 namespace KExiv2Iface
0026 {
0027 
0028 bool KExiv2::getGPSInfo(double& altitude, double& latitude, double& longitude) const
0029 {
0030     // Some GPS device do not set Altitude. So a valid GPS position can be with a zero value.
0031     // No need to check return value.
0032     getGPSAltitude(&altitude);
0033 
0034     if (!getGPSLatitudeNumber(&latitude))
0035          return false;
0036 
0037     if (!getGPSLongitudeNumber(&longitude))
0038          return false;
0039 
0040     return true;
0041 }
0042 
0043 bool KExiv2::getGPSLatitudeNumber(double* const latitude) const
0044 {
0045     try
0046     {
0047         *latitude=0.0;
0048 
0049         // Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
0050         if ( convertFromGPSCoordinateString(getXmpTagString("Xmp.exif.GPSLatitude"), latitude) )
0051             return true;
0052 
0053         // Now try to get the reference from Exif.
0054         const QByteArray latRef = getExifTagData("Exif.GPSInfo.GPSLatitudeRef");
0055 
0056         if (!latRef.isEmpty())
0057         {
0058             Exiv2::ExifKey exifKey("Exif.GPSInfo.GPSLatitude");
0059             Exiv2::ExifData exifData(d->exifMetadata());
0060             Exiv2::ExifData::iterator it = exifData.findKey(exifKey);
0061 
0062             if (it != exifData.end() && (*it).count() == 3)
0063             {
0064                 // Latitude decoding from Exif.
0065                 double num, den, min, sec;
0066 
0067                 num = (double)((*it).toRational(0).first);
0068                 den = (double)((*it).toRational(0).second);
0069 
0070                 if (den == 0)
0071                     return false;
0072 
0073                 *latitude = num/den;
0074 
0075                 num = (double)((*it).toRational(1).first);
0076                 den = (double)((*it).toRational(1).second);
0077 
0078                 if (den == 0)
0079                     return false;
0080 
0081                 min = num/den;
0082 
0083                 if (min != -1.0)
0084                     *latitude = *latitude + min/60.0;
0085 
0086                 num = (double)((*it).toRational(2).first);
0087                 den = (double)((*it).toRational(2).second);
0088 
0089                 if (den == 0)
0090                 {
0091                     // be relaxed and accept 0/0 seconds. See #246077.
0092                     if (num == 0)
0093                         den = 1;
0094                     else
0095                         return false;
0096                 }
0097 
0098                 sec = num/den;
0099 
0100                 if (sec != -1.0)
0101                     *latitude = *latitude + sec/3600.0;
0102             }
0103             else
0104             {
0105                 return false;
0106             }
0107 
0108             if (latRef[0] == 'S')
0109                 *latitude *= -1.0;
0110 
0111             return true;
0112         }
0113     }
0114     catch( Exiv2::Error& e )
0115     {
0116         d->printExiv2ExceptionError(QString::fromLatin1("Cannot get GPS tag using Exiv2 "), e);
0117     }
0118     catch(...)
0119     {
0120         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0121     }
0122 
0123     return false;
0124 }
0125 
0126 bool KExiv2::getGPSLongitudeNumber(double* const longitude) const
0127 {
0128     try
0129     {
0130         *longitude=0.0;
0131 
0132         // Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
0133         if ( convertFromGPSCoordinateString(getXmpTagString("Xmp.exif.GPSLongitude"), longitude) )
0134             return true;
0135 
0136         // Now try to get the reference from Exif.
0137         const QByteArray lngRef = getExifTagData("Exif.GPSInfo.GPSLongitudeRef");
0138 
0139         if (!lngRef.isEmpty())
0140         {
0141             // Longitude decoding from Exif.
0142 
0143             Exiv2::ExifKey exifKey2("Exif.GPSInfo.GPSLongitude");
0144             Exiv2::ExifData exifData(d->exifMetadata());
0145             Exiv2::ExifData::iterator it = exifData.findKey(exifKey2);
0146 
0147             if (it != exifData.end() && (*it).count() == 3)
0148             {
0149                 /// @todo Decoding of latitude and longitude works in the same way,
0150                 ///       code here can be put in a separate function
0151                 double num, den;
0152 
0153                 num = (double)((*it).toRational(0).first);
0154                 den = (double)((*it).toRational(0).second);
0155 
0156                 if (den == 0)
0157                 {
0158                     return false;
0159                 }
0160 
0161                 *longitude = num/den;
0162 
0163                 num = (double)((*it).toRational(1).first);
0164                 den = (double)((*it).toRational(1).second);
0165 
0166                 if (den == 0)
0167                 {
0168                     return false;
0169                 }
0170 
0171                 const double min = num/den;
0172 
0173                 if (min != -1.0)
0174                 {
0175                     *longitude = *longitude + min/60.0;
0176                 }
0177 
0178                 num = (double)((*it).toRational(2).first);
0179                 den = (double)((*it).toRational(2).second);
0180 
0181                 if (den == 0)
0182                 {
0183                     // be relaxed and accept 0/0 seconds. See #246077.
0184                     if (num == 0)
0185                         den = 1;
0186                     else
0187                         return false;
0188                 }
0189 
0190                 const double sec = num/den;
0191 
0192                 if (sec != -1.0)
0193                 {
0194                     *longitude = *longitude + sec/3600.0;
0195                 }
0196             }
0197             else
0198             {
0199                 return false;
0200             }
0201 
0202             if (lngRef[0] == 'W')
0203             {
0204                 *longitude *= -1.0;
0205             }
0206 
0207             return true;
0208         }
0209     }
0210     catch( Exiv2::Error& e )
0211     {
0212         d->printExiv2ExceptionError(QString::fromLatin1("Cannot get GPS tag using Exiv2 "), e);
0213     }
0214     catch(...)
0215     {
0216         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0217     }
0218 
0219     return false;
0220 }
0221 
0222 bool KExiv2::getGPSAltitude(double* const altitude) const
0223 {
0224     try
0225     {
0226         double num, den;
0227         *altitude=0.0;
0228 
0229         // Try XMP first. Reason: XMP in sidecar may be more up-to-date than EXIF in original image.
0230         const QString altRefXmp = getXmpTagString("Xmp.exif.GPSAltitudeRef");
0231 
0232         if (!altRefXmp.isEmpty())
0233         {
0234             const QString altXmp = getXmpTagString("Xmp.exif.GPSAltitude");
0235 
0236             if (!altXmp.isEmpty())
0237             {
0238                 num = altXmp.section(QString::fromLatin1("/"), 0, 0).toDouble();
0239                 den = altXmp.section(QString::fromLatin1("/"), 1, 1).toDouble();
0240 
0241                 if (den == 0)
0242                     return false;
0243 
0244                 *altitude = num/den;
0245 
0246                 if (altRefXmp == QString::fromLatin1("1"))
0247                     *altitude *= -1.0;
0248 
0249                 return true;
0250             }
0251         }
0252 
0253         // Get the reference from Exif (above/below sea level)
0254         const QByteArray altRef = getExifTagData("Exif.GPSInfo.GPSAltitudeRef");
0255 
0256         if (!altRef.isEmpty())
0257         {
0258             // Altitude decoding from Exif.
0259 
0260             Exiv2::ExifKey exifKey3("Exif.GPSInfo.GPSAltitude");
0261             Exiv2::ExifData exifData(d->exifMetadata());
0262             Exiv2::ExifData::iterator it = exifData.findKey(exifKey3);
0263             if (it != exifData.end() && (*it).count())
0264             {
0265                 num = (double)((*it).toRational(0).first);
0266                 den = (double)((*it).toRational(0).second);
0267 
0268                 if (den == 0)
0269                     return false;
0270 
0271                 *altitude = num/den;
0272             }
0273             else
0274             {
0275                 return false;
0276             }
0277 
0278             if (altRef[0] == '1')
0279                 *altitude *= -1.0;
0280 
0281             return true;
0282         }
0283     }
0284     catch( Exiv2::Error& e )
0285     {
0286         d->printExiv2ExceptionError(QString::fromLatin1("Cannot get GPS tag using Exiv2 "), e);
0287     }
0288     catch(...)
0289     {
0290         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0291     }
0292 
0293     return false;
0294 }
0295 
0296 QString KExiv2::getGPSLatitudeString() const
0297 {
0298     double latitude;
0299 
0300     if (!getGPSLatitudeNumber(&latitude))
0301         return QString();
0302 
0303     return convertToGPSCoordinateString(true, latitude);
0304 }
0305 
0306 QString KExiv2::getGPSLongitudeString() const
0307 {
0308     double longitude;
0309 
0310     if (!getGPSLongitudeNumber(&longitude))
0311         return QString();
0312 
0313     return convertToGPSCoordinateString(false, longitude);
0314 }
0315 
0316 bool KExiv2::initializeGPSInfo(const bool setProgramName)
0317 {
0318     if (!setProgramId(setProgramName))
0319         return false;
0320 
0321     try
0322     {
0323         // TODO: what happens if these already exist?
0324 
0325         // Do all the easy constant ones first.
0326         // GPSVersionID tag: standard says is should be four bytes: 02 00 00 00
0327         // (and, must be present).
0328 #if EXIV2_TEST_VERSION(0,28,0)
0329         Exiv2::Value::UniquePtr value = Exiv2::Value::create(Exiv2::unsignedByte);
0330 #else
0331         Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::unsignedByte);
0332 #endif
0333         value->read("2 0 0 0");
0334         d->exifMetadata().add(Exiv2::ExifKey("Exif.GPSInfo.GPSVersionID"), value.get());
0335 
0336         // Datum: the datum of the measured data. If not given, we insert WGS-84.
0337         d->exifMetadata()["Exif.GPSInfo.GPSMapDatum"] = "WGS-84";
0338 
0339 #ifdef _XMP_SUPPORT_
0340         setXmpTagString("Xmp.exif.GPSVersionID", QString::fromLatin1("2.0.0.0"), false);
0341         setXmpTagString("Xmp.exif.GPSMapDatum",  QString::fromLatin1("WGS-84"),  false);
0342 #endif // _XMP_SUPPORT_
0343 
0344         return true;
0345     }
0346     catch( Exiv2::Error& e )
0347     {
0348         d->printExiv2ExceptionError(QString::fromLatin1("Cannot initialize GPS data using Exiv2 "), e);
0349     }
0350     catch(...)
0351     {
0352         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0353     }
0354 
0355     return false;
0356 }
0357 
0358 bool KExiv2::setGPSInfo(const double altitude, const double latitude, const double longitude, const bool setProgramName)
0359 {
0360     return setGPSInfo(&altitude, latitude, longitude, setProgramName);
0361 }
0362 
0363 bool KExiv2::setGPSInfo(const double* const altitude, const double latitude, const double longitude, const bool setProgramName)
0364 {
0365     if (!setProgramId(setProgramName))
0366         return false;
0367 
0368     try
0369     {
0370         // In first, we need to clean up all existing GPS info.
0371         removeGPSInfo();
0372 
0373         // now re-initialize the GPS info:
0374         if (!initializeGPSInfo(setProgramName))
0375             return false;
0376 
0377         char scratchBuf[100];
0378         long int nom, denom;
0379         long int deg, min;
0380 
0381         // Now start adding data.
0382 
0383         // ALTITUDE.
0384         if (altitude)
0385         {
0386             // Altitude reference: byte "00" meaning "above sea level", "01" mening "behing sea level".
0387 #if EXIV2_TEST_VERSION(0,28,0)
0388             Exiv2::Value::UniquePtr value = Exiv2::Value::create(Exiv2::unsignedByte);
0389 #else
0390             Exiv2::Value::AutoPtr value = Exiv2::Value::create(Exiv2::unsignedByte);
0391 #endif
0392 
0393             if ((*altitude) >= 0) value->read("0");
0394             else               value->read("1");
0395 
0396             d->exifMetadata().add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"), value.get());
0397 
0398             // And the actual altitude, as absolute value..
0399             convertToRational(fabs(*altitude), &nom, &denom, 4);
0400             snprintf(scratchBuf, 100, "%ld/%ld", nom, denom);
0401             d->exifMetadata()["Exif.GPSInfo.GPSAltitude"] = scratchBuf;
0402 
0403 #ifdef _XMP_SUPPORT_
0404             setXmpTagString("Xmp.exif.GPSAltitudeRef", ((*altitude) >= 0) ? QString::fromLatin1("0") : QString::fromLatin1("1"), false);
0405             setXmpTagString("Xmp.exif.GPSAltitude",    QString::fromLatin1(scratchBuf),                                          false);
0406 #endif // _XMP_SUPPORT_
0407         }
0408 
0409         // LATITUDE
0410         // Latitude reference:
0411         // latitude < 0 : "S"
0412         // latitude > 0 : "N"
0413         //
0414         d->exifMetadata()["Exif.GPSInfo.GPSLatitudeRef"] = (latitude < 0 ) ? "S" : "N";
0415 
0416         // Now the actual latitude itself.
0417         // This is done as three rationals.
0418         // I choose to do it as:
0419         //   dd/1 - degrees.
0420         //   mmmm/100 - minutes
0421         //   0/1 - seconds
0422         // Exif standard says you can do it with minutes
0423         // as mm/1 and then seconds as ss/1, but its
0424         // (slightly) more accurate to do it as
0425         //  mmmm/100 than to split it.
0426         // We also absolute the value (with fabs())
0427         // as the sign is encoded in LatRef.
0428         // Further note: original code did not translate between
0429         //   dd.dddddd to dd mm.mm - that's why we now multiply
0430         //   by 6000 - x60 to get minutes, x1000000 to get to mmmm/1000000.
0431         deg   = (int)floor(fabs(latitude)); // Slice off after decimal.
0432         min   = (int)floor((fabs(latitude) - floor(fabs(latitude))) * 60000000);
0433         snprintf(scratchBuf, 100, "%ld/1 %ld/1000000 0/1", deg, min);
0434         d->exifMetadata()["Exif.GPSInfo.GPSLatitude"] = scratchBuf;
0435 
0436 #ifdef _XMP_SUPPORT_
0437         /** @todo The XMP spec does not mention Xmp.exif.GPSLatitudeRef,
0438          * because the reference is included in Xmp.exif.GPSLatitude.
0439          * Is there a historic reason for writing it anyway?
0440          */
0441         setXmpTagString("Xmp.exif.GPSLatitudeRef", (latitude < 0) ? QString::fromLatin1("S") : QString::fromLatin1("N"), false);
0442         setXmpTagString("Xmp.exif.GPSLatitude",    convertToGPSCoordinateString(true, latitude),                         false);
0443 #endif // _XMP_SUPPORT_
0444 
0445         // LONGITUDE
0446         // Longitude reference:
0447         // longitude < 0 : "W"
0448         // longitude > 0 : "E"
0449         d->exifMetadata()["Exif.GPSInfo.GPSLongitudeRef"] = (longitude < 0 ) ? "W" : "E";
0450 
0451         // Now the actual longitude itself.
0452         // This is done as three rationals.
0453         // I choose to do it as:
0454         //   dd/1 - degrees.
0455         //   mmmm/100 - minutes
0456         //   0/1 - seconds
0457         // Exif standard says you can do it with minutes
0458         // as mm/1 and then seconds as ss/1, but its
0459         // (slightly) more accurate to do it as
0460         //  mmmm/100 than to split it.
0461         // We also absolute the value (with fabs())
0462         // as the sign is encoded in LongRef.
0463         // Further note: original code did not translate between
0464         //   dd.dddddd to dd mm.mm - that's why we now multiply
0465         //   by 6000 - x60 to get minutes, x1000000 to get to mmmm/1000000.
0466         deg   = (int)floor(fabs(longitude)); // Slice off after decimal.
0467         min   = (int)floor((fabs(longitude) - floor(fabs(longitude))) * 60000000);
0468         snprintf(scratchBuf, 100, "%ld/1 %ld/1000000 0/1", deg, min);
0469         d->exifMetadata()["Exif.GPSInfo.GPSLongitude"] = scratchBuf;
0470 
0471 #ifdef _XMP_SUPPORT_
0472         /** @todo The XMP spec does not mention Xmp.exif.GPSLongitudeRef,
0473          * because the reference is included in Xmp.exif.GPSLongitude.
0474          * Is there a historic reason for writing it anyway?
0475          */
0476         setXmpTagString("Xmp.exif.GPSLongitudeRef", (longitude < 0) ? QString::fromLatin1("W") : QString::fromLatin1("E"), false);
0477         setXmpTagString("Xmp.exif.GPSLongitude",    convertToGPSCoordinateString(false, longitude),                        false);
0478 #endif // _XMP_SUPPORT_
0479 
0480         return true;
0481     }
0482     catch( Exiv2::Error& e )
0483     {
0484         d->printExiv2ExceptionError(QString::fromLatin1("Cannot set Exif GPS tag using Exiv2 "), e);
0485     }
0486     catch(...)
0487     {
0488         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0489     }
0490 
0491     return false;
0492 }
0493 
0494 bool KExiv2::setGPSInfo(const double altitude, const QString& latitude, const QString& longitude, const bool setProgramName)
0495 {
0496     double longitudeValue, latitudeValue;
0497 
0498     if (!convertFromGPSCoordinateString(latitude, &latitudeValue))
0499         return false;
0500 
0501     if (!convertFromGPSCoordinateString(longitude, &longitudeValue))
0502         return false;
0503 
0504     return setGPSInfo(&altitude, latitudeValue, longitudeValue, setProgramName);
0505 }
0506 
0507 bool KExiv2::removeGPSInfo(const bool setProgramName)
0508 {
0509     if (!setProgramId(setProgramName))
0510         return false;
0511 
0512     try
0513     {
0514         QStringList gpsTagsKeys;
0515 
0516         for (Exiv2::ExifData::iterator it = d->exifMetadata().begin();
0517              it != d->exifMetadata().end(); ++it)
0518         {
0519             QString key = QString::fromLocal8Bit(it->key().c_str());
0520 
0521             if (key.section(QString::fromLatin1("."), 1, 1) == QString::fromLatin1("GPSInfo"))
0522                 gpsTagsKeys.append(key);
0523         }
0524 
0525         for(QStringList::const_iterator it2 = gpsTagsKeys.constBegin(); it2 != gpsTagsKeys.constEnd(); ++it2)
0526         {
0527             Exiv2::ExifKey gpsKey((*it2).toLatin1().constData());
0528             Exiv2::ExifData::iterator it3 = d->exifMetadata().findKey(gpsKey);
0529 
0530             if (it3 != d->exifMetadata().end())
0531                 d->exifMetadata().erase(it3);
0532         }
0533 
0534 #ifdef _XMP_SUPPORT_
0535         /** @todo The XMP spec does not mention Xmp.exif.GPSLongitudeRef,
0536          * and Xmp.exif.GPSLatitudeRef. But because we write them in setGPSInfo(),
0537          * we should also remove them here.
0538          */
0539         removeXmpTag("Xmp.exif.GPSLatitudeRef", false);
0540         removeXmpTag("Xmp.exif.GPSLongitudeRef", false);
0541         removeXmpTag("Xmp.exif.GPSVersionID", false);
0542         removeXmpTag("Xmp.exif.GPSLatitude", false);
0543         removeXmpTag("Xmp.exif.GPSLongitude", false);
0544         removeXmpTag("Xmp.exif.GPSAltitudeRef", false);
0545         removeXmpTag("Xmp.exif.GPSAltitude", false);
0546         removeXmpTag("Xmp.exif.GPSTimeStamp", false);
0547         removeXmpTag("Xmp.exif.GPSSatellites", false);
0548         removeXmpTag("Xmp.exif.GPSStatus", false);
0549         removeXmpTag("Xmp.exif.GPSMeasureMode", false);
0550         removeXmpTag("Xmp.exif.GPSDOP", false);
0551         removeXmpTag("Xmp.exif.GPSSpeedRef", false);
0552         removeXmpTag("Xmp.exif.GPSSpeed", false);
0553         removeXmpTag("Xmp.exif.GPSTrackRef", false);
0554         removeXmpTag("Xmp.exif.GPSTrack", false);
0555         removeXmpTag("Xmp.exif.GPSImgDirectionRef", false);
0556         removeXmpTag("Xmp.exif.GPSImgDirection", false);
0557         removeXmpTag("Xmp.exif.GPSMapDatum", false);
0558         removeXmpTag("Xmp.exif.GPSDestLatitude", false);
0559         removeXmpTag("Xmp.exif.GPSDestLongitude", false);
0560         removeXmpTag("Xmp.exif.GPSDestBearingRef", false);
0561         removeXmpTag("Xmp.exif.GPSDestBearing", false);
0562         removeXmpTag("Xmp.exif.GPSDestDistanceRef", false);
0563         removeXmpTag("Xmp.exif.GPSDestDistance", false);
0564         removeXmpTag("Xmp.exif.GPSProcessingMethod", false);
0565         removeXmpTag("Xmp.exif.GPSAreaInformation", false);
0566         removeXmpTag("Xmp.exif.GPSDifferential", false);
0567 #endif // _XMP_SUPPORT_
0568 
0569         return true;
0570     }
0571     catch( Exiv2::Error& e )
0572     {
0573         d->printExiv2ExceptionError(QString::fromLatin1("Cannot remove Exif GPS tag using Exiv2 "), e);
0574     }
0575     catch(...)
0576     {
0577         qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
0578     }
0579 
0580     return false;
0581 }
0582 
0583 void KExiv2::convertToRational(const double number, long int* const numerator, 
0584                                long int* const denominator, const int rounding)
0585 {
0586     // This function converts the given decimal number
0587     // to a rational (fractional) number.
0588     //
0589     // Examples in comments use Number as 25.12345, Rounding as 4.
0590 
0591     // Split up the number.
0592     double whole      = trunc(number);
0593     double fractional = number - whole;
0594 
0595     // Calculate the "number" used for rounding.
0596     // This is 10^Digits - ie, 4 places gives us 10000.
0597     double rounder = pow(10.0, rounding);
0598 
0599     // Round the fractional part, and leave the number
0600     // as greater than 1.
0601     // To do this we: (for example)
0602     //  0.12345 * 10000 = 1234.5
0603     //  floor(1234.5) = 1234 - now bigger than 1 - ready...
0604     fractional = round(fractional * rounder);
0605 
0606     // Convert the whole thing to a fraction.
0607     // Fraction is:
0608     //     (25 * 10000) + 1234   251234
0609     //     ------------------- = ------ = 25.1234
0610     //           10000            10000
0611     double numTemp = (whole * rounder) + fractional;
0612     double denTemp = rounder;
0613 
0614     // Now we should reduce until we can reduce no more.
0615 
0616     // Try simple reduction...
0617     // if   Num
0618     //     ----- = integer out then....
0619     //      Den
0620     if (trunc(numTemp / denTemp) == (numTemp / denTemp))
0621     {
0622         // Divide both by Denominator.
0623         numTemp /= denTemp;
0624         denTemp /= denTemp;
0625     }
0626 
0627     // And, if that fails, brute force it.
0628     while (1)
0629     {
0630         // Jump out if we can't integer divide one.
0631         if ((numTemp / 2) != trunc(numTemp / 2)) break;
0632         if ((denTemp / 2) != trunc(denTemp / 2)) break;
0633         // Otherwise, divide away.
0634         numTemp /= 2;
0635         denTemp /= 2;
0636     }
0637 
0638     // Copy out the numbers.
0639     *numerator   = (int)numTemp;
0640     *denominator = (int)denTemp;
0641 }
0642 
0643 void KExiv2::convertToRationalSmallDenominator(const double number, long int* const numerator, long int* const denominator)
0644 {
0645     // This function converts the given decimal number
0646     // to a rational (fractional) number.
0647     //
0648     // This method, in contrast to the method above, will retrieve the smallest possible
0649     // denominator. It is tested to retrieve the correct value for 1/x, with 0 < x <= 1000000.
0650     // Note: This requires double precision, storing in float breaks some numbers (49, 59, 86,...)
0651 
0652     // Split up the number.
0653     double whole      = trunc(number);
0654     double fractional = number - whole;
0655 
0656     /*
0657      * Find best rational approximation to a double
0658      * by C.B. Falconer, 2006-09-07. Released to public domain.
0659      *
0660      * Newsgroups: comp.lang.c, comp.programming
0661      * From: CBFalconer <cbfalconer@yahoo.com>
0662      * Date: Thu, 07 Sep 2006 17:35:30 -0400
0663      * Subject: Rational approximations
0664      */
0665     int      lastnum = 500; // this is _not_ the largest possible denominator
0666     long int num, approx, bestnum=0, bestdenom=1;
0667     double   value, error, leasterr, criterion;
0668 
0669     value = fractional;
0670 
0671     if (value == 0.0)
0672     {
0673         *numerator   = (long int)whole;
0674         *denominator = 1;
0675         return;
0676     }
0677 
0678     criterion = 2 * value * DBL_EPSILON;
0679 
0680     for (leasterr = value, num = 1; num < lastnum; ++num)
0681     {
0682         approx = (int)(num / value + 0.5);
0683         error  = fabs((double)num / approx - value);
0684 
0685         if (error < leasterr)
0686         {
0687             bestnum   = num;
0688             bestdenom = approx;
0689             leasterr  = error;
0690 
0691             if (leasterr <= criterion) break;
0692         }
0693     }
0694 
0695     // add whole number part
0696     if (bestdenom * whole > (double)INT_MAX)
0697     {
0698         // In some cases, we would generate an integer overflow.
0699         // Fall back to Gilles's code which is better suited for such numbers.
0700         convertToRational(number, numerator, denominator, 5);
0701     }
0702     else
0703     {
0704         bestnum      += bestdenom * (long int)whole;
0705         *numerator   =  bestnum;
0706         *denominator =  bestdenom;
0707     }
0708 }
0709 
0710 QString KExiv2::convertToGPSCoordinateString(const long int numeratorDegrees, const long int denominatorDegrees,
0711                                              const long int numeratorMinutes, const long int denominatorMinutes,
0712                                              const long int numeratorSeconds, long int denominatorSeconds,
0713                                              const char directionReference)
0714 {
0715     /**
0716      * Precision:
0717      * A second at sea level measures 30m for our purposes, a minute 1800m.
0718      * (for more details, see https://en.wikipedia.org/wiki/Geographic_coordinate_system)
0719      * This means with a decimal precision of 8 for minutes we get +/-0,018mm.
0720      * (if I calculated correctly)
0721      */
0722 
0723     QString coordinate;
0724 
0725     // be relaxed with seconds of 0/0
0726     if (denominatorSeconds == 0 && numeratorSeconds == 0)
0727         denominatorSeconds = 1;
0728 
0729     if (denominatorDegrees == 1 &&
0730         denominatorMinutes == 1 &&
0731         denominatorSeconds == 1)
0732     {
0733         // use form DDD,MM,SSk
0734         coordinate = QString::fromLatin1("%1,%2,%3%4");
0735         coordinate = coordinate.arg(numeratorDegrees).arg(numeratorMinutes).arg(numeratorSeconds).arg(directionReference);
0736     }
0737     else if (denominatorDegrees == 1   &&
0738              denominatorMinutes == 100 &&
0739              denominatorSeconds == 1)
0740     {
0741         // use form DDD,MM.mmk
0742         coordinate             = QString::fromLatin1("%1,%2%3");
0743         double minutes         = (double)numeratorMinutes / (double)denominatorMinutes;
0744         minutes               += (double)numeratorSeconds / 60.0;
0745         QString minutesString =  QString::number(minutes, 'f', 8);
0746 
0747         while (minutesString.endsWith(QString::fromLatin1("0")) && !minutesString.endsWith(QString::fromLatin1(".0")))
0748         {
0749             minutesString.chop(1);
0750         }
0751 
0752         coordinate = coordinate.arg(numeratorDegrees).arg(minutesString).arg(directionReference);
0753     }
0754     else if (denominatorDegrees == 0 ||
0755              denominatorMinutes == 0 ||
0756              denominatorSeconds == 0)
0757     {
0758         // Invalid. 1/0 is everything but 0. As is 0/0.
0759         return QString();
0760     }
0761     else
0762     {
0763         // use form DDD,MM.mmk
0764         coordinate             = QString::fromLatin1("%1,%2%3");
0765         double degrees         = (double)numeratorDegrees / (double)denominatorDegrees;
0766         double wholeDegrees    = trunc(degrees);
0767         double minutes         = (double)numeratorMinutes / (double)denominatorMinutes;
0768         minutes               += (degrees - wholeDegrees) * 60.0;
0769         minutes               += ((double)numeratorSeconds / (double)denominatorSeconds) / 60.0;
0770         QString minutesString  = QString::number(minutes, 'f', 8);
0771 
0772         while (minutesString.endsWith(QString::fromLatin1("0")) && !minutesString.endsWith(QString::fromLatin1(".0")))
0773         {
0774             minutesString.chop(1);
0775         }
0776 
0777         coordinate = coordinate.arg((int)wholeDegrees).arg(minutesString).arg(directionReference);
0778     }
0779 
0780     return coordinate;
0781 }
0782 
0783 QString KExiv2::convertToGPSCoordinateString(const bool isLatitude, double coordinate)
0784 {
0785     if (coordinate < -360.0 || coordinate > 360.0)
0786         return QString();
0787 
0788     QString coordinateString;
0789 
0790     char directionReference;
0791 
0792     if (isLatitude)
0793     {
0794         if (coordinate < 0)
0795             directionReference = 'S';
0796         else
0797             directionReference = 'N';
0798     }
0799     else
0800     {
0801         if (coordinate < 0)
0802             directionReference = 'W';
0803         else
0804             directionReference = 'E';
0805     }
0806 
0807     // remove sign
0808     coordinate     = fabs(coordinate);
0809 
0810     int degrees    = (int)floor(coordinate);
0811     // get fractional part
0812     coordinate     = coordinate - (double)(degrees);
0813     // To minutes
0814     double minutes = coordinate * 60.0;
0815 
0816     // use form DDD,MM.mmk
0817     coordinateString = QString::fromLatin1("%1,%2%3");
0818     coordinateString = coordinateString.arg(degrees);
0819     coordinateString = coordinateString.arg(minutes, 0, 'f', 8).arg(directionReference);
0820 
0821     return coordinateString;
0822 }
0823 
0824 bool KExiv2::convertFromGPSCoordinateString(const QString& gpsString,
0825                                             long int* const numeratorDegrees, long int* const denominatorDegrees,
0826                                             long int* const numeratorMinutes, long int* const denominatorMinutes,
0827                                             long int* const numeratorSeconds, long int* const denominatorSeconds,
0828                                             char* const directionReference)
0829 {
0830     if (gpsString.isEmpty())
0831         return false;
0832 
0833     *directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
0834     QString coordinate  = gpsString.left(gpsString.length() - 1);
0835     QStringList parts   = coordinate.split(QString::fromLatin1(","));
0836 
0837     if (parts.size() == 2)
0838     {
0839         // form DDD,MM.mmk
0840         *denominatorDegrees = 1;
0841         *denominatorMinutes = 1000000;
0842         *denominatorSeconds = 1;
0843 
0844         *numeratorDegrees   = parts[0].toLong();
0845 
0846         double minutes      = parts[1].toDouble();
0847         minutes            *= 1000000;
0848 
0849         *numeratorMinutes   = (long)round(minutes);
0850         *numeratorSeconds   = 0;
0851 
0852         return true;
0853     }
0854     else if (parts.size() == 3)
0855     {
0856         // use form DDD,MM,SSk
0857         *denominatorDegrees = 1;
0858         *denominatorMinutes = 1;
0859         *denominatorSeconds = 1;
0860 
0861         *numeratorDegrees   = parts[0].toLong();
0862         *numeratorMinutes   = parts[1].toLong();
0863         *numeratorSeconds   = parts[2].toLong();
0864 
0865         return true;
0866     }
0867     else
0868     {
0869         return false;
0870     }
0871 }
0872 
0873 bool KExiv2::convertFromGPSCoordinateString(const QString& gpsString, double* const degrees)
0874 {
0875     if (gpsString.isEmpty())
0876         return false;
0877 
0878     char directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
0879     QString coordinate      = gpsString.left(gpsString.length() - 1);
0880     QStringList parts       = coordinate.split(QString::fromLatin1(","));
0881 
0882     if (parts.size() == 2)
0883     {
0884         // form DDD,MM.mmk
0885         *degrees =  parts[0].toLong();
0886         *degrees += parts[1].toDouble() / 60.0;
0887 
0888         if (directionReference == 'W' || directionReference == 'S')
0889             *degrees *= -1.0;
0890 
0891         return true;
0892     }
0893     else if (parts.size() == 3)
0894     {
0895         // use form DDD,MM,SSk
0896 
0897         *degrees =  parts[0].toLong();
0898         *degrees += parts[1].toLong() / 60.0;
0899         *degrees += parts[2].toLong() / 3600.0;
0900 
0901         if (directionReference == 'W' || directionReference == 'S')
0902             *degrees *= -1.0;
0903 
0904         return true;
0905     }
0906     else
0907     {
0908         return false;
0909     }
0910 }
0911 
0912 bool KExiv2::convertToUserPresentableNumbers(const QString& gpsString,
0913                                              int* const degrees, int* const minutes,
0914                                              double* const seconds, char* const directionReference)
0915 {
0916     if (gpsString.isEmpty())
0917         return false;
0918 
0919     *directionReference = gpsString.at(gpsString.length() - 1).toUpper().toLatin1();
0920     QString coordinate  = gpsString.left(gpsString.length() - 1);
0921     QStringList parts   = coordinate.split(QString::fromLatin1(","));
0922 
0923     if (parts.size() == 2)
0924     {
0925         // form DDD,MM.mmk
0926         *degrees                 = parts[0].toInt();
0927         double fractionalMinutes = parts[1].toDouble();
0928         *minutes                 = (int)trunc(fractionalMinutes);
0929         *seconds                 = (fractionalMinutes - (double)(*minutes)) * 60.0;
0930 
0931         return true;
0932     }
0933     else if (parts.size() == 3)
0934     {
0935         // use form DDD,MM,SSk
0936         *degrees = parts[0].toInt();
0937         *minutes = parts[1].toInt();
0938         *seconds = (double)parts[2].toInt();
0939 
0940         return true;
0941     }
0942     else
0943     {
0944         return false;
0945     }
0946 }
0947 
0948 void KExiv2::convertToUserPresentableNumbers(const bool isLatitude, double coordinate,
0949                                              int* const degrees, int* const minutes,
0950                                              double* const seconds, char* const directionReference)
0951 {
0952     if (isLatitude)
0953     {
0954         if (coordinate < 0)
0955             *directionReference = 'S';
0956         else
0957             *directionReference = 'N';
0958     }
0959     else
0960     {
0961         if (coordinate < 0)
0962             *directionReference = 'W';
0963         else
0964             *directionReference = 'E';
0965     }
0966 
0967     // remove sign
0968     coordinate  = fabs(coordinate);
0969     *degrees    = (int)floor(coordinate);
0970     // get fractional part
0971     coordinate  = coordinate - (double)(*degrees);
0972     // To minutes
0973     coordinate *= 60.0;
0974     *minutes    = (int)floor(coordinate);
0975     // get fractional part
0976     coordinate  = coordinate - (double)(*minutes);
0977     // To seconds
0978     coordinate *= 60.0;
0979     *seconds    = coordinate;
0980 }
0981 
0982 }  // NameSpace KExiv2Iface