2012-04-26 15 views
26

Estoy tratando de trazar una imagen tridimensional del lecho marino a partir de los datos de un sonar que recorre una porción de 500m por 40m del lecho marino. Estoy usando matplotlib/mplot3d con Axes3D y quiero poder cambiar la relación de aspecto de los ejes para que el eje x de x & y esté a escala. Un script de ejemplo con datos generados en lugar de los datos reales es:Configuración de la relación de aspecto de la trama 3D

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') 
la imagen de salida de este script

Y:

matplotlib output image

Ahora me gustaría cambiar de modo que 1 metro en la el eje a lo largo de la trayectoria (x) es igual a 1 metro en el eje del rango (y) (o tal vez una relación diferente dependiendo de los tamaños relativos involucrados). También me gustaría establecer la relación del eje z, de nuevo innecesariamente a 1: 1 debido a los tamaños relativos en los datos, pero el eje es más pequeño que el gráfico actual.

que han intentado construir y usar this branch of matplotlib, siguiendo el ejemplo de script en this message from the mailing list, pero la adición de la línea ax.pbaspect = [1.0, 1.0, 0.25] a mi guión (después de haber desinstalado la versión 'estándar' de matplotlib para asegurar la versión personalizada estaba siendo utilizado) no hizo cualquier diferencia en la imagen generada.

Editar: Por lo tanto, la salida deseada sería algo así como la siguiente imagen (crudamente editada con Inkscape). En este caso, no he establecido una relación 1: 1 en los ejes x/y porque parece ridículamente delgada, pero la he extendido para que no esté cuadrada como en la salida original.

Desired output

Respuesta

18

Agregar siguiente código antes de savefig:

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

enter image description here

Si desea ningún eje cuadrado:

editar la función get_proj dentro site-packages \ mpl_toolkits \ mplot3d \ axes3d.py:

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

continuación, añadir una línea para establecer pbaspect:

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

enter image description here

+0

Hmmm. Eso hace que la escala de los ejes sea correcta, pero da como resultado un gran desperdicio de espacio. Si bien * podía * guardar esto como SVG y editarlo manualmente (como lo que hice con la imagen deseada con la que acabo de actualizar la pregunta), esto sería muy tedioso cuando tengo una gran cantidad de imágenes para crear, y no estoy Asegúrese de que se pueda automatizar ... – Blair

+1

Puede usar la modificación pbaspect para obtener ejes cuadrados. He editado la respuesta. – HYRY

+0

Impresionante, gracias! – Blair

5

La respuesta a this question funciona perfectamente para mí. Y no necesita configurar ninguna proporción, hace todo automáticamente.

0

Que cómo he resuelto el problema de espacio perdido:

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)) 
Cuestiones relacionadas