You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Qt结合QGIS插件以C++完成波兰GIS相关任务?

已完成的GIS基础工作
  • 安装带PostGIS空间扩展的PostgreSQL数据库
  • 导入波兰国界及省界SHP文件
  • 创建points(X,Y,Z)表
  • 通过pqxx库实现数据库连接,代码如下:
#include <string>
#include <iostream>
#include <pqxx/pqxx>
#include <random>
#include <pqxx/stream_to.hxx>    
#include <pqxx/transaction_base.hxx>
#include <pqxx/stream_from.hxx>

int main()
{

    std::string connectionString = "host=localhost port=5432 dbname=test user=postgres password =Kotarba2020";

    try
    {
        pqxx::connection connectionObject(connectionString.c_str());
        pqxx::work worker(connectionObject);

        pqxx::result response = worker.exec("SELECT * FROM Wojewodztwa");

        for (size_t i = 0; i < response.size(); i++)
        {
            std::cout << "Id: " << response[i][0] << " X: " << response[i][1] << " Y: " << response[i][2] << " Z: " << response[i][3] << std::endl;
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    system("pause");
}

剩余任务解决方案

1. 生成波兰境内1000+随机点并保存到points表

利用PostGIS的空间范围约束确保点在波兰境内,结合C++随机数生成Z值,用pqxx流批量插入提升效率:

void generateRandomPoints(pqxx::connection& conn) {
    pqxx::work txn(conn);
    // 获取波兰国界的边界范围(假设国界表为poland_boundary,geom为边界几何字段)
    pqxx::result bounds = txn.exec("SELECT ST_XMin(geom), ST_YMin(geom), ST_XMax(geom), ST_YMax(geom) FROM poland_boundary");
    double minX = bounds[0][0].as<double>();
    double minY = bounds[0][1].as<double>();
    double maxX = bounds[0][2].as<double>();
    double maxY = bounds[0][3].as<double>();

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> distX(minX, maxX);
    std::uniform_real_distribution<> distY(minY, maxY);
    std::uniform_real_distribution<> distZ(0.0, 300.0);

    pqxx::stream_to stream(txn, "points");
    stream << "X" << "Y" << "Z"; // 指定表列名
    for (int i = 0; i < 1000; ++i) {
        double x, y;
        // 循环验证点是否在波兰境内
        do {
            x = distX(gen);
            y = distY(gen);
            pqxx::result res = txn.exec_params(
                "SELECT ST_Contains((SELECT geom FROM poland_boundary), ST_SetSRID(ST_MakePoint($1, $2), 4326))",
                x, y
            );
        } while (!res[0][0].as<bool>());
        double z = distZ(gen);
        stream << x << y << z;
    }
    stream.complete();
    txn.commit();
}

注:若国界表使用非WGS84(EPSG:4326)坐标系,需调整ST_SetSRID的参数值。

2. 验证生成点间距至少3公里

通过PostGIS的ST_DWithin函数批量检查点间距,C++中执行SQL查询验证:

bool verifyPointSpacing(pqxx::connection& conn) {
    pqxx::work txn(conn);
    // 用球面距离计算(单位米),无需转换投影
    pqxx::result res = txn.exec(
        "SELECT COUNT(*) FROM points p1 JOIN points p2 ON p1.ctid != p2.ctid "
        "WHERE ST_DWithin(ST_SetSRID(ST_MakePoint(p1.X, p1.Y), 4326), ST_SetSRID(ST_MakePoint(p2.X, p2.Y), 4326), 3000, true)"
    );
    int count = res[0][0].as<int>();
    txn.commit();
    return count == 0;
}

3. 创建省界表并导入数据

若未创建省界表,可通过C++执行SQL创建,或调用PostGIS的shp2pgsql工具导入SHP文件:

void createVoivodeshipTable(pqxx::connection& conn) {
    pqxx::work txn(conn);
    txn.exec(
        "CREATE TABLE IF NOT EXISTS Wojewodztwa ("
        "id SERIAL PRIMARY KEY,"
        "name VARCHAR(100),"
        "geom GEOMETRY(MultiPolygon, 4326)"
        ")"
    );
    txn.commit();
    // 调用系统命令导入SHP文件(需替换为你的SHP路径)
    system("shp2pgsql -s 4326 voivodeships.shp Wojewodztwa | psql -d test -U postgres");
}

4. 按省份统计生成点

通过PostGIS空间包含查询,C++中执行并返回统计结果:

std::map<std::string, int> countPointsByVoivodeship(pqxx::connection& conn) {
    std::map<std::string, int> stats;
    pqxx::work txn(conn);
    pqxx::result res = txn.exec(
        "SELECT w.name, COUNT(p.X) as point_count "
        "FROM Wojewodztwa w LEFT JOIN points p "
        "ON ST_Contains(w.geom, ST_SetSRID(ST_MakePoint(p.X, p.Y), 4326)) "
        "GROUP BY w.name"
    );
    for (auto& row : res) {
        stats[row[0].as<std::string>()] = row[1].as<int>();
    }
    txn.commit();
    return stats;
}

5. Qt结合QGIS绘制GIS元素

需创建Qt GUI项目并集成QGIS库,以下是核心实现代码:

5.1 Qt项目配置(.pro文件)

QT += core gui widgets
LIBS += -lqgis_core -lqgis_gui
INCLUDEPATH += /usr/include/qgis # 根据你的QGIS安装路径调整

5.2 绘制省界、分省着色点及三角剖分

#include <QMainWindow>
#include <qgsmapcanvas.h>
#include <qgsvectorlayer.h>
#include <qgsmarkerSymbol.h>
#include <qgsfillSymbol.h>
#include <qgsproject.h>
#include <qgscategorizedsymbolrenderer.h>

class GISWindow : public QMainWindow {
    Q_OBJECT
public:
    GISWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        // 初始化地图画布
        m_canvas = new QgsMapCanvas(this);
        setCentralWidget(m_canvas);

        // 加载省界图层
        QgsVectorLayer* voivodeshipLayer = new QgsVectorLayer(
            "dbname='test' host=localhost port=5432 user='postgres' password='Kotarba2020' table=\"Wojewodztwa\" (geom) sql=",
            "Voivodeships", "postgres"
        );
        if (voivodeshipLayer->isValid()) {
            QgsFillSymbol* fillSymbol = QgsFillSymbol::createSimple({{"color", "#f0f0f0"}, {"outline_color", "#000000"}});
            voivodeshipLayer->setRenderer(new QgsSingleSymbolRenderer(fillSymbol));
            QgsProject::instance()->addMapLayer(voivodeshipLayer);
        }

        // 加载生成点图层并分省着色
        QgsVectorLayer* pointsLayer = new QgsVectorLayer(
            "dbname='test' host=localhost port=5432 user='postgres' password='Kotarba2020' table=\"points\" (X,Y) sql=",
            "Random Points", "postgres"
        );
        if (pointsLayer->isValid()) {
            // 连接数据库获取省份统计结果
            pqxx::connection conn("host=localhost port=5432 dbname=test user=postgres password=Kotarba2020");
            auto stats = countPointsByVoivodeship(conn);

            QgsCategorizedSymbolRenderer* renderer = new QgsCategorizedSymbolRenderer();
            int idx = 0;
            for (auto& pair : stats) {
                QColor color = QColor::fromHslF((double)idx/stats.size(), 0.7, 0.5);
                QgsMarkerSymbol* symbol = QgsMarkerSymbol::createSimple({{"color", color.name()}, {"size", "2"}});
                renderer->addCategory(QgsRendererCategory(pair.first, symbol, pair.first));
                idx++;
            }
            pointsLayer->setRenderer(renderer);
            QgsProject::instance()->addMapLayer(pointsLayer);
        }

        // 生成并加载三角剖分图层(借助QGIS Processing工具)
        QgsProcessingContext context;
        QgsProcessingFeedback feedback;
        QVariantMap params;
        params["INPUT"] = pointsLayer->id();
        params["Z_FIELD"] = "Z";
        params["OUTPUT"] = "memory:TIN";
        QgsProcessingUtils::runAlgorithm("qgis:tininterpolation", params, context, &feedback);
        QgsVectorLayer* tinLayer = qobject_cast<QgsVectorLayer*>(QgsProject::instance()->mapLayer(params["OUTPUT"].toString()));
        if (tinLayer) {
            QgsFillSymbol* tinSymbol = QgsFillSymbol::createSimple({{"color", "#transparent"}, {"outline_color", "#666666"}});
            tinLayer->setRenderer(new QgsSingleSymbolRenderer(tinSymbol));
        }

        // 设置画布范围并刷新
        m_canvas->setExtent(voivodeshipLayer->extent());
        m_canvas->setLayers(QgsProject::instance()->mapLayers().values());
        m_canvas->refresh();
    }

private:
    QgsMapCanvas* m_canvas;
};

注:三角剖分使用QGIS内置的Processing工具实现,需确保项目已引入QGIS Processing模块。

内容的提问来源于stack exchange,提问作者Kuba Kotarba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:45:34