I aim to colour a surface in a 3d plot, created using plotly.graph_objects.
I've been working on an approach incorporating Mesh3d, the code runs, but the wanted surface is not coloured. How can this be solved in plotly?
The final outcome should look similar to this example plot: 
And this is where I am at now:

So, the aim is to colour the surface where the vertical lines are connecting the two curves (i.e., a 3-dimensional curve and its projection onto the x-y plane).
Example data can be obtained here.
Here is my code:
import pandas as pd
import plotly.graph_objects as go
import numpy as np
data = pd.read_excel(r"")
x = data['x'].values
y = data['y'].values
z = data['alt'].values
z_min_adjusted = np.maximum(z, 350)
def plot_3d_course_profile(x, y, z, color_attribute, colorbar_label):
fig = go.Figure()
# Projection at z = 350
fig.add_trace(go.Scatter3d(
x=x,
y=y,
z=[350] * len(x),
mode='lines',
line=dict(
color='black',
width=5,
)
))
# Profile curve
fig.add_trace(go.Scatter3d(
x=x,
y=y,
z=z,
mode='lines',
line=dict(
color=color_attribute,
width=13,
colorscale='Jet',
colorbar=dict(
title=dict(
text=colorbar_label,
font=dict(size=14, color='black')
),
thickness=20,
len=0.6,
tickfont=dict(size=12, color='black'),
tickmode='linear',
tickformat='.2f',
outlinewidth=1,
outlinecolor='black'
)
)
))
# Vertical lines
for i in range(0, len(x), 20):
xi, yi, zi = x[i], y[i], z[i]
fig.add_trace(go.Scatter3d(
x=[xi, xi],
y=[yi, yi],
z=[350, zi],
mode='lines',
line=dict(color='dimgray', width=4),
opacity=0.6,
showlegend=False
))
#Layout
fig.update_layout(
template='plotly_white',
scene=dict(
xaxis=dict(title='Meters north from start position', showgrid=True, gridcolor='lightblue', zeroline=False),
yaxis=dict(title='Meters east from start position', showgrid=True, gridcolor='lightblue', zeroline=False),
zaxis=dict(title='Altitude [m]', showgrid=True, gridcolor='lightblue', zeroline=False),
aspectmode='manual',
aspectratio=dict(x=1.25, y=1, z=0.7)
),
title=f'{colorbar_label}',
margin=dict(l=0, r=0, b=0, t=50)
)
fig.show()
plot_3d_course_profile(x, y, z_min_adjusted, z_min_adjusted, 'Altitude [m]')
I could solve the problem by creating simple arrays using numpy for surface plotting and switching to go.Surface from Mesh3D. Any solutions concerning the latter are still welcomed.
The complete code:
import pandas as pd
import plotly.graph_objects as go
import numpy as np
data = pd.read_excel(r'')
x = data['x'].values
y = data['y'].values
z = data['alt'].values
i = data['incl'].values
X = np.vstack([x, x])
Y = np.vstack([y, y])
Z = np.vstack([[350] * len(x), z -0.1])
I = np.vstack([i, i])
fig = go.Figure()
fig.add_trace(go.Scatter3d(
x=x,
y=y,
z=z,
mode='lines',
line=dict(
color=i,
width=13,
colorscale='Jet',
colorbar=dict(
title=dict(
text='Incline [deg]',
font=dict(size=14, color='black')
),
thickness=20,
len=0.6,
tickfont=dict(size=12, color='black'),
tickmode='linear',
tickformat='.2f',
outlinewidth=1,
outlinecolor='black'
)
)
))
for j in range(0, len(x), 20):
color_value= i[j]
fig.add_trace(go.Surface(
z=Z,
x=X,
y=Y,
surfacecolor=I,
colorscale='jet',
cmin=min(i), cmax=max(i),
showscale=False,
opacity=0.7
))
fig.add_trace(go.Scatter3d(
x=x,
y=y,
z=[350] * len(x),
mode='lines',
line=dict(color='black', width=3),
name="Profile Curve"
))
for j in range(0, len(x), 20):
fig.add_trace(go.Scatter3d(
x=[x[j], x[j]],
y=[y[j], y[j]],
z=[350, z[j]],
mode='lines',
line=dict(color='dimgray', width=2),
opacity=0.6,
showlegend=False
))
fig.update_layout(
title="3D Course Profile Colored by Incline",
template='plotly_white',
scene=dict(
xaxis=dict(title='Meters north from start position', showgrid=True, zeroline=False),
yaxis=dict(title='Meters east from start position', showgrid=True, zeroline=False),
zaxis=dict(title='Altitude [m]', showgrid=True, zeroline=False),
aspectmode='manual',
aspectratio=dict(x=1.25, y=1, z=0.7)
),
margin=dict(l=0, r=0, b=0, t=50)
)
fig.show()
Version nr.2 using matplotlib.pyplot:
I post a simpler solution using matplotlib.pyplot but without edge colouring:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib import cm
import matplotlib.ticker as ticker
data = pd.read_excel(r'')
X_1d = data['x'].values
Y_1d = data['y'].values
Z_1d = data['alt'].values
I_1d = data['incl'].values
fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot(111, projection='3d')
X = np.vstack([X_1d, X_1d])
Y = np.vstack([Y_1d, Y_1d])
Z = np.vstack([Z_1d, Z_1d + (max(Z_1d)-min(Z_1d))]) #[350]*len(X_1d), Z_1d + 50 (and below twice)
I = np.vstack([I_1d, I_1d])
norm = plt.Normalize(I.min(), I.max())
colors = cm.jet(norm(I))
fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot(111, projection='3d')
ax.plot(X_1d, Y_1d, Z_1d + (max(Z_1d)-min(Z_1d)), color='black', linewidth=2)
surf = ax.plot_surface(X, Y, Z, facecolors=cm.jet(norm(I)), rstride=1, cstride=1, linewidth=0, antialiased=True, alpha=0.7)
ax.plot_wireframe(X, Y, Z, color='k', linewidth=1, alpha=1.0)
mappable = cm.ScalarMappable(norm=norm, cmap=cm.jet)
mappable.set_array(I)
cbar = fig.colorbar(mappable, ax=ax, shrink=0.5, alpha=0.7, aspect=5, extend='both', extendrect=True, spacing='proportional', format=ticker.FormatStrFormatter('%.1f'))
cbar.set_label('Incline [deg]', rotation=270, labelpad=15)
cbar.ax.axhline(max(I_1d), color='black', linewidth=1.2, linestyle='--')
cbar.ax.axhline(min(I_1d), color='black', linewidth=1.2, linestyle='--')
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])
ax.xaxis.line.set_color((0.0, 0.0, 0.0, 0.0))
ax.yaxis.line.set_color((0.0, 0.0, 0.0, 0.0))
ax.zaxis.line.set_color((0.0, 0.0, 0.0, 0.0))
ax.xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
plt.tight_layout()
plt.show()