hvplot geo=True参数致UTM坐标错误映射为北半球纬度的问题
问题描述
我有一份澳大利亚北部海滩的栅格数据,采用该区域的UTM投影坐标系(56S)。使用hvplot.xarray扩展调用da.hvplot(x="x", y="y", geo=True)绘制时,geo=True参数会触发将y坐标(米)转换为经纬度坐标,但纬度被错误映射为北半球坐标,计算方式变成lat = 90 + (-y),而正确转换应该是lat = -y。请问geo=True触发的、将XY坐标推导为经纬度的是哪个函数?
用matplotlib绘制可正常显示。
目标
整体目标是将矢量数据(岸线)叠加到栅格数据(NDWI)之上。
软件版本
python==3.10.8
hvplot.__version__ # '0.8.2' holoviews.__version__ # 1.15.3
可复现问题的最小完整代码
import cartopy.crs as ccrs import geopandas as gpd import geoviews as gv import geoviews.tile_sources as gts import holoviews as hv import hvplot.xarray import xarray as xr import matplotlib.pyplot as plt ndwi = xr.open_dataset( "https://s3.eu-central-1.amazonaws.com/floris.calkoen.open.data/narrabeen_ndwi_2023-01-24T00%3A02%3A21.tif", engine="rasterio", ) shoreline = gpd.read_file( "https://s3.eu-central-1.amazonaws.com/floris.calkoen.open.data/narrabeen_shoreline_2023-01-24T00%3A02%3A21.geojson" ) ndwi.rio.crs == shoreline.crs ## 返回True utm_zone = shoreline.crs.utm_zone southern_hemisphere = True if utm_zone[-1] == "S" else False utm_code = int(utm_zone[:-1]) utm_crs = ccrs.UTM( utm_code, southern_hemisphere ) # 为什么cartopy.crs.UTM()不支持直接传入UTM带的字符串? shoreline_plot = gv.Path([shoreline], crs=utm_crs) ndwi_plot = ndwi["band_data"].squeeze().hvplot(x="x", y="y", geo=True, crs=utm_crs) shoreline_plot * gts.EsriImagery # 这个显示正常 ndwi_plot # 可以看到XY坐标从UTM 56S转换为纬度时被错误映射到北半球(计算方式为90 + (-y)) shoreline_plot * ndwi_plot # 因此叠加无法正常显示 # matplotlib绘制代码 fig, ax = plt.subplots() ndwi["band_data"].squeeze().plot(ax=ax) shoreline.plot(ax=ax, color='black', linewidth=5, label="岸线") plt.legend() plt.show()
解答
当hvplot设置geo=True时,触发坐标转换的核心逻辑链是:
hvplot调用Holoviews的GeoInterface标识地理数据类型;- 最终通过Cartopy的
cartopy.crs.Projection.transform_points()方法完成投影坐标到经纬度的转换。
你遇到的南半球UTM纬度偏移问题,是旧版本hvplot/Holoviews(你的0.8.2/1.15.3版本较老)的已知bug:传递南半球UTM参数时,部分逻辑遗漏了南半球标识,导致转换时默认按北半球计算,出现错误的纬度偏移。
修复方案
- 优先升级依赖库:将hvplot升级到0.10.0+、Holoviews升级到1.17.0+,新版本已修复南半球UTM投影的转换问题。
- 手动转换坐标(临时方案):如果无法升级,可提前用Cartopy完成坐标转换后再绘图:
# 手动将UTM坐标转为经纬度 x_coords, y_coords = ndwi.x.values, ndwi.y.values # 从UTM 56S转换到WGS84经纬度 lon, lat, _ = utm_crs.transform_points(ccrs.PlateCarree(), x_coords, y_coords).T # 为数据集添加经纬度坐标 ndwi_with_latlon = ndwi.assign_coords(lon=("x", lon), lat=("y", lat)) # 用经纬度坐标绘制 ndwi_plot = ndwi_with_latlon["band_data"].squeeze().hvplot(x="lon", y="lat", geo=True) # 此时叠加岸线即可正常显示 shoreline_plot * ndwi_plot * gts.EsriImagery
- 直接使用rasterio的CRS对象:尝试将
crs参数改为ndwi.rio.crs(rasterio原生CRS对象),而非Cartopy构造的UTM对象,旧版本可能对原生CRS支持更好:
ndwi_plot = ndwi["band_data"].squeeze().hvplot(x="x", y="y", geo=True, crs=ndwi.rio.crs)
内容的提问来源于stack exchange,提问作者sir_olf
相关产品推荐
相关产品推荐

