告别91卫图!用QGIS+Python脚本免费批量下载Google/Bing高清卫星图(附完整代码)
开源GIS实战QGISPython自动化获取高分辨率卫星影像全攻略当我们需要获取特定区域的高清卫星影像时商业软件往往设置重重限制。本文将带你探索一套完全开源的工作流利用QGIS和Python脚本实现Google、Bing等平台卫星影像的批量下载无需支付任何授权费用。1. 开源GIS工具链搭建1.1 QGIS环境配置QGIS作为开源GIS的旗舰产品其3.x版本已具备媲美商业软件的稳定性。推荐从官网直接下载独立安装包当前稳定版为3.28避免使用OSGeo4W安装器的网络问题。安装时注意Windows用户建议勾选将QGIS添加到系统PATHmacOS用户需在安装后执行xattr -d com.apple.quarantine /Applications/QGIS.appLinux用户通过snap安装最便捷sudo snap install qgis提示安装完成后建议在插件管理中启用QuickMapServices插件这是后续获取在线地图的关键1.2 Python环境集成QGIS内置了Python 3.x环境但需要额外配置几个关键包# 在QGIS Python控制台执行 import pip pip.main([install, --user, geopandas, owslib])验证关键模块是否可用from qgis.core import * import ogr, osr print(Qgis.QGIS_VERSION) # 应输出当前版本号2. 卫星影像源接入方案2.1 主流地图服务对比服务提供商最大分辨率更新频率覆盖范围免费政策Bing Maps0.3m季度更新全球主要城市非商业用途免费Google Satellite0.15m实时更新全球覆盖严格限制批量下载ESRI World Imagery0.5m年度更新全球覆盖免费API调用限制OSM Standard1.0m社区维护不完整覆盖完全开放2.2 快速添加地图服务通过QuickMapServices插件添加基础图层菜单栏选择Web → QuickMapServices → Settings点击More services标签页选择Get contributed pack获取社区维护的扩展服务在搜索框输入Bing或Google即可添加对应图层高级用户可通过XYZ Tiles自定义服务# 添加自定义WMTS服务 service_url https://mt1.google.com/vt/lyrssx{x}y{y}z{z} raster_layer QgsRasterLayer(service_url, Google Satellite, wms) QgsProject.instance().addMapLayer(raster_layer)3. 自动化下载核心逻辑3.1 坐标系转换原理所有在线地图服务默认使用Web墨卡托投影EPSG:3857而实际分析可能需要地理坐标系EPSG:4326。关键转换公式def web_mercator_to_wgs84(x, y): 将Web墨卡托坐标转为WGS84经纬度 import math lon (x / 20037508.34) * 180 lat (y / 20037508.34) * 180 lat 180/math.pi * (2*math.atan(math.exp(lat*math.pi/180)) - math.pi/2) return lon, lat3.2 影像下载参数计算下载前需要确定三个核心参数DPI设置影响输出质量建议96-300dpi比例尺决定影像细节程度像素尺寸与地理范围的关系公式地理范围宽度 像素宽度 × 分辨率 × 0.0254 / 比例尺分母 地理范围高度 像素高度 × 分辨率 × 0.0254 / 比例尺分母示例计算代码def calculate_pixel_size(scale, dpi96): 计算指定比例尺下的像素地理尺寸 # 0.0254为英寸转米系数 return (scale * 0.0254) / dpi # 示例1:5000比例尺下的像素尺寸 print(calculate_pixel_size(5000)) # 输出约1.322米/像素4. 批量化处理实战方案4.1 基于矢量范围的批量下载准备一个包含多个多边形区域的Shapefile执行自动化下载import processing def batch_export_by_polygon(input_shp, output_dir): 根据矢量面批量导出影像 # 加载矢量层 vector_layer QgsVectorLayer(input_shp, areas, ogr) # 设置输出参数 params { EXTENT: None, # 自动获取 MAP_THEME: None, LAYERS: [iface.activeLayer()], # 当前激活的影像层 DPI: 96, TARGET_EXTENT: None, OUTPUT: os.path.join(output_dir, export_{}.png) } # 遍历每个面要素 for feature in vector_layer.getFeatures(): # 设置当前要素范围为输出范围 params[EXTENT] feature.geometry().boundingBox() # 生成唯一文件名 fid feature.id() params[OUTPUT] os.path.join(output_dir, farea_{fid}.png) # 执行导出 processing.run(qgis:saveasimage, params) # 调用示例 batch_export_by_polygon(study_areas.shp, /output/images)4.2 分块下载大范围区域对于超出一个屏幕显示范围的大区域采用网格分块下载策略def grid_export(bbox, output_dir, rows3, cols3): 将大范围区域网格分块下载 xmin, ymin, xmax, ymax bbox x_step (xmax - xmin) / cols y_step (ymax - ymin) / rows for i in range(rows): for j in range(cols): # 计算当前分块范围 block_xmin xmin j * x_step block_xmax block_xmin x_step block_ymin ymin i * y_step block_ymax block_ymin y_step # 设置QGIS视图范围 rect QgsRectangle(block_xmin, block_ymin, block_xmax, block_ymax) iface.mapCanvas().setExtent(rect) # 导出设置 settings iface.mapCanvas().mapSettings() settings.setOutputDpi(300) settings.setOutputSize(QSize(2000, 2000)) # 执行导出 job QgsMapRendererSequentialJob(settings) job.start() job.waitForFinished() image job.renderedImage() # 保存文件 filename ftile_{i}_{j}.jpg image.save(os.path.join(output_dir, filename))5. 成果后处理与质量控制5.1 空间参考文件生成为每个导出的图片生成配套的World Filedef generate_world_file(image_path, pixel_size, top_left): 生成Esri World File wld_path os.path.splitext(image_path)[0] .wld with open(wld_path, w) as f: f.write(f{pixel_size[0]}\n) # x方向像素大小 f.write(0\n) # 旋转项 f.write(0\n) # 旋转项 f.write(f{-pixel_size[1]}\n) # y方向像素大小(负值) f.write(f{top_left[0]}\n) # 左上角x坐标 f.write(f{top_left[1]}\n) # 左上角y坐标5.2 影像拼接与重采样使用GDAL合并分块下载的影像# 使用gdal_merge.py拼接多个分块 gdal_merge.py -o merged.tif -of GTiff tile_*.jpg # 重采样到统一分辨率 gdalwarp -tr 10 10 -r bilinear merged.tif resampled.tif实际项目中建议将下载的原始影像组织为如下目录结构/project_name ├── /raw_images # 原始下载切片 ├── /processed # 处理后影像 ├── /vector_data # 使用的矢量数据 └── metadata.json # 项目元数据6. 性能优化与常见问题6.1 下载加速技巧多线程下载使用Python的concurrent.futures实现并行处理缓存利用在QGIS设置中增大内存缓存默认50MB可提升至200MB网络优化修改QNetworkRequest的超时设置from PyQt5.QtNetwork import QNetworkRequest request QNetworkRequest() request.setTransferTimeout(30000) # 30秒超时6.2 典型错误处理错误现象可能原因解决方案空白影像视图范围设置错误检查坐标范围是否有效图像模糊DPI设置过低提升DPI至150-300下载中断网络超时增加超时时间或分小块下载坐标偏移投影不匹配确认所有图层使用EPSG:3857调试时可启用QGIS的日志记录# 启用详细日志 QgsApplication.messageLog().messageReceived.connect(lambda msg: print(msg))7. 进阶应用场景7.1 时序影像对比分析通过定期执行下载脚本建立影像时间序列import schedule import time def monthly_download(): 每月自动下载目标区域影像 today datetime.now().strftime(%Y%m%d) output_dir f/archive/{today} os.makedirs(output_dir, exist_okTrue) batch_export_by_polygon(monitoring_area.shp, output_dir) # 设置每月1号执行 schedule.every().month.at(00:00).do(monthly_download) while True: schedule.run_pending() time.sleep(60)7.2 与深度学习框架集成将下载的影像直接输入到PyTorch数据管道from torch.utils.data import Dataset class SatelliteDataset(Dataset): def __init__(self, image_dir, transformNone): self.image_files [f for f in os.listdir(image_dir) if f.endswith(.png)] self.transform transform def __len__(self): return len(self.image_files) def __getitem__(self, idx): img_path os.path.join(self.image_dir, self.image_files[idx]) image Image.open(img_path) if self.transform: image self.transform(image) return image8. 法律与伦理注意事项虽然技术手段可以实现批量下载但需特别注意遵守各平台的服务条款特别是Google Earth Engine的严格限制商业用途需获得正式授权敏感区域影像可能受到特殊管制下载频率控制在合理范围建议每分钟不超过10次请求可通过以下方式检查当前服务的许可信息layer iface.activeLayer() print(layer.metadata().rights()) # 输出图层使用权限信息在实际项目中建议优先考虑官方提供的API服务如Google Maps Platform或Bing Maps API当确实需要更高灵活性时再采用本文方案。记得定期检查QGIS的插件更新社区开发者经常会发布新的地图服务连接方式。