1 /*************************************************************************** 2 qgsbacckgroundcachedshareddata.h 3 --------------------- 4 begin : October 2019 5 copyright : (C) 2016-2019 by Even Rouault 6 email : even.rouault at spatialys.com 7 *************************************************************************** 8 * * 9 * This program is free software; you can redistribute it and/or modify * 10 * it under the terms of the GNU General Public License as published by * 11 * the Free Software Foundation; either version 2 of the License, or * 12 * (at your option) any later version. * 13 * * 14 ***************************************************************************/ 15 16 #ifndef QGSBACKGROUNDCACHEDSHAREDDATA_H 17 #define QGSBACKGROUNDCACHEDSHAREDDATA_H 18 19 #include "qgsbackgroundcachedfeatureiterator.h" 20 #include "qgsrectangle.h" 21 #include "qgsspatialiteutils.h" 22 #include "qgscachedirectorymanager.h" 23 24 #include <QSet> 25 26 #include <map> 27 28 class QgsBackgroundCachedFeatureIterator; 29 class QgsFeatureDownloader; 30 class QgsFeatureDownloaderImpl; 31 class QgsThreadedFeatureDownloader; 32 33 /** 34 * This class holds data, and logic, shared between the provider, QgsBackgroundCachedFeatureIterator 35 * and QgsFeatureDownloader. It manages the on-disk cache, as a SpatiaLite 36 * database. 37 * 38 * The structure of the table in the database is the following one : 39 * 40 * - attribute fields of the DescribeFeatureType response 41 * - __qgis_gen_counter: generation counter 42 * - __qgis_unique_id: typically feature 'fid' or 'gml:id' 43 * - __qgis_hexwkb_geom: feature geometry as a hexadecimal encoded WKB string. 44 * - geometry: polygon with the bounding box of the geometry. 45 * 46 * The generation counter is a synchronization mechanism between the iterator 47 * that will try to return cached features first and then downloaded features. 48 * It avoids the iterator to return features in duplicates, by returning features 49 * that have just been serialized by the live downloader and notified to the 50 * iterator. 51 * 52 * The reason for not storing directly the geometry is that we may potentially 53 * store in the future non-linear geometries that aren't handled by SpatiaLite. 54 * 55 * It contains also methods used in WFS-T context to update the cache content, 56 * from the changes initiated by the user. 57 */ 58 class QgsBackgroundCachedSharedData 59 { 60 public: 61 QgsBackgroundCachedSharedData( const QString &providerName, const QString &componentTranslated ); 62 virtual ~QgsBackgroundCachedSharedData(); 63 64 //////////// Methods to be used by provider 65 66 //! Returns extent computed from currently downloaded features 67 const QgsRectangle &computedExtent() const; 68 69 //! Returns whether the feature download is finished downloadFinished()70 bool downloadFinished() const { return mDownloadFinished; } 71 72 //! Returns layer feature count. Might issue a network request if issueRequestIfNeeded == true 73 long long getFeatureCount( bool issueRequestIfNeeded = true ); 74 75 //! Return a "consolidated" extent mixing the one from the capabilities from the one of the features already downloaded. 76 QgsRectangle consolidatedExtent() const; 77 78 /** 79 * Used by provider's reloadData(). The effect is to invalid 80 * all the caching state, so that a new request results in fresh download. 81 */ 82 void invalidateCache(); 83 84 //! Give a feature id, find the correspond fid/gml.id. Used for edition. 85 QString findUniqueId( QgsFeatureId fid ) const; 86 87 //! Delete from the on-disk cache the features of given fid. Used byedition. 88 bool deleteFeatures( const QgsFeatureIds &fidlist ); 89 90 //! Change into the on-disk cache the passed geometries. Used by edition. 91 bool changeGeometryValues( const QgsGeometryMap &geometry_map ); 92 93 //! Change into the on-disk cache the passed attributes. Used by edition. 94 bool changeAttributeValues( const QgsChangedAttributesMap &attr_map ); 95 96 //////////// Methods to be used by feature iterator & downloader 97 98 //// Getters 99 100 //! Retrieve the dbId from the qgisId 101 QgsFeatureIds dbIdsFromQgisIds( const QgsFeatureIds &qgisIds ) const; 102 103 //! Returns maximum number of features to download, or 0 if unlimited requestLimit()104 int requestLimit() const { return mRequestLimit; } 105 106 //! Returns whether the feature count is exact, or approximate/transient isFeatureCountExact()107 bool isFeatureCountExact() const { return mFeatureCountExact; } 108 109 //! Return source/provider CRS sourceCrs()110 const QgsCoordinateReferenceSystem &sourceCrs() const { return mSourceCrs; } 111 112 //! Return the provider for the Spatialite cache. cacheDataProvider()113 QgsVectorDataProvider *cacheDataProvider() { return mCacheDataProvider.get(); } 114 115 //! Return provider fields. fields()116 const QgsFields &fields() const { return mFields; } 117 118 //! Return the spatialite field name from the user visible column name 119 QString getSpatialiteFieldNameFromUserVisibleName( const QString &columnName ) const; 120 121 //! Return the user visible FeatureID from the spatialite FeatureID 122 bool getUserVisibleIdFromSpatialiteId( QgsFeatureId dbId, QgsFeatureId &outId ) const; 123 124 //! Return current BBOX used by the downloader currentRect()125 const QgsRectangle ¤tRect() const { return mRect; } 126 127 //! Returns a unique identifier made from feature content 128 static QString getMD5( const QgsFeature &f ); 129 130 //! Filter expression to apply on client side clientSideFilterExpression()131 const QString &clientSideFilterExpression() const { return mClientSideFilterExpression; } 132 133 //// Actions 134 135 /** 136 * Used by a QgsBackgroundCachedFeatureIterator to start a downloader and get the 137 * generation counter. 138 */ 139 int registerToCache( QgsBackgroundCachedFeatureIterator *iterator, int limit, const QgsRectangle &rect = QgsRectangle() ); 140 141 /** 142 * Used by the rewind() method of an iterator so as to get the up-to-date 143 * generation counter. 144 */ 145 int getUpdatedCounter(); 146 147 /** 148 * Used by the background downloader to serialize downloaded features into 149 * the cache. Also used by a insert operation. 150 */ 151 void serializeFeatures( QVector<QgsFeatureUniqueIdPair> &featureList ); 152 153 //! Called by QgsFeatureDownloader::run() at the end of the download process. 154 void endOfDownload( bool success, long long featureCount, bool truncatedResponse, bool interrupted, const QString &errorMsg ); 155 156 //! Force an update of the feature count 157 void setFeatureCount( long long featureCount, bool featureCountExact ); 158 159 //! Returns the name of temporary directory. To be paired with releaseCacheDirectory() 160 QString acquireCacheDirectory(); 161 162 //! To be called when a temporary file is removed from the directory 163 void releaseCacheDirectory(); 164 165 //! Set whether the progress dialog should be hidden setHideProgressDialog(bool b)166 void setHideProgressDialog( bool b ) { mHideProgressDialog = b; } 167 168 //////// Pure virtual methods 169 170 //! Instantiate a new feature downloader implementation. 171 virtual std::unique_ptr<QgsFeatureDownloaderImpl> newFeatureDownloaderImpl( QgsFeatureDownloader *, bool requestMadeFromMainThread ) = 0; 172 173 //! Return whether the GetFeature request should include the request bounding box. 174 virtual bool isRestrictedToRequestBBOX() const = 0; 175 176 //! Return whether the layer has a geometry field 177 virtual bool hasGeometry() const = 0; 178 179 //! Return layer name 180 virtual QString layerName() const = 0; 181 182 //! Called when an error must be raised to the provider 183 virtual void pushError( const QString &errorMsg ) const = 0; 184 185 protected: 186 187 //////////// Input members. Implementations should define them to meaningful values 188 189 //! Attribute fields of the layer 190 QgsFields mFields; 191 192 //! Source CRS 193 QgsCoordinateReferenceSystem mSourceCrs; 194 195 //! SELECT DISTINCT 196 bool mDistinctSelect = false; 197 198 //! Filter expression to apply on client side 199 QString mClientSideFilterExpression; 200 201 //! Server-side or user-side limit of downloaded features (including with paging). Valid if > 0 202 long long mMaxFeatures = 0; 203 204 //! Server-side limit of downloaded features (including with paging). Valid if > 0 205 long long mServerMaxFeatures = 0; 206 207 //! Bounding box for the layer as returned by GetCapabilities 208 QgsRectangle mCapabilityExtent; 209 210 //! Extent computed from downloaded features 211 QgsRectangle mComputedExtent; 212 213 //! Flag is a /items request returns a numberMatched property 214 bool mHasNumberMatched = false; 215 216 //! Whether progress dialog should be hidden 217 bool mHideProgressDialog = false; 218 219 //////////// Methods 220 221 //! Should be called in the destructor of the implementation of this class ! 222 void cleanup(); 223 224 //! Returns true if it is likely that the server doesn't properly honor axis order. detectPotentialServerAxisOrderIssueFromSingleFeatureExtent()225 virtual bool detectPotentialServerAxisOrderIssueFromSingleFeatureExtent() const { return false; } 226 227 private: 228 229 //! Cache directory manager 230 QgsCacheDirectoryManager &mCacheDirectoryManager; 231 232 //! Main mutex to protect most data members that can be modified concurrently 233 mutable QMutex mMutex; 234 235 //! Mutex used specifically by registerToCache() 236 QMutex mMutexRegisterToCache; 237 238 //! Mutex used only by serializeFeatures() 239 QMutex mCacheWriteMutex; 240 241 //! For error messages, name of the translated component. For example tr("WFS") 242 QString mComponentTranslated; 243 244 //! Whether the downloader has finished (or been canceled) 245 bool mDownloadFinished = false; 246 247 /** 248 * The generation counter. When a iterator is built or rewind, it gets the 249 * current value of the generation counter to query the features in the cache 250 * whose generation counter is <= the current value. That way the iterator 251 * can consume first cached features, and then deal with the features that are 252 * notified in live by the downloader. 253 */ 254 int mGenCounter = 0; 255 256 //! Spatial index of requested cached regions 257 QgsSpatialIndex mCachedRegions; 258 259 //! Requested cached regions 260 QVector< QgsFeature > mRegions; 261 262 //! Limit of retrieved number of features for the current request 263 int mRequestLimit = 0; 264 265 //! Current BBOX used by the downloader 266 QgsRectangle mRect; 267 268 //! The background feature downloader 269 std::unique_ptr<QgsThreadedFeatureDownloader> mDownloader; 270 271 //! Filename of the on-disk cache 272 QString mCacheDbname; 273 274 //! Tablename of the on-disk cache 275 QString mCacheTablename; 276 277 //! The data provider of the on-disk cache 278 std::unique_ptr<QgsVectorDataProvider> mCacheDataProvider; 279 280 //! Name of the gmlid, spatialite_id, qgis_id cache. This cache persists even after a layer reload so as to ensure feature id stability. 281 QString mCacheIdDbname; 282 283 //! Connection to mCacheIdDbname 284 sqlite3_database_unique_ptr mCacheIdDb; 285 286 //! Map each user visible field name to the column name in the spatialite DB cache 287 // This is useful when there are user visible fields with same name, but different case 288 std::map<QString, QString> mMapUserVisibleFieldNameToSpatialiteColumnName; 289 290 //! Next value for qgisId column 291 QgsFeatureId mNextCachedIdQgisId = 1; 292 293 //! Number of features that have been cached, or attempted to be cached 294 long long mTotalFeaturesAttemptedToBeCached = 0; 295 296 //! Whether we have already tried fetching one feature after realizing that the capabilities extent is wrong 297 bool mTryFetchingOneFeature = false; 298 299 //! Number of features of the layer 300 long long mFeatureCount = 0; 301 302 //! Whether mFeatureCount value is exact or approximate / in construction 303 bool mFeatureCountExact = false; 304 305 //! Whether a request has been issued to retrieve the number of features 306 bool mFeatureCountRequestIssued = false; 307 308 ///////////////// METHODS //////////////////////// 309 310 //! Create the on-disk cache and connect to it 311 bool createCache(); 312 313 /** 314 * Returns the set of unique ids that have already been downloaded and 315 * cached, so as to avoid to cache duplicates. 316 */ 317 QSet<QString> getExistingCachedUniqueIds( const QVector<QgsFeatureUniqueIdPair> &featureList ); 318 319 /** 320 * Returns the set of md5 of features that have already been downloaded and 321 * cached, so as to avoid to cache duplicates. 322 */ 323 QSet<QString> getExistingCachedMD5( const QVector<QgsFeatureUniqueIdPair> &featureList ); 324 325 ///////////////// PURE VIRTUAL METHODS //////////////////////// 326 327 //! Called when the extent is updated 328 virtual void emitExtentUpdated() = 0; 329 330 //! called by invalidateCache() 331 virtual void invalidateCacheBaseUnderLock() = 0; 332 333 //! Return whether the server limit downloading a limiter number of features 334 virtual bool supportsLimitedFeatureCountDownloads() const = 0; 335 336 //! Return whether a server-side (non-spatial) filter is applied 337 virtual bool hasServerSideFilter() const = 0; 338 339 //! Return whether the server supports a (relatively) fast way of reporting the feature count 340 virtual bool supportsFastFeatureCount() const = 0; 341 342 //! Launch a synchronous request for a single feature and return its extent. 343 virtual QgsRectangle getExtentFromSingleFeatureRequest() const = 0; 344 345 //! Launch a synchronous request to count the number of features (return -1 in case of error) 346 virtual long long getFeatureCountFromServer() const = 0; 347 }; 348 349 #endif 350