File indexing completed on 2025-01-19 03:53:04

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2021-03-20
0007  * Description : a tool to export images to iNaturalist web service
0008  *
0009  * SPDX-FileCopyrightText: 2021      by Joerg Lohse <joergmlpts at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "inatutils.h"
0016 
0017 // Qt includes
0018 
0019 #include <QHttpPart>
0020 #include <QFile>
0021 #include <QFileInfo>
0022 #include <QtMath>
0023 
0024 // KDE includes
0025 
0026 #include <klocalizedstring.h>
0027 
0028 // Local includes
0029 
0030 #include "digikam_debug.h"
0031 
0032 namespace DigikamGenericINatPlugin
0033 {
0034 
0035 // Constants
0036 
0037 const QLocale locale      = QLocale();
0038 const bool    isEnglish   = (locale.language() == QLocale::English)     ||
0039                             (locale.language() == QLocale::C)           ||
0040                             (locale.language() == QLocale::AnyLanguage);
0041 
0042 const double meterInFeet  = 3.28084;
0043 const double meterInMiles = 0.00062137;
0044 
0045 // Functions
0046 
0047 QHttpMultiPart* getMultiPart(const QList<Parameter>& parameters,
0048                              const QString& imageName,
0049                              const QString& imageNameArg,
0050                              const QString& imagePath)
0051 {
0052     static const QString paramForm = QLatin1String("form-data; name=\"%1\"");
0053     static const QString imageForm = QLatin1String("form-data; name=\"%1\"; "
0054                                                    "filename=\"%2\"");
0055 
0056     QHttpMultiPart* const result   = new QHttpMultiPart(QHttpMultiPart::FormDataType);
0057 
0058     for (const auto& param : parameters)
0059     {
0060         QHttpPart part;
0061         part.setHeader(QNetworkRequest::ContentDispositionHeader,
0062                        QVariant(paramForm.arg(param.first)));
0063         part.setBody(param.second.toUtf8());
0064         result->append(part);
0065     }
0066 
0067     QHttpPart imagePart;
0068     QFileInfo fileInfo(imagePath);
0069     imagePart.setHeader(QNetworkRequest::ContentTypeHeader,
0070                         QVariant(QString(QLatin1String("image/%1")).
0071                                  arg(fileInfo.suffix().toLower())));
0072     imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
0073                         QVariant(imageForm.arg(imageName, imageNameArg)));
0074     QFile* const file = new QFile(imagePath);
0075 
0076     if (!file->open(QIODevice::ReadOnly))
0077     {
0078         qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Cannot open file to read" << imagePath;
0079     }
0080 
0081     imagePart.setBodyDevice(file);
0082     file->setParent(result);
0083     result->append(imagePart);
0084 
0085     return result;
0086 }
0087 
0088 /**
0089  * converts degree to radian
0090  */
0091 static inline double deg2rad(double deg)
0092 {
0093     return (deg * M_PI / 180);
0094 }
0095 
0096 /**
0097  * returns distance in meters between two coordinates, Haversine formula
0098  */
0099 double distanceBetween(double latitude1, double longitude1,
0100                        double latitude2, double longitude2)
0101 {
0102     static const double earthRadiusInMeters = 6.371e6;
0103 
0104     double lat1r = deg2rad(latitude1);
0105     double lon1r = deg2rad(longitude1);
0106     double lat2r = deg2rad(latitude2);
0107     double lon2r = deg2rad(longitude2);
0108     double u     = sin((lat2r - lat1r) / 2);
0109     double v     = sin((lon2r - lon1r) / 2);
0110 
0111     return (2.0 * earthRadiusInMeters *
0112             asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v)));
0113 }
0114 
0115 QString localizedTaxonomicRank(const QString& rank)
0116 {
0117     if      (rank == QLatin1String("kingdom"))
0118     {
0119         return i18nc("taxonomic rank", "kingdom");
0120     }
0121     else if (rank == QLatin1String("subkingdom"))
0122     {
0123         return i18nc("taxonomic rank", "subkingdom");
0124     }
0125     else if (rank == QLatin1String("phylum"))
0126     {
0127         return i18nc("taxonomic rank", "phylum");
0128     }
0129     else if (rank == QLatin1String("subphylum"))
0130     {
0131         return i18nc("taxonomic rank", "subphylum");
0132     }
0133     else if (rank == QLatin1String("superorder"))
0134     {
0135         return i18nc("taxonomic rank", "superorder");
0136     }
0137     else if (rank == QLatin1String("order"))
0138     {
0139         return i18nc("taxonomic rank", "order");
0140     }
0141     else if (rank == QLatin1String("suborder"))
0142     {
0143         return i18nc("taxonomic rank", "suborder");
0144     }
0145     else if (rank == QLatin1String("infraorder"))
0146     {
0147         return i18nc("taxonomic rank", "infraorder");
0148     }
0149     else if (rank == QLatin1String("parvorder"))
0150     {
0151         return i18nc("taxonomic rank", "parvorder");
0152     }
0153     else if (rank == QLatin1String("zoosection"))
0154     {
0155         return i18nc("taxonomic rank", "zoosection");
0156     }
0157     else if (rank == QLatin1String("zoosubsection"))
0158     {
0159         return i18nc("taxonomic rank", "zoosubsection");
0160     }
0161     else if (rank == QLatin1String("superfamily"))
0162     {
0163         return i18nc("taxonomic rank", "superfamily");
0164     }
0165     else if (rank == QLatin1String("epifamily"))
0166     {
0167         return i18nc("taxonomic rank", "epifamily");
0168     }
0169     else if (rank == QLatin1String("family"))
0170     {
0171         return i18nc("taxonomic rank", "family");
0172     }
0173     else if (rank == QLatin1String("subfamily"))
0174     {
0175         return i18nc("taxonomic rank", "subfamily");
0176     }
0177     else if (rank == QLatin1String("supertribe"))
0178     {
0179         return i18nc("taxonomic rank", "supertribe");
0180     }
0181     else if (rank == QLatin1String("tribe"))
0182     {
0183         return i18nc("taxonomic rank", "tribe");
0184     }
0185     else if (rank == QLatin1String("subtribe"))
0186     {
0187         return i18nc("taxonomic rank", "subtribe");
0188     }
0189     else if (rank == QLatin1String("genus"))
0190     {
0191         return i18nc("taxonomic rank", "genus");
0192     }
0193     else if (rank == QLatin1String("genushybrid"))
0194     {
0195         return i18nc("taxonomic rank", "genushybrid");
0196     }
0197     else if (rank == QLatin1String("subgenus"))
0198     {
0199         return i18nc("taxonomic rank", "subgenus");
0200     }
0201     else if (rank == QLatin1String("section"))
0202     {
0203         return i18nc("taxonomic rank", "section");
0204     }
0205     else if (rank == QLatin1String("subsection"))
0206     {
0207         return i18nc("taxonomic rank", "subsection");
0208     }
0209     else if (rank == QLatin1String("complex"))
0210     {
0211         return i18nc("taxonomic rank", "complex");
0212     }
0213     else if (rank == QLatin1String("species"))
0214     {
0215         return i18nc("taxonomic rank", "species");
0216     }
0217     else if (rank == QLatin1String("hybrid"))
0218     {
0219         return i18nc("taxonomic rank", "hybrid");
0220     }
0221     else if (rank == QLatin1String("subspecies"))
0222     {
0223         return i18nc("taxonomic rank", "subspecies");
0224     }
0225     else if (rank == QLatin1String("variety"))
0226     {
0227         return i18nc("taxonomic rank", "variety");
0228     }
0229     else if (rank == QLatin1String("form"))
0230     {
0231         return i18nc("taxonomic rank", "form");
0232     }
0233     else if (rank == QLatin1String("infrahybrid"))
0234     {
0235         return i18nc("taxonomic rank", "infrahybrid");
0236     }
0237     else
0238     {
0239         return rank;
0240     }
0241 }
0242 
0243 QString localizedLocation(double latitude, double longitude, int precision)
0244 {
0245     return (locale.toString(latitude, 'f', precision) +
0246             QLatin1String(", ")                       +
0247             locale.toString(longitude, 'f', precision));
0248 }
0249 
0250 QString localizedDistance(double distMeters, char format, int precision)
0251 {
0252     if (locale.measurementSystem() == QLocale::ImperialUSSystem)
0253     {
0254         if (locale.toString(distMeters * meterInMiles, format, precision) ==
0255             locale.toString(0.0, format, precision))
0256         {
0257             return locale.toString(distMeters * meterInFeet, format, precision) +
0258                    QLatin1String(" ft");
0259         }
0260         else
0261         {
0262             return locale.toString(distMeters * meterInMiles, format,
0263                                    precision) + QLatin1String(" mi");
0264         }
0265     }
0266     else
0267     {
0268         if (distMeters >= 1000.0)
0269         {
0270             return locale.toString(distMeters / 1000.0, format, precision) +
0271                    QLatin1String(" km");
0272         }
0273         else
0274         {
0275             QString one    = locale.toString(1.0, format, precision);
0276             QString result = locale.toString(distMeters, format, precision);
0277 
0278             return (result + QLatin1Char(' ') + ((result == one)
0279                                                  ? i18nc("distance", "meter")
0280                                                  : i18nc("distance", "meters")));
0281         }
0282     }
0283 }
0284 
0285 QString localizedTimeDifference(quint64 diffSeconds)
0286 {
0287     QString result;
0288 
0289     quint64 hours = diffSeconds / 3600;
0290 
0291     if (hours)
0292     {
0293         diffSeconds %= 3600;
0294         result       = QString::number(hours) + QLatin1Char(' ') +
0295                        (hours == 1 ? i18nc("time", "hour")
0296                                    : i18nc("time", "hours"));
0297     }
0298 
0299     quint64 minutes = diffSeconds / 60;
0300 
0301     if (minutes)
0302     {
0303         diffSeconds %= 60;
0304 
0305         if (!result.isEmpty())
0306         {
0307             result += QLatin1String(", ");
0308         }
0309 
0310         result += QString::number(minutes) + QLatin1Char(' ') +
0311                   (minutes == 1 ? i18nc("time", "minute")
0312                                 : i18nc("time", "minutes"));
0313     }
0314 
0315     if (diffSeconds || result.isEmpty())
0316     {
0317         if (!result.isEmpty())
0318         {
0319             result += QLatin1String(", ");
0320         }
0321 
0322         result += QString::number(diffSeconds) + QLatin1Char(' ') +
0323                   (diffSeconds == 1 ? i18nc("time", "second")
0324                                     : i18nc("time", "seconds"));
0325     }
0326 
0327     return result;
0328 }
0329 
0330 } // namespace DigikamGenericINatPlugin