pythonmatplotlib3dcontourfwireframe

3D wireframe plot with 2D projections: Spatial organiszation & frequency of projection


I'm working on a 3D plot displayed by a wireframe, where 2D plots are projected on the x, y, and z surface, respectively. Below you can find a minimum example.

I have 2 questions:

  1. With contourf, the 2D plots for every x=10, x=20,... or y=10, y=20,... are displayed on the plot walls. Is there a possibility to define for which x or y, respectively, the contour plots are displayed? For example, in case I only want to have the xz contour plot for y = 0.5 mirrored on the wall?

ADDITION: To display what I mean with "2D plots", I changed "contourf" in the code to "contour" and added the resulting plot to this question. Here you can see now the xz lines for different y values, all offset to y=90. What if I do not want to have all the lines, but only two of them for defined y values?

3D_plot_with_2D_contours

  1. As you can see in the minimum example, the 2D contour plot optically covers the wireframe 3D plot. With increasing the transparency with alpha=0.5 I can increase the transparency of the 2D contours to at least see the wireframe, but it is still optically wrong. Is it possible to sort the objects correctly?
import matplotlib.pyplot as plt,numpy as np
import pylab as pl

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np

plt.clf()

fig = plt.figure(1,figsize=(35,17),dpi=600,facecolor='w',edgecolor='k')
fig.set_size_inches(10.5,8)
ax  = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)

Xnew = X + 50
Ynew = Y + 50

cset = ax.contourf(Xnew, Ynew, Z, zdir='z', offset=-100, cmap=plt.cm.coolwarm, alpha=0.5)
cset = ax.contourf(Xnew, Ynew, Z, zdir='x', offset=10, cmap=plt.cm.coolwarm, alpha=0.5) 
cset = ax.contourf(Xnew, Ynew, Z, zdir='y', offset=90, cmap=plt.cm.coolwarm, alpha = 0.5) 

ax.plot_wireframe(Xnew, Ynew, Z, rstride=5, cstride=5, color='black')

Z=Z-Z.min()
Z=Z/Z.max()

from scipy.ndimage.interpolation import zoom

Xall=zoom(Xnew,5)
Yall=zoom(Ynew,5)
Z=zoom(Z,5)

ax.set_xlim(10, 90)
ax.set_ylim(10, 90)
ax.set_zlim(-100, 100)

ax.tick_params(axis='z', which='major', pad=10)

ax.set_xlabel('X',labelpad=10)
ax.set_ylabel('Y',labelpad=10)
ax.set_zlabel('Z',labelpad=17)


ax.view_init(elev=35., azim=-70)

fig.tight_layout()

plt.show()

ADDITION 2: Here is the actual code I'm working with. However, the original data are hidden in the csv files which are too big to be included in the minimal example. That's why was initially replacing them by the test data. However, maybe the actual code helps nevertheless.

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np
import pylab as pl
from matplotlib.markers import MarkerStyle

import csv
with open("X.csv", 'r') as f:
  X = list(csv.reader(f, delimiter=";"))
import numpy as np
X = np.array(X[1:], dtype=np.float)

import csv
with open("Z.csv", 'r') as f:
  Z = list(csv.reader(f, delimiter=";"))
import numpy as np
Z = np.array(Z[1:], dtype=np.float)

Y = [[7,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8,8.1,8.2,8.3,8.4,8.5,8.6,8.7,8.8,8.9,9]]

Xall = np.repeat(X[:],21,axis=1)
Yall = np.repeat(Y[:],30,axis=0)

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np


plt.clf()

fig = plt.figure(1,figsize=(35,17),dpi=600,facecolor='w',edgecolor='k')
fig.set_size_inches(10.5,8) 
ax  = fig.gca(projection='3d')

cset = ax.contourf(Xall, Yall, Z, 2, zdir='x', offset=0,  cmap=plt.cm.coolwarm, shade = False, edgecolor='none', alpha=0.5)
cset = ax.contourf(Xall, Yall, Z, 2, zdir='y', offset=9, cmap=plt.cm.coolwarm, shade = False, edgecolor='none', alpha=0.5)

ax.plot_wireframe(Xall, Yall, Z, rstride=1, cstride=1, color='black')

Z=Z-Z.min()
Z=Z/Z.max()

from scipy.ndimage.interpolation import zoom

Xall=zoom(Xall,5)
Yall=zoom(Yall,5)
Z=zoom(Z,5)

cset = ax.plot_surface(Xall, Yall, np.zeros_like(Z)-0,facecolors=plt.cm.coolwarm(Z),shade=False,alpha=0.5,linewidth=False)

ax.set_xlim(-0.5, 31)
ax.set_ylim(6.9, 9.1)
ax.set_zlim(0, 500)

