QGIS C++ 1. 地图工程 与 SLD geoserver

31 阅读2分钟

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的样式,因此可以办到无缝对接。