Skip to content

Commit b42bf82

Browse files
committed
[ArcGIS REST] Introduce QgsAfsSharedData to remove dependency on QgsAfsProvider by QfsAfsFeatureSource
1 parent f237c6c commit b42bf82

File tree

7 files changed

+253
-155
lines changed

7 files changed

+253
-155
lines changed

src/providers/arcgisrest/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ SET (AFS_SRCS
2626
qgsafsprovider.cpp
2727
qgsafsproviderextern.cpp
2828
qgsafssourceselect.cpp
29+
qgsafsshareddata.cpp
2930
)
3031
SET (AFS_MOC_HDRS
3132
qgsarcgisrestutils.h
3233
qgsafsdataitems.h
3334
qgsafsprovider.h
3435
qgsafssourceselect.h
36+
qgsafsshareddata.h
3537
)
3638

3739
QT5_WRAP_CPP (AFS_MOC_SRCS ${AFS_MOC_HDRS})

src/providers/arcgisrest/qgsafsfeatureiterator.cpp

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@
1414
***************************************************************************/
1515
#include "qgsafsfeatureiterator.h"
1616
#include "qgsspatialindex.h"
17-
#include "qgsafsprovider.h"
17+
#include "qgsafsshareddata.h"
1818
#include "qgsmessagelog.h"
1919
#include "geometry/qgsgeometry.h"
2020
#include "qgscsexception.h"
2121

22-
QgsAfsFeatureSource::QgsAfsFeatureSource( const QgsAfsProvider *provider )
23-
// FIXME: ugly const_cast...
24-
: mProvider( const_cast<QgsAfsProvider *>( provider ) )
25-
, mCrs( provider->crs() )
22+
QgsAfsFeatureSource::QgsAfsFeatureSource( const QSharedPointer<QgsAfsSharedData> &sharedData )
23+
: mSharedData( sharedData )
2624
{
2725
}
2826

@@ -31,19 +29,19 @@ QgsFeatureIterator QgsAfsFeatureSource::getFeatures( const QgsFeatureRequest &re
3129
return QgsFeatureIterator( new QgsAfsFeatureIterator( this, false, request ) );
3230
}
3331

34-
QgsAfsProvider *QgsAfsFeatureSource::provider() const
32+
QgsAfsSharedData *QgsAfsFeatureSource::sharedData() const
3533
{
36-
return mProvider;
34+
return mSharedData.data();
3735
}
3836

3937
///////////////////////////////////////////////////////////////////////////////
4038

