使用带有 cartopy 投影的箭袋时箭头长度错误
Wrong arrow length using quiver with cartopy projections
我想用 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 个投影中有箭头,但我为了看到它们,我需要在调用 quiver
时使用 scale=0.00001
将它们缩放 10000,这给出:
我在使用 cartopy
时是否犯了错误 API,这是预期的行为,我在文档中遗漏了什么,还是这是一个错误?
虽然关于 github 关于 cartopy 实现 quiver-plot 转换 GitHub-issues 有相当多的争论 GitHub-issues 实际上有一种方法可以让你的情节看起来像你想要的那样...
但是,在考虑这个问题时...我注意到在使用投影 quiver-plots 时您可能需要考虑一件事...
在我看来,re-projected 箭头很可能需要弯曲才能真正可视化原始数据中提供的相同方向!
(在 input-crs 中,箭头指向 A 点到 B 点的直线,但是如果 re-project 这些点,则连接 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()
我想用 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 个投影中有箭头,但我为了看到它们,我需要在调用 quiver
时使用 scale=0.00001
将它们缩放 10000,这给出:
我在使用 cartopy
时是否犯了错误 API,这是预期的行为,我在文档中遗漏了什么,还是这是一个错误?
虽然关于 github 关于 cartopy 实现 quiver-plot 转换 GitHub-issues 有相当多的争论 GitHub-issues 实际上有一种方法可以让你的情节看起来像你想要的那样...
但是,在考虑这个问题时...我注意到在使用投影 quiver-plots 时您可能需要考虑一件事...
在我看来,re-projected 箭头很可能需要弯曲才能真正可视化原始数据中提供的相同方向!
(在 input-crs 中,箭头指向 A 点到 B 点的直线,但是如果 re-project 这些点,则连接 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()