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

906 views Asked by At

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

There are 2 answers

1
swatchai On BEST ANSWER

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

6
swatchai On

(Answer to question 1) To plot the intersections between the surface and the specified planes (y=-20, and y=20), one need to find what Y[?]=-20 and 20. By inspection, I found that Y[100]=20, Y[20]=-20.

The relevant code to plot the lines of intersection:

# By inspection, Y[100]=20, Y[20]=-20
ax.plot3D(X[100], Y[100], Z[100], color='red', lw=6)  # line-1 at y=20
ax.plot3D(X[20], Y[20], Z[20], color='green', lw=6)   # line-2 at y=-20

# Project them on Z=-100 plane
ax.plot3D(X[100], Y[100], -100, color='red', lw=3)  # projection of Line-1
ax.plot3D(X[20], Y[20], -100, color='green', lw=3)  # projection of Line-2

The output plot:

y20-20

(Answer to question 2) To get better plot with the wireframe standout from the surface plot. The surface plot must be partially transparent, which is achieved by setting option alpha=0.6. The relevant code follows.

Z1 = Z-Z.min()
Z1 = Z1/Z.max()
Xall = zoom(X,3)
Yall = zoom(Y,3)
Zz = zoom(Z1, 3)

surf = ax.plot_surface(Xall, Yall, Zz, rstride=10, cstride=10, 
                       facecolors = cm.jet(Zz/np.amax(Zz)),
                       linewidth=0, antialiased=True,
                       alpha= 0.6)

# Wireframe
ax.plot_wireframe(X, Y, Z, rstride=5, cstride=5, color='black', alpha=1, lw=0.8)

The plot is:

combined