错误的箭头长度使用箭袋和摄影投影

发布于 2025-01-19 05:38:21 字数 3360 浏览 3 评论 0原文

我想用vector绘制一个向量字段,该矢量字段代表用Cartopy在地图上的一个点之间的位移。

使用PlateCarree()转换时,我的代码按预期工作,但是箭头长度是我测试的所有其他预测的几个数量级。

这是一个MWE,应该清楚地说明问题:

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

import numpy

# Want to test for several different projections
projections = [
    ccrs.PlateCarree(),
    ccrs.EqualEarth(),
    ccrs.Mollweide(),
    ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()

# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()

# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)

# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()

for i, projection in enumerate(projections):
    # Replace the existing ax with an ax with the desired projection.
    ax = axes[i]
    fig.delaxes(ax)
    ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
    # Make the plot readable.
    ax.set_global()
    ax.gridlines(draw_labels="x")

    # Non pertubed points are plotted in black.
    ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
    # Perturbed points are plotted in red.
    ax.plot(
        lon + lon_perturbation,
        lat + lat_perturbation,
        "r.",
        ms=5,
        transform=coordinate_ccrs,
    )
    # We try to draw arrows from a given black dot to its corresponding
    # red dot.
    ax.quiver(
        lon,
        lat,
        lon_perturbation,
        lat_perturbation,
        transform=coordinate_ccrs,
        # From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
        # look at the documentation of the "scale_unit" parameter.
        # The next 3 parameters are what matplotlib tell us to do. From
        # matplotlib documentation:
        #   To plot vectors in the x-y plane, with u and v having the same units
        #   as x and y, use angles='xy', scale_units='xy', scale=1.
        angles="xy",
        scale_units="xy",
        scale=1,
        # Simply make the arrows nicer, removing these last 3 parameters do not
        # solve the issue.
        minshaft=2,
        minlength=0.5,
        width=0.002,
    )

# Show everything
plt.show()

在屏幕上显示以下图像:

“在此处输入图像说明”

PlateCarree转换是唯一显示箭头的一个。实际上,其他3个预测中有箭头,但是我为了看到它们,我需要用scale = 0.00001在调用QUIVER中将它们缩放为10000,给出:

“

使用CATTOPY api时,我是否犯了一个错误,这是预期的行为,我错过了文档中的某些内容,还是这是一个错误?

I want to plot a vector field with vectors representing a displacement between one point to another on the map with cartopy.

My code works as expected when using the PlateCarree() transformation, but arrow length is several orders of magnitude off for all the other projections I tested.

Here is a MWE that should illustrate quite clearly the issue:

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

import numpy

# Want to test for several different projections
projections = [
    ccrs.PlateCarree(),
    ccrs.EqualEarth(),
    ccrs.Mollweide(),
    ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()

# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()

# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)

# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()

for i, projection in enumerate(projections):
    # Replace the existing ax with an ax with the desired projection.
    ax = axes[i]
    fig.delaxes(ax)
    ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
    # Make the plot readable.
    ax.set_global()
    ax.gridlines(draw_labels="x")

    # Non pertubed points are plotted in black.
    ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
    # Perturbed points are plotted in red.
    ax.plot(
        lon + lon_perturbation,
        lat + lat_perturbation,
        "r.",
        ms=5,
        transform=coordinate_ccrs,
    )
    # We try to draw arrows from a given black dot to its corresponding
    # red dot.
    ax.quiver(
        lon,
        lat,
        lon_perturbation,
        lat_perturbation,
        transform=coordinate_ccrs,
        # From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
        # look at the documentation of the "scale_unit" parameter.
        # The next 3 parameters are what matplotlib tell us to do. From
        # matplotlib documentation:
        #   To plot vectors in the x-y plane, with u and v having the same units
        #   as x and y, use angles='xy', scale_units='xy', scale=1.
        angles="xy",
        scale_units="xy",
        scale=1,
        # Simply make the arrows nicer, removing these last 3 parameters do not
        # solve the issue.
        minshaft=2,
        minlength=0.5,
        width=0.002,
    )

# Show everything
plt.show()

which display on the screen the following image:

enter image description here

The PlateCarree transformation is the only one displaying arrows. In fact, there are arrows in the other 3 projections, but I order to see them I need to scale them by 10000 with scale=0.00001 in the call to quiver, which gives:

enter image description here

Did I make a mistake when using cartopy API, is this expected behaviour and I missed something in the documentation, or is this a bug?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

紅太極 2025-01-26 05:38:21

虽然 github 上关于 cartopy 的 quiver-plot 转换的实现存在相当多的争论 GitHub-issues 事实上有一种方法可以让你的情节看起来像你想要的那样......

但是,在思考这个......我注意到有一个在使用投影箭袋图时您可能需要考虑的事情...

正如我所见,重新投影的箭头很可能需要弯曲才能真正可视化与原始数据中提供的相同方向!

(在 input-crs 中,箭头指向从点 A 到 B 的直线,但如果重新投影这些点,连接 A 和 B 的“直线”现在通常是一条曲线,因此如果原始方向是正确的,我认为新方向应该指示为弯曲箭头...)

话虽这么说,您可以通过手动转换点来实现您想要的,而不是让 cartopy 完成工作:

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

import numpy

# Want to test for several different projections
projections = [
    ccrs.PlateCarree(),
    ccrs.EqualEarth(),
    ccrs.Mollweide(),
    ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()

# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()

# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)

# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()

for i, projection in enumerate(projections):
    # Replace the existing ax with an ax with the desired projection.
    ax = axes[i]
    fig.delaxes(ax)
    ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
    # Make the plot readable.
    ax.set_global()
    ax.gridlines(draw_labels="x")

    # Non pertubed points are plotted in black.
    ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
    # Perturbed points are plotted in red.
    ax.plot(
        lon + lon_perturbation,
        lat + lat_perturbation,
        "r.",
        ms=5,
        transform=coordinate_ccrs,
    )
    
    
    xy_start = projection.transform_points(coordinate_ccrs, lon, lat)[:,:-1].T
    xy_end = projection.transform_points(coordinate_ccrs, lon + lon_perturbation, 
                                         lat + lat_perturbation)[:,:-1].T
    
    # We try to draw arrows from a given black dot to its corresponding
    # red dot.
    ax.quiver(
        *xy_start, 
        *(xy_end - xy_start),
        # From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
        # look at the documentation of the "scale_unit" parameter.
        # The next 3 parameters are what matplotlib tell us to do. From
        # matplotlib documentation:
        #   To plot vectors in the x-y plane, with u and v having the same units
        #   as x and y, use angles='xy', scale_units='xy', scale=1.
        angles="xy",
        scale_units="xy",
        scale=1,
        # Simply make the arrows nicer, removing these last 3 parameters do not
        # solve the issue.
        minshaft=2,
        minlength=0.5,
        width=0.002,
    )

# Show everything
plt.show()

在此处输入图像描述

while there's quite some debate on github about cartopy's implementation of quiver-plot transformations GitHub-issues there is in fact a way on how to get your plot look as you want it to look...

However, while thinking about this... I noticed that there's a thing that you might want to consider when using projected quiver-plots...

As I see it, the re-projected arrows would would most probably need to be curved to really visualize the same direction as provided in the original data!

(in the input-crs the arrow points as a straight line from point A to B, but if you re-project the points, the "straight line" that connected A and B is now in general a curved line, and so if the original direction was correct, I think the new direction should be indicated as a curved arrow...)

That being said, you could achieve what you want by transforming the points manually instead of letting cartopy do the job:

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

import numpy

# Want to test for several different projections
projections = [
    ccrs.PlateCarree(),
    ccrs.EqualEarth(),
    ccrs.Mollweide(),
    ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()

# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()

# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)

# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()

for i, projection in enumerate(projections):
    # Replace the existing ax with an ax with the desired projection.
    ax = axes[i]
    fig.delaxes(ax)
    ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
    # Make the plot readable.
    ax.set_global()
    ax.gridlines(draw_labels="x")

    # Non pertubed points are plotted in black.
    ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
    # Perturbed points are plotted in red.
    ax.plot(
        lon + lon_perturbation,
        lat + lat_perturbation,
        "r.",
        ms=5,
        transform=coordinate_ccrs,
    )
    
    
    xy_start = projection.transform_points(coordinate_ccrs, lon, lat)[:,:-1].T
    xy_end = projection.transform_points(coordinate_ccrs, lon + lon_perturbation, 
                                         lat + lat_perturbation)[:,:-1].T
    
    # We try to draw arrows from a given black dot to its corresponding
    # red dot.
    ax.quiver(
        *xy_start, 
        *(xy_end - xy_start),
        # From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
        # look at the documentation of the "scale_unit" parameter.
        # The next 3 parameters are what matplotlib tell us to do. From
        # matplotlib documentation:
        #   To plot vectors in the x-y plane, with u and v having the same units
        #   as x and y, use angles='xy', scale_units='xy', scale=1.
        angles="xy",
        scale_units="xy",
        scale=1,
        # Simply make the arrows nicer, removing these last 3 parameters do not
        # solve the issue.
        minshaft=2,
        minlength=0.5,
        width=0.002,
    )

# Show everything
plt.show()

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文