4139
QgsAfsFeatureIterator::QgsAfsFeatureIterator( QgsAfsFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
4240
: QgsAbstractFeatureIteratorFromSource<QgsAfsFeatureSource>( source, ownSource, request )
4341
{
44-
if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs )
42+
if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->sharedData()->crs() )
4543
{
46-
mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() );
44+
mTransform = QgsCoordinateTransform( mSource->sharedData()->crs(), mRequest.destinationCrs() );
4745
}
4846
try
4947
{
@@ -67,7 +65,7 @@ bool QgsAfsFeatureIterator::fetchFeature( QgsFeature &f )
6765
if ( mClosed )
6866
return false;
6967

70-
if ( mFeatureIterator >= mSource->provider()->featureCount() )
68+
if ( mFeatureIterator >= mSource->sharedData()->featureCount() )
7169
return false;
7270

7371
bool fetchGeometries = ( mRequest.flags() & QgsFeatureRequest::NoGeometry ) == 0;
@@ -76,24 +74,24 @@ bool QgsAfsFeatureIterator::fetchFeature( QgsFeature &f )
7674
fetchAttribures = mRequest.subsetOfAttributes();
7775
else
7876
{
79-
for ( int i = 0; i < mSource->provider()->fields().size(); ++i )
77+
for ( int i = 0; i < mSource->sharedData()->fields().size(); ++i )
8078
fetchAttribures.append( i );
8179
}
8280

8381
if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
8482
{
85-
bool result = mSource->provider()->getFeature( mRequest.filterFid(), f, fetchGeometries, fetchAttribures );
83+
bool result = mSource->sharedData()->getFeature( mRequest.filterFid(), f, fetchGeometries, fetchAttribures );
8684
geometryToDestinationCrs( f, mTransform );
8785
return result;
8886
}
8987
else
9088
{
91-
QgsRectangle filterRect = mSource->provider()->extent();
89+
QgsRectangle filterRect = mSource->sharedData()->extent();
9290
if ( !mRequest.filterRect().isNull() )
9391
filterRect = filterRect.intersect( &mFilterRect );
94-
while ( mFeatureIterator < mSource->provider()->featureCount() )
92+
while ( mFeatureIterator < mSource->sharedData()->featureCount() )
9593
{
96-
bool success = mSource->provider()->getFeature( mFeatureIterator, f, fetchGeometries, fetchAttribures, filterRect );
94+
bool success = mSource->sharedData()->getFeature( mFeatureIterator, f, fetchGeometries, fetchAttribures, filterRect );
9795
++mFeatureIterator;
9896
if ( !success )
9997
continue;

src/providers/arcgisrest/qgsafsfeatureiterator.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@
1616
#define QGSAFSFEATUREITERATOR_H
1717

1818
#include "qgsfeatureiterator.h"
19+
#include "qgsafsshareddata.h"
20+
#include <QSharedPointer>
1921

20-
class QgsAfsProvider;
2122
class QgsSpatialIndex;
2223

2324

2425
class QgsAfsFeatureSource : public QgsAbstractFeatureSource
2526
{
2627

2728
public:
28-
QgsAfsFeatureSource( const QgsAfsProvider *provider );
29+
QgsAfsFeatureSource( const QSharedPointer<QgsAfsSharedData> &sharedData );
2930
QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override;
30-
QgsAfsProvider *provider() const;
31+
QgsAfsSharedData *sharedData() const;
3132

3233
protected:
33-
QgsAfsProvider *mProvider = nullptr;
34-
QgsCoordinateReferenceSystem mCrs;
34+
QSharedPointer<QgsAfsSharedData> mSharedData;
3535

3636
friend class QgsAfsFeatureIterator;
3737
};

src/providers/arcgisrest/qgsafsprovider.cpp

Lines changed: 55 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,18 @@
3232
QgsAfsProvider::QgsAfsProvider( const QString &uri )
3333
: QgsVectorDataProvider( uri )
3434
, mValid( false )
35-
, mGeometryType( QgsWkbTypes::Unknown )
3635
, mObjectIdFieldIdx( -1 )
3736
{
38-
mDataSource = QgsDataSourceUri( uri );
37+
mSharedData = QSharedPointer<QgsAfsSharedData>( new QgsAfsSharedData() );
38+
mSharedData->mGeometryType = QgsWkbTypes::Unknown;
39+
mSharedData->mDataSource = QgsDataSourceUri( uri );
3940

4041
// Set CRS
41-
mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mDataSource.param( QStringLiteral( "crs" ) ) );
42+
mSharedData->mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mSharedData->mDataSource.param( QStringLiteral( "crs" ) ) );
4243

4344
// Get layer info
4445
QString errorTitle, errorMessage;
45-
QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage );
46+
QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage );
4647
if ( layerData.isEmpty() )
4748
{
4849
pushError( errorTitle + ": " + errorMessage );
@@ -53,25 +54,25 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )
5354
mLayerDescription = layerData[QStringLiteral( "description" )].toString();
5455

