手把手教你用Python调用天地图WMS/WFS服务,5分钟获取地理数据并可视化
Python实战:5分钟调用天地图WMS/WFS服务实现地理数据可视化
第一次接触地理信息服务时,我被各种缩写搞晕了——WMS、WFS、WMTS这些OGC标准接口文档读起来像天书。直到用Python实际调用了一次天地图服务,才发现原来获取专业地图数据可以如此简单。本文将带你用最少的代码,快速实现从数据获取到可视化的完整流程。
1. 环境准备与基础概念
在开始编码前,我们需要明确几个关键概念。WMS(Web Map Service)提供的是地图图片,适合作为底图;WFS(Web Feature Service)则返回矢量地理要素数据,适合做进一步分析。天地图作为国内权威的地理信息服务,其接口符合OGC标准,但需要注意坐标系统一使用CGCS2000。
安装必要的Python库:
pip install requests geopandas matplotlib folium核心工具说明:
requests:用于发送HTTP请求获取服务数据geopandas:处理地理空间数据的利器matplotlib:基础可视化工具folium:生成交互式Leaflet地图
注意:所有示例基于福州天地图公开接口,实际使用时请遵守其服务条款。
2. 获取WMS底图并显示
WMS服务最常用的操作是GetMap,我们需要构造包含以下关键参数的URL:
layers:指定地图图层bbox:地图范围(左下角经度,左下角纬度,右上角经度,右上角纬度)size:图片尺寸(width x height)srs:坐标参考系统(EPSG代码)
import requests from PIL import Image from io import BytesIO # 福州天地图WMS服务地址 wms_url = "http://tdt.fuzhou.gov.cn/serviceaccess/WMS/DMDZ" params = { "service": "WMS", "request": "GetMap", "version": "1.1.1", "layers": "1", "styles": "default", "bbox": "119.28,26.08,119.30,26.10", # 福州某区域 "width": "800", "height": "600", "srs": "EPSG:4490", # CGCS2000坐标系 "format": "image/png" } response = requests.get(wms_url, params=params) img = Image.open(BytesIO(response.content)) img.show()常见问题处理:
- 图片显示为空白:检查bbox参数是否超出服务范围
- 坐标系统错误:确保srs参数与服务支持的坐标系一致
- 请求被拒绝:可能需要添加token等认证参数
3. 调用WFS服务获取矢量数据
WFS的GetFeature操作可以获取地理要素的详细信息。以下示例获取福州市某区域的地名点数据:
import geopandas as gpd wfs_url = "http://tdt.fuzhou.gov.cn/serviceaccess/WFS/DMDZ_WFS-G" params = { "service": "WFS", "request": "GetFeature", "version": "1.0.0", "typeName": "DMDZ:地名", "bbox": "119.28,26.08,119.30,26.10", "srsname": "EPSG:4490", "outputFormat": "application/json" } response = requests.get(wfs_url, params=params) features = gpd.read_file(response.text) print(features.head())关键参数说明:
typeName:要素类型名称(格式为"命名空间:要素类型")outputFormat:指定返回格式(GML/JSON)maxFeatures:限制返回要素数量(避免数据量过大)
4. 数据叠加可视化实战
将WMS底图与WFS矢量数据结合展示,是地理分析的常见需求。我们使用matplotlib实现:
import matplotlib.pyplot as plt from matplotlib.offsetbox import OffsetImage, AnnotationBbox # 创建画布 fig, ax = plt.subplots(figsize=(10, 8)) # 显示WMS底图 ax.imshow(img, extent=[119.28, 119.30, 26.08, 26.10]) # 叠加WFS数据 features.plot(ax=ax, color='red', markersize=50, alpha=0.7) # 添加标注 for idx, row in features.iterrows(): ax.annotate(row['名称'], (row.geometry.x, row.geometry.y), xytext=(3,3), textcoords="offset points", bbox=dict(boxstyle="round", fc="w")) plt.title("福州市地名点分布") plt.xlabel("经度") plt.ylabel("纬度") plt.grid() plt.show()进阶技巧:使用folium创建交互式地图
import folium # 创建底图 m = folium.Map(location=[26.09, 119.29], zoom_start=15) # 添加WMS图层 folium.WmsTileLayer( url=wms_url, name='福州底图', fmt='image/png', layers='1', transparent=True, attribution="天地图" ).add_to(m) # 添加WFS要素 for idx, row in features.iterrows(): folium.Marker( location=[row.geometry.y, row.geometry.x], popup=row['名称'], icon=folium.Icon(color='red') ).add_to(m) # 添加图层控制 folium.LayerControl().add_to(m) m.save('fuzhou_map.html')5. 性能优化与实用技巧
实际项目中,我们还需要考虑以下问题:
缓存策略优化
from requests_cache import install_cache install_cache('ogc_cache', expire_after=3600) # 缓存1小时坐标系转换示例
from pyproj import Transformer # CGCS2000转WGS84 transformer = Transformer.from_crs("EPSG:4490", "EPSG:4326") lon, lat = transformer.transform(119.29, 26.09)参数自动编码处理
from urllib.parse import quote bbox = "119.28,26.08,119.30,26.10" encoded_bbox = quote(bbox) # 处理特殊字符错误处理最佳实践
try: response = requests.get(wms_url, params=params, timeout=10) response.raise_for_status() except requests.exceptions.RequestException as e: print(f"请求失败: {e}") if hasattr(e, 'response') and e.response is not None: print(f"错误详情: {e.response.text}")最后分享一个实用技巧:天地图服务通常有QPS限制,在循环请求时建议添加延迟:
import time time.sleep(0.1) # 每次请求间隔0.1秒