labelsx = [item.get_text() for item in ax.get_xticklabels()]
empty_string_labelsx = ['']*len(labelsx)
ax.set_xticklabels(empty_string_labelsx)

labelsy = [item.get_text() for item in ax.get_yticklabels()]
empty_string_labelsy = ['']*len(labelsy)
ax.set_yticklabels(empty_string_labelsy)

labelsz = [item.get_text() for item in ax.get_zticklabels()]
empty_string_labelsz = ['']*len(labelsz)
ax.set_zticklabels(empty_string_labelsz)

import matplotlib.ticker as ticker
ax.xaxis.set_major_locator(ticker.MultipleLocator(5))
ax.xaxis.set_minor_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax.yaxis.set_minor_locator(ticker.MultipleLocator(0.25))
ax.zaxis.set_major_locator(ticker.MultipleLocator(100))
ax.zaxis.set_minor_locator(ticker.MultipleLocator(50))

ax.tick_params(axis='z', which='major', pad=10)

ax.set_xlabel('X',labelpad=5,fontsize=15)
ax.set_ylabel('Y',labelpad=5,fontsize=15)
ax.set_zlabel('Z',labelpad=5,fontsize=15)


ax.view_init(elev=35., azim=-70)

fig.tight_layout()

plt.show()

Solution

  • Alternate possible answer.

    This code demonstrates

    1. A plot of a surface and its correponding wireframe
    2. The creation of data and its plot of 3d lines (draped on the surface in 1) at specified values of x and y
    3. Projections of the 3d lines (in 2) on to the frame walls
    from mpl_toolkits.mplot3d import axes3d
    import matplotlib.pyplot as plt
    from scipy import interpolate
    import numpy as np
    
    # use the test data for plotting
    fig = plt.figure(1, figsize=(6,6), facecolor='w', edgecolor='gray')
    ax  = fig.gca(projection='3d')
    X, Y, Z = axes3d.get_test_data(0.1)  #get 3d data at appropriate density
    
    # create an interpolating function
    # can take a long time if data is too large
    f1 = interpolate.interp2d(X, Y, Z, kind='linear')
    
    # in general, one can use a set of other X,Y,Z that cover a surface
    # preferably, (X,Y) are in grid arrangement
    
    # make up a new set of 3d data to plot
    # ranges of x1, and y1 will be inside (X,Y) of the data obtained above
    # related grid, x1g,y1g,z1g will be obtained from meshgrid and the interpolated function
    x1 = np.linspace(-15,15,10)
    y1 = np.linspace(-15,15,10)
    x1g, y1g = np.meshgrid(x1, y1)
    z1g = f1(x1, y1)  #dont use (x1g, y1g)
    
    # prep data for 3d line on the surface (X,Y,Z) at x=7.5
    n = 12
    x_pf = 7.5
    x5 = x_pf*np.ones(n)
    y5 = np.linspace(-15, 15, n)
    z5 = f1(x_pf, y5)
    # x5,y5,z5 can be used to plot 3d line on the surface (X,Y,Z)
    
    # prep data for 3d line on the surface (X,Y,Z) at y=6
    y_pf = 6
    x6 = np.linspace(-15, 15, n)
    y6 = x_pf*np.ones(n)
    z6 = f1(x6, y_pf)
    # x6,y6,z6 can be used to plot 3d line on the surface (X,Y,Z)
    
    ax  = fig.gca(projection='3d')
    
    ax.plot_surface(x1g, y1g, z1g, alpha=0.25)
    ax.plot_wireframe(x1g, y1g, z1g, rstride=2, cstride=2, color='black', zorder=10, alpha=1, lw=0.8)
    
    # 3D lines that follow the surface
    ax.plot(x5,y5,z5.flatten(), color='red', lw=4)
    ax.plot(x6,y6,z6.flatten(), color='green', lw=4)
    
    # projections of 3d curves
    # project red and green lines to the walls
    ax.plot(-15*np.ones(len(y5)), y5, z5.flatten(), color='red', lw=4, linestyle=':', alpha=0.6)
    ax.plot(x6, 15*np.ones(len(x6)), z6.flatten(), color='green', lw=4, linestyle=':', alpha=0.6)
    
    # projections on other sides (become vertical lines)
    # change to if True, to plot these
    if False:
        ax.plot(x5, 15*np.ones(len(x5)), z5.flatten(), color='red', lw=4, alpha=0.3)
        ax.plot(-15*np.ones(len(x6)), y6, z6.flatten(), color='green', lw=4, alpha=0.3)
    
    ax.set_title("Projections of 3D lines")
    
    # set limits
    ax.set_xlim(-15, 15.5)
    ax.set_ylim(-15.5, 15)
    
    plt.show();
    

    enter image description here