如何用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
相关产品推荐
相关产品推荐

