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

时间:2018-07-23 15:11:26

标签: cartopy

我想为沿着地图上从一个位置到另一个位置的路径移动的点设置动画。

例如,我使用大地坐标变换绘制了一条从纽约到新德里的路线。例如。取自文档Adding data to the map

[Export("control:textView:completions:forPartialWordRange:indexOfSelectedItem:")]
public string[] GetCompletions(NSControl control, NSTextView textView, string[] words, NSRange charRange, ref nint index)
{
    requestor = null;
    return completionWords;
`}

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

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

我在类plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat], color='blue', linewidth=2, marker='o', transform=ccrs.Geodetic(), ) 下找到了一个函数transform_points,但是我无法使用它,因为这给了我相同数量的点,而不是中间的点。

谢谢!

1 个答案:

答案 0 :(得分:6)

有两种方法。

matplotlib方法

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

Line2D对象(从_get_transformed_path返回的东西)上有一个专用的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方法

在底层,cartopy的matplotlib实现(如上所述)正在调用project_geometry方法。我们也可以直接使用它,因为使用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

最终remaaaaarrrrrrks ....

该方法自然可以概括为对任何类型的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上找到。

相关问题