2012-04-26 32 views
26

Próbuję wykreślić obraz 3D dna morskiego z danych sonaru przebiegającego przez odcinek dna morskiego o długości 500 m na 40 m. Używam matplotlib/mplot3d z Axes3D i chcę mieć możliwość zmiany proporcji osi tak, aby oś y x & była skalowana. Przykładowy skrypt z wygenerowanych danych, a nie rzeczywistych danych jest:Ustawianie współczynnika kształtu wykresu 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') 

a obraz wyjściowy z tego skryptu:

matplotlib output image

Teraz chciałbym go zmienić tak, że 1 metr w Oś wzdłuż osi (x) jest taka sama jak 1 metr na osi (y) (lub może inny współczynnik w zależności od zaangażowanych wielkości względnych). Chciałbym również ustawić stosunek osi Z, również nie koniecznie na 1: 1 ze względu na względne rozmiary danych, ale oś jest mniejsza niż bieżący wykres.

Próbowałem budynku i używając this branch of matplotlib, idąc za przykładem skryptu w this message from the mailing list, ale dodając linię ax.pbaspect = [1.0, 1.0, 0.25] do mojego skryptu (po odinstalowaniu „norma” wersję matplotlib zapewnienia niestandardowej wersji był używany) nie zrobić jakakolwiek różnica w generowanym obrazie.

Edytuj: Tak więc pożądane wyjście będzie podobne do następującego (z grubsza edytowane za pomocą Inkscape) obrazu. W tym przypadku nie ustawiłem proporcji 1: 1 na osiach x/y, ponieważ wygląda to śmiesznie cienko, ale rozłożyłem ją tak, aby nie była kwadratowa jak na oryginalnym wyjściu.

Desired output

Odpowiedz

18

Dodaj następujący kod przed savefig:

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

enter image description here

Jeśli chcesz żadnego kwadratowy oś:

edit funkcja get_proj wewnątrz 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] 

następnie dodać jedną linię, aby ustawić pbaspect:

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

enter image description here

+0

Hmmm. To sprawia, że ​​skalowanie osi jest poprawne, ale powoduje dużo zmarnowanej przestrzeni. Podczas gdy ja * mogłem * zapisać to jako SVG i edytować je ręcznie (jak to, co zrobiłem z pożądanym obrazem, właśnie zaktualizowałem to pytanie) byłoby to bardzo nudne, gdy mam dużą liczbę obrazów do stworzenia, a ja nie jestem pewien, że to może być zautomatyzowane ... – Blair

+1

Możesz użyć modyfikacji pbaspect, aby uzyskać brak kwadratów. Edytowałem odpowiedź. – HYRY

+0

Awesome, thanks! – Blair

5

odpowiedź na this question działa idealnie dla mnie. I nie musisz ustawiać żadnego współczynnika, robi to wszystko automatycznie.

0

że jak I rozwiązać problemu marnowania przestrzeni:

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)) 
Powiązane problemy