Настройка соотношения сторон 3D-графика
Я пытаюсь построить трехмерное изображение морского дна из данных гидролокатора, проходящего через 500 м на морскую дне на 40 м. Я использую matplotlib/mplot3d с Axes3D, и я хочу иметь возможность изменять соотношение сторон осей, чтобы оси x и y были масштабированы. Пример script с сгенерированными данными, а не с реальными данными:
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')
И выходное изображение из этого script:
![matplotlib output image]()
Теперь я хотел бы изменить его так, чтобы 1 метр вдоль оси (x) был таким же, как 1 метр в оси диапазона (y) (или, возможно, другое отношение в зависимости от относительных размеров). Я также хотел бы установить отношение оси z, опять же необязательно к 1:1 из-за относительных размеров данных, но поэтому ось меньше, чем текущий график.
Я попытался построить и использовать эту ветку matplotlib, следуя примеру script в это сообщение из списка рассылки, но добавив строку ax.pbaspect = [1.0, 1.0, 0.25]
к моему script (удалив "стандартную" версию matplotlib, чтобы гарантировать, что пользовательская версия используется), не вносить изменения в сгенерированное изображение.
Изменить: Таким образом, желаемый результат будет выглядеть примерно следующим образом (грубо отредактированный с помощью Inkscape). В этом случае я не устанавливал соотношение 1:1 на оси x/y, потому что это выглядит смехотворно тонким, но я его разложил, поэтому он не квадратный, как на исходном выходе.
![Desired output]()
Ответы
Ответ 1
Добавьте следующий код перед сохранением:
ax.auto_scale_xyz([0, 500], [0, 500], [0, 0.15])
![enter image description here]()
Если вы не хотите иметь квадратную ось:
отредактируйте функцию get_proj
внутри site-packages\mpl_toolkits\mplot3d\axes3d.py:
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
Ответ на этот вопрос отлично работает для меня. И вам не нужно настраивать какое-либо соотношение, оно делает все автоматически.
Ответ 3
Как я решил проблему впустую пространства:
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))
Ответ 4
Проблема была открыта на github: https://github.com/matplotlib/matplotlib/issues/8593.
Вышеуказанные решения, похоже, больше не работают. get_proj
нужно отредактировать функцию 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() )