5556
// Set extent
56-
QStringList coords = mDataSource.param( QStringLiteral( "bbox" ) ).split( QStringLiteral( "," ) );
57+
QStringList coords = mSharedData->mDataSource.param( QStringLiteral( "bbox" ) ).split( QStringLiteral( "," ) );
5758
if ( coords.size() == 4 )
5859
{
5960
bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
60-
mExtent.setXMinimum( coords[0].toDouble( &xminOk ) );
61-
mExtent.setYMinimum( coords[1].toDouble( &yminOk ) );
62-
mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) );
63-
mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) );
61+
mSharedData->mExtent.setXMinimum( coords[0].toDouble( &xminOk ) );
62+
mSharedData->mExtent.setYMinimum( coords[1].toDouble( &yminOk ) );
63+
mSharedData->mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) );
64+
mSharedData->mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) );
6465
if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk )
65-
mExtent = QgsRectangle();
66+
mSharedData->mExtent = QgsRectangle();
6667
}
67-
if ( mExtent.isEmpty() )
68+
if ( mSharedData->mExtent.isEmpty() )
6869
{
6970
QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap();
7071
bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
71-
mExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) );
72-
mExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) );
73-
mExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) );
74-
mExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) );
72+
mSharedData->mExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) );
73+
mSharedData->mExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) );
74+
mSharedData->mExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) );
75+
mSharedData->mExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) );
7576
if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk )
7677
{
7778
appendError( QgsErrorMessage( tr( "Could not retrieve layer extent" ), QStringLiteral( "AFSProvider" ) ) );
@@ -83,7 +84,7 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )
8384
appendError( QgsErrorMessage( tr( "Could not parse spatial reference" ), QStringLiteral( "AFSProvider" ) ) );
8485
return;
8586
}
86-
mExtent = QgsCoordinateTransform( extentCrs, mSourceCRS ).transformBoundingBox( mExtent );
87+
mSharedData->mExtent = QgsCoordinateTransform( extentCrs, mSharedData->mSourceCRS ).transformBoundingBox( mSharedData->mExtent );
8788
}
8889

8990
// Read fields
@@ -98,24 +99,24 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )
9899
continue;
99100
}
100101
QgsField field( fieldName, type, fieldDataMap[QStringLiteral( "type" )].toString(), fieldDataMap[QStringLiteral( "length" )].toInt() );
101-
mFields.append( field );
102+
mSharedData->mFields.append( field );
102103
}
103104

104105
// Determine geometry type
105106
bool hasM = layerData[QStringLiteral( "hasM" )].toBool();
106107
bool hasZ = layerData[QStringLiteral( "hasZ" )].toBool();
107-
mGeometryType = QgsArcGisRestUtils::mapEsriGeometryType( layerData[QStringLiteral( "geometryType" )].toString() );
108-
if ( mGeometryType == QgsWkbTypes::Unknown )
108+
mSharedData->mGeometryType = QgsArcGisRestUtils::mapEsriGeometryType( layerData[QStringLiteral( "geometryType" )].toString() );
109+
if ( mSharedData->mGeometryType == QgsWkbTypes::Unknown )
109110
{
110111
appendError( QgsErrorMessage( tr( "Failed to determine geometry type" ), QStringLiteral( "AFSProvider" ) ) );
111112
return;
112113
}
113-
mGeometryType = QgsWkbTypes::zmType( mGeometryType, hasZ, hasM );
114+
mSharedData->mGeometryType = QgsWkbTypes::zmType( mSharedData->mGeometryType, hasZ, hasM );
114115

