对沿着两点之间的路径移动的点进行动画处理

2023-12-13

我想要制作一个点的动画,该点沿着地图上的一个位置到另一个位置的路径移动。

例如,我使用大地测量变换绘制了从纽约到新德里的路径。例如。取自文档将数据添加到地图

plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
     color='blue', linewidth=2, marker='o',
     transform=ccrs.Geodetic(),
     )

现在我想沿着这条路径移动一个点。

我的想法是以某种方式沿着路径获取一些(比如 50 个)点,并在每个帧的每个点上绘制一个标记。但我无法找到一种方法来获得路径上的点。

我发现了一个函数transform_points下课CRS,但我无法使用它,因为这给了我相同数量的点数,而不是之间的点数。

提前致谢!


有几种方法可以实现这一点。

matplotlib 方法

如果您熟悉 matplotlib,我将从最基本的开始,但这种方法会间接使用 cartopy 的功能,因此更难配置/扩展。

有一个私人_get_transformed_pathLine2D 对象上的方法(从返回的东西plt.plot)。生成的 TransformedPath 对象有一个get_transformed_path_and_affine方法,它基本上会给我们投影线(在正在绘制的轴的坐标系中)。

In [1]: import cartopy.crs as ccrs

In [3]: import matplotlib.pyplot as plt

In [4]: ax = plt.axes(projection=ccrs.Robinson())

In [6]: ny_lon, ny_lat = -75, 43

In [7]: delhi_lon, delhi_lat = 77.23, 28.61

In [8]: [line] = plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
   ...:          color='blue', linewidth=2, marker='o',
   ...:          transform=ccrs.Geodetic(),
   ...:          )

In [9]: t_path = line._get_transformed_path()

In [10]: path_in_data_coords, _ = t_path.get_transformed_path_and_affine()

In [11]: path_in_data_coords.vertices
Out[11]: 
array([[-6425061.82215208,  4594257.92617961],
       [-5808923.84969279,  5250795.00604155],
       [-5206753.88613758,  5777772.51828996],
       [-4554622.94040482,  6244967.03723341],
       [-3887558.58343227,  6627927.97123701],
       [-3200922.19194864,  6932398.19937816],
       [-2480001.76507805,  7165675.95095855],
       [-1702269.5101901 ,  7332885.72276795],
       [ -859899.12295981,  7431215.78426759],
       [   23837.23431173,  7453455.61302756],
       [  889905.10635756,  7397128.77301289],
       [ 1695586.66856764,  7268519.87627204],
       [ 2434052.81300274,  7073912.54130764],
       [ 3122221.22299409,  6812894.40443648],
       [ 3782033.80448001,  6478364.28561403],
       [ 4425266.18173684,  6062312.15662039],
       [ 5049148.25986903,  5563097.6328901 ],
       [ 5616318.74912886,  5008293.21452795],
       [ 6213232.98764984,  4307186.23400115],
       [ 6720608.93929235,  3584542.06839575],
       [ 7034261.06659143,  3059873.62740856]])

我们可以将其与 matplotlib 的动画功能结合起来,按照要求进行操作:

import cartopy.crs as ccrs
import matplotlib.animation as animation
import matplotlib.pyplot as plt

ax = plt.axes(projection=ccrs.Robinson())
ax.stock_img()

ny_lon, ny_lat = -75, 43
delhi_lon, delhi_lat = 77.23, 28.61

[line] = plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
         color='blue', linewidth=2, marker='o',
         transform=ccrs.Geodetic(),
         )

t_path = line._get_transformed_path()
path_in_data_coords, _ = t_path.get_transformed_path_and_affine()


# Draw the point that we want to animate.
[point] = plt.plot(ny_lon, ny_lat, marker='o', transform=ax.projection)

def animate_point(i):
    verts = path_in_data_coords.vertices
    i = i % verts.shape[0]
    # Set the coordinates of the line to the coordinate of the path.
    point.set_data(verts[i, 0], verts[i, 1])

ani = animation.FuncAnimation(
    ax.figure, animate_point,
    frames= path_in_data_coords.vertices.shape[0],
    interval=125, repeat=True)

ani.save('point_ani.gif', writer='imagemagick')
plt.show()

The matplotlib way

卡托比方法

