美文网首页
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