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矢量图层对象的代码是:
1
2
3
4
5
6
7QgsDataSourceUri uri; uri.setDatabase(path);//path是sqlite数据库文件的路径 uri.setTable(tableName);//tableName是矢量图层名称 uri.setGeometryColumn("geom");//矢量图层对应数据库中表的几何字段 uri.setKeyColumn("pk");//表的键值字段,建立空间索引必须有键值字段 QgsVectorLayer* layer = new QgsVectorLayer(uri.uri(), tableName, "spatialite");
如果有必要可以继续对该图层创建空间索引,这段代码展示了图层创建索引的过程,也为了说明图层对象的操作最终由插件库执行的实现逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//如果没有空间索引,则创建索引 //没有创建索引权限的时候,会抛出不能创建索引的异常 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对象,如下:
1
2
3
4
5
6
7bool QgsVectorLayer::setDataProvider( QString const &provider,..) { mDataProvider = qobject_cast<QgsVectorDataProvider *>( QgsProviderRegistry::instance()->createProvider( provider, mDataSource, options,flags )); }
3.getFeatures()
以读取矢量图层要素的源码为例,说明QGIS的编程思想。
1
2
3
4
5
6
7QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest &request ) const { if ( !isValid() || !mDataProvider ) return QgsFeatureIterator(); return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( new QgsVectorLayerFeatureSource( this ), true, request ) ); }
在QgsVectorLayerFeatureSource类中,获取到dataprovider对象的数据源:
1
2
3
4
5
6
7
8
9
10
11QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *layer ) { QMutexLocker locker( &layer->mFeatureSourceConstructorMutex ); //将读取图层的要素,转换为从图层的dataprovider中读取要素 //那么,将由QgsSpatiaLiteFeatureSource类来实现具体的读取功能 mProviderFeatureSource.reset( layer->dataProvider()->featureSource() ); mFields = layer->fields(); mId = layer->id(); ... }
最终,实际读取要素的代码是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94bool 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内容请搜索靠谱客的其他文章。
发表评论 取消回复