设置3D图的纵横比

问题描述 投票:0回答:4

我正在尝试根据在 500m x 40m 海底部分运行的声纳数据绘制海底 3D 图像。我将 matplotlib/mplot3d 与 Axes3D 一起使用,我希望能够更改轴的纵横比,以便 x 和 y 轴缩放。包含生成数据而不是真实数据的示例脚本是:

import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

# Create figure.
fig = plt.figure()
ax = fig.gca(projection = '3d')

# Generate example data.
R, Y = np.meshgrid(np.arange(0, 500, 0.5), np.arange(0, 40, 0.5))
z = 0.1 * np.abs(np.sin(R/40) * np.sin(Y/6))

# Plot the data.
surf = ax.plot_surface(R, Y, z, cmap=cm.jet, linewidth=0)
fig.colorbar(surf)

# Set viewpoint.
ax.azim = -160
ax.elev = 30

# Label axes.
ax.set_xlabel('Along track (m)')
ax.set_ylabel('Range (m)')
ax.set_zlabel('Height (m)')

# Save image.
fig.savefig('data.png')

该脚本的输出图像:

matplotlib output image

现在我想更改它,使沿轨迹 (x) 轴上的 1 米与范围 (y) 轴上的 1 米相同(或者可能是不同的比率,具体取决于所涉及的相对尺寸)。我还想设置 z 轴的比率,由于数据中的相对大小,同样不必设置为 1:1,但因此轴比当前绘图小。

我已经尝试构建和使用matplotlib的这个分支,遵循邮件列表中的此消息中的示例脚本,但将

ax.pbaspect = [1.0, 1.0, 0.25]
行添加到我的脚本中(已卸载matplotlib的“标准”版本以确保正在使用自定义版本)对生成的图像没有任何影响。

编辑: 因此,所需的输出将类似于以下(使用 Inkscape 粗略编辑的)图像。在本例中,我没有在 x/y 轴上设置 1:1 的比例,因为这看起来非常薄,但我已将其展开,因此它不像原始输出那样是方形的。

Desired output

python matplotlib mplot3d
4个回答
26
投票

在savefig之前添加以下代码:

ax.auto_scale_xyz([0, 500], [0, 500], [0, 0.15])

enter image description here

如果你不需要方轴:

编辑 site-packages\mpl_toolkits\mplot3d xes3d.py 中的

get_proj
函数:

xmin, xmax = np.divide(self.get_xlim3d(), self.pbaspect[0])
ymin, ymax = np.divide(self.get_ylim3d(), self.pbaspect[1])
zmin, zmax = np.divide(self.get_zlim3d(), self.pbaspect[2])

然后添加一行来设置pbaspect:

ax = fig.gca(projection = '3d')
ax.pbaspect = [2.0, 0.6, 0.25]

enter image description here


2
投票

已在 github 上打开一个问题:https://github.com/matplotlib/matplotlib/issues/8593

以上解决方案似乎不再起作用。现在必须按以下方式编辑

get_proj
中的
site-packages\mpl_toolkits\mplot3d\axes3d.py
函数:

        try:
            self.localPbAspect = self.pbaspect
        except AttributeError:
            self.localPbAspect = [1,1,1]

        xmin, xmax = ( lim / self.localPbAspect[0] for lim in self.get_xlim3d() )
        ymin, ymax = ( lim / self.localPbAspect[1] for lim in self.get_ylim3d() )
        zmin, zmax = ( lim / self.localPbAspect[2] for lim in self.get_zlim3d() )

1
投票

暂时在这里声明完整的解决方案(2024,matmplotlib:3.9)。 Axes3D.set_box_aspect

ax.set_box_aspect(aspect=(x_scale, y_scale, z_scale))

完整代码:

import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np

# Create figure.
fig = plt.figure(figsize=(16, 9))
ax = fig.add_subplot(projection='3d')

# Generate example data.
R, Y = np.meshgrid(np.arange(0, 500, 0.5), np.arange(0, 40, 0.5))
z = 0.1 * np.abs(np.sin(R/40) * np.sin(Y/6))

# Plot the data.
surf = ax.plot_surface(R, Y, z, cmap=cm.jet, linewidth=0)
fig.colorbar(surf)

# Set viewpoint.
ax.azim = -160
ax.elev = 30

# Label axes.
ax.set_xlabel('Along track (m)')
ax.set_ylabel('Range (m)')
ax.set_zlabel('Height (m)')

# padding for axis label not to overlap with axis ticks
ax.xaxis.labelpad=30    

# changing aspect ratio
ax.set_box_aspect(aspect=(4, 1, 0.5), zoom=1.3)

# Save image.
fig.savefig('data.jpg', dpi=200)

感谢@ImportanceOfBeingErnest 引用问题和答案。


0
投票

我是如何解决浪费空间问题的:

try: 
    self.localPbAspect=self.pbaspect
    zoom_out = (self.localPbAspect[0]+self.localPbAspect[1]+self.localPbAspect[2]) 
except AttributeError: 
    self.localPbAspect=[1,1,1]
    zoom_out = 0 
xmin, xmax = self.get_xlim3d() /  self.localPbAspect[0]
ymin, ymax = self.get_ylim3d() /  self.localPbAspect[1]
zmin, zmax = self.get_zlim3d() /  self.localPbAspect[2]

# transform to uniform world coordinates 0-1.0,0-1.0,0-1.0
worldM = proj3d.world_transformation(xmin, xmax,
                                         ymin, ymax,
                                         zmin, zmax)

# look into the middle of the new coordinates
R = np.array([0.5*self.localPbAspect[0], 0.5*self.localPbAspect[1], 0.5*self.localPbAspect[2]])
xp = R[0] + np.cos(razim) * np.cos(relev) * (self.dist+zoom_out)
yp = R[1] + np.sin(razim) * np.cos(relev) * (self.dist+zoom_out)
zp = R[2] + np.sin(relev) * (self.dist+zoom_out)
E = np.array((xp, yp, zp))
© www.soinside.com 2019 - 2024. All rights reserved.