Skip to main content
 首页 » 编程设计

python之无法使用 Matplotlib/Cartopy/Python 在高缩放级别(即缩小)检索 map 图 block

2024年08月15日39leader

我正在使用 Python 和 Cartopy 创建一个 map 应用程序,并尝试使用开源 map 图 block 作为背景,以获得比默认 Cartopy map 更多的选项。

它非常适合放大到相当近的 map ,但是当我尝试从更高的高度获取 View 时,出现了问题。如果我将缩放设置为 11,它就可以工作。如果我将它设置为 12,它会无限期地挂起并且不提供回溯。

OSM 和 Stamen map 服务器的结果相同。

这是一个简短的自包含示例(请注意,一两行可能是我尝试过的各种方法的产物)

import matplotlib as mpl 
mpl.use('Agg') 
import matplotlib.pyplot as plt 
import cartopy.crs as ccrs 
import cartopy.io.img_tiles as cimgt 
 
 
def custom_background(source_point): 
 
    source_point = source_point.split(" ") 
    source_point = (float(source_point[0]), float(source_point[1])) 
    dx = 1.5 
    dy = 1.5 
    lon_min, lon_max = source_point[0]-dx, source_point[0]+dx 
    lat_min, lat_max = source_point[1]-dy, source_point[1]+dy 
    zoom = 7 
    map_url = "https://www.openstreetmap.org/#map={}/{}/{}".format(zoom,source_point[0],source_point[1]) 
    tile = cimgt.OSM(url=map_url) 
    tile = cimgt.StamenTerrain() 
    ax = plt.axes(projection=ccrs.PlateCarree()) 
    ax.set_extent([lat_min, lat_max, lon_min, lon_max]) 
    ax.add_image(tile, zoom) 
    #~ ax.add_image(tile) 
    return ax 
 
custom_background("45.068466 -66.45477") 
plt.savefig("tile.png") 

结果,缩放 = 7:

但如果我将缩放比例更改为 14,则无论我允许它运行多长时间,程序都不会完成。

传递给 cimgt.OSM() 的 url 参数是可选的。无论有没有它,我都会得到相同的结果。 (参见:https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html#cartopy.io.img_tiles.OSM)

我是不是漏掉了什么?任何帮助将不胜感激,谢谢。

请您参考如下方法:

"zoom" levels are based on a Quadtree 。从本质上讲,增加“缩放”分辨率会使图 block 数量增加四次方。

所以:

zoom level 0: 4^0 = 1 tile(s) to cover the globe 
zoom level 1: 4^1 = 4 tile(s) to cover the globe 
... 
zoom level 7: 4^7 = 16,384 tile(s) to cover the globe 
... 
zoom level 14: 4^14 = 268,435,456 tile(s) to cover the globe 

因此,如果您请求大面积的​​高缩放级别的图 block ,您最终可能会请求很多的图 block 。

在名为 find_images 的 Tiler 对象上有一个有用但未记录的方法。它的实现并不太复杂:
https://github.com/SciTools/cartopy/blob/v0.16.0/lib/cartopy/io/img_tiles.py#L103-L122 .

通过这种方法,我们实际上可以看到将用于给定范围的图 block 。重要的是,需要在平铺程序的坐标系中提供范围(几乎完全是 Web Mercator)。

In [1]: import cartopy.io.img_tiles as cimgt 
 
In [2]: import shapely.geometry as sgeom 
 
In [3]: import cartopy.crs as ccrs 
 
In [4]: tiler = cimgt.OSM() 
 
In [5]: pt = 45.068466, -66.45477 
 
In [6]: target = sgeom.box(pt[0] - 1.5, pt[1] - 1.5, pt[0] + 1.5, pt[1] + 1.5) 
 
In [7]: target_mercator = tiler.crs.project_geometry(target, ccrs.Geodetic()).geoms[0] 

因此,所有的部分都准备就绪后,我们就可以开始找出我们需要在特定缩放级别绘制哪些图 block :

对于您指定的目标,在缩放级别 0,我们需要以下图 block (x, y, z):

In [8]: list(tiler.find_images(target_mercator, 0)) 
Out[8]: [(0, 0, 0)] 

对于z=1,还是只有一个瓦片:

In [9]: list(tiler.find_images(target_mercator, 1)) 
Out[9]: [(1, 1, 1)] 

但是对于 z=2,我们显然跨越了一个 tile 边界,因为我们现在需要两个 tile 来覆盖目标域:

In [10]: list(tiler.find_images(target_mercator, 2)) 
Out[10]: [(2, 2, 2), (2, 3, 2)] 
 
Naturally, the list grows as we increase the zoom level: 
 
In [11]: list(tiler.find_images(target_mercator, 3)) 
Out[11]: [(4, 5, 3), (5, 5, 3), (4, 6, 3), (5, 6, 3)] 
 
In [12]: list(tiler.find_images(target_mercator, 6)) 
Out[12]: [(39, 47, 6), (40, 47, 6), (39, 48, 6), (40, 48, 6)] 

当我们点击 z=7 时,我们发现我们需要 8 个图 block 来表示相关区域:

In [13]: list(tiler.find_images(target_mercator, 7)) 
Out[13]:  
[(79, 94, 7), 
 (79, 95, 7), 
 (80, 94, 7), 
 (80, 95, 7), 
 (79, 96, 7), 
 (79, 97, 7), 
 (80, 96, 7), 
 (80, 97, 7)] 

我相信您可以看到这是怎么回事,但是让我们尝试将缩放级别设置为 14 以了解我们需要多少个图 block 。为了节省电力和阅读时间,我们只打印该列表的长度...

In [14]: len(list(tiler.find_images(target_mercator, 14))) 
Out[14]: 47334 

是的,因此在您想要的范围内请求缩放级别 14 需要您以 256x256(8 位彩色映射 PNG)下载约 47,000 个图 block 。 z=0 磁贴 ( https://a.tile.openstreetmap.org/0/0/0.png ) 压缩后约为 8882 字节,因此假设这是典型情况,您最终会下载 ~420420588 字节 (400MB)。为了将这些数据保存在内存中,您还需要大约 2.9GB 的 RAM。最后,要将此数据重新投影到 PlateCarree,您至少需要将此数量的 RAM 翻倍,并且假设一个高效的重新投影实现(cartopy 不是)。

希望这会告诉您为什么您的代码似乎需要很长时间 - 您要求它做很多工作。已经有一些关于 cartopy 是否应该在请求过多的图 block 时发出警告的讨论,但它总是归结为一个不合理的数字(你可能实际上想要获取那么多图 block ! ).我们还讨论了自动缩放级别选择 - 如果有足够的需求,这就是 really feasible。

HTH