2012-04-26 5 views
26

J'essaie de tracer une image 3D du fond marin à partir des données d'un sonar sur une portion de 500m par 40m du fond marin. J'utilise matplotlib/mplot3d avec Axes3D et je veux être en mesure de changer le rapport d'aspect des axes de sorte que l'axe y x & y soient à l'échelle. Un exemple de script avec des données générées plutôt que les données réelles est:Réglage du rapport d'aspect du tracé 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') 

Et l'image de sortie de ce script:

matplotlib output image

Maintenant, je voudrais changer de sorte que 1 mètre dans la l'axe longitudinal (x) est le même que 1 mètre dans l'axe de distance (y) (ou peut-être un rapport différent selon les tailles relatives). Je voudrais également définir le rapport de l'axe z, encore une fois pas nécessairement à 1: 1 en raison des tailles relatives dans les données, mais si l'axe est plus petit que le tracé actuel.

J'ai essayé la construction et l'utilisation this branch of matplotlib, suivant l'exemple de script dans this message from the mailing list, mais en ajoutant la ligne ax.pbaspect = [1.0, 1.0, 0.25] à mon script (après avoir désinstallé la version « standard » de matplotlib pour assurer la version personnalisée a été utilisée) n'a pas fait toute différence dans l'image générée.

Editer: Donc la sortie désirée serait quelque chose comme l'image suivante (grossièrement éditée avec Inkscape). Dans ce cas, je n'ai pas mis un rapport de 1: 1 sur les axes x/y parce que cela semble ridiculement mince, mais je l'ai étalé de sorte qu'il ne soit pas carré comme sur la sortie d'origine.

Desired output

Répondre

18

Ajouter code suivant avant savefig:

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

enter image description here

Si vous voulez pas d'axe carré:

modifier la fonction get_proj dans 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] 

puis ajoutez une ligne pour définir pbaspect:

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

enter image description here

+0

Hmmm. Cela permet de corriger la mise à l'échelle des axes, mais entraîne beaucoup d'espace gaspillé. Bien que je * puisse * enregistrer ceci en tant que SVG et l'éditer manuellement (comme ce que j'ai fait avec l'image que je viens de mettre à jour) cela deviendrait très fastidieux quand j'ai un grand nombre d'images à créer, et je ne suis pas Assurez-vous qu'il peut être automatisé ... – Blair

+1

Vous pouvez utiliser la modification pbaspect pour ne pas avoir d'axes carrés. J'ai édité la réponse. – HYRY

+0

Génial, merci! – Blair

5

La réponse à this question fonctionne parfaitement pour moi. Et vous n'avez pas besoin d'établir un ratio, il fait tout automatiquement.

0

Ce que je résolu le problème de l'espace perdu:

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