0

I'm trying to figure out how I can get the 3D matplotlib images below to plot higher on the canvas so it doesn't get clipped. Here is the code I'm using to create the plot. I couldn't find a way to attach the text file containing the Z elevations (referenced in the code below), but it is simply a 2D array containing a surface made up of values ranging between 0 and 1.

import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D

nrow=30
ncol=100

f = open(r'C:\temp\fracEvapCume_200.txt','r')
fracEvapTS = np.loadtxt(f)
f.close()

X, Y = np.meshgrid(ncol, nrow)
Y3d, X3d = np.mgrid[0:Y, 0:X]

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.auto_scale_xyz([0, 100], [0, 30], [0, 0.2])
Y3d, X3d = np.mgrid[0:Y, 0:X]
Z = fracEvapTS

surf = ax.plot_surface(X3d, Y3d, Z, cmap='autumn', cstride=2, rstride=2)
ax.set_xlabel("X-Label")
ax.set_ylabel("Y-Label")
ax.set_zlabel("Z-Label")
ax.pbaspect = [1., .33, 0.25]
ax.dist  = 7
plt.tight_layout()
plt.savefig('clipped.png')

In order to get the ax.pbaspect=[1., .33, 0.25] line to work, changes to the get_proj function inside site-packages\mpl_toolkits\mplot3d\axes3d.py were made as suggested in this post. In order to get the figure to draw larger, I added ax.dist = 7 based on this post. Lastly, based on this post I was hoping that plt.tight_layout() would roll back the margins and prevent the red/yellow surface shown below from being clipped, but that didn't work either. I'm failing to find the command that will move the image up on the canvas, thereby avoiding all of the unnecessary white space at the top of the figure and preventing the red/yellow surface from getting clipped. Is there one line of Python that will accomplish this?

enter image description here

after adding the line plt.tight_layout(), it made matters worse:

enter image description here

Community
  • 1
  • 1
user2256085
  • 483
  • 4
  • 15

1 Answers1

1

The problem is that your modification to site-packages\mpl_toolkits\mplot3d\axes3d.py changes the projection matrix, without changing the center of the view, messing up the position of the scene once transfomed in camera coordinates.

So when the view is zoomed (with ax.dist) then moved, the plot sometimes gets out of the canvas.

enter image description here

You need to replace the following line to the get_proj function in axes3d.py :

    # look into the middle of the new coordinates
    R = np.array([0.5, 0.5, 0.5])

By :

    # look into the middle of the new coordinates
    try:
        R = np.array(self.pbaspect)/2
    except AttributeError:
        R = np.array([0.5, 0.5, 0.5])

And this should work :

enter image description here

PS : Code used to make the figures :

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D

nrow=30
ncol=100

X, Y = np.meshgrid(ncol, nrow)
Y3d, X3d = np.mgrid[0:Y, 0:X]
Z = np.sin(Y3d/Y)*np.sin(X3d/X)

fig = plt.figure()
for i in range(4):
    ax = fig.add_subplot(2,2,i,projection='3d')
    ax.auto_scale_xyz([0, 100], [0, 30], [0, 0.2])

    surf = ax.plot_surface(X3d, Y3d, Z, cmap='autumn', cstride=2, rstride=2)
    ax.set_xlabel("X-Label")
    ax.set_ylabel("Y-Label")
    ax.set_zlabel("Z-Label")
    ax.pbaspect = [1., .33, 0.25]
    ax.dist  = 7
Yann
  • 534
  • 4
  • 9
  • Editing `get_proj` did the trick. Do you know what line of script will reduce the number labels appearing on the z-axis to cut down on the clutter? For example, in your plots, you labeled z every 0.1, what if you wanted ticks and labels only every 0.25? – user2256085 Jul 26 '15 at 12:32
  • You can do that with `ax.set_zticks( np.arange(zmin,zmax,step))`, just give the values you want as arguments (in you example : `(0,0.25,0.16)`) – Yann Jul 26 '15 at 12:56