2012-04-26 8 views
26

Ich versuche, ein 3D-Bild des Meeresbodens aus den Daten eines Sonar-Laufs über einen 500m x 40m großen Teil des Meeresbodens zu zeichnen. Ich verwende matplotlib/mplot3d mit Axes3D und möchte das Seitenverhältnis der Achsen ändern können, so dass die x & y-Achse skaliert werden soll. Ein Beispielskript mit erzeugten Daten, anstatt den wirklichen Daten:Einstellung des Seitenverhältnisses von 3D-Plot

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

Und das Ausgangsbild von diesem Skript:

matplotlib output image

Nun würde Ich mag es zu ändern, so dass 1 Meter in der Entlang der Spur (x) Achse ist das gleiche wie 1 Meter in der Bereich (y) Achse (oder vielleicht ein anderes Verhältnis abhängig von den relativen Größen beteiligt). Ich möchte auch das Verhältnis der z-Achse einstellen, wiederum nicht unbedingt 1: 1 aufgrund der relativen Größen in den Daten, aber so ist die Achse kleiner als die aktuelle Darstellung.

Ich habe versucht Gebäude und this branch of matplotlib verwenden, nach dem Beispiel Skript in this message from the mailing list, aber das Hinzufügen der ax.pbaspect = [1.0, 1.0, 0.25] Linie zu meinem Skript (nach der ‚Standard‘ -Version von matplotlib deinstalliert die benutzerdefinierte Version, um sicherzustellen, wurde verwendet) hat noch keinen irgendein Unterschied im erzeugten Bild.

Bearbeiten: Also die gewünschte Ausgabe wäre etwas wie folgt (grob bearbeitet mit Inkscape) Bild. In diesem Fall habe ich kein 1: 1-Verhältnis auf der x/y-Achse eingestellt, weil das lächerlich dünn aussieht, aber ich habe es ausgebreitet, so dass es nicht quadratisch ist wie bei der ursprünglichen Ausgabe.

Desired output

Antwort

18

In folgenden Code vor savefig:

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

enter image description here

Wenn Sie keine quadratische Achse wollen:

bearbeiten die get_proj Funktion innerhalb Website-Pakete \ 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] 

dann eine Zeile hinzufügen setzen pbaspect:

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

enter image description here

+0

Hmmm. Dadurch wird zwar die Skalierung der Achsen korrekt, aber viel Platz verschwendet. Während ich das * als * SVG speichern und es manuell bearbeiten konnte (wie das, was ich mit dem gewünschten Bild gemacht habe, habe ich gerade die Frage aktualisiert), würde es sehr mühsam werden, wenn ich eine große Anzahl von Bildern erstellen müsste, und das bin ich nicht sicher, es könnte automatisiert werden ... – Blair

+1

Sie können die Pbaspect-Änderung verwenden, um keine quadratischen Achsen zu erhalten. Ich habe die Antwort bearbeitet. – HYRY

+0

Super, danke! – Blair

5

Die Antwort auf this question funktioniert perfekt für mich. Und Sie müssen kein Verhältnis einstellen, es macht alles automatisch.

0

Dass, wie ich löste das Platzverschwendung Problem:

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