Geotiff 叠加位置在 Holoviews/Bokeh tilemap 上略微偏离

问题描述

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

screenshot of the problem

这是代码的相关部分:

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 (see live code) 中最初的样子:

screenshot of everything working fine in Earth Engine


如您所见,图像随处可见。

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

screenshot of everything being fine in QGis


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

知道为什么会这样吗?

解决方法

我从一位开发人员那里得到了关于 Holoviz Discourse 的答案。看到推荐的功能几乎没有记录,我将其复制到这里,以防有人寻找一种简单的方法来加载 geotiff 并添加到 Holoviews/Geoviews 中的瓷砖地图:

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

philippjfr
我不希望手动转换坐标来工作 特别好。虽然它对重量的依赖要大得多 我建议使用 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 解决方案(据说可以自动处理所有内容)相比,该解决方案有一个缺点,即如果您使用悬停工具提示显示鼠标光标下的值和坐标,则坐标会显示在新投影的网络中以百万米为单位的墨卡托系统,而不是预期的度数。对此的解决方案超出了本答案的范围,但我刚刚完成了一个详细的分步指南,其中也包含一个解决方案,我将在发布后立即将其链接到此处。当然,如果您不使用 Hover Tooltip,上面的代码将非常适合您,无需再做任何修改。