如何基于范围更新Cartopy+FuncAnimation中的瓦片地图?
解决Cartopy+FuncAnimation底图随轨迹范围更新的问题
核心问题是Cartopy的瓦片图层(如GoogleTiles)只会在首次添加时基于当前轴范围加载瓦片,轴范围变更后不会自动刷新。重复调用add_image只会叠加新瓦片,不会替换旧的,所以需要手动清理并重新加载。
具体实现步骤
- 在动画的
update函数中,先清除已加载的瓦片图层:Cartopy的Axes对象会将瓦片图层存在ax.images列表里,直接清空该列表即可(如果你的轴上只有瓦片底图的话)。 - 根据当前帧的轨迹数据计算新的显示范围,设置轴的
xlim和ylim,建议给范围加一点缓冲值(比如±0.5度),避免轨迹贴边。 - 重新调用
ax.add_image(tiler, zoom_level),此时会基于新的轴范围加载对应区域的瓦片。 - 更新轨迹的显示(比如更新线的坐标或标记的位置)。
完整代码示例
import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import cartopy.crs as ccrs from cartopy.io.img_tiles import GoogleTiles import geopandas as gpd import pandas as pd # 模拟轨迹数据:全球移动的点 dates = pd.date_range(start='2023-01-01', periods=50, freq='H') lons = pd.Series([-180 + 7.2*i for i in range(50)]) # 从西向东绕全球 lats = pd.Series([0]*25 + [10]*25) # 前半段赤道,后半段北纬10度 gdf = gpd.GeoDataFrame( {'datetime': dates}, geometry=gpd.points_from_xy(lons, lats), crs='EPSG:4326' ) # 初始化绘图 tiler = GoogleTiles(style='satellite') proj = tiler.crs # 瓦片使用墨卡托投影,轴要对应这个投影 fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': proj}) # 初始化轨迹线和标记 trail_line, = ax.plot([], [], 'r-', transform=ccrs.Geodetic()) current_marker, = ax.plot([], [], 'ro', markersize=8, transform=ccrs.Geodetic()) # 动画初始化函数 def init(): trail_line.set_data([], []) current_marker.set_data([], []) return trail_line, current_marker # 动画更新函数 def update(frame): # 获取当前及之前的轨迹数据 current_data = gdf.iloc[:frame+1] lons = current_data.geometry.x lats = current_data.geometry.y # 1. 清除旧的瓦片图层 ax.images.clear() # 2. 计算并设置新的显示范围(加缓冲) lon_min, lon_max = lons.min() - 0.5, lons.max() + 0.5 lat_min, lat_max = lats.min() - 0.5, lats.max() + 0.5 # 将经纬度范围转换为墨卡托投影的坐标 x_min, y_min = proj.transform_point(lon_min, lat_min, ccrs.Geodetic()) x_max, y_max = proj.transform_point(lon_max, lat_max, ccrs.Geodetic()) ax.set_xlim(x_min, x_max) ax.set_ylim(y_min, y_max) # 3. 重新添加瓦片底图 ax.add_image(tiler, 5) # zoom_level根据需要调整 # 4. 更新轨迹和标记 trail_line.set_data(lons, lats) current_marker.set_data(lons.iloc[-1], lats.iloc[-1]) return trail_line, current_marker # 创建并运行动画 ani = FuncAnimation(fig, update, frames=len(gdf), init_func=init, interval=200, blit=True) plt.show()
注意事项
- 确保轴的投影和瓦片的投影一致(比如GoogleTiles用的是墨卡托投影
ccrs.Mercator(),所以轴的projection要设为tiler.crs)。 - 缩放级别
zoom_level可以根据轨迹的范围调整,范围越小(比如局部区域)可以设更高的级别,范围越大(比如全球)设较低级别。 - 如果你的轴上还有其他图像元素,不要直接
ax.images.clear(),可以通过判断元素的类型来筛选清除瓦片:比如遍历ax.images,移除那些是cartopy.mpl.singleton_artist.SingletonArtist类型的(瓦片图层的类型)。
内容的提问来源于stack exchange,提问作者August
相关产品推荐
相关产品推荐

