You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

QGIS 3.22导出OpenStreetMap图层模糊问题求助

QGIS导出OSM瓦片图层模糊问题排查与解决

问题背景

在QGIS 3.22中通过https://tile.openstreetmap.org/{z}/{x}/{y}.png加载OSM瓦片图层后,使用自定义Python代码导出局部区域PNG时图像模糊,但相同区域在QGIS画布及OSM官网显示清晰。已尝试调整瓦片分辨率、导出分辨率、DPI、缩放级别等参数,均未改善导出质量。

代码问题分析

  1. DPI设置不同步:仅设置了QImagedotspermeter,未同步配置QgsMapSettings的DPI参数。QGIS渲染瓦片时会根据自身DPI计算加载的瓦片层级,两者不一致会导致瓦片被强制缩放拉伸,出现模糊。
  2. dotspermeter参数值不合理:设置的1、10等值过小,不符合实际导出分辨率需求,应根据目标DPI计算正确数值(1 DPI ≈ 39.37 dots/meter)。
  3. 缩放与尺寸不匹配:手动设置的zoom值和输出尺寸未对应OSM瓦片原生分辨率,导致渲染时瓦片被插值缩放,丢失细节。
  4. 坐标转换方式过时:使用了pyproj旧版Proj(init=...)语法,QGIS内置了更可靠的坐标转换方法,无需依赖外部库。

修正后的代码

def to3857Coords(lat, lon):
    # 使用QGIS内置坐标转换,替代旧版pyproj
    crs_4326 = QgsCoordinateReferenceSystem("EPSG:4326")
    crs_3857 = QgsCoordinateReferenceSystem("EPSG:3857")
    transform = QgsCoordinateTransform(crs_4326, crs_3857, QgsProject.instance())
    point = transform.transform(QgsPointXY(lon, lat))
    return point.x(), point.y()

def saveImg(xsize, ysize, dpi):
    # 计算dotspermeter:1 DPI = 39.37 dots/meter
    dotspermeter = int(dpi * 39.37)
    img = QImage(QSize(xsize, ysize), QImage.Format_ARGB32_Premultiplied)
    img.setDotsPerMeterX(dotspermeter)
    img.setDotsPerMeterY(dotspermeter)
    
    ms = QgsMapSettings()
    # 同步设置QgsMapSettings的DPI,确保瓦片加载层级正确
    ms.setDpi(dpi)
    
    p = QPainter()
    p.begin(img)
    # 添加渲染优化提示,减少缩放模糊
    p.setRenderHint(QPainter.HighQualityAntialiasing)
    p.setRenderHint(QPainter.SmoothPixmapTransform)
    
    # 直接获取图层,保持正确顺序
    layers = [lyr for lyr in QgsProject.instance().mapLayers().values()]
    ms.setLayers(layers)
    
    # 设置渲染范围
    rect = iface.mapCanvas().extent()
    ms.setExtent(rect)
    ms.setOutputSize(img.size())
    
    # 执行渲染
    render = QgsMapRendererCustomPainterJob(ms, p)
    render.start()
    render.waitForFinished()
    p.end()
    
    # 保存图像
    img.save(f'/tmp/test_render_{xsize}_{ysize}_{dpi}.png')
    print('Done')

# 配置参数:纬度、经度、目标DPI、输出宽高
# 输出尺寸需与画布范围、DPI匹配,避免强制缩放
conf=[
[51.11, 17.032222, 300, 1540, 770],  # 300 DPI,对应OSM层级18的清晰瓦片
[51.11, 17.032222, 150, 770, 385],
[51.11, 17.032222, 600, 3080, 1540],
]
lat, lon, dpi, xsize, ysize = conf[0]

canvas = iface.mapCanvas()
x, y = to3857Coords(lat, lon)
canvas.setCenter(QgsPointXY(x, y))
# OSM层级18对应QGIS缩放值约1128.5,保持此值并同步DPI
canvas.zoomScale(1128.5) 
saveImg(xsize, ysize, dpi)

关键优化说明

  • 同步DPI设置:必须同时配置QgsMapSettingsQImage的DPI,确保QGIS加载的瓦片层级与输出分辨率匹配,避免瓦片拉伸。
  • 启用平滑渲染:添加SmoothPixmapTransform渲染提示,减少瓦片缩放时的模糊感。
  • 匹配尺寸与缩放:输出尺寸需根据画布范围和DPI计算,保证每个瓦片像素对应输出图像的整数像素,避免插值模糊。
  • 优先使用内置工具:如果无需自定义逻辑,直接使用QGIS自带的「导出地图为图像」功能(菜单栏项目 > 导出 > 导出地图为图像),可更精准控制参数,保证导出质量。

内容的提问来源于stack exchange,提问作者Simone

火山引擎 最新活动