引言
在 GIS 二次开发中,底图(Basemap)是几乎所有可视化场景的基础。OpenStreetMap(OSM)作为一套开源、免费、覆盖全球的瓦片地图服务,常被用作项目中的背景底图。借助 PyQGIS 我们可以在脚本或插件中以编程方式加载 OSM 瓦片,而无需手动在 GUI 中操作。
本文将围绕一段完整的示例代码,逐步讲解如何使用 PyQGIS 加载 OpenStreetMap XYZ 瓦片底图,并将其置于图层树的最底部,使其作为背景层呈现。
前置准备
运行本文代码需要以下环境:
- 已安装 QGIS(建议 3.x 及以上版本),其自带 Python 解释器;
- 具备可访问
tile.openstreetmap.org 的网络连接;
- 对 PyQGIS 的
QgsProject、QgsRasterLayer 等核心类有基本了解。
如果你使用的是 QGIS 内置的 Python 控制台或脚本编辑器,相关模块已自动可用,无需额外安装。若在外部 Python 环境中调用,则需要正确配置 QGIS 的 Python 路径与 qgis.core 模块。
代码实现
下面是完整的示例代码:
from qgis.core import QgsProject, QgsRasterLayer, QgsVectorLayer
from qgis.PyQt.QtCore import Qt, QVariant
from qgis.PyQt.QtGui import QColor, QFont, QBrush
# 获取当前项目实例
project = QgsProject.instance()
# OSM XYZ 瓦片服务地址
osm_url = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
# 构建 WMS/XYZ 数据源字符串
# type=xyz 表示使用 XYZ 瓦片服务;zmin/zmax 控制缩放级别范围
data_source = f'type=xyz&url={osm_url}&zmax=19&zmin=0'
# 创建栅格图层,图层名称为 OpenStreetMap,提供程序为 wms
osm_layer = QgsRasterLayer(data_source, 'OpenStreetMap', 'wms')
# 检查图层是否有效
if not osm_layer.isValid():
raise RuntimeError('无法加载 OpenStreetMap 底图,请检查网络连接或数据源 URL。')
# 将图层添加到当前项目
project.addMapLayer(osm_layer)
# 可选:将该底图移动到图层树底部,使其作为背景底图显示
root = project.layerTreeRoot()
layer_node = root.findLayer(osm_layer.id())
if layer_node:
cloned_node = layer_node.clone()
parent = layer_node.parent()
# -1 表示插入到末尾,即图层树最底部
parent.insertChildNode(-1, cloned_node)
parent.removeChildNode(layer_node)
print(f'已成功添加 OSM 底图:{osm_layer.name()}')
代码解析
1. 获取项目实例
project = QgsProject.instance()
QgsProject.instance() 返回当前正在使用的项目对象,它是所有图层、样式、布局等资源的容器。无论是脚本还是插件,对图层的一切增删改查都应通过该实例进行。
2. 拼接 XYZ 瓦片数据源
osm_url = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
data_source = f'type=xyz&url={osm_url}&zmax=19&zmin=0'
OSM 采用的是 XYZ 瓦片方案,其 URL 模板中 {z}、{x}、{y} 分别代表缩放级别、列号和行号。QGIS 的 wms 提供程序支持通过 type=xyz 参数直接解析这类瓦片服务。
其中:
type=xyz 声明数据源类型为 XYZ 瓦片;
url 指向瓦片模板地址;
zmin=0 与 zmax=19 限定可用缩放级别范围,0 为全球视图,19 为街道级细节。
3. 创建栅格图层
osm_layer = QgsRasterLayer(data_source, 'OpenStreetMap', 'wms')
QgsRasterLayer 用于加载栅格数据。三个参数依次为:数据源字符串、图层显示名称、提供程序名称。这里提供程序使用 wms,因为 QGIS 将 XYZ 瓦片统一归入 WMS 提供程序处理。
4. 有效性校验
if not osm_layer.isValid():
raise RuntimeError('无法加载 OpenStreetMap 底图,请检查网络连接或数据源 URL。')
加载完成后必须检查图层是否有效。无效通常意味着网络不通、URL 错误或提供程序不支持当前数据源。提前校验可以避免后续图层操作抛出难以定位的异常。
5. 添加到项目
project.addMapLayer(osm_layer)
将校验通过的图层注册到当前项目。此时图层会出现在 QGIS 的图层树顶部,也就是渲染顺序的最上层。
6. 移动到底部作为背景
root = project.layerTreeRoot()
layer_node = root.findLayer(osm_layer.id())
if layer_node:
cloned_node = layer_node.clone()
parent = layer_node.parent()
parent.insertChildNode(-1, cloned_node)
parent.removeChildNode(layer_node)
默认情况下,新添加的图层位于图层树顶部,会覆盖在其余图层之上。但底图应当位于最底层,因此需要调整其位置。
QGIS 的图层树 API 没有提供直接的"移动到底部"方法,这里采用的是"克隆并重新插入"的间接方式:先克隆当前图层节点,将克隆节点以 -1 索引插入到父节点的末尾(即最底部),再移除原始节点。最终效果等同于把该图层移至图层树底部,使其在渲染时作为背景显示。
7. 输出确认信息
print(f'已成功添加 OSM 底图:{osm_layer.name()}')
在脚本末尾打印确认信息,便于在控制台日志中核对执行结果。
使用建议
关于网络访问:OSM 官方瓦片服务器对请求频率有一定限制,频繁拉取瓦片可能触发限流,导致瓦片加载不全或变灰。在生产环境中,建议考虑使用自建瓦片缓存、镜像服务或国内可访问的替代瓦片源。
关于坐标系:XYZ 瓦片服务基于 Web Mercator(EPSG:3857)投影。当项目当前坐标系与其不一致时,QGIS 会自动进行投影变换,但加载大量矢量数据时可能出现性能开销。
关于图层叠加顺序:底图置于最底部是通用做法。若后续还需添加矢量数据,建议在底图加载完成后再按顺序加入,以免叠加顺序混乱。
关于封装复用:可将上述逻辑封装为函数,接收瓦片 URL、图层名称、缩放范围等参数,从而支持快速切换其他 XYZ 底图(如高德、天地图、CartoDB 等)。
总结
本文介绍了在 PyQGIS 中加载 OpenStreetMap 底图的完整流程:从获取项目实例、拼接 XYZ 数据源,到创建并校验栅格图层,最后调整图层顺序使其作为背景底图。整段代码结构清晰,逻辑自洽,适合直接嵌入自动化脚本或插件初始化流程中,作为 GIS 二次开发中"加载底图"这一通用需求的参考实现。
更多GIS开发问题,欢迎留言。转载须注明出处。