尧图精选

基于QT的GIS源码改造:从编译环境搭建到坐标转换与地图渲染实践

🕒 发布时间:2026/10/2 17:59:15 📁 来源:尧图网络
简介一套基于Qt开发的GIS地理信息系统完整源码面向GIS初学者、C/Qt开发者以及需要搭建桌面地图应用的工程师是理解GIS底层机制与工程实践的直观样例。项目运用Qt的信号与槽、模型/视图架构实现了从地图渲染到空间分析的一整套基础能力。源码可分为地图显示模块基于QGraphicsView加载与缩放瓦片、图层管理模块图层增删、排序与显示控制、空间数据模块借助GDAL/OGR读写Shapefile、GeoTIFF并通过proj完成投影转换、以及缓冲区分析、叠置分析等空间操作核心代码还包含WMS/WFS数据源接入与多线程缓存等性能优化设计。压缩包整体约103.94MB已有772人学习下载。研读这些代码不仅能弄清QGIS类系统的功能拆解与设计思路还能直接复用其模块骨架快速上手C/Qt环境下的GIS开发适合作为毕业设计、课设或行业项目二次开发的参考资料。1. 基于QT的GIS源代码为什么我劝你别从零写地图引擎上个月帮客户清理旧项目资产翻出一份从移动硬盘里扒出来的GIS地理信息系统源代码基于QT开发解压后就是一套带.pro文件的桌面地图框架。原本以为是练手demo等我把工程文件打开、编译、加载 shp 跑起来才发现这块小框架里挤着渲染、投影、拾取三条很不省心的技术线。如果你也在找一个能自己掌控、能继续改业务的 QT 地图框架与其硬啃 QGIS 源码不如先花点时间把这类源码包吃透它适合有 C 和 QT 基础、准备做轻量 GIS 客户端或教学演示工具的开发者。新手按着下面的步骤也能把它编译出来但你要做好心理准备这类源码包的坑九成不在功能而在环境和坐标转换上。这篇笔记就按我实际踩过的顺序来写。2. 先让源码跑起来QT版本、编译器与依赖库的三重选择2.1 版本决定成败为什么默认对齐 QT 5.15.2源码包解压后大概率带一个.pro文件。这个文件里的QT 、QMAKE_CXXFLAGS、LIBS等变量决定了你只能在什么版本、什么编译器组合下把它编出来。我前前后后帮人改过七八套同类框架结论很统一Qt 5.15.2 是这批源码的对齐基准。它是 Qt 5 分支里最后一个长期支持版本在 Win7、Win10 上都能跑而 Qt 6 把QDesktopWidget、QRegExp、部分 Multimedia 接口要么删除、要么挪了位置你直接把源码拖进 Qt 6 编译第一波报错基本全是这些历史接口。Qt 版本在这个项目里的表现我的建议5.12.8老版 LTS能编译但第三方 proj 库可能要求降级不推荐拿来做新改动顶多救老机器5.15.2Qt 5 最后一个 LTSAPI 完整与源码匹配度最高首选安装时勾选 MSVC 2019 64-bit 组件6.5 以上QDesktopWidget 等老接口被拆pro 文件要重写除非你想整体迁移到 Qt 6否则先别碰检查开发环境先跑这一条命令qmake -v只要输出里有Using Qt version 5.15.2说明你的 PATH 已经指向正确版本。如果机器上装过多个 Qt记得把C:\Qt\5.15.2\msvc2019_64\bin放到 PATH 最前面或者直接用 Qt Creator 的套件选择器切换。套件切不对时#include QtWidgets/QGraphicsView这类头文件搜索路径会全部串到别的版本上报错会像打翻的棋盘一样收不住。2.2 编译器二选一MSVC 2019 还是 MinGW 8.1拿到.pro之后要做的第二件事是决定工具链。我的习惯是先看 pro 里有没有针对 msvc 的写法比如QMAKE_CXXFLAGS /utf-8这种/开头的编译选项有就说明源码作者默认用 MSVC 工具链。MinGW 不是不能编碰撞点在于 proj、gdal 这类第三方依赖库通常只发布 MSVC 编译的.lib/.dllMinGW 去链会直接报format not recognized。所以在 Windows 上我一般默认选 Desktop Qt 5.15.2 MSVC2019 64bit 套件。命令行编译不想依赖 Qt Creator 时是这样走mkdir build cd build qmake ../GIS_QT.pro -spec win32-msvc CONFIGrelease nmake-spec win32-msvc是让 qmake 生成适合 nmake 的 MakefileCONFIGrelease会去掉调试符号运行速度更快、exe 更小。如果你执意用 MinGW把-spec换成win32-g最后一步换成mingw32-make -j8。两条路线千万别混MSVC 编译出的 moc 文件和目标文件放到 MinGW 的 Makefile 里链接阶段会炸得莫名其妙。2.3 动手编译前先改 pro、资源路径、编码这三处我打开这套源码的.pro文件后第一件事是把依赖模块写全顺便去掉CONFIG console——那个选项会在发布版启动时弹一个黑框交付给非技术人员很难解释清楚QT core gui xml network sql widgets CONFIG c11 msvc { QMAKE_CXXFLAGS /utf-8 } LIBS -L$$PWD/third_party/proj/lib -lproj INCLUDEPATH $$PWD/third_party/proj/includeQT core gui xml network sql widgets里xml 是配置文件解析用的network 是后面接在线底图要用的sql 是属性数据落库需要的如果你确实用不到网络功能删掉 network 模块能让打包体积小一些。LIBS -L$$PWD/third_party/proj/lib -lproj里的$$PWD是 qmake 内置变量指向当前 pro 所在目录很多人的编译错误恰恰是这里写死了绝对路径换一台机器就找不到库。然后是资源路径。源码默认去./data/shp读样例数据这个写法在开发机上碰巧能用一旦你从其他目录启动 exe 就崩。我一般统一改成基于运行目录的拼法QString dataDir QCoreApplication::applicationDirPath() /data;最后是编码。老框架的源码几乎必有中文注释和中文提示语MSVC 下必须依赖上面 pro 里的/utf-8编译选项如果你发现源码在 Qt Creator 里显示中文正常、编译出来却乱码把源文件另存为 UTF-8 with BOM 再试一次。这一条我后面避坑章还会再提。3. 把源码改造成自己能用的GIS框架图层、坐标与鼠标拾取的落地改法3.1 渲染管线从shp数据到QGraphicsPathItem这套框架的主窗口没有在 Qt Designer 里堆复杂布局而是一个QGraphicsView占满地图区域所有数据最终都转成QGraphicsItem挂到QGraphicsScene上。这个设计在 QGIS 里也能看到影子QgsMapCanvas 本质上就是场景加视图的思路。相比直接用 QWidget 重写paintEvent场景方案能白拿局部更新、碰撞检测、item 级 z 排序缩放时重绘负担小很多。shp 文件读出来后面要素转成QPolygonF再塞进QPainterPath生成QGraphicsPathItemvoid GisLayer::buildPathItems(const QPolygonF ring, int fid) { QPainterPath path; path.addPolygon(ring); QGraphicsPathItem *item new QGraphicsPathItem(path); item-setPen(QPen(QColor(255, 90, 0), 1.2)); item-setBrush(QColor(255, 180, 60, 70)); item-setData(0, fid); // 绑定要素ID后面拾取直接用 m_scene-addItem(item); }setData(0, fid)是给 item 挂一个业务主键鼠标点选时不需要再做几何交集查询直接取出来就能联动属性表。注意这里的线宽1.2是屏幕像素地图缩放后必须按比例修正否则放大看像一条粗钢筋。我的习惯是把所有 path item 放到同一个自定义图层对象里每次缩放结束后统一遍历修正一遍。3.2 三层坐标换算屏幕坐标、投影坐标、地理坐标的进出规则坐标转换是这类源码里最容易被当成玄学的部分。很多人直接把 shp 里的 X、Y 当作屏幕像素加到 scene 上结果图是出来了一换底图全错位。正确的做法是分三层数据本身的投影坐标、场景里的屏幕坐标、交互时需要的经纬度坐标。我的做法是打开 shp 时先把所有顶点转成 EPSG:3857Web 墨卡托然后用m_originX/m_originY视口左上角对应的投影坐标和m_scale每像素代表的米数换算到 scene 坐标。鼠标拿到的 scene 坐标反算回经纬度是这个函数QPointF GisView::sceneToGeo(const QPointF p) { double mx m_originX p.x() * m_scale; double my m_originY - p.y() * m_scale; double lon mx * 180.0 / 20037508.34; double lat 180.0 / M_PI * (2.0 * atan(exp(my * M_PI / 20037508.34)) - M_PI / 2.0); return QPointF(lon, lat); }注意 MSVC 下要先#define _USE_MATH_DEFINES再包含cmath否则M_PI不存在。这里的m_originY减号是因为投影坐标 Y 向上、屏幕坐标 Y 向下漏掉这个符号点位会在南北方向上直接镜像。如果 shp 原始坐标系不是 3857务必在进入渲染管线之前用 proj 库转好而不是在 scene 坐标上做平移——这个错误就是后面 4.3 节数据对不齐的根源。3.3 鼠标点选要素拾取命中与高亮在 QGraphicsView 上做要素拾取非常直接因为 scene 帮你做了碰撞检测。核心逻辑在mousePressEvent里void GisView::mousePressEvent(QMouseEvent *e) { QPointF scenePos mapToScene(e-pos()); const QListQGraphicsItem * items scene()-items(scenePos); for (QGraphicsItem *it : items) { if (it-type() ! QGraphicsPathItem::Type) continue; // 过滤掉底图瓦片和辅助线 int fid it-data(0).toInt(); highlightFeature(fid); // 旧高亮复位新高亮置顶 emit featureSelected(fid); // 联动属性表 break; } QGraphicsView::mousePressEvent(e); }scene()-items()默认按 z 值从高到低返回命中项所以要先判断type()过滤掉底图图层。高亮功能不要新建 item而是把上次高亮的 item 复位、这次命中的 item 的 pen 加粗并setZValue(10)提到最上层否则地图上会堆满高亮残留对象。如果业务上需要双击编辑几何注意在mouseDoubleClickEvent里做幂等处理否则第一次双击会同时触发两次 press 拾取。4. 编译运行避坑记录五个现场与对应的解决办法源码本身不会骗人骗人的都是环境。下面五条是我实际修这套基于 QT 的 GIS 源码时碰到的最高频问题按现象、原因、解决记在这里。4.1 运行时报 qt.qpa.plugin: could not find the qt platform plugin linuxfb现象把程序放到无显示服务器的机器或嵌入式板子上运行控制台输出qt.qpa.plugin: could not find the qt platform plugin linuxfb in ...后退出。原因Qt 要通过 QPA 插件连接具体的显示系统。要么环境变量QT_QPA_PLATFORM被指定成了linuxfb但系统里没有这个插件要么 platform 插件目录不在 Qt 库的搜索路径里。解决桌面 Linux 服务器上改用xcb无界面自动化测试用offscreenexport QT_QPA_PLATFORMxcb ./GisAppWindows 上如果看到could not find the qt platform plugin windows检查 exe 同目录下是否存在platforms\qwindows.dll缺的话用windeployqt GisApp.exe补齐并把 Qt 的 bin 目录加进 PATH。这类缺插件问题本质是部署时没把运行时打全不是源码逻辑错误。4.2 编译期 cannot find -lproj 与依赖库位数不匹配现象链接阶段报cannot find -lproj后面跟着ld returned 1 exit status。原因pro 里的-lproj让链接器去找proj.lib但-L指到的目录里没有这个文件或者依赖库是 64 位、你当前编译器是 32 位链接器直接忽略。解决先确认库文件的位数再用相对路径改 proLIBS -L$$PWD/third_party/proj/lib/x64 -lproj INCLUDEPATH $$PWD/third_party/proj/include-L指定搜索目录-lproj让链接器在数组里找proj.lib或libproj.a。如果库和编译器位数不匹配用 Dependencies 工具打开 proj.dll 看目标机器架构确认是 x64 再换到 MSVC2019 64bit 套件。这个错经常在别人发你的源码包、而你自己装的 Qt 是 32 位时出现别急着重装 Qt先查位数。4.3 GIS 图层与底图数据对不齐现象shp 里的房屋轮廓和天地图底图错位几十米到几百米越往图幅边缘越明显。原因shp 是 CGCS2000 高斯投影EPSG:4547 这一类底图是 EPSG:3857代码里没做投影转换直接把高斯坐标当成米来用。边缘误差大是因为高斯投影本身在分带边缘变形明显。解决用 proj 库在进入渲染前统一转 EPSG:3857PJ *trans proj_create_crs_to_crs(nullptr, EPSG:4547, EPSG:3857, nullptr); double x 526000.0, y 3568000.0; proj_trans_generic(trans, PJ_FWD, x, 0, 1, y, 0, 1, nullptr, 0, 0); proj_destroy(trans);坐标转换必须发生在加载图层时而不是在 scene 坐标上做加减平移。平移只能临时对齐一个点整幅图换到另一个区域又会偏。代码里如果出现offsetX/offsetY这类“手动纠偏”变量基本就是投影转换没做干净的信号。4.4 属性表中文乱码GBK、UTF-8 与 /utf-8 编译选项现象属性表里中文变成??或者经典的“锟斤拷”。原因老式 dbf 文件的字段编码多为 GBK/GB2312而解析代码按 UTF-8 或 Latin1 转成 QString另外源码文件本身的中文字符串字面量在 MSVC 下被按本地代码页解读。解决读取 dbf 字段时显式指定 GBKQTextCodec *codec QTextCodec::codecForName(GBK); QString name codec-toUnicode(dbfRawText);同时在 pro 里保留QMAKE_CXXFLAGS /utf-8处理你自己源码里的中文字符串。还没有好转就把源文件另存为 UTF-8 with BOM。注意 dbf 编码不一定是 GBK也有 UTF-8 的新数据稳妥做法是先读 dbf 头部的语言驱动标识再决定用哪个 codec。4.5 地图缩放后闪烁、线宽失控现象滚轮缩放时地图闪烁拖影放大后线变成钢筋缩小后线又细到看不见。原因QGraphicsView 默认全量重绘缩放时重绘范围过大同时 path item 的 pen 宽度是固定像素没有跟随地图比例尺变化。解决设置更新模式为局部重绘并在每次缩放后修正线宽setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); void GisView::updatePenWidth(QGraphicsPathItem *it, double base) { double w base / m_scale; it-setPen(QPen(it-pen().color(), qMax(0.5, w))); }BoundingRectViewportUpdate只重绘变化区域比全图刷新快很多线宽除以m_scale后放大时线宽会变细视觉上保持与地理宽度一致。如果你发现修正后线宽频繁跳变检查是否在drawBackground里也放了同样的修正逻辑别让两个地方抢着改 pen。5. 进阶两步接天地图在线底图给矢量数据加“尖锐角”自检5.1 把天地图 WMTS 底图塞进 QGraphicsScene要给这套源码加在线底图最稳妥的合规路子是接天地图 WMTS 服务。先去天地图官网注册开发者申请一个浏览器端 key然后用QNetworkAccessManager请求瓦片。瓦片 URL 模板这样拼const QString key 你的天地图key; QString url QString( https://t0.tianditu.gov.cn/vec_w/wmts?serviceWMTSversion1.0.0 requestGetTilelayervecstyledefaulttilematrixsetw formattilesTileMatrix%1TileCol%2TileRow%3tk%4) .arg(z).arg(col).arg(row).arg(key); m_network-get(QNetworkRequest(QUrl(url)));天地图w坐标系的行列规则和通用 XYZ 瓦片一致z 从 1 开始下载的瓦片会按对应的行列号拼到你现有的 scene 坐标里。一个很实用的习惯在 Qt 里调通之前先把 URL 整个复制到浏览器地址栏能直接出图再去排查代码很多人“在线地图加载不了”的案例最后都出在参数顺序或tk没放在末尾。5.2 用几何自检把尖锐角扫出来几千个面的 shp人工检查尖锐角不现实。我习惯写一个逐顶点扫描的函数用向量夹角判断相邻边是否小于阈值小于 10 度就认为是个问题角bool hasSharpAngle(const QPolygonF poly, double maxAngleDeg 10.0) { double cosT cos(maxAngleDeg * M_PI / 180.0); int n poly.size(); for (int i 0; i n; i) { QVector2D a(poly[(i n - 1) % n] - poly[i]); QVector2D b(poly[(i 1) % n] - poly[i]); double c QVector2D::dotProduct(a, b) / (a.length() * b.length()); if (c cosT) return true; } return false; }阈值maxAngleDeg按业务需要调整夹角越小cos越接近 1把c cosT换成c -cosT还能顺手查出逆向重复点。查出来之后不要只记日志直接把该顶点画成一个红点圈在地图上。最后导出地图视图给非技术同事复核收尾做验证QImage img(1024, 768, QImage::Format_ARGB32); QPainter p(img); view-render(p); img.save(check_preview.png);非要说这套源码给我最大的教训就是地图框架的环境排查必须一条路走到底。从那以后我每次拿到新的 GIS 源码包都强制先过一遍 qmake -v、依赖库位数、编码、投影转换这四件事再碰业务逻辑。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →