QGIS C++ 1. 地图工程 与 SLD geoserver
之前上文介绍了 一个基础的地图工程的概念,接下来就详细的分析以下对应的原理以及代码实现。
首先导入和导出的入口都是图层右键属性对话框,主要介绍SLD的导入流程,导出同理。
流程
首先图层右键属性对话框 然后选择 load style(导入样式) 这一步是最关键的,当我们load style对话框结束后,其实只是临时在对话框中导入了样式,并没有把样式生效到图层上。为了使图层显示正确的效果,必须点击属性对话框中的右下角的ok/apply按钮才能生效。 通过上面的流程可以知道整个代码中至少有 属性对话框(QgsVectorLayerProperties) 加载样式对话框(QgsMapLayerLoadStyleDialog) 矢量图层(QgsVectorLayer)3个类的参与
UML类图
其UML-类图结构如下所示
其中唯一需要注意的就是,虽然Mapayer的loadldtyle是一个虚函数,但是其实际的实现还是在MapLayer,因此其子类VectorLayer实际上是继承的父类的MapLayer的实现。
UML-[时序图]
由于UML的时序图和实际的人机交互是高度匹配的,因此可以直观的分析其对应的代码结构。
核心代码
QgsMapLayer::*loadSldStyle -> QgsVectorLayer::readSld -> *QgsVectorLayer::setRenderer
loadSldStyle
简而言之就是读取sld的xml内容,赋值给QDomDocument对象
QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag )
{
resultFlag = false;
QDomDocument myDocument;
// location of problem associated with errorMsg
int line, column;
QString myErrorMessage;
if ( myFile.open( QFile::ReadOnly ) )
{
resultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
}
const QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "StyledLayerDescriptor" ) );
const QDomElement namedLayerElem = myRoot.firstChildElement( QStringLiteral( "NamedLayer" ) );
resultFlag = readSld( namedLayerElem, errorMsg );
}
readSld
获取QDomDocument对象中的实际值,并初始化一个renderer
bool QgsVectorLayer::readSld( const QDomNode &node, QString &errorMessage )
{
QDomElement nameElem = node.firstChildElement( QStringLiteral( "Name" ) );
QgsFeatureRenderer *r = QgsFeatureRenderer::loadSld( node, geometryType(), errorMessage );
// defer style changed signal until we've set the renderer, labeling, everything.
// we don't want multiple signals!
ScopedIntIncrementor styleChangedSignalBlocker( &mBlockStyleChangedSignal );
setRenderer( r );
// labeling
readSldLabeling( node );
styleChangedSignalBlocker.release();
}
setRenderer
激活renderer并使其生效
void QgsVectorLayer::setRenderer( QgsFeatureRenderer *r )
{
if ( r != mRenderer )
{
delete mRenderer;
mRenderer = r;
mSymbolFeatureCounted = false;
mSymbolFeatureCountMap.clear();
mSymbolFeatureIdMap.clear();
if ( mRenderer )
{
const double refreshRate = QgsSymbolLayerUtils::rendererFrameRate( mRenderer );
if ( refreshRate <= 0 )
{
mRefreshRendererTimer->stop();
mRefreshRendererTimer->setInterval( 0 );
}
else
{
mRefreshRendererTimer->setInterval( 1000 / refreshRate );
mRefreshRendererTimer->start();
}
}
emit rendererChanged();
}
}
整个流程就是上面这样了。
使用场景
本章分析了整个QGIS SLD的加载策略,那我们具体的使用场景是是什么样的呢?这就要回归到SLD的设计思想了。SLD是希望不同的平台针对图层显示保持统一的样式风格。因此QGIS生成的SLD需要给其他平台使用。
一般GIS的使用流程是桌面端配置地图工程 -> 服务端发布配好的地图工程 -> 发布各类服务 -> 应用层调用
因此SLD的下一环就是服务端使用,以开源的Geoserver为例,本身就是支持SLD的样式,因此可以办到无缝对接。