我是靠谱客的博主 懵懂石头,最近开发中收集的这篇文章主要介绍QGIS图层数据接口类源码解析1.QGIS图层数据接口类2.QgsSpatiaLiteProvider3.getFeatures(),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1.QGIS图层数据接口类

常用的GIS数据类型是矢量、栅格,QGIS也提供了网格(mesh)数据模型mdalprovider类。矢量数据格式较为丰富,常见的如esri的shape file,CAD的dxf等都是属于ogr格式,所以新版本ogrdataprovider类内置到qgis_core中,不再作为单独的ogrprovider.dll插件。此外还有空间数据库格式的矢量图层,不同的数据库分别用相应的插件库进行实现,如spatial sqlite数据库的spatialiteprovider.dll插件库,postgres gis数据库图层的postgresprovider.dll插件。这里详细解析spatial sqlite矢量数据库图层类的源码,其他类型的编码思想基本类似。

«interface» QgsDataProvider «interface» QgsVectorDataProvider «interface» QgsFeatureSink «interface» QgsFeatureSource QgsSpatiaLiteProvider friend QgsSpatiaLiteFeatureSource ... void closeDb bool checkLayerType bool getGeometryDetails bool getTableGeometryDetails bool prepareStatement bool getFeature void updatePrimaryKeyCapabilities QString createIndexName QgsAbstractFeatureSource *featureSource() QgsPostgresProvider QgsOgrProvider «interface» QgsRasterDataProvider «interface» QgsRasterInterface QgsMapLayer QgsVectorLayer QgsVectorDataProvider* mDataProvider; QgsDataProvider *dataProvider()

2.QgsSpatiaLiteProvider

创建spatialite矢量图层对象的代码是:

QgsDataSourceUri uri;
uri.setDatabase(path);//path是sqlite数据库文件的路径
uri.setTable(tableName);//tableName是矢量图层名称
uri.setGeometryColumn("geom");//矢量图层对应数据库中表的几何字段
uri.setKeyColumn("pk");//表的键值字段,建立空间索引必须有键值字段
QgsVectorLayer* layer = new QgsVectorLayer(uri.uri(), tableName, "spatialite");

如果有必要可以继续对该图层创建空间索引,这段代码展示了图层创建索引的过程,也为了说明图层对象的操作最终由插件库执行的实现逻辑。

//如果没有空间索引,则创建索引
//没有创建索引权限的时候,会抛出不能创建索引的异常
if (layer->hasSpatialIndex() != QgsFeatureSource::SpatialIndexPresence::SpatialIndexPresent)
{
const QString providerName{ layer->dataProvider()->name() };
QgsProviderMetadata *providerMetadata{ QgsProviderRegistry::instance()->providerMetadata(providerName) };
if (providerMetadata)
{
// Retrieve the DB connection (if any)
std::unique_ptr< QgsAbstractDatabaseProviderConnection > conn{ static_cast<QgsAbstractDatabaseProviderConnection *>(providerMetadata->createConnection(layer->dataProvider()->uri().uri(),{})) };
if (conn)
{
QString tableSchema;
QString tableName;
const QVariantMap sourceParts = providerMetadata->decodeUri(layer->source());
tableName = sourceParts.value(QStringLiteral("layerName")).toString();
// This works for PG and spatialite
if (tableName.isEmpty())
{
tableName = sourceParts.value(QStringLiteral("table")).toString();
tableSchema = sourceParts.value(QStringLiteral("schema")).toString();
}
conn->createSpatialIndex(tableSchema, tableName);
}
}
}

当然,在spatial sqlite中建立空间索引又引用了spatialsqlite和sqlite3这两个开源库,这些开源库又在qgis的插件库中做了一层封装。关于这两个开源库的用法不再详述。

通过图层对象可以获取其对应的dataprovider指针对象,根据不同的图层类型,运行时确定具体的dataprovider类型。图层对象与数据库的交互操作本质上都将由dataprovider指针对象来执行。那么,可以判定dataprovider对象是在QgsVectorLayer的构造函数中创建的。实际上,源码中在构造函数内调用了setDataSource()函数,setDataSource再调用setDataProvider()函数,从而构造了dataprovider对象,如下:

bool QgsVectorLayer::setDataProvider( QString const &provider,..)
{
mDataProvider = qobject_cast<QgsVectorDataProvider *>(
QgsProviderRegistry::instance()->createProvider( provider, mDataSource, options,flags
));
}

3.getFeatures()