在底层,cartopy 的 matplotlib 实现(如上面使用的)正在调用项目几何方法。我们也可以直接使用它,因为使用 Shapely 几何图形通常比使用 matplotlib 路径更方便。

通过这种方法,我们只需定义一个形状几何体,然后构造我们想要将几何体转换为源和目标坐标参考系:

target_cs.project_geometry(geometry, source_cs)

我们唯一需要注意的是结果可以是 MultiLineString(或者更一般地说,任何 Multi-geometry 类型)。但是,在我们的简单情况下,我们不需要处理这个问题(顺便说一句,第一个示例中返回的简单 Path 也是如此)。

生成与上面类似的图的代码:

import cartopy.crs as ccrs
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import shapely.geometry as sgeom


ax = plt.axes(projection=ccrs.Robinson())
ax.stock_img()

ny_lon, ny_lat = -75, 43
delhi_lon, delhi_lat = 77.23, 28.61


line = sgeom.LineString([[ny_lon, ny_lat], [delhi_lon, delhi_lat]])

projected_line = ccrs.PlateCarree().project_geometry(line, ccrs.Geodetic())

# We only animate along one of the projected lines.
if isinstance(projected_line, sgeom.MultiLineString):
    projected_line = projected_line.geoms[0]

ax.add_geometries(
    [projected_line], ccrs.PlateCarree(),
    edgecolor='blue', facecolor='none')

[point] = plt.plot(ny_lon, ny_lat, marker='o', transform=ccrs.PlateCarree())


def animate_point(i):
    verts = np.array(projected_line.coords)
    i = i % verts.shape[0]
    # Set the coordinates of the line to the coordinate of the path.
    point.set_data(verts[i, 0], verts[i, 1])

ani = animation.FuncAnimation(
    ax.figure, animate_point,
    frames=len(projected_line.coords),
    interval=125, repeat=True)

ani.save('projected_line_ani.gif', writer='imagemagick')
plt.show()

The cartopy way

最后的剩余内容……

该方法自然地推广到对任何类型的 matplotlib Arrrrtist 进行动画处理......在这种情况下,我对大圆分辨率进行了更多控制,并沿大圆对图像进行了动画处理:

import cartopy.crs as ccrs
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import shapely.geometry as sgeom


ax = plt.axes(projection=ccrs.Mercator())
ax.stock_img()

line = sgeom.LineString([[-5.9845, 37.3891], [-82.3666, 23.1136]])


# Higher resolution version of Mercator. Same workaround as found in
# https://github.com/SciTools/cartopy/issues/8#issuecomment-326987465.
class HighRes(ax.projection.__class__):
    @property
    def threshold(self):
        return super(HighRes, self).threshold / 100


projected_line = HighRes().project_geometry(line, ccrs.Geodetic())

# We only animate along one of the projected lines.
if isinstance(projected_line, sgeom.MultiLineString):
    projected_line = projected_line.geoms[0]

# Add the projected line to the map.
ax.add_geometries(
    [projected_line], ax.projection,
    edgecolor='blue', facecolor='none')


def ll_to_extent(x, y, ax_size=(4000000, 4000000)):
    """
    Return an image extent in centered on the given
    point with the given width and height.

    """
    return [x - ax_size[0] / 2, x + ax_size[0] / 2,
            y - ax_size[1] / 2, y + ax_size[1] / 2]


# Image from https://pixabay.com/en/sailing-ship-boat-sail-pirate-28930/.
pirate = plt.imread('pirates.png')
img = ax.imshow(pirate, extent=ll_to_extent(0, 0), transform=ax.projection, origin='upper')

ax.set_global()


def animate_ship(i):
    verts = np.array(projected_line.coords)
    i = i % verts.shape[0]

    # Set the extent of the image to the coordinate of the path.
    img.set_extent(ll_to_extent(verts[i, 0], verts[i, 1]))


ani = animation.FuncAnimation(
    ax.figure, animate_ship,
    frames=len(projected_line.coords),
    interval=125, repeat=False)

ani.save('arrrr.gif', writer='imagemagick')
plt.show()

Arrrr, here be pirates!

该答案的所有代码和图像可以在以下位置找到https://gist.github.com/pelson/618a5f4ca003e56f06d43815b21848f6.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对沿着两点之间的路径移动的点进行动画处理 的相关文章

随机推荐