美文网首页
QgsPostgresProvider源码分析(4)之QgsPo

QgsPostgresProvider源码分析(4)之QgsPo

作者: NullUser | 来源:发表于2022-06-27 17:31 被阅读0次

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()关闭迭代器。

  1. 如果要素队列为空并且不是最后的fetch,则从数据库获取数据,并将新feature入队,再调用getFeature()获取feature的具体数据和属性值等。
  2. 每次fetchFeature()从队列中出队一个feature,并将mFetched自增1。
  3. 将要素几何转到对应的坐标系。
  4. 设置要素的属性字段。
  5. 要素获取完后,关闭迭代器。

(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数据。

  1. 通过mFetchGeometry判断是否获取几何,如果要获取的话,从sql结果的第一列获取到wkb数据。调用setGeometry()设置feature的几何信息。
  2. 获取featureId。
  3. 调用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属性。

  1. 主键属性则跳过。
  2. 从FeatureSource找出当前的字段。
  3. 判断字段类型,并取出属性值。
  4. 为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()在构造函数中被调用,用以开启数据库游标。

  1. 追加几何列查询。
  2. 追加主键查询语句。
  3. 追加属性字段的查询,通过mRequest的标志位判断是否查询属性子集,当进行地图缩放时,mRequest会要求不必查询属性字段,只查几何字段。当打开属性表时,才会从mRequest中获取到所有的属性字段。
  4. 完善过滤条件,开启游标。

(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()增加范围过滤条件,过滤出规定范围内的要素。
在对地图进行缩放时会更新范围矩形,获取矩形范围内的要素。
使用要素拾取时,也会通过当前拾取范围过滤出范围内的全部要素从而进行要素拾取。

  1. 使用SQL的st_makeenvelope函数与shape字段进行&&操作,得到是否与矩形有交集。

相关文章

网友评论

      本文标题:QgsPostgresProvider源码分析(4)之QgsPo

      本文链接:https://www.haomeiwen.com/subject/xvbnvrtx.html