Setting aspect ratio of 3D plot

24,475

Solution 1

Add following code before savefig:

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

enter image description here

If you want no square axis:

edit the get_proj function inside site-packages\mpl_toolkits\mplot3d\axes3d.py:

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

then add one line to set pbaspect:

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

enter image description here

Solution 2

An issue has been opened over at github: https://github.com/matplotlib/matplotlib/issues/8593

The above solutions don't seem to work any more. One has to edit the get_proj function inside site-packages\mpl_toolkits\mplot3d\axes3d.py in the following way now:

        try:
            self.localPbAspect = self.pbaspect
        except AttributeError:
            self.localPbAspect = [1,1,1]

        xmin, xmax = ( lim / self.localPbAspect[0] for lim in self.get_xlim3d() )
        ymin, ymax = ( lim / self.localPbAspect[1] for lim in self.get_ylim3d() )
        zmin, zmax = ( lim / self.localPbAspect[2] for lim in self.get_zlim3d() )
Share:
24,475
Blair
Author by

Blair

Updated on July 02, 2020

Comments

  • Blair
    Blair almost 4 years

    I am trying to plot a 3D image of the seafloor from the data of a sonar run over a 500m by 40m portion of the seafloor. I am using matplotlib/mplot3d with Axes3D and I want to be able to change the aspect ratio of the axes so that the x & y axis are to scale. An example script with generated data rather than the real data is:

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

    And the output image from this script:

    matplotlib output image

    Now I would like to change it so that 1 metre in the along-track (x) axis is the same as 1 metre in the range (y) axis (or maybe a different ratio depending on the relative sizes involved). I would also like to set the ratio of the z-axis, again not neccessarily to 1:1 due to the relative sizes in the data, but so the axis is smaller than the current plot.

    I have tried building and using this branch of matplotlib, following the example script in this message from the mailing list, but adding the ax.pbaspect = [1.0, 1.0, 0.25] line to my script (having uninstalled the 'standard' version of matplotlib to ensure the custom version was being used) didn't make any difference in the generated image.

    Edit: So the desired output would be something like the following (crudely edited with Inkscape) image. In this case I haven't set a 1:1 ratio on the x/y axes because that looks ridiculously thin, but I have spread it out so it isn't square as on the original output.

    Desired output