将 QGIS 可见图层导出为离线地图、并与 Laravel 这样的 Web 后端结合使用,MBTiles 格式配合前端的 Leaflet.js 或 OpenLayers 是目前比较常见的一套组合。
如果地图包含栅格数据(DEM/地形晕渲)和矢量数据(河流),直接导出为 Web 切片(瓦片)是个可行的方向——QGIS 的切片工具只会渲染当前处于勾选(可见)状态的图层,省去了手动筛选的麻烦。
以下是完整的工作流记录:
第一阶段:在 QGIS 中导出数据 (MBTiles)
为什么选 MBTiles?
相比于导出成千上万个碎小的 .png 图片文件夹(XYZ 目录结构),MBTiles 本质上是一个单文件的 SQLite 数据库,里面存储了所有层级的瓦片图片。它极大地简化了文件的传输、存储,并且 Laravel(PHP)原生支持极速读取 SQLite。
具体步骤:
- 检查图层与投影: 确保左侧图层树中,只有你需要导出的图层被勾选。建议将 QGIS 右下角的项目坐标系(CRS)设置为 Web 地图通用的 EPSG:3857 (WGS 84 / Pseudo-Mercator)。在 Web 离线地图开发中,使用非 EPSG:3857 作为切片坐标系,容易打破 Web 地图的”默认规则”,引发一系列兼容性问题。条件允许的话,尽量统一采用 EPSG:3857。
- 打开切片工具: 在顶部菜单栏选择 处理 (Processing) -> 工具箱 (Toolbox)。
- 搜索工具: 在右侧弹出的工具箱中搜索
XYZ,找到 栅格工具 (Raster tools) 下的 生成 XYZ 瓦片 (MBTiles) / Generate XYZ tiles (MBTiles)。 - 配置参数:
- 范围 (Extent): 点击右侧的
...,选择”使用当前画布范围 (Use Map Canvas Extent)”,或者选择某个图层的范围。 - 最小缩放级别 (Minimum zoom): 通常设为
0或3。 - 最大缩放级别 (Maximum zoom): ⚠️ 注意: 如果地图范围较大(比如覆盖整个东亚这样的区域),级别设得太高会导致生成文件呈指数级暴增且耗时极长。建议先从较低的缩放级别(如
7或8)开始测试,确认文件大小和生成时间可接受后,再酌情提高到10左右。 - 输出文件: 选择一个保存路径,命名为
offline_map.mbtiles。
- 范围 (Extent): 点击右侧的
- 点击 运行 (Run),等待生成完毕。
第二阶段:在 Laravel 中提供服务
现在你有了一个 offline_map.mbtiles(SQLite 数据库文件)。你需要让 Laravel 读取它并输出图片流。
- 放置文件: 将生成的
offline_map.mbtiles放入 Laravel 项目中,例如storage/app/maps/offline_map.mbtiles。 -
配置数据库连接: 在
config/database.php中添加一个新的 SQLite 连接:'connections' => [ // ... 其他连接 'mbtiles' => [ 'driver' => 'sqlite', 'database' => storage_path('app/maps/offline_map.mbtiles'), 'prefix' => '', ], ], -
创建路由与控制器: MBTiles 数据库里通常有一张名为
tiles的表,包含zoom_level、tile_column、tile_row和tile_data(BLOB 格式的图片)。在routes/web.php中添加一个路由:use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\DB; use Illuminate\Http\Response; Route::get('/map-tiles/{z}/{x}/{y}', function ($z, $x, $y) { // MBTiles 使用的是 TMS 坐标系,Y 轴与标准的 XYZ 相反,需要转换 $tms_y = (pow(2, $z) - 1) - $y; $tile = DB::connection('mbtiles') ->table('tiles') ->where('zoom_level', $z) ->where('tile_column', $x) ->where('tile_row', $tms_y) ->first(); if (!$tile) { abort(404); // 或者返回一张透明的占位图片 } // 假设导出的格式是 PNG return response($tile->tile_data)->header('Content-Type', 'image/png'); });
第三阶段:前端渲染 (Leaflet.js)
在你的 Laravel Blade 视图(或 Vue/React 组件)中,使用常用的开源地图库 Leaflet.js 来加载这些离线瓦片。
-
引入 Leaflet 的 CSS 和 JS:
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" /> <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script> <style> #map { height: 600px; width: 100%; } </style> -
初始化地图并接入 Laravel 接口:
<div id="map"></div> <script> // 根据实际地图区域调整中心点坐标和初始缩放级别 var map = L.map('map').setView([35.0, 105.0], 4); // 添加离线瓦片图层,指向 Laravel 的路由 L.tileLayer('/map-tiles/{z}/{x}/{y}', { minZoom: 3, maxZoom: 8, // 必须与你在 QGIS 中导出的层级匹配 attribution: 'Offline Map Data' }).addTo(map); </script>
替代方案(最简单,但文件多):导出目录结构 (Directory)
如果你的服务器对 SQLite 拓展支持有问题,或者你想直接用 Nginx 代理静态文件来追求极致性能:
- 在 QGIS 中选择 生成 XYZ 瓦片 (目录)。
- 将生成的包含层级数字(如
0、1、2…)的文件夹整个复制到 Laravel 的public/tiles/目录下。 - 前端直接写静态路径:
L.tileLayer('/tiles/{z}/{x}/{y}.png', {...})。 - 缺点: 高层级地图会产生几十万个碎片文件,非常难移动和删除。
