2012-04-26 16 views
26

Sto provando a tracciare un'immagine 3D del fondo marino dai dati di un sonar eseguito su una porzione di 500 m per 40 m del fondo marino. Sto usando matplotlib/mplot3d con Axes3D e voglio essere in grado di modificare le proporzioni degli assi in modo che l'asse x & sia in scala. Uno script di esempio con dati generati piuttosto che i dati reali sono:Impostazione del rapporto di aspetto del grafico 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') 

E l'immagine in uscita da questo script:

matplotlib output image

Ora vorrei cambiare in modo che 1 metro nella l'asse lungo la traccia (x) è lo stesso di 1 metro nell'asse (y) (o forse un rapporto diverso a seconda delle dimensioni relative coinvolte). Mi piacerebbe anche impostare il rapporto tra l'asse z, ancora non necessariamente a 1: 1 a causa delle dimensioni relative nei dati, ma così l'asse è più piccolo del grafico corrente.

ho provato costruzione e l'utilizzo di this branch of matplotlib, seguendo l'script di esempio in this message from the mailing list, ma aggiungendo la linea ax.pbaspect = [1.0, 1.0, 0.25] al mio script (dopo aver disinstallato la versione 'standard' di matplotlib per assicurare la versione personalizzata che veniva usata) non ha fatto qualsiasi differenza nell'immagine generata.

Modifica: Quindi l'output desiderato sarebbe qualcosa come la seguente immagine (modificata con Inkscape). In questo caso, non ho impostato un rapporto 1: 1 sugli assi x/y perché sembra ridicolmente sottile, ma l'ho steso in modo che non sia quadrato come nell'output originale.

Desired output

risposta

18

Add seguente codice prima savefig:

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

enter image description here

Se non si desidera asse piazza:

modificare la funzione get_proj all'interno 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] 

quindi aggiungere una riga per impostare pbaspect:

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

enter image description here

+0

Hmmm. Ciò comporta il corretto ridimensionamento degli assi, ma comporta un notevole spreco di spazio. Mentre io * potevo * salvare questo come SVG e modificarlo manualmente (come quello che ho fatto con l'immagine desiderata con la quale ho appena aggiornato la domanda) questo diventerebbe molto noioso quando ho un gran numero di immagini da creare, e non lo sono sicuro che potrebbe essere automatizzato ... – Blair

+1

Puoi usare la modifica pbaspect per non ottenere assi quadrati. Ho modificato la risposta. – HYRY

+0

Fantastico, grazie! – Blair

5

La risposta a this question opere perfettamente per me. E non è necessario impostare alcun rapporto, fa tutto automaticamente.

0

Ecco come ho risolto il problema spazio sprecato:

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