diff --git a/src/providers/arcgisrest/CMakeLists.txt b/src/providers/arcgisrest/CMakeLists.txt index df9a013563cc..56ec4a6f3fbe 100644 --- a/src/providers/arcgisrest/CMakeLists.txt +++ b/src/providers/arcgisrest/CMakeLists.txt @@ -26,12 +26,14 @@ SET (AFS_SRCS qgsafsprovider.cpp qgsafsproviderextern.cpp qgsafssourceselect.cpp + qgsafsshareddata.cpp ) SET (AFS_MOC_HDRS qgsarcgisrestutils.h qgsafsdataitems.h qgsafsprovider.h qgsafssourceselect.h + qgsafsshareddata.h ) QT5_WRAP_CPP (AFS_MOC_SRCS ${AFS_MOC_HDRS}) diff --git a/src/providers/arcgisrest/qgsafsfeatureiterator.cpp b/src/providers/arcgisrest/qgsafsfeatureiterator.cpp index 25289e5c8923..64bdd88506b9 100644 --- a/src/providers/arcgisrest/qgsafsfeatureiterator.cpp +++ b/src/providers/arcgisrest/qgsafsfeatureiterator.cpp @@ -14,15 +14,13 @@ ***************************************************************************/ #include "qgsafsfeatureiterator.h" #include "qgsspatialindex.h" -#include "qgsafsprovider.h" +#include "qgsafsshareddata.h" #include "qgsmessagelog.h" #include "geometry/qgsgeometry.h" #include "qgscsexception.h" -QgsAfsFeatureSource::QgsAfsFeatureSource( const QgsAfsProvider *provider ) -// FIXME: ugly const_cast... - : mProvider( const_cast( provider ) ) - , mCrs( provider->crs() ) +QgsAfsFeatureSource::QgsAfsFeatureSource( const QSharedPointer &sharedData ) + : mSharedData( sharedData ) { } @@ -31,9 +29,9 @@ QgsFeatureIterator QgsAfsFeatureSource::getFeatures( const QgsFeatureRequest &re return QgsFeatureIterator( new QgsAfsFeatureIterator( this, false, request ) ); } -QgsAfsProvider *QgsAfsFeatureSource::provider() const +QgsAfsSharedData *QgsAfsFeatureSource::sharedData() const { - return mProvider; + return mSharedData.data(); } /////////////////////////////////////////////////////////////////////////////// @@ -41,9 +39,9 @@ QgsAfsProvider *QgsAfsFeatureSource::provider() const QgsAfsFeatureIterator::QgsAfsFeatureIterator( QgsAfsFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource( source, ownSource, request ) { - if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) + if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->sharedData()->crs() ) { - mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); + mTransform = QgsCoordinateTransform( mSource->sharedData()->crs(), mRequest.destinationCrs() ); } try { @@ -67,7 +65,7 @@ bool QgsAfsFeatureIterator::fetchFeature( QgsFeature &f ) if ( mClosed ) return false; - if ( mFeatureIterator >= mSource->provider()->featureCount() ) + if ( mFeatureIterator >= mSource->sharedData()->featureCount() ) return false; bool fetchGeometries = ( mRequest.flags() & QgsFeatureRequest::NoGeometry ) == 0; @@ -76,24 +74,24 @@ bool QgsAfsFeatureIterator::fetchFeature( QgsFeature &f ) fetchAttribures = mRequest.subsetOfAttributes(); else { - for ( int i = 0; i < mSource->provider()->fields().size(); ++i ) + for ( int i = 0; i < mSource->sharedData()->fields().size(); ++i ) fetchAttribures.append( i ); } if ( mRequest.filterType() == QgsFeatureRequest::FilterFid ) { - bool result = mSource->provider()->getFeature( mRequest.filterFid(), f, fetchGeometries, fetchAttribures ); + bool result = mSource->sharedData()->getFeature( mRequest.filterFid(), f, fetchGeometries, fetchAttribures ); geometryToDestinationCrs( f, mTransform ); return result; } else { - QgsRectangle filterRect = mSource->provider()->extent(); + QgsRectangle filterRect = mSource->sharedData()->extent(); if ( !mRequest.filterRect().isNull() ) filterRect = filterRect.intersect( &mFilterRect ); - while ( mFeatureIterator < mSource->provider()->featureCount() ) + while ( mFeatureIterator < mSource->sharedData()->featureCount() ) { - bool success = mSource->provider()->getFeature( mFeatureIterator, f, fetchGeometries, fetchAttribures, filterRect ); + bool success = mSource->sharedData()->getFeature( mFeatureIterator, f, fetchGeometries, fetchAttribures, filterRect ); ++mFeatureIterator; if ( !success ) continue; diff --git a/src/providers/arcgisrest/qgsafsfeatureiterator.h b/src/providers/arcgisrest/qgsafsfeatureiterator.h index e7cfbf0a22e3..ae4732b7369f 100644 --- a/src/providers/arcgisrest/qgsafsfeatureiterator.h +++ b/src/providers/arcgisrest/qgsafsfeatureiterator.h @@ -16,8 +16,9 @@ #define QGSAFSFEATUREITERATOR_H #include "qgsfeatureiterator.h" +#include "qgsafsshareddata.h" +#include -class QgsAfsProvider; class QgsSpatialIndex; @@ -25,13 +26,12 @@ class QgsAfsFeatureSource : public QgsAbstractFeatureSource { public: - QgsAfsFeatureSource( const QgsAfsProvider *provider ); + QgsAfsFeatureSource( const QSharedPointer &sharedData ); QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override; - QgsAfsProvider *provider() const; + QgsAfsSharedData *sharedData() const; protected: - QgsAfsProvider *mProvider = nullptr; - QgsCoordinateReferenceSystem mCrs; + QSharedPointer mSharedData; friend class QgsAfsFeatureIterator; }; diff --git a/src/providers/arcgisrest/qgsafsprovider.cpp b/src/providers/arcgisrest/qgsafsprovider.cpp index 46f49911af72..474b9d103bc7 100644 --- a/src/providers/arcgisrest/qgsafsprovider.cpp +++ b/src/providers/arcgisrest/qgsafsprovider.cpp @@ -32,17 +32,18 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri ) : QgsVectorDataProvider( uri ) , mValid( false ) - , mGeometryType( QgsWkbTypes::Unknown ) , mObjectIdFieldIdx( -1 ) { - mDataSource = QgsDataSourceUri( uri ); + mSharedData = QSharedPointer( new QgsAfsSharedData() ); + mSharedData->mGeometryType = QgsWkbTypes::Unknown; + mSharedData->mDataSource = QgsDataSourceUri( uri ); // Set CRS - mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mDataSource.param( QStringLiteral( "crs" ) ) ); + mSharedData->mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mSharedData->mDataSource.param( QStringLiteral( "crs" ) ) ); // Get layer info QString errorTitle, errorMessage; - QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage ); + QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage ); if ( layerData.isEmpty() ) { pushError( errorTitle + ": " + errorMessage ); @@ -53,25 +54,25 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri ) mLayerDescription = layerData[QStringLiteral( "description" )].toString(); // Set extent - QStringList coords = mDataSource.param( QStringLiteral( "bbox" ) ).split( QStringLiteral( "," ) ); + QStringList coords = mSharedData->mDataSource.param( QStringLiteral( "bbox" ) ).split( QStringLiteral( "," ) ); if ( coords.size() == 4 ) { bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; - mExtent.setXMinimum( coords[0].toDouble( &xminOk ) ); - mExtent.setYMinimum( coords[1].toDouble( &yminOk ) ); - mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) ); - mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) ); + mSharedData->mExtent.setXMinimum( coords[0].toDouble( &xminOk ) ); + mSharedData->mExtent.setYMinimum( coords[1].toDouble( &yminOk ) ); + mSharedData->mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) ); + mSharedData->mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) - mExtent = QgsRectangle(); + mSharedData->mExtent = QgsRectangle(); } - if ( mExtent.isEmpty() ) + if ( mSharedData->mExtent.isEmpty() ) { QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap(); bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; - mExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) ); - mExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) ); - mExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) ); - mExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) ); + mSharedData->mExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) ); + mSharedData->mExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) ); + mSharedData->mExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) ); + mSharedData->mExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) { appendError( QgsErrorMessage( tr( "Could not retrieve layer extent" ), QStringLiteral( "AFSProvider" ) ) ); @@ -83,7 +84,7 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri ) appendError( QgsErrorMessage( tr( "Could not parse spatial reference" ), QStringLiteral( "AFSProvider" ) ) ); return; } - mExtent = QgsCoordinateTransform( extentCrs, mSourceCRS ).transformBoundingBox( mExtent ); + mSharedData->mExtent = QgsCoordinateTransform( extentCrs, mSharedData->mSourceCRS ).transformBoundingBox( mSharedData->mExtent ); } // Read fields @@ -98,24 +99,24 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri ) continue; } QgsField field( fieldName, type, fieldDataMap[QStringLiteral( "type" )].toString(), fieldDataMap[QStringLiteral( "length" )].toInt() ); - mFields.append( field ); + mSharedData->mFields.append( field ); } // Determine geometry type bool hasM = layerData[QStringLiteral( "hasM" )].toBool(); bool hasZ = layerData[QStringLiteral( "hasZ" )].toBool(); - mGeometryType = QgsArcGisRestUtils::mapEsriGeometryType( layerData[QStringLiteral( "geometryType" )].toString() ); - if ( mGeometryType == QgsWkbTypes::Unknown ) + mSharedData->mGeometryType = QgsArcGisRestUtils::mapEsriGeometryType( layerData[QStringLiteral( "geometryType" )].toString() ); + if ( mSharedData->mGeometryType == QgsWkbTypes::Unknown ) { appendError( QgsErrorMessage( tr( "Failed to determine geometry type" ), QStringLiteral( "AFSProvider" ) ) ); return; } - mGeometryType = QgsWkbTypes::zmType( mGeometryType, hasZ, hasM ); + mSharedData->mGeometryType = QgsWkbTypes::zmType( mSharedData->mGeometryType, hasZ, hasM ); // Read OBJECTIDs of all features: these may not be a continuous sequence, // and we need to store these to iterate through the features. This query // also returns the name of the ObjectID field. - QVariantMap objectIdData = QgsArcGisRestUtils::getObjectIds( mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage ); + QVariantMap objectIdData = QgsArcGisRestUtils::getObjectIds( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage ); if ( objectIdData.isEmpty() ) { appendError( QgsErrorMessage( tr( "getObjectIds failed: %1 - %2" ).arg( errorTitle, errorMessage ), QStringLiteral( "AFSProvider" ) ) ); @@ -126,25 +127,25 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri ) appendError( QgsErrorMessage( tr( "Failed to determine objectIdFieldName and/or objectIds" ), QStringLiteral( "AFSProvider" ) ) ); return; } - mObjectIdFieldName = objectIdData[QStringLiteral( "objectIdFieldName" )].toString(); - for ( int idx = 0, nIdx = mFields.count(); idx < nIdx; ++idx ) + QString objectIdFieldName = objectIdData[QStringLiteral( "objectIdFieldName" )].toString(); + for ( int idx = 0, nIdx = mSharedData->mFields.count(); idx < nIdx; ++idx ) { - if ( mFields.at( idx ).name() == mObjectIdFieldName ) + if ( mSharedData->mFields.at( idx ).name() == objectIdFieldName ) { mObjectIdFieldIdx = idx; // primary key is not null, unique - QgsFieldConstraints constraints = mFields.at( idx ).constraints(); + QgsFieldConstraints constraints = mSharedData->mFields.at( idx ).constraints(); constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider ); constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider ); - mFields[ idx ].setConstraints( constraints ); + mSharedData->mFields[ idx ].setConstraints( constraints ); break; } } foreach ( const QVariant &objectId, objectIdData["objectIds"].toList() ) { - mObjectIds.append( objectId.toInt() ); + mSharedData->mObjectIds.append( objectId.toInt() ); } mValid = true; @@ -152,111 +153,46 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri ) QgsAbstractFeatureSource *QgsAfsProvider::featureSource() const { - return new QgsAfsFeatureSource( this ); + return new QgsAfsFeatureSource( mSharedData ); } QgsFeatureIterator QgsAfsProvider::getFeatures( const QgsFeatureRequest &request ) const { - return new QgsAfsFeatureIterator( new QgsAfsFeatureSource( this ), true, request ); + return new QgsAfsFeatureIterator( new QgsAfsFeatureSource( mSharedData ), true, request ); } -bool QgsAfsProvider::getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeometry, const QList & /*fetchAttributes*/, const QgsRectangle &filterRect ) +QgsWkbTypes::Type QgsAfsProvider::wkbType() const { - // If cached, return cached feature - QMap::const_iterator it = mCache.find( id ); - if ( it != mCache.end() ) - { - f = it.value(); - return filterRect.isNull() || f.geometry().intersects( filterRect ); - } - - // Determine attributes to fetch - /*QStringList fetchAttribNames; - foreach ( int idx, fetchAttributes ) - fetchAttribNames.append( mFields.at( idx ).name() ); - */ - - // When fetching from server, fetch all attributes and geometry by default so that we can cache them - QStringList fetchAttribNames; - QList fetchAttribIdx; - fetchAttribIdx.reserve( mFields.size() ); - for ( int idx = 0, n = mFields.size(); idx < n; ++idx ) - { - fetchAttribNames.append( mFields.at( idx ).name() ); - fetchAttribIdx.append( idx ); - } - fetchGeometry = true; - - // Fetch 100 features at the time - int startId = ( id / 100 ) * 100; - int stopId = qMin( startId + 100, mObjectIds.length() ); - QList objectIds; - objectIds.reserve( stopId ); - for ( int i = startId; i < stopId; ++i ) - { - objectIds.append( mObjectIds[i] ); - } - + return mSharedData->mGeometryType; +} - // Query - QString errorTitle, errorMessage; - QVariantMap queryData = QgsArcGisRestUtils::getObjects( - mDataSource.param( QStringLiteral( "url" ) ), objectIds, mDataSource.param( QStringLiteral( "crs" ) ), fetchGeometry, - fetchAttribNames, QgsWkbTypes::hasM( mGeometryType ), QgsWkbTypes::hasZ( mGeometryType ), - filterRect, errorTitle, errorMessage ); - if ( queryData.isEmpty() ) - { - const_cast( this )->pushError( errorTitle + ": " + errorMessage ); - QgsDebugMsg( "Query returned empty result" ); - return false; - } +long QgsAfsProvider::featureCount() const +{ + return mSharedData->mObjectIds.size(); +} - QVariantList featuresData = queryData[QStringLiteral( "features" )].toList(); - if ( featuresData.isEmpty() ) - { - QgsDebugMsg( "Query returned no features" ); - return false; - } - for ( int i = 0, n = featuresData.size(); i < n; ++i ) - { - QVariantMap featureData = featuresData[i].toMap(); - QgsFeature feature; +QgsFields QgsAfsProvider::fields() const +{ + return mSharedData->mFields; +} - // Set FID - feature.setId( startId + i ); +void QgsAfsProvider::setDataSourceUri( const QString &uri ) +{ + mSharedData->mDataSource = QgsDataSourceUri( uri ); + QgsDataProvider::setDataSourceUri( uri ); +} - // Set attributes - if ( !fetchAttribIdx.isEmpty() ) - { - QVariantMap attributesData = featureData[QStringLiteral( "attributes" )].toMap(); - feature.setFields( mFields ); - QgsAttributes attributes( mFields.size() ); - foreach ( int idx, fetchAttribIdx ) - { - attributes[idx] = attributesData[mFields.at( idx ).name()]; - } - feature.setAttributes( attributes ); - } +QgsCoordinateReferenceSystem QgsAfsProvider::crs() const +{ + return mSharedData->crs(); +} - // Set geometry - if ( fetchGeometry ) - { - QVariantMap geometryData = featureData[QStringLiteral( "geometry" )].toMap(); - QgsAbstractGeometry *geometry = QgsArcGisRestUtils::parseEsriGeoJSON( geometryData, queryData[QStringLiteral( "geometryType" )].toString(), - QgsWkbTypes::hasM( mGeometryType ), QgsWkbTypes::hasZ( mGeometryType ) ); - // Above might return 0, which is ok since in theory empty geometries are allowed - feature.setGeometry( QgsGeometry( geometry ) ); - } - feature.setValid( true ); - mCache.insert( feature.id(), feature ); - } - f = mCache[id]; - Q_ASSERT( f.isValid() ); - return filterRect.isNull() || ( f.hasGeometry() && f.geometry().intersects( filterRect ) ); +QgsRectangle QgsAfsProvider::extent() const +{ + return mSharedData->extent(); } -void QgsAfsProvider::setDataSourceUri( const QString &uri ) +void QgsAfsProvider::reloadData() { - mDataSource = QgsDataSourceUri( uri ); - QgsDataProvider::setDataSourceUri( uri ); + mSharedData->mCache.clear(); } diff --git a/src/providers/arcgisrest/qgsafsprovider.h b/src/providers/arcgisrest/qgsafsprovider.h index 523899557519..9392d9c11da1 100644 --- a/src/providers/arcgisrest/qgsafsprovider.h +++ b/src/providers/arcgisrest/qgsafsprovider.h @@ -18,8 +18,10 @@ #ifndef QGSAFSPROVIDER_H #define QGSAFSPROVIDER_H +#include #include "qgsvectordataprovider.h" #include "qgsdatasourceuri.h" +#include "qgsafsshareddata.h" #include "qgscoordinatereferencesystem.h" #include "geometry/qgswkbtypes.h" #include "qgsfields.h" @@ -35,15 +37,13 @@ class QgsAfsProvider : public QgsVectorDataProvider QgsAfsProvider( const QString &uri ); - bool getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeometry, const QList &fetchAttributes, const QgsRectangle &filterRect = QgsRectangle() ); - /* Inherited from QgsVectorDataProvider */ QgsAbstractFeatureSource *featureSource() const override; QString storageType() const override { return QStringLiteral( "ESRI ArcGIS Feature Server" ); } QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override; - QgsWkbTypes::Type wkbType() const override { return static_cast( mGeometryType ); } - long featureCount() const override { return mObjectIds.size(); } - QgsFields fields() const override { return mFields; } + QgsWkbTypes::Type wkbType() const override; + long featureCount() const override; + QgsFields fields() const override; /* Read only for the moment bool addFeatures( QgsFeatureList &flist ) override{ return false; } bool deleteFeatures( const QgsFeatureIds &id ) override{ return false; } @@ -57,30 +57,23 @@ class QgsAfsProvider : public QgsVectorDataProvider QgsAttrPalIndexNameHash palAttributeIndexNames() const override { return QgsAttrPalIndexNameHash(); } /* Inherited from QgsDataProvider */ - QgsCoordinateReferenceSystem crs() const override { return mSourceCRS; } + QgsCoordinateReferenceSystem crs() const override; void setDataSourceUri( const QString &uri ) override; - QgsRectangle extent() const override { return mExtent; } + QgsRectangle extent() const override; bool isValid() const override { return mValid; } /* Read only for the moment void updateExtents() override{} */ QString name() const override { return mLayerName; } QString description() const override { return mLayerDescription; } - void reloadData() override { mCache.clear(); } + void reloadData() override; private: bool mValid; - QgsDataSourceUri mDataSource; - QgsRectangle mExtent; - QgsWkbTypes::Type mGeometryType; - QgsFields mFields; + QSharedPointer mSharedData; int mObjectIdFieldIdx; - QString mObjectIdFieldName; QString mLayerName; QString mLayerDescription; - QList mObjectIds; - QgsCoordinateReferenceSystem mSourceCRS; - QMap mCache; }; #endif // QGSAFSPROVIDER_H diff --git a/src/providers/arcgisrest/qgsafsshareddata.cpp b/src/providers/arcgisrest/qgsafsshareddata.cpp new file mode 100644 index 000000000000..26e9ed401c85 --- /dev/null +++ b/src/providers/arcgisrest/qgsafsshareddata.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + qgsafsshareddata.cpp + --------------------- + begin : June 2017 + copyright : (C) 2017 by Sandro Mani + email : manisandro at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsafsshareddata.h" +#include "qgsarcgisrestutils.h" +#include "qgslogger.h" + +QgsAfsSharedData::QgsAfsSharedData() +{ + + +} + +bool QgsAfsSharedData::getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeometry, const QList & /*fetchAttributes*/, const QgsRectangle &filterRect ) +{ + // If cached, return cached feature + QMap::const_iterator it = mCache.find( id ); + if ( it != mCache.end() ) + { + f = it.value(); + return filterRect.isNull() || f.geometry().intersects( filterRect ); + } + + // Determine attributes to fetch + /*QStringList fetchAttribNames; + foreach ( int idx, fetchAttributes ) + fetchAttribNames.append( mFields.at( idx ).name() ); + */ + + // When fetching from server, fetch all attributes and geometry by default so that we can cache them + QStringList fetchAttribNames; + QList fetchAttribIdx; + fetchAttribIdx.reserve( mFields.size() ); + for ( int idx = 0, n = mFields.size(); idx < n; ++idx ) + { + fetchAttribNames.append( mFields.at( idx ).name() ); + fetchAttribIdx.append( idx ); + } + fetchGeometry = true; + + // Fetch 100 features at the time + int startId = ( id / 100 ) * 100; + int stopId = qMin( startId + 100, mObjectIds.length() ); + QList objectIds; + objectIds.reserve( stopId ); + for ( int i = startId; i < stopId; ++i ) + { + objectIds.append( mObjectIds[i] ); + } + + + // Query + QString errorTitle, errorMessage; + QVariantMap queryData = QgsArcGisRestUtils::getObjects( + mDataSource.param( QStringLiteral( "url" ) ), objectIds, mDataSource.param( QStringLiteral( "crs" ) ), fetchGeometry, + fetchAttribNames, QgsWkbTypes::hasM( mGeometryType ), QgsWkbTypes::hasZ( mGeometryType ), + filterRect, errorTitle, errorMessage ); + if ( queryData.isEmpty() ) + { +// const_cast( this )->pushError( errorTitle + ": " + errorMessage ); + QgsDebugMsg( "Query returned empty result" ); + return false; + } + + QVariantList featuresData = queryData[QStringLiteral( "features" )].toList(); + if ( featuresData.isEmpty() ) + { + QgsDebugMsg( "Query returned no features" ); + return false; + } + for ( int i = 0, n = featuresData.size(); i < n; ++i ) + { + QVariantMap featureData = featuresData[i].toMap(); + QgsFeature feature; + + // Set FID + feature.setId( startId + i ); + + // Set attributes + if ( !fetchAttribIdx.isEmpty() ) + { + QVariantMap attributesData = featureData[QStringLiteral( "attributes" )].toMap(); + feature.setFields( mFields ); + QgsAttributes attributes( mFields.size() ); + foreach ( int idx, fetchAttribIdx ) + { + attributes[idx] = attributesData[mFields.at( idx ).name()]; + } + feature.setAttributes( attributes ); + } + + // Set geometry + if ( fetchGeometry ) + { + QVariantMap geometryData = featureData[QStringLiteral( "geometry" )].toMap(); + QgsAbstractGeometry *geometry = QgsArcGisRestUtils::parseEsriGeoJSON( geometryData, queryData[QStringLiteral( "geometryType" )].toString(), + QgsWkbTypes::hasM( mGeometryType ), QgsWkbTypes::hasZ( mGeometryType ) ); + // Above might return 0, which is ok since in theory empty geometries are allowed + feature.setGeometry( QgsGeometry( geometry ) ); + } + feature.setValid( true ); + mCache.insert( feature.id(), feature ); + } + f = mCache[id]; + Q_ASSERT( f.isValid() ); + return filterRect.isNull() || ( f.hasGeometry() && f.geometry().intersects( filterRect ) ); +} diff --git a/src/providers/arcgisrest/qgsafsshareddata.h b/src/providers/arcgisrest/qgsafsshareddata.h new file mode 100644 index 000000000000..780909c7a830 --- /dev/null +++ b/src/providers/arcgisrest/qgsafsshareddata.h @@ -0,0 +1,50 @@ +/*************************************************************************** + qgsafsshareddata.h + --------------------- + begin : June 2017 + copyright : (C) 2017 by Sandro Mani + email : manisandro at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSAFSSHAREDDATA_H +#define QGSAFSSHAREDDATA_H + +#include +#include "qgsfields.h" +#include "qgsfeature.h" +#include "qgsdatasourceuri.h" + +/** + * \brief This class holds data, shared between QgsAfsProvider and QgsAfsFeatureIterator + **/ +class QgsAfsSharedData : public QObject +{ + Q_OBJECT + public: + QgsAfsSharedData(); + long featureCount() const { return mObjectIds.size(); } + const QgsFields &fields() const { return mFields; } + QgsRectangle extent() const { return mExtent; } + QgsCoordinateReferenceSystem crs() const { return mSourceCRS; } + + bool getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeometry, const QList &fetchAttributes, const QgsRectangle &filterRect = QgsRectangle() ); + + private: + friend class QgsAfsProvider; + QgsDataSourceUri mDataSource; + QgsRectangle mExtent; + QgsWkbTypes::Type mGeometryType; + QgsFields mFields; + QList mObjectIds; + QMap mCache; + QgsCoordinateReferenceSystem mSourceCRS; +}; + +#endif