Holoviews/Bokeh tilemap 上的 Geotiff 叠加位置略有偏差

Geotiff overlay position is slightly off on Holoviews/Bokeh tilemap

我有一个显示在瓷砖地图上的 Geotiff,但它稍微偏南。例如,在此屏幕截图中,图像的边缘应该是国家边界所在的位置,但它有点偏南:

这是代码的相关部分:

tiff_rio_500 = rioxarray.open_rasterio('/content/mw/mw_dist_to_light_at_all_from_light_mask_mw_cut_s3_500.tif')
dataarray_500 = tiff_rio_500[0]
dataarray_500_meters = dataarray_500.copy()
dataarray_500_meters['x'], dataarray_500_meters['y'] = ds.utils.lnglat_to_meters(dataarray_500.x, dataarray_500.y)
hv_dataset_500_meters = hv.Dataset(dataarray_500_meters, name='nightlights', vdims='cumulative_cost')
hv_tiles_osm_bokeh = hv.element.tiles.OSM().opts(width=1000, height=800)
hv_image_500_meters_bokeh = hv.Image(hv_dataset_500_meters, kdims=['x', 'y'], vdims=['cumulative_cost'], rtol=1).opts(cmap='inferno_r')
hv_combined_osm_500_meters_bokeh = hv_tiles_osm_bokeh * hv_image_500_meters_bokeh
hv_combined_osm_500_meters_bokeh

You can see the live notebook on google colab.

现在这不是不将地图转换为 Web 墨卡托时通常出现的“一切都偏离了”的问题。 几乎完美,只是不完美。

Geotiff 是 Earth Engine 导出的。这是它最初在 Earth Engine 中的样子 (see live code):

如您所见,图像处处都遵循边界。

起初,我怀疑可能导出出错了,或者 google 地图瓦片集有些不同,但是没有,如果我在 windows 上的 QGis 应用程序中打开相同的导出 Tiff ] 笔记本电脑并在同一个 OSM tilemap 上查看它,就像我在 colab 笔记本上所做的那样,它看起来不错:

好吧,图像并没有完美地遵循边界,但我知道原因并且这无关紧要(我过度简化了国家/地区边界的几何形状)。关键是,它被投影到正确的位置。因此,基于此,tiff 包含正确的信息,它可以显示在与 OSM tilemap 中边框相同的位置,但在我的 Holoviews-Datashader-Bokeh 项目中它仍然略有偏差。

知道为什么会这样吗?

我从一位开发者那里得到了关于 Holoviz Discourse 的答案。看到推荐的函数实际上是如何未记录的,我将其复制到这里以防有人寻找一种简单的方法来加载 geotiff 并添加到 Holoviews/Geoviews:

中的 tilemap

https://discourse.holoviz.org/t/geotiff-overlay-position-is-slightly-off-on-holoviews-bokeh-tilemap/2071

philippjfr
I wouldn’t expect manually transforming the coordinates to work particularly well. While it’s a much heavier weight dependency for accurate coordinate transforms I’d recommend using GeoViews.

img = gv.util.load_tiff( '/content/mw/mw_dist_to_light_at_all_from_light_mask_mw_cut_s3_500.tif' ) gv.tile_sources.OSM() * img.opts(cmap='inferno_r')

编辑:现在可能有人不想使用 Geoviews,因为它有一个相当沉重的依赖链,需要很大的耐心和运气才能正确设置它。幸运的是 rioxarray(通过 rasterio)有一个重新投影的工具,只需将“.rio.reproject('EPSG:3857')”附加到第一行然后你就不必使用 lnglat_to_meters 这是不用于此目的。

所以更正后的代码变成:

tiff_rio_500 = rioxarray.open_rasterio('/content/mw/mw_dist_to_light_at_all_from_light_mask_mw_cut_s3_500.tif').rio.reproject('EPSG:3857')
hv_dataset_500_meters = hv.Dataset(tiff_rio_500[0], name='nightlights', vdims='cumulative_cost')
hv_tiles_osm_bokeh = hv.element.tiles.OSM().opts(width=1000, height=800)
hv_image_500_meters_bokeh = hv.Image(hv_dataset_500_meters, kdims=['x', 'y'], vdims=['cumulative_cost'], rtol=1).opts(cmap='inferno_r')
hv_combined_osm_500_meters_bokeh = hv_tiles_osm_bokeh * hv_image_500_meters_bokeh
hv_combined_osm_500_meters_bokeh

现在与 Geoviews 解决方案(据说可以自动处理所有内容)相比,此解决方案有一个缺点,即如果您使用悬停工具提示来显示鼠标光标下的值和坐标,则坐标会显示在新的以百万米而不是预期度数为单位的投影 web 墨卡托系统。该问题的解决方案超出了本答案的范围,但我刚刚完成了详细的分步指南,其中也包含该问题的解决方案,我会在发布后立即 link 这里。当然,如果您不使用 Hover Tooltip,上面的代码将非常适合您,无需任何修改。