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