QGIS C++ 2. 地图工程 与 SLD 代码实战

5 阅读3分钟

QGIS C++ 2. 地图工程 与 SLD 代码实战

上一节讲解了如何SLD的基本原理和代码逻辑 这一章节就进入实战节奏。 自己实现C++ 插件来实现批量的SLD的自定义导出。

回到最开始的

基础

我们再实现一个C++的导出插件 ,按照之前的教程里面实现4个基本的部分

  • CMakeLists.txt 编译文件
  • sldexport.h sldexport.cpp qgis插件基础头文件和业务逻辑实现
  • sldexport.qrc 插件图片等资源目录
  • sldExportDock.h sldExportDock.cpp sldExportDock.ui qgis插件UI的风格和人机交互代码

下面就是一个新的SLD的C++导出插件的效果,相比原来的单个图层的不停的右键导出的重复操作,一下子就把什么操作批量化处理了。

核心代码

遍历图层

由于qgis的组图层在组织上是一层逻辑结构导致每一个实际的矢量图层是无法了解其具体的组图层的归属的。

遍历图层并提取组图层信息,除了基础的针对qgis的地图文档进行对应的一维数组的遍历以外,还需要通过顶层的layerTreeRoot进行对应的向上搜索的行为。

可以简单的理解为在物理上QGIS在绘制样式的时候图层是按照一个数组进行组织的,这样方便排序。在逻辑上有个树形的逻辑类来组织对应的父子关系。物理层图层的主键id是逻辑层图层的外键id。

void sldExportDock::listLayers() {
    mLayers.clear();
    mGroupName.clear();

    QgsLayerTree* layerTree = QgsProject::instance()->layerTreeRoot();
    QMap<QString, QgsMapLayer*> mapLayers = QgsProject::instance()->mapLayers();
    QMap<QString, QgsMapLayer*>::iterator layer_it = mapLayers.begin();

    for (; layer_it != mapLayers.end(); ++layer_it)
    {
        QgsMapLayer* vl = dynamic_cast<QgsMapLayer*>(layer_it.value());
        if (!vl) {
            continue;
        } else {
            if (layerTree) {
                QgsLayerTreeLayer *treeLayer = layerTree->findLayer(vl->id());
                if (treeLayer) {
                    QgsLayerTreeNode* layerParent = treeLayer->parent();
                    if (layerParent) {
                        mGroupName.push_back((layerParent->name()));
                    } else {
                        mGroupName.push_back("");
                    }
                }
            } else {
                mGroupName.push_back("");
            }
            mLayers.push_back(vl);
        }
    }
}

SLD转换

sld的转换逻辑相比上面清晰很多,直接把图层的组图层信息处理成新的目录进行导出即可。

void sldExportDock::on_excuteButton_clicked() {
    isGroup = checkGroupBox->isChecked();
    outputPath = selectDirctionEdit->text();
    // progressBar
    int count = mLayers.size();
    progressBar->setValue(0);
    if (mGroupName.size() == mLayers.size()) {
        for(int i = 0; i < count; i++) {
            QgsMapLayer* ml = mLayers[i];
            QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>(ml);
            QString groupname = mGroupName[i];
            if (vl) {
                QString url;
                QDir dir(outputPath);
                if (isGroup) {
                    dir.mkdir(groupname);
                    dir.cd(groupname);
                }
                QString path = dir.absolutePath();
                QFileInfo file(path, vl->name() + ".sld");
                bool resultFlag = false;
                vl->saveSldStyle(file.absoluteFilePath(), resultFlag);
            }
            progressBar->setValue(i * 100 / count);
        }
    }
    progressBar->setValue(100);
}

结果

我们可以发现可以批量处理SLD的转换了

直接结果

针对组图层同名的情况,可以通过文件夹的方式实现对应的区分,通过文件夹的组织方式实现对应的物理的组图层。

不同的文件夹在实际使用中处理成不同比例尺的数据是相对较为实用的技巧,一般不同比例尺的数据源是不同的,导致不同比例尺下存在相同的图层名称

看到上面其实不难发现,整个文件夹就是一棵SLD组成的树,即地图工程(mxd/qgs)。

展望

为什么需要大费周章的实现一个这样的功能呢,其实是和[开源库]之间的差异有关系,不同的开源体系的gis平台针对sld的组织和理解甚至实现是有细微的区别的,除非是很简单的样式才能在不同的GIS平台中直接使用。

下一节我将在geoserver中直接使用我们的sld来进行桌面端到服务端的跨平台调用。我们将直观的感受了不同厂商对OGC的理解的偏差共识(尤其是符号与制图这一块)。