对两点之间的点以及旋转的地球进行动画处理

时间:2018-08-11 15:21:12

标签: python-3.x matplotlib cartopy

这是this question的扩展,其中只有点在移动。

现在,我也想与动画点一起移动地球,以使移动点始终位于中心。 例如:

Rotating Earth with an animated path

现在,我可以使用此代码创建每个帧并将它们添加在一起以创建动画图像。

<alpha, epsilon, beta, delta, gamma>

有什么办法只能在不保存图像的情况下在绘图窗口中获得此动画吗?

1 个答案:

答案 0 :(得分:2)

在开始之前,重要的是要记住,cartopy的长处在于处理投影数据。无论正投影的3D效果如何,它实际上只是数据的2D表示-matplotlib和cartopy的matplotlib接口从根本上在2D空间中运行,因此必须计算3D外观(在CPU而不是GPU上),每个角度。例如,如果将一些海岸线投影到正交投影上,然后想要稍微旋转正交投影,则需要再次进行所有投影计算。可以想象在3d中使用OpenGL这样的东西,但是cartopy / matpltolib中没有任何东西可以使之成为现成的东西。

好吧,顺便提一下,也许关于这个问题需要注意的最重要的事情是:Cartopy的GeoAxes是为单个不变的投影设计的。 没有API 允许您在实例化投影后对其进行更改。因此,我们唯一能做的就是为每次旋转创建一个新的GeoAxes。

实现这种目标的模式可能类似于:

def decorate_axes(ax):
    ax.coastlines()
    ...

def animate(i):
    ax = plt.gca()
    ax.remove()

    ax = plt.axes(projection=...)
    decorate_axes(ax)

ani = animation.FuncAnimation(
    plt.gcf(), animate,
    ...)

概念的快速证明:

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

plt.figure(figsize=(6, 6))


def decorate_axes(ax):
    ax.set_global()
    ax.coastlines()


def animate(i):
    lon = i

    ax = plt.gca()
    ax.remove()

    ax = plt.axes([0, 0, 1, 1], projection=ccrs.Orthographic(
        central_latitude=0, central_longitude=lon))
    decorate_axes(ax)


ani = animation.FuncAnimation(
    plt.gcf(), animate,
    frames=np.linspace(0, 360, 40),
    interval=125, repeat=False)

ani.save('poc.gif', writer='imagemagick', dpi=plt.gcf().dpi)

Rotating globe

现在我们有了基本知识,让我们以the answer you referred to为基础,使用上面开发的模式,基于大圆的路径为正交投影的旋转设置动画...

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.animation as animation
import matplotlib.image as mimage
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import numpy as np
import shapely.geometry as sgeom


plt.figure(figsize=(6, 6))

line = sgeom.LineString([[0, 15], [-140, -40], [120, -20],
                         [0, -20], [-140, 15], [90, 45],
                         [0, 15]])


class HighResPC(ccrs.PlateCarree):
    @property
    def threshold(self):
        return super(HighResPC, self).threshold / 100


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


verts = np.concatenate([np.array(l.coords) for l in projected_line])


def setup_axes(ax, x, y):
    ax.set_global()

    ax.add_feature(cfeature.LAND)
    ax.add_feature(cfeature.OCEAN)

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

    # Image from http://madmen.wikia.com/wiki/File:Superman.gif.
    superman = plt.imread('superman.png')

    # Scale the actual image down a little.
    img_size = np.array(superman.shape) / 2

    x, y = ax.projection.transform_point(x, y, ccrs.PlateCarree())
    # Convert the projected coordinates into pixels.
    x_pix, y_pix = ax.transData.transform((x, y))

    # Make the extent handle the appropriate image size.
    extent = [x_pix - 0.5 * img_size[1], y_pix - 0.5 * img_size[0],
              x_pix + 0.5 * img_size[1], y_pix + 0.5 * img_size[0]]

    bbox = mtransforms.Bbox.from_extents(extent)
    img = mimage.BboxImage(bbox, zorder=10)
    img.set_data(superman)
    ax.add_artist(img)

    return img


def animate_superman(i):
    i = i % verts.shape[0]

    ax = plt.gca()
    ax.remove()

    ax = plt.axes([0, 0, 1, 1], projection=ccrs.Orthographic(
        central_latitude=verts[i, 1], central_longitude=verts[i, 0]))
    ax.coastlines()

    img = setup_axes(ax, verts[i, 0], verts[i, 1])


ani = animation.FuncAnimation(
    plt.gcf(), animate_superman,
    frames=verts.shape[0],
    interval=125, repeat=False)

ani.save('superman.gif', writer='imagemagick', dpi=plt.gcf().dpi)

Animated spinning globe using cartopy

我最喜欢这个动画的地方是,跟随的大圆刚开始出现在地球上时就非常弯曲,并且当它们包括投影的中心点时就变成了直线。更一般而言,对于所有方位角投影(正交投影就是其中之一),从中心点开始的大圆圈始终是直线... Wikipedia指出:

  

因此,通过中心点的大圆圈在地图上用直线表示。

     

来源:https://en.wikipedia.org/wiki/Map_projection#Azimuthal_.28projections_onto_a_plane.29

此动画有一些值得注意的问题:

  • 动画不是特别流畅:解决方案是使用自定义投影(HighResPC)的阈值来提高分辨率。
  • 地球不是以恒定的速度旋转:解决方案可能是使用geoliblib工具根据特定的步长(以米为单位)生成大圆圈
  • 动画的速度不是特别快:尽管Cartopy只能以合理的帧速率处理投影的低分辨率海岸线,但图像转换功能却不能(实际上并没有针对性能进行优化)。因此,您将无法在想要互动动画的频率附近的任何地方做类似ax.stock_img()的事情。