4.QgsPostgresFeatureIterator类
class QgsPostgresFeatureIterator final: public QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>
{
};
该类需要对rewind()、close()、fetchFeature()三个关键函数进行重写。
部分成员变量:
- QgsPostgresConn *mConn = nullptr;
数据库连接。 - QString mCursorName;
游标名,通过该游标查询features。 - QQueue<QgsFeature> mFeatureQueue;
保存从数据库提取出的features。 - int mFetched = 0;
已经检索的feature数量。
(1) QgsPostgresFeatureIterator()
QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
: QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>( source, ownSource, request )
{
}
(2) rewind() override
bool QgsPostgresFeatureIterator::rewind()
{
if ( mClosed )
return false;
// move cursor to first record
mConn->PQexecNR( QStringLiteral( "move absolute 0 in %1" ).arg( mCursorName ) );
mFeatureQueue.clear();
mFetched = 0;
mLastFetch = false;
return true;
}
rewind()重置检索,将要素的迭代返回到初始状态。
(3) closed() override
bool QgsPostgresFeatureIterator::close()
{
if ( !mConn )
return false;
mConn->closeCursor( mCursorName );
if ( !mIsTransactionConnection )
{
QgsPostgresConnPool::instance()->releaseConnection( mConn );
}
mConn = nullptr;
while ( !mFeatureQueue.empty() )
{
mFeatureQueue.dequeue();
}
iteratorClosed();
mClosed = true;
return true;
}
关闭该迭代器,关闭了数据库连接的游标,并将要素队列的数据全部出队。
(4) fetchFeature() override
bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature &feature )
{
......
while ( true )
{
// 1)
if ( mFeatureQueue.empty() && !mLastFetch )
{
QString fetch = QStringLiteral( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
mConn->PQsendQuery( fetch );
queryResult = mConn->PQgetResult();
int rows = queryResult.PQntuples();
mLastFetch = rows < mFeatureQueueSize;
for ( int row = 0; row < rows; row++ )
{
mFeatureQueue.enqueue( QgsFeature() );
getFeature( queryResult, row, mFeatureQueue.back() );
} // for each row in queue
}// if
.......
// 2)
feature = mFeatureQueue.dequeue();
mFetched++;
// 3)
geometryToDestinationCrs( feature, mTransform );
if ( mDistanceWithinEngine && mDistanceWithinEngine->distance( feature.geometry().constGet() ) > mRequest.distanceWithin() )
{
continue;
}
// 4)
feature.setValid( true );
feature.setFields( mSource->mFields ); // allow name-based attribute lookups
return true;
}// while(true)
// 5)
close();
mSource->mShared->ensureFeaturesCountedAtLeast( mFetched );
}
fetchFeature()从数据源每次获取一个要素。所以该函数会被多次调用,直至要素提取完后,调用close()关闭迭代器。
- 如果要素队列为空并且不是最后的fetch,则从数据库获取数据,并将新feature入队,再调用getFeature()获取feature的具体数据和属性值等。
- 每次fetchFeature()从队列中出队一个feature,并将mFetched自增1。
- 将要素几何转到对应的坐标系。
- 设置要素的属性字段。
- 要素获取完后,关闭迭代器。
(5) nextFeatureFilterExpression() override
bool QgsPostgresFeatureIterator::nextFeatureFilterExpression( QgsFeature &f )
{
if ( !mExpressionCompiled )
return QgsAbstractFeatureIterator::nextFeatureFilterExpression( f );
else
return fetchFeature( f );
}
带过滤表达式的获取下一个要素,根据QgsAbstractFeatureIterator中对该接口的描述,可以在内部将该函数重定向到fetchFeature()。
(6) getFeature()
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
......
if ( mFetchGeometry )
{
......
// 1)
int returnedLength = ::PQgetlength( queryResult.result(), row, col );
if(returnedLength > 0)
{
unsigned char *featureGeom = new unsigned char[returnedLength + 1];
memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
memset( featureGeom + returnedLength, 0, 1 );
......
QgsGeometry g;
g.fromWkb( featureGeom, returnedLength + 1 );
feature.setGeometry( g );
......
}
}
// 2)
QgsFeatureId fid = 0;
......
feature.setId( fid );
// 3)
// iterate attributes
if ( subsetOfAttributes )
{
const auto constFetchAttributes = fetchAttributes;
for ( int idx : constFetchAttributes )
getFeatureAttribute( idx, queryResult, row, col, feature );
}
else
{
for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
getFeatureAttribute( idx, queryResult, row, col, feature );
}
}
getFeature()从sql结果中获取具体的feature数据。
- 通过mFetchGeometry判断是否获取几何,如果要获取的话,从sql结果的第一列获取到wkb数据。调用setGeometry()设置feature的几何信息。
- 获取featureId。
- 调用getFeatureAttribute()获取要素的具体属性。
(7) getFeatureAttribute()
void QgsPostgresFeatureIterator::getFeatureAttribute( int idx, QgsPostgresResult &queryResult, int row, int &col, QgsFeature &feature )
{
// 1)
if ( mSource->mPrimaryKeyAttrs.contains( idx ) )
return;
// 2)
const QgsField fld = mSource->mFields.at( idx );
// 3)
switch ( fld.type() )
{
case QVariant::ByteArray:
{
......
v = QgsPostgresProvider::convertValue( fld.type(), fld.subType(), queryResult.PQgetvalue( row, col ), fld.typeName(), mConn );
}
}
// 4)
feature.setAttribute( idx, v );
}
从sql数据集里获取具体的feature属性。
- 主键属性则跳过。
- 从FeatureSource找出当前的字段。
- 判断字段类型,并取出属性值。
- 为feature设置属性值。
(8) declareCursor()
bool QgsPostgresFeatureIterator::declareCursor( const QString &whereClause, long limit, bool closeOnFail, const QString &orderBy )
{
QString query( QStringLiteral( "SELECT " ) );
QString delim;
// 1)
if ( mFetchGeometry )
{
......
geom = QStringLiteral( "%1(%2,'%3')" )
.arg( mConn->majorVersion() < 2 ? "asbinary" : "st_asbinary",
geom,
QgsPostgresProvider::endianString() );
query += delim + geom;
......
}
// 2)
switch ( mSource->mPrimaryKeyType )
{
case PktOid:
query += delim + "oid";
delim = ',';
break;
......
}
......
// 3)
const auto constAllAttributesList = subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
for ( int idx : constAllAttributesList )
{
if ( mSource->mPrimaryKeyAttrs.contains( idx ) )
continue;
query += delim + mConn->fieldExpression( mSource->mFields.at( idx ) );
}
// 4)
query += " FROM " + mSource->mQuery;
if ( !whereClause.isEmpty() )
query += QStringLiteral( " WHERE %1" ).arg( whereClause );
if ( limit >= 0 )
query += QStringLiteral( " LIMIT %1" ).arg( limit );
if ( !orderBy.isEmpty() )
query += QStringLiteral( " ORDER BY %1 " ).arg( orderBy );
if ( !mConn->openCursor( mCursorName, query ) )
{
// reloading the fields might help next time around
// TODO how to cleanly force reload of fields? P->loadFields();
if ( closeOnFail )
close();
return false;
}
}
declareCursor()在构造函数中被调用,用以开启数据库游标。
- 追加几何列查询。
- 追加主键查询语句。
- 追加属性字段的查询,通过mRequest的标志位判断是否查询属性子集,当进行地图缩放时,mRequest会要求不必查询属性字段,只查几何字段。当打开属性表时,才会从mRequest中获取到所有的属性字段。
- 完善过滤条件,开启游标。
(9) whereClauseRect()
QString QgsPostgresFeatureIterator::whereClauseRect()
{
QgsRectangle rect = mFilterRect;
QString qBox;
const QString bboxSrid = mSource->mRequestedSrid.isEmpty() ? mSource->mDetectedSrid : mSource->mRequestedSrid;
if ( mConn->majorVersion() < 2 )
{
qBox = QStringLiteral( "setsrid('BOX3D(%1)'::box3d,%2)" )
.arg( rect.asWktCoordinates(),
bboxSrid );
}
else
{
qBox = QStringLiteral( "st_makeenvelope(%1,%2,%3,%4,%5)" )
.arg( qgsDoubleToString( rect.xMinimum() ),
qgsDoubleToString( rect.yMinimum() ),
qgsDoubleToString( rect.xMaximum() ),
qgsDoubleToString( rect.yMaximum() ),
bboxSrid );
}
......
// 1)
QString whereClause = QStringLiteral( "%1%2 && %3" )
.arg( QgsPostgresConn::quotedIdentifier( mSource->mBoundingBoxColumn ),
castToGeometry ? "::geometry" : "",
qBox );
}
whereClauseRect()增加范围过滤条件,过滤出规定范围内的要素。
在对地图进行缩放时会更新范围矩形,获取矩形范围内的要素。
使用要素拾取时,也会通过当前拾取范围过滤出范围内的全部要素从而进行要素拾取。
- 使用SQL的st_makeenvelope函数与shape字段进行&&操作,得到是否与矩形有交集。
网友评论