GIS Project Handbook 🗺️
基于 Qt/C++ 的 GIS 开发学习笔记与项目实战
📚 仓库简介
本仓库记录 GIS 开发的学习过程,重点聚焦:
- 安装:QT的下载安装全步骤
- 基础:项目所包含的技术点讲解以及原理
- 实战:Qt 离线/在线地图引擎开发以及运行中可能出现的问题
- 进阶:项目的高频面试题以及简历优化模板
这是一个功能完整的 Qt/C++ 地理信息系统(GIS)桌面应用程序。
你可以从这个项目学到什么?
| 序号 | 技能 | 难度 |
|---|---|---|
| 1 | Qt 界面编程(布局、控件、事件) | ⭐⭐ |
| 2 | QPainter 绘图(画图、画线、画文字) | ⭐⭐⭐ |
| 3 | 网络编程(HTTP 请求、JSON 解析) | ⭐⭐⭐ |
| 4 | 地图投影算法(墨卡托公式) | ⭐⭐⭐⭐ |
| 5 | 缓存设计与 LRU 淘汰 | ⭐⭐⭐ |
| 6 | C++17 现代特性(variant、visit) | ⭐⭐⭐⭐ |
| 7 | 异步编程思维(信号槽、回调) | ⭐⭐⭐ |
| 8 | API 集成与签名计算(MD5) | ⭐⭐⭐ |
| 9 | 坐标转换算法(WGS84/GCJ02/BD09) | ⭐⭐⭐⭐ |
| 10 | 完整的项目架构设计 | ⭐⭐⭐⭐⭐ |
运行这个项目需要什么?
- 开发环境:Qt 5.12+ 和 MinGW 编译器
- 网络:能访问高德、百度地图服务器
- API 密钥(个人开发不需要): - 百度地图 AK(访问路线规划、周边搜索) - 高德地图 Key(地理编码)
- C++17:项目配置文件中需添加
CONFIG += c++17
二、七天学习计划
适合初学者,每天 2-3 小时,目标是理解项目架构并能独立讲解/二次开发
第1天:环境搭建与项目概览
| 时间段 | 内容 |
|---|---|
| 30分钟 | 安装 Qt 5.12 + MinGW,配置环境变量 |
| 30分钟 | 克隆项目,编译运行,确认能看到地图 |
| 30分钟 | 浏览项目文件结构,理解每个文件的作用 |
| 30分钟 | 阅读 MainWindow.cpp 的 setupUi(),了解界面布局 |
| 30分钟 | 动手:修改窗口标题、调整左侧面板宽度,观察变化 |
学习目标:能无障碍编译运行,知道每个文件大致负责什么。
验收:成功修改地图初始中心点为上海(121.47, 31.23)。
第2天:Qt 基础与坐标转换算法
| 时间段 | 内容 |
|---|---|
| 45分钟 | 学习 Qt 信号槽机制(connect 用法、Lambda 表达式) |
| 45分钟 | 学习 Qt 绘图基础(QPainter:画图、画线、画文字) |
| 30分钟 | 阅读 CoordConvert.h 全部代码 |
| 30分钟 | 理解墨卡托投影公式(lngLatToTile / tileToLngLat) |
| 30分钟 | 动手:手写代码,将天安门坐标(116.397, 39.909)转换为瓦片坐标(z=12) |
学习目标:理解坐标转换原理,能写出墨卡托投影的关键公式。
验收:在纸上画出墨卡托投影的基本公式。
第3天:地图瓦片加载核心(最难的一天)
| 时间段 | 内容 |
|---|---|
| 30分钟 | 理解瓦片金字塔:什么是 z/x/y?0 级有 1 张,1 级有 4 张... |
| 45分钟 | 阅读 TileManager.h:TileId、TilePair、QHash 缓存 |
| 60分钟 | 阅读 TileManager.cpp 的 requestTile 和 buildUrl |
| 30分钟 | 理解混合模式的双缓存同步逻辑 |
| 30分钟 | 动手:修改 buildUrl 将高德源换成 OpenStreetMap 瓦片 |
学习目标:能解释瓦片加载的全流程(计算→检查缓存→异步下载→绘制)。
验收:完整口述一次请求瓦片到显示的全过程,并能够理解瓦片加载原理。
第4天:地图控件与鼠标交互
| 时间段 | 内容 |
|---|---|
| 30分钟 | 阅读 MapWidget 的 lngLatToScreen / screenToLngLat |
| 45分钟 | 理解 mousePressEvent / mouseMoveEvent 的拖拽逻辑 |
| 30分钟 | 理解 wheelEvent 的缩放逻辑 |
| 45分钟 | 阅读 drawTiles:如何计算可见瓦片范围 |
| 30分钟 | 动手:修改代码,让滚轮缩放速度加倍 |
学习目标:理解鼠标拖拽平移和滚轮缩放的数学原理。
验收:能用语言描述从鼠标拖拽到地图移动的计算过程。
第5天:覆盖物系统与交互绘制
| 时间段 | 内容 |
|---|---|
| 30分钟 | 阅读 Overlay.h:5 种覆盖物结构体 |
| 45分钟 | 学习 std::variant + std::visit 的使用 |
| 45分钟 | 阅读 drawOverlays:如何统一绘制不同的覆盖物 |
| 30分钟 | 阅读 mousePressEvent 中的绘制逻辑 + drawDrawingPreview |
| 30分钟 | 动手:添加一种新的覆盖物类型 —— 三角形 |
| 30分钟 | 总结:if constexpr + std::is_same_v 如何实现编译期多态 |
学习目标:理解 variant + visit 的使用场景和优势。
验收:能向面试官解释为什么要用 variant 而不是继承。
第6天:API 集成与网络请求
| 时间段 | 内容 |
|---|---|
| 30分钟 | 阅读 BaiduApi.h:了解提供了哪些服务 |
| 45分钟 | 阅读 BaiduApi.cpp 的 geocode / planRoute / areaSearch |
| 30分钟 | 理解 SN 签名算法(calcSn) |
| 30分钟 | 理解 onReplyFinished 中的 JSON 解析 |
| 30分钟 | 阅读 MainWindow.cpp 中如何连接 API 信号和界面 |
| 30分钟 | 动手:添加一个"当前位置天气"功能(调用和风天气 API) |
学习目标:能独立接入一个新的 REST API 并解析 JSON。
验收:成功添加天气查询功能并显示在日志面板。
第7天:项目总结与面试准备
| 时间段 | 内容 |
|---|---|
| 30分钟 | 画出完整的项目架构图(模块、依赖、数据流向) |
| 30分钟 | 整理 5 个最值得讲的亮点(混合同步、variant、LRU、异步、坐标转换) |
| 30分钟 | 模拟面试:口述项目难点 + 技术选型理由 |
| 30分钟 | 完善简历:将项目经历填入简历模板 |
| 30分钟 | 动手:录制 2-3 分钟的功能演示视频 |
| 30分钟 | 发布到 GitHub + 写 README + 推送到技术社区 |
学习目标:能自信地向面试官介绍项目,并展示代码。
验收:完成一篇技术博客或 GitHub README。
三、学习资料推荐
| 类型 | 资源 |
|---|---|
| Qt 官方文档 | Qt Documentation |
| 墨卡托投影 | Wikipedia - Mercator projection |
| GCJ02 原理 | 火星坐标系解密 |
| std::variant | C++17 特性详解 |
| 百度地图 API | 百度地图 Web API 文档 |
| 高德地图 API | 高德地图 Web API 文档 |
| 项目技术点讲解 | 刨析项目技术点,以及针对初学者的讲解 |
| 面试问题 | 涵盖该项目技术面试问题以及相关大厂面试题 |
| 简历模板 | 针对不同岗位对项目描写进行优化 |
四、学习进度追踪表
| 天数 | 核心模块 | 完成打 ✅ |
|---|---|---|
| Day1 | 环境搭建 + 项目运行 | ☐ |
| Day2 | Qt 基础 + 坐标转换 | ☐ |
| Day3 | 瓦片加载机制 | ☐ |
| Day4 | 鼠标交互 + 拖拽缩放 | ☐ |
| Day5 | 覆盖物系统 + variant | ☐ |
| Day6 | API 集成 + 网络请求 | ☐ |
| Day7 | 总结 + 面试准备 | ☐ |
五、常见问题速查
| 问题 | 答案位置 |
|---|---|
| 瓦片 URL 怎么构建的? | TileManager::buildUrl() |
| 混合模式如何同步? | requestTile 中的 hasBase + hasOverlay 判断 |
| 坐标转换精度多少? | CoordConvert 中的多项式拟合 |
| 缓存满时怎么淘汰? | 优先删 abs(key.z - currentZ) >= 2 的瓦片 |
| 信号槽怎么连接的? | MainWindow::setupConnections() |
祝学习顺利!如有任何问题,欢迎随时交流。
部分技术点和讲解展示:
部分技术点:
一、核心技术框架
- Qt 框架应用
- QWidget 体系:自定义地图控件
MapWidget继承自QWidget - 事件系统:重写
paintEvent、mousePressEvent、mouseMoveEvent、wheelEvent、resizeEvent - 信号与槽:大量使用 Qt 的信号槽机制进行模块间通信
- 网络模块:
QNetworkAccessManager异步 HTTP 请求瓦片和 API 数据 - 定时器:
QTimer实现轨迹回放的动画效果 - 布局管理:
QSplitter、QVBoxLayout、QHBoxLayout、QFormLayout等
2. C++ 现代特性(C++17)
std::variant:OverlayData使用 variant 存储多种覆盖物类型std::visit:遍历 variant 时使用,配合 lambda 模板实现类型安全的多态绘制std::clamp:限制缩放级别范围if constexpr:编译期条件判断,用于 variant 访问constexpr:编译期常量(如kTileSize)
二、地图渲染核心
3. 瓦片地图机制
- 墨卡托投影:Web 墨卡托投影(EPSG:3857)的经纬度与屏幕坐标转换
- 瓦片坐标系统:基于 TMS(Tile Map Service)标准,计算当前视口所需的瓦片
- 异步加载:瓦片通过网络异步下载,避免阻塞 UI
- LRU 缓存:
QHash<TileId, TilePair>实现瓦片缓存,限制最大数量 2048 - 混合模式支持:卫星图 + 透明路网叠加层,需等待两张图都加载完成才显示
4.坐标转换算法
// 经纬度 → 屏幕坐标(核心公式)
tX = (lng + 180) / 360 2^z
tY = (1 - ln(tan(lat π/180) + sec(lat π/180)) / (2π)) / 2 2^z
- 双向转换:
lngLatToScreen()和screenToLngLat() - 坐标系支持:WGS84、GCJ02(国测局)、BD09(百度)三种坐标系的相互转换
5. 地图交互
- 拖拽平移:鼠标拖拽时动态计算新中心点
- 滚轮缩放:改变
m_z后清空缓存并刷新 - 点击定位:点击地图发射
mapClicked信号,返回经纬度 详细请看技术点与原理
部分技术讲解
一、这个项目是做什么的?
这个项目就是用 C++ 和 Qt 自己实现一个简化版的地图应用,可以:
- 拖拽、缩放查看地图
- 在上面画点、线、圆、矩形
- 输入地址找到位置
- 规划从A到B的路线
- 搜索附近的餐厅
二、核心技术分层讲解
第一层:Qt 基础 - 什么是 Qt?
通俗理解:Qt 是一个工具箱,里面有很多现成的"零件",帮你快速开发带界面的程序。
// 你不需要自己写代码来创建窗口、按钮、处理鼠标点击
// Qt 已经做好了:
QWidget window = new QWidget(); // 创建一个窗口
QPushButton btn = new QPushButton("点我"); // 创建一个按钮
本项目用到的 Qt 组件:
| 组件 | 作用 | 比喻 |
|---|---|---|
| QWidget | 所有界面的基础 | 一张白纸 |
| QPainter | 画图(画瓦片、画点线面) | 一支笔 |
| QNetworkAccessManager | 从互联网下载地图图片 | 快递员 |
| QTimer | 定时执行任务(轨迹移动) | 闹钟 |
| QHash | 缓存瓦片图片 | 临时储物柜 |
| QVector | 存储点坐标列表 | 购物清单 |
| 详细请看技术点与原理 |
部分面试题展示
适用于:C++/Qt 开发工程师、GIS 开发工程师、客户端开发工程师面试
一、项目概述(30秒自我介绍)
项目名称:基于 Qt/C++ 的跨平台 GIS 桌面应用
核心功能:
- 多源地图瓦片加载(普通/卫星/混合模式)
- 交互式图形绘制(点/线/面/圆/矩形)
- 地理/逆地理编码、路线规划、POI 搜索
- 轨迹回放、坐标转换(WGS84/GCJ02/BD09)
技术栈:C++17、Qt 5.12、QNetworkAccessManager、高德/百度/OSM API
二、架构设计(面试官常问)
2.1 整体架构图
┌─────────────────────────────────────────────────────────────┐
│ MainWindow (UI层) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │地图初始化│ │覆盖物管理│ │API调用面板│ │日志与调试面板│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ 信号/槽
┌─────────────────────────────────────────────────────────────┐
│ MapWidget (核心控件) │
│ • 瓦片绘制与坐标转换 • 鼠标交互(拖拽/缩放/绘制) │
│ • 覆盖物渲染 • 轨迹回放动画 │
└─────────────────────────────────────────────────────────────┘
│ │
┌─────────┴─────────┐ ┌───────┴────────┐
▼ ▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ TileManager │ │ BaiduApi │ │ CoordConvert │
│ 瓦片请求/缓存/ │ │ 路线规划/搜索/ │ │ WGS84/GCJ02/ │
│ URL构建(高德源) │ │ 签名计算/JSON解析│ │ BD09转换 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
2.2 模块职责
| 模块 | 职责 | 关键类 / 结构 |
|---|---|---|
| UI 层 | 用户交互、参数输入、结果显示 | MainWindow |
| 地图控件 | 瓦片渲染、坐标转换、事件处理 | MapWidget |
| 瓦片管理 | 异步加载、LRU 缓存、多源 URL | TileManager、TileId |
| API 封装 | 网络请求、签名、JSON 解析 | BaiduApi |
| 覆盖物系统 | 类型安全的图形数据存储 | OverlayData(variant) |
| 坐标转换 | 三种坐标系互转 | CoordConvert |
三、核心技术点详解
3.1 地图瓦片渲染(必问)
Q:地图瓦片是怎么加载和显示的?
A:采用金字塔模型 + 异步加载 + LRU缓存架构。
// 瓦片坐标结构
struct TileId {
int x, y, z; // 列、行、缩放级别
QString type; // satellite / hybrid_road / normal
};
// 核心流程
void drawTiles(QPainter& p) {
// 1. 根据中心点和缩放计算可见瓦片范围
double n = pow(2.0, m_z);
int cTX = floor((m_centerLng + 180) / 360 * n);
// 2. 遍历可见瓦片
for (dy in -vT/2 .. vT/2) {
for (dx in -hT/2 .. hT/2) {
// 3. 请求瓦片(同步检查缓存,异步下载)
if (m_tiles->requestTile(id, tilePair)) {
p.drawPixmap(px, py, tilePair.base); // 绘制底图
p.drawPixmap(px, py, tilePair.overlay); // 绘制叠加层
} else {
p.fillRect(px, py, bgColor); // 占位色
}
}
}
}
技术要点:
- 墨卡托投影:
lat → log(tan(lat) + sec(lat))将球面坐标映射到平面 - 瓦片尺寸固定 256x256,便于缓存和拼接
- 异步下载 + 信号槽触发重绘,保证 UI 不卡顿 详细请面试问题
部分简历模板展示:
简历使用指南(精准匹配岗位)
| 模板编号 | 适合投递岗位 | 核心突出侧重点 |
|---|---|---|
| 模板一 | Qt/C++ 客户端开发工程师 | Qt框架应用、C++17特性、异步网络、客户端封装 |
| 模板二 | GIS 算法/开发工程师 | 坐标系转换、瓦片渲染、混合地图模式、GIS核心算法 |
| 模板三 | C++ 网络编程工程师 | HTTP异步请求、API集成、请求去重、缓存设计 |
| 模板四 | 图形学/渲染工程师 | QPainter渲染、几何计算、渲染性能优化 |
| 模板五 | 全栈GIS开发工程师 | 桌面端开发+多地图服务集成、全流程落地 |
| 模板六 | 应届生/校招(GIS/Qt方向) | 项目完整度、自主学习能力、动手能力 |
| 模板七 | Qt界面/交互工程师 | UI布局、QSS美化、交互设计、用户体验 |
| 模板八 | 后端/API集成工程师 | REST API调用、签名验证、数据解析、降级机制 |
| 模板九 | 算法/数据结构工程师 | 坐标转换算法、缓存淘汰、几何计算、单元测试 |
| 模板十 | 技术文档/培训/宣讲岗 | 文档撰写、成果演示、知识传播、教学能力 |
模板一:Qt/C++ 桌面开发工程师(纯客户端方向)
核心项目经验
跨平台 GIS 桌面客户端开发 | 核心开发者 | 技术栈:Qt 5.12、C++17、QNetworkAccessManager、QPainter
•独立设计并封装可复用地图控件「MapWidget」,核心实现瓦片异步加载、墨卡托投影坐标转换,支撑地图浏览核心功能落地。
•基于QPainter引擎完成5种覆盖物(点/线/面/圆/矩形)的实时渲染与交互式绘制,支持鼠标拖拽编辑、属性自定义,提升用户交互体验。
•采用QTimer+轨迹点队列设计平滑轨迹回放动画,支持自定义播放速度(0.5x-2x),实现轨迹移动无卡顿、无跳帧。
•基于QNetworkAccessManager构建全异步HTTP请求框架,调用高德/百度地图API,结合Qt信号槽机制实现非阻塞UI,避免界面卡死。
•封装LRU瓦片缓存机制(基于QHash实现),设计智能淘汰策略,优先淘汰缩放级别差异≥2的瓦片,将内存占用稳定控制在200MB以内。
•运用C++17核心特性(std::variant、std::visit)实现类型安全的覆盖物统一存储,替代传统继承体系,降低代码耦合度、提升可维护性。
项目成果:独立完成可交互GIS桌面客户端开发,完整实现地图浏览、图形绘制、轨迹回放、地理编码等8+核心功能,代码可复用性强,运行稳定无异常。
```
**详细请看简历模板**
全部评论
(2) 回帖