概述
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);//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()所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复