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矢量数据库图层类的源码,其他类型的编码思想基本类似。
<
2.QgsSpatiaLiteProvider
创建spatialite矢量图层对象的代码是:
QgsDataSourceUri uri;
uri.setDatabase(path);
uri.setTable(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)
{
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();
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 );
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;
...
if ( sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &sqliteStatement, nullptr ) != SQLITE_OK )
{
QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %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 )
{
return false;
}
if ( ret != SQLITE_ROW )
{
QgsMessageLog::logMessage( QObject::tr( "SQLite error getting feature: %1" ).arg( QString::fromUtf8( sqlite3_errmsg( mSqliteHandle ) ) ), QObject::tr( "SpatiaLite" ) );
return false;
}
if ( !mFetchGeometry )
{
feature.clearGeometry();
}
feature.initAttributes( mSource->mFields.count() );
feature.setFields( mSource->mFields );
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 )
{
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 );
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;
}
对图层的操作,本质上是对数据库进行操作,无外乎增删改查这些,当然空间数据库有其特有的性质。阅读源码过程中,按照和读取要素同样的道理,就很容易其他编码逻辑了,再去理解、调试、维护源码就容易得多了。
Original: https://blog.csdn.net/wenjunkai19881029/article/details/120563321
Author: kingkaa
Title: QGIS图层数据接口类源码解析
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/816324/
转载文章受原作者版权保护。转载请注明原作者出处!