115116
// Read OBJECTIDs of all features: these may not be a continuous sequence,
116117
// and we need to store these to iterate through the features. This query
117118
// also returns the name of the ObjectID field.
118-
QVariantMap objectIdData = QgsArcGisRestUtils::getObjectIds( mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage );
119+
QVariantMap objectIdData = QgsArcGisRestUtils::getObjectIds( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage );
119120
if ( objectIdData.isEmpty() )
120121
{
121122
appendError( QgsErrorMessage( tr( "getObjectIds failed: %1 - %2" ).arg( errorTitle, errorMessage ), QStringLiteral( "AFSProvider" ) ) );
@@ -126,137 +127,72 @@ QgsAfsProvider::QgsAfsProvider( const QString &uri )
126127
appendError( QgsErrorMessage( tr( "Failed to determine objectIdFieldName and/or objectIds" ), QStringLiteral( "AFSProvider" ) ) );
127128
return;
128129
}
129-
mObjectIdFieldName = objectIdData[QStringLiteral( "objectIdFieldName" )].toString();
130-
for ( int idx = 0, nIdx = mFields.count(); idx < nIdx; ++idx )
130+
QString objectIdFieldName = objectIdData[QStringLiteral( "objectIdFieldName" )].toString();
131+
for ( int idx = 0, nIdx = mSharedData->mFields.count(); idx < nIdx; ++idx )
131132
{
132-
if ( mFields.at( idx ).name() == mObjectIdFieldName )
133+
if ( mSharedData->mFields.at( idx ).name() == objectIdFieldName )
133134
{
134135
mObjectIdFieldIdx = idx;
135136

136137
// primary key is not null, unique
137-
QgsFieldConstraints constraints = mFields.at( idx ).constraints();
138+
QgsFieldConstraints constraints = mSharedData->mFields.at( idx ).constraints();
138139
constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginProvider );
139140
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
140-
mFields[ idx ].setConstraints( constraints );
141+
mSharedData->mFields[ idx ].setConstraints( constraints );
141142

142143
break;
143144
}
144145
}
145146
foreach ( const QVariant &objectId, objectIdData["objectIds"].toList() )
146147
{
147-
mObjectIds.append( objectId.toInt() );
148+
mSharedData->mObjectIds.append( objectId.toInt() );
148149
}
149150

150151
mValid = true;
151152
}
152153

153154
QgsAbstractFeatureSource *QgsAfsProvider::featureSource() const
154155
{
155-
return new QgsAfsFeatureSource( this );
156+
return new QgsAfsFeatureSource( mSharedData );
156157
}
157158

158159
QgsFeatureIterator QgsAfsProvider::getFeatures( const QgsFeatureRequest &request ) const
159160
{
160-
return new QgsAfsFeatureIterator( new QgsAfsFeatureSource( this ), true, request );
161+
return new QgsAfsFeatureIterator( new QgsAfsFeatureSource( mSharedData ), true, request );
161162
}
162163

163-
bool QgsAfsProvider::getFeature( QgsFeatureId id, QgsFeature &f, bool fetchGeometry, const QList<int> & /*fetchAttributes*/, const QgsRectangle &filterRect )
164+
QgsWkbTypes::Type QgsAfsProvider::wkbType() const
164165
{
165-
// If cached, return cached feature
166-
QMap<QgsFeatureId, QgsFeature>::const_iterator it = mCache.find( id );
167-
if ( it != mCache.end() )
168-
{
169-
f = it.value();
170-
return filterRect.isNull() || f.geometry().intersects( filterRect );
171-
}
172-
173-
// Determine attributes to fetch
174-
/*QStringList fetchAttribNames;
175-
foreach ( int idx, fetchAttributes )
176-
fetchAttribNames.append( mFields.at( idx ).name() );
177-
*/
178-
179-
// When fetching from server, fetch all attributes and geometry by default so that we can cache them
180-
QStringList fetchAttribNames;
181-
QList<int> fetchAttribIdx;
182-
fetchAttribIdx.reserve( mFields.size() );
183-
for ( int idx = 0, n = mFields.size(); idx < n; ++idx )
184-
{
185-
fetchAttribNames.append( mFields.at( idx ).name() );
186-
fetchAttribIdx.append( idx );
187-
}
188-
fetchGeometry = true;
189-
190-
// Fetch 100 features at the time
191-
int startId = ( id / 100 ) * 100;
192-
int stopId = qMin( startId + 100, mObjectIds.length() );
193-
QList<quint32> objectIds;
194-
objectIds.reserve( stopId );
195-
for ( int i = startId; i < stopId; ++i )
196-
{
197-
objectIds.append( mObjectIds[i] );
198-
}
199-
166+
return mSharedData->mGeometryType;
167+
}
200168

