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-2022 by Joerg Lohse <joergmlpts at gmail dot com> 0010 * SPDX-FileCopyrightText: 2005-2009 by Vardhman Jain <vardhman at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * SPDX-FileCopyrightText: 2017-2019 by Maik Qualmann <metzpinguin at gmail dot com> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #ifndef DIGIKAM_INAT_TALKER_H 0019 #define DIGIKAM_INAT_TALKER_H 0020 0021 // Qt includes 0022 0023 #include <QUrl> 0024 #include <QList> 0025 #include <QPair> 0026 #include <QString> 0027 #include <QObject> 0028 #include <QNetworkReply> 0029 #include <QNetworkCookie> 0030 0031 // Local includes 0032 0033 #include "dinfointerface.h" 0034 #include "ditemslist.h" 0035 #include "inattaxon.h" 0036 #include "inatscore.h" 0037 0038 class QProgressDialog; 0039 0040 using namespace Digikam; 0041 0042 namespace DigikamGenericINatPlugin 0043 { 0044 0045 class INatPhotoInfo; 0046 class INatPhotoSet; 0047 0048 class AuthRequest; 0049 class ApiTokenRequest; 0050 0051 typedef QPair<QString, QList<Taxon> > AutoCompletions; 0052 0053 class INatTalker : public QObject 0054 { 0055 Q_OBJECT 0056 0057 public: 0058 0059 /** 0060 * This struct contains all the data needed for photo uploads. 0061 */ 0062 struct PhotoUploadRequest 0063 { 0064 PhotoUploadRequest() 0065 : m_observationId(-1), 0066 m_totalImages (0), 0067 m_updateIds (false), 0068 m_rescale (false), 0069 m_maxDim (-1), 0070 m_quality (-1) 0071 { 0072 } 0073 0074 PhotoUploadRequest(const QList<QUrl>& imgs, 0075 bool updId, 0076 bool resize, 0077 int mxDim, 0078 int q, const QString& userName) 0079 : m_observationId(-1), 0080 m_totalImages (imgs.count()), 0081 m_images (imgs), 0082 m_user (userName), 0083 m_updateIds (updId), 0084 m_rescale (resize), 0085 m_maxDim (mxDim), 0086 m_quality (q) 0087 { 0088 } 0089 0090 int m_observationId; 0091 int m_totalImages; 0092 QList<QUrl> m_images; 0093 QString m_apiKey; 0094 QString m_user; 0095 bool m_updateIds; 0096 bool m_rescale; 0097 int m_maxDim; 0098 int m_quality; 0099 }; 0100 0101 /** 0102 * This struct is sent after each photo has been uploaded. 0103 */ 0104 struct PhotoUploadResult 0105 { 0106 PhotoUploadResult() 0107 : m_observationPhotoId(-1), 0108 m_photoId (-1) 0109 { 0110 } 0111 0112 PhotoUploadResult(const PhotoUploadRequest& req, int obsPhId, int phId) 0113 : m_request (req), 0114 m_observationPhotoId(obsPhId), 0115 m_photoId (phId) 0116 { 0117 } 0118 0119 PhotoUploadRequest m_request; 0120 int m_observationPhotoId; 0121 int m_photoId; 0122 }; 0123 0124 struct NearbyObservation 0125 { 0126 0127 NearbyObservation() 0128 : m_observationId (-1), 0129 m_latitude (0.0), 0130 m_longitude (0.0), 0131 m_distanceMeters (-1.0), 0132 m_obscured (false), 0133 m_referenceTaxon (0), 0134 m_referenceLatitude (0.0), 0135 m_referenceLongitude(0.0) 0136 { 0137 } 0138 0139 NearbyObservation(int id, 0140 double latitude, 0141 double longitude, 0142 double distanceMeters, 0143 bool obscured, 0144 uint taxon, 0145 double referenceLatitude, 0146 double referenceLongitude) 0147 : m_observationId (id), 0148 m_latitude (latitude), 0149 m_longitude (longitude), 0150 m_distanceMeters (distanceMeters), 0151 m_obscured (obscured), 0152 m_referenceTaxon (taxon), 0153 m_referenceLatitude (referenceLatitude), 0154 m_referenceLongitude (referenceLongitude) 0155 { 0156 } 0157 0158 void updateObservation(int id, 0159 double latitude, 0160 double longitude, 0161 double distanceMeters) 0162 { 0163 m_observationId = id; 0164 m_latitude = latitude; 0165 m_longitude = longitude; 0166 m_distanceMeters = distanceMeters; 0167 } 0168 0169 bool isValid() const 0170 { 0171 return (m_observationId != -1); 0172 } 0173 0174 int m_observationId; 0175 double m_latitude; 0176 double m_longitude; 0177 double m_distanceMeters; 0178 bool m_obscured; 0179 uint m_referenceTaxon; 0180 double m_referenceLatitude; 0181 double m_referenceLongitude; 0182 }; 0183 0184 INatTalker(QWidget* const parent, 0185 const QString& serviceName, 0186 DInfoInterface* const iface); 0187 ~INatTalker() override; 0188 0189 void unLink(); 0190 void cancel(); 0191 void removeUserName(const QString& userName); 0192 0193 /** 0194 * Are we still uploading observations or photos? 0195 */ 0196 bool stillUploading() const; 0197 0198 /** 0199 * Returns -1 if there is no valid token; number of seconds otherwise. 0200 */ 0201 int apiTokenExpiresIn() const; 0202 0203 /** 0204 * Try to restore a valid API token; they are good for 24 hours. 0205 */ 0206 bool restoreApiToken(const QString& username, 0207 QList<QNetworkCookie>&, 0208 bool emitSignal); 0209 0210 /** 0211 * Download an image from iNaturalist servers. 0212 */ 0213 void loadUrl(const QUrl& url, int retries = 0); 0214 0215 /** 0216 * Obtain auto completions for taxa from iNaturalist servers. 0217 */ 0218 void taxonAutoCompletions(const QString& partialName); 0219 0220 /** 0221 * Retrieve login, name, and icon of current user. 0222 */ 0223 void userInfo(const QList<QNetworkCookie>&); 0224 0225 /** 0226 * Get list of nearby places. 0227 */ 0228 void nearbyPlaces(double latitude, double longitude); 0229 0230 /** 0231 * Get closest nearby observation. 0232 */ 0233 void closestObservation(uint taxon, double latitude, double longitude, 0234 double radius_km = 10.0, 0235 const QString& origQuery = QString()); 0236 0237 /** 0238 * Identify taxa from a given photo. 0239 */ 0240 void computerVision(const QUrl& localImage); 0241 0242 /** 0243 * Create an iNaturalist observation; photo uploads follow. 0244 */ 0245 void createObservation(const QByteArray&, const PhotoUploadRequest&); 0246 0247 /** 0248 * Check whether an iNaturalist observation has been created. Called 0249 * upon timeout in createObservation(). 0250 */ 0251 void verifyCreateObservation(const QByteArray&, 0252 const PhotoUploadRequest&, 0253 int page, int retries); 0254 0255 /** 0256 * Delete an observation; called when canceling uploads. 0257 */ 0258 void deleteObservation(int id, const QString& apiKey, int retries = 0); 0259 0260 /** 0261 * Upload another photo to previously created observation. 0262 */ 0263 void uploadNextPhoto(const PhotoUploadRequest&); 0264 0265 /** 0266 * Check whether an observation photo has been uploaded. Called 0267 * upon timeout in uploadNextPhoto(). 0268 */ 0269 void verifyUploadNextPhoto(const PhotoUploadRequest&, int retries); 0270 0271 public: 0272 0273 QProgressDialog* m_authProgressDlg; 0274 0275 Q_SIGNALS: 0276 0277 void signalBusy(bool val); 0278 0279 void signalLoadUrlSucceeded(const QUrl&, const QByteArray&); 0280 void signalTaxonAutoCompletions(const AutoCompletions&); 0281 void signalNearbyObservation(const INatTalker::NearbyObservation&); 0282 void signalComputerVisionResults(const ImageScores&); 0283 void signalNearbyPlaces(const QStringList&); 0284 void signalObservationCreated(const INatTalker::PhotoUploadRequest&); 0285 void signalObservationDeleted(int id); 0286 void signalPhotoUploaded(const INatTalker::PhotoUploadResult&); 0287 0288 void signalLinkingSucceeded(const QString& login, 0289 const QString& name, const QUrl& iconUrl); 0290 void signalLinkingFailed(const QString& error); 0291 0292 private: 0293 0294 QString tmpFileName(const QString& path); 0295 0296 private Q_SLOTS: 0297 0298 void slotApiToken(const QString&, const QList<QNetworkCookie>&); 0299 void slotFinished(QNetworkReply* reply); 0300 void slotTimeout(); 0301 0302 private: 0303 0304 class Private; 0305 Private* const d; 0306 0307 friend class UserRequest; 0308 friend class LoadUrlRequest; 0309 friend class AutoCompletionRequest; 0310 friend class NearbyPlacesRequest; 0311 friend class NearbyObservationRequest; 0312 friend class ComputerVisionRequest; 0313 }; 0314 0315 } // namespace DigikamGenericINatPlugin 0316 0317 #endif // DIGIKAM_INAT_TALKER_H