以读取矢量图层要素的源码为例,说明QGIS的编程思想。

QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest &request ) const
{
if ( !isValid() || !mDataProvider )
return QgsFeatureIterator();
return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( new QgsVectorLayerFeatureSource( this ), true, request ) );
}

在QgsVectorLayerFeatureSource类中,获取到dataprovider对象的数据源:

QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *layer )
{
QMutexLocker locker( &layer->mFeatureSourceConstructorMutex );
//将读取图层的要素,转换为从图层的dataprovider中读取要素
//那么,将由QgsSpatiaLiteFeatureSource类来实现具体的读取功能
mProviderFeatureSource.reset( layer->dataProvider()->featureSource() );
mFields = layer->fields();
mId = layer->id();
...
}

最终,实际读取要素的代码是:

bool QgsSpatiaLiteFeatureIterator::prepareStatement( const QString &whereClause, long limit, const QString &orderBy )
{
if ( !mSqliteHandle )
return false;
try
{
QString sql = QStringLiteral( "SELECT %1" ).arg( mHasPrimaryKey ? quotedPrimaryKey() : QStringLiteral( "0" ) );
int colIdx = 1; // column 0 is primary key
...
//省略掉组织sql语句的代码
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &sqliteStatement, nullptr ) != SQLITE_OK )
{
// some error occurred
QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2nSQL: %1" ).arg( sql, sqlite3_errmsg( mSqliteHandle ) ), QObject::tr( "SpatiaLite" ) );
return false;
}
}
catch ( QgsSpatiaLiteProvider::SLFieldNotFound )
{
rewind();
return false;
}
return true;
}
bool QgsSpatiaLiteFeatureIterator::getFeature( sqlite3_stmt *stmt, QgsFeature &feature )
{
bool subsetAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
int ret = sqlite3_step( stmt );
if ( ret == SQLITE_DONE )
{
// there are no more rows to fetch
return false;
}
if ( ret != SQLITE_ROW )
{
// some unexpected error occurred
QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( mSqliteHandle ) ) ), QObject::tr( "SpatiaLite" ) );
return false;
}
// one valid row has been fetched from the result set
if ( !mFetchGeometry )
{
// no geometry was required
feature.clearGeometry();
}
feature.initAttributes( mSource->mFields.count() );
feature.setFields( mSource->mFields ); // allow name-based attribute lookups
int ic;
int n_columns = sqlite3_column_count( stmt );
for ( ic = 0; ic < n_columns; ic++ )
{
if ( ic == 0 )
{
if ( mHasPrimaryKey && sqlite3_column_type( stmt, ic ) == SQLITE_INTEGER )
{
// first column always contains the ROWID (or the primary key)
QgsFeatureId fid = sqlite3_column_int64( stmt, ic );
QgsDebugMsgLevel( QStringLiteral( "fid=%1" ).arg( fid ), 3 );
feature.setId( fid );
}
else
{
QgsDebugMsgLevel( QStringLiteral( "Primary key is not an integer field: setting autoincrement fid" ), 3 );
// autoincrement a row number
mRowNumber++;
feature.setId( mRowNumber );
}
}
else if ( mFetchGeometry && ic == mGeomColIdx )
{
getFeatureGeometry( stmt, ic, feature );
}
else
{
if ( subsetAttributes )
{
if ( ic <= mRequest.subsetOfAttributes().size() )
{
const int attrIndex = mRequest.subsetOfAttributes().at( ic - 1 );
const QgsField field = mSource->mFields.at( attrIndex );
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, field.type(), field.subType() ) );
}
}
else
{
const int attrIndex = ic - 1;
const QgsField field = mSource->mFields.at( attrIndex );
feature.setAttribute( attrIndex, getFeatureAttribute( stmt, ic, field.type(), field.subType() ) );
}
}
}
return true;
}

对图层的操作,本质上是对数据库进行操作,无外乎增删改查这些,当然空间数据库有其特有的性质。阅读源码过程中,按照和读取要素同样的道理,就很容易其他编码逻辑了,再去理解、调试、维护源码就容易得多了。

最后

以上就是懵懂石头为你收集整理的QGIS图层数据接口类源码解析1.QGIS图层数据接口类2.QgsSpatiaLiteProvider3.getFeatures()的全部内容,希望文章能够帮你解决QGIS图层数据接口类源码解析1.QGIS图层数据接口类2.QgsSpatiaLiteProvider3.getFeatures()所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部