201-
// Query
202-
QString errorTitle, errorMessage;
203-
QVariantMap queryData = QgsArcGisRestUtils::getObjects(
204-
mDataSource.param( QStringLiteral( "url" ) ), objectIds, mDataSource.param( QStringLiteral( "crs" ) ), fetchGeometry,
205-
fetchAttribNames, QgsWkbTypes::hasM( mGeometryType ), QgsWkbTypes::hasZ( mGeometryType ),
206-
filterRect, errorTitle, errorMessage );
207-
if ( queryData.isEmpty() )
208-
{
209-
const_cast<QgsAfsProvider *>( this )->pushError( errorTitle + ": " + errorMessage );
210-
QgsDebugMsg( "Query returned empty result" );
211-
return false;
212-
}
169+
long QgsAfsProvider::featureCount() const
170+
{
171+
return mSharedData->mObjectIds.size();
172+
}
213173

214-
QVariantList featuresData = queryData[QStringLiteral( "features" )].toList();
215-
if ( featuresData.isEmpty() )
216-
{
217-
QgsDebugMsg( "Query returned no features" );
218-
return false;
219-
}
220-
for ( int i = 0, n = featuresData.size(); i < n; ++i )
221-
{
222-
QVariantMap featureData = featuresData[i].toMap();
223-
QgsFeature feature;
174+
QgsFields QgsAfsProvider::fields() const
175+
{
176+
return mSharedData->mFields;
177+
}
224178

225-
// Set FID
226-
feature.setId( startId + i );
179+
void QgsAfsProvider::setDataSourceUri( const QString &uri )
180+
{
181+
mSharedData->mDataSource = QgsDataSourceUri( uri );
182+
QgsDataProvider::setDataSourceUri( uri );
183+
}
227184

228-
// Set attributes
229-
if ( !fetchAttribIdx.isEmpty() )
230-
{
231-
QVariantMap attributesData = featureData[QStringLiteral( "attributes" )].toMap();
232-
feature.setFields( mFields );
233-
QgsAttributes attributes( mFields.size() );
234-
foreach ( int idx, fetchAttribIdx )
235-
{
236-
attributes[idx] = attributesData[mFields.at( idx ).name()];
237-
}
238-
feature.setAttributes( attributes );
239-
}
185+
QgsCoordinateReferenceSystem QgsAfsProvider::crs() const
186+
{
187+
return mSharedData->crs();
188+
}
240189

241-
// Set geometry
242-
if ( fetchGeometry )
243-
{
244-
QVariantMap geometryData = featureData[QStringLiteral( "geometry" )].toMap();
245-
QgsAbstractGeometry *geometry = QgsArcGisRestUtils::parseEsriGeoJSON( geometryData, queryData[QStringLiteral( "geometryType" )].toString(),
246-
QgsWkbTypes::hasM( mGeometryType ), QgsWkbTypes::hasZ( mGeometryType ) );
247-
// Above might return 0, which is ok since in theory empty geometries are allowed
248-
feature.setGeometry( QgsGeometry( geometry ) );
249-
}
250-
feature.setValid( true );
251-
mCache.insert( feature.id(), feature );
252-
}
253-
f = mCache[id];
254-
Q_ASSERT( f.isValid() );
255-
return filterRect.isNull() || ( f.hasGeometry() && f.geometry().intersects( filterRect ) );
190+
QgsRectangle QgsAfsProvider::extent() const
191+
{
192+
return mSharedData->extent();
256193
}
257194

258-
void QgsAfsProvider::setDataSourceUri( const QString &uri )
195+
void QgsAfsProvider::reloadData()
259196
{
260-
mDataSource = QgsDataSourceUri( uri );
261-
QgsDataProvider::setDataSourceUri( uri );
197+
mSharedData->mCache.clear();
262198
}

0 commit comments

Comments
 (0)