import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.interpolate import griddata
from matplotlib.colors import LinearSegmentedColormap, Normalize
import os
from matplotlib.colorbar import ColorbarBase
def generate_multi_fsimc_surfaces():
# 1. 数据加载部分
print("从Excel加载原始数据...")
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
excel_path = os.path.join(script_dir, 'fsim_data.xlsx')
df = pd.read_excel(excel_path)
required_columns = ['BOW', 'WAVE']
if not all(col in df.columns for col in required_columns):
missing = [col for col in required_columns if col not in df.columns]
raise ValueError(f"Excel文件缺少必要的列: {missing}")
fsimc_columns = [col for col in df.columns if col.startswith('FsimC')]
if not fsimc_columns:
raise ValueError("未找到任何以'FsimC'开头的列")
print(f"找到 {len(fsimc_columns)} 个FsimC列: {', '.join(fsimc_columns)}")
# 曲面描述信息
column_descriptions = {
'FsimC_10': '10m viewing distance',
'FsimC_20': '20m viewing distance',
'FsimC_30': '30m viewing distance',
'FsimC_40': '40m viewing distance',
'FsimC_50': '50m viewing distance',
'FsimC_60': '60m viewing distance',
'FsimC_70': '70m viewing distance',
'FsimC_80': '80m viewing distance',
'FsimC_90': '90m viewing distance',
'FsimC_100': '100m viewing distance',
}
points = df[['BOW', 'WAVE']].values
fsimc_values = {col: df[col].values for col in fsimc_columns}
all_fsimc_min = float('inf')
all_fsimc_max = float('-inf')
for col in fsimc_columns:
col_min = df[col].min()
col_max = df[col].max()
if col_min < all_fsimc_min:
all_fsimc_min = col_min
if col_max > all_fsimc_max:
all_fsimc_max = col_max
z_padding = (all_fsimc_max - all_fsimc_min) * 0.1
z_min = all_fsimc_min - z_padding
z_max = all_fsimc_max + z_padding
print(f"全局FsimC范围: {all_fsimc_min:.4f} 到 {all_fsimc_max:.4f}")
print(f"设置Z轴范围: {z_min:.4f} 到 {z_max:.4f}")
except FileNotFoundError:
print(f"错误: 找不到数据文件 {excel_path}")
print("请确保fsim_data.xlsx文件位于脚本同一目录下")
return
except Exception as e:
print(f"加载数据时出错: {str(e)}")
return
# 2. 创建高分辨率网格
print("创建高分辨率网格...")
x_fine = np.linspace(1, 5, 150)
y_fine = np.linspace(0.1, 1.0, 100)
X_fine, Y_fine = np.meshgrid(x_fine, y_fine)
# 3. 创建3D图表 - 调整布局确保居中
print("创建3D图表...")
fig = plt.figure(figsize=(18, 14))
# 使用add_axes确保图表居中
ax = fig.add_axes([0.1, 0.07, 0.7, 0.83], projection='3d')
# 4. 定义全局颜色映射
print("创建全局颜色映射...")
global_cmap = LinearSegmentedColormap.from_list(
"global_cmap",
[(0.1, 0.2, 0.8), (0.2, 0.8, 0.4), (1.0, 0.9, 0.1)],
N=512
)
global_norm = Normalize(vmin=all_fsimc_min, vmax=all_fsimc_max)
alphas = [0.65, 0.55, 0.45, 0.35, 0.25, 0.15]
# 5. 曲面绘制和标签添加
print(f"开始处理 {len(fsimc_columns)} 个曲面并添加标签...")
mid_y_idx = len(y_fine) // 2
for i, (col_name, values) in enumerate(fsimc_values.items()):
print(f"正在处理列: {col_name} ({i+1}/{len(fsimc_columns)})")
print(" 执行插值...")
Z_fine = griddata(points, values, (X_fine, Y_fine), method='cubic')
Z_top_left = Z_fine[:-1, :-1]
Z_top_right = Z_fine[:-1, 1:]
Z_bottom_left = Z_fine[1:, :-1]
Z_bottom_right = Z_fine[1:, 1:]
Z_midpoints = (Z_top_left + Z_top_right + Z_bottom_left + Z_bottom_right) / 4.0
facecolors = global_cmap(global_norm(Z_midpoints))
alpha = alphas[min(i, len(alphas)-1)]
facecolors[:, :, 3] = alpha
print(f" 渲染曲面 (透明度: {alpha:.2f})...")
stride_x = 1
stride_y = 1
if len(x_fine) > 100:
render_density = max(1, int(len(x_fine)/75))
stride_x = render_density
stride_y = max(1, int(render_density/1.5))
surf = ax.plot_surface(
X_fine[::stride_y, ::stride_x],
Y_fine[::stride_y, ::stride_x],
Z_fine[::stride_y, ::stride_x],
facecolors=facecolors[::stride_y, ::stride_x],
shade=False,
rcount=min(300, len(y_fine)),
ccount=min(300, len(x_fine)),
alpha=alpha,
antialiased=True,
edgecolor='black',
linewidth=0.1,
zorder=len(fsimc_columns)-i
)
print(" 添加曲面标签...")
num_segments = 5
segment_idx = i % num_segments
x_idx = int((segment_idx / num_segments) * (len(x_fine) - 1))
label_x = X_fine[mid_y_idx, x_idx]
label_y = Y_fine[mid_y_idx, x_idx]
label_z = Z_fine[mid_y_idx, x_idx] + 0.03
if i == 0:
max_idx_y = np.argmax(Z_fine[mid_y_idx, :])
label_x = X_fine[mid_y_idx, max_idx_y]
label_y = Y_fine[mid_y_idx, max_idx_y]
label_z = Z_fine[mid_y_idx, max_idx_y] + 0.06
ax.text(
label_x, label_y, label_z,
col_name,
fontsize=11,
color='darkred',
fontweight='bold',
ha='center',
va='center',
zorder=100,
bbox=dict(boxstyle='round,pad=0.2', facecolor='white', alpha=0.8, edgecolor='none')
)
# 6. 添加全局注释框 - 位置右移与颜色条中心对齐
print("添加全局注释框...")
annotation_text = "\n".join([
f"{col}: {column_descriptions.get(col, 'Undefined description')}"
for col in fsimc_columns
])
# 关键修改1:计算颜色条中心位置
cbar_left_edge = 0.85 # 颜色条左侧位置
cbar_width = 0.02 # 颜色条宽度
cbar_center_x = cbar_left_edge + (cbar_width / 2) +0.25# 颜色条中心位置
ax.annotate(
annotation_text,
xy=(cbar_center_x, 0.02), # 右移与颜色条中心对齐
xycoords='axes fraction',
fontsize=12,
color='darkblue',
ha='right', # 右对齐
va='bottom', # 底部对齐
bbox=dict(
boxstyle='round,pad=0.5',
facecolor='white',
alpha=0.85,
edgecolor='gray'
),
zorder=100
)
# 7. 坐标轴设置
ax.set_xlabel('BOW (‰)', fontsize=15, labelpad=25)
ax.set_ylabel('WAVE (‰)', fontsize=15, labelpad=25)
ax.set_zlabel('FsimC', fontsize=15, labelpad=25)
# 抬高标题位置
title = ax.set_title(
f'Multi-FsimC Analysis ({len(fsimc_columns)} Surfaces)',
fontsize=20,
pad=15,
y=0.98
)
# 轴刻度设置
ax.set_xticks(np.linspace(1, 5, 5))
ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
z_step = 0.05
z_min_rounded = np.floor(z_min / z_step) * z_step
z_max_rounded = np.ceil(z_max / z_step) * z_step
z_ticks = np.arange(z_min_rounded, z_max_rounded + z_step/2, z_step)
if len(z_ticks) > 10:
step_idx = max(1, int(len(z_ticks) / 5))
z_ticks = z_ticks[::step_idx]
ax.set_zticks(z_ticks)
ax.set_zticklabels([f"{z:.2f}" for z in z_ticks])
# 刻度标签大小和间距优化
ax.tick_params(axis='both', which='major', labelsize=11, pad=10)
# 8. 网格和平面设置
print("添加背景网格线...")
# 网格线参数
grid_params = {
'visible': True,
'linestyle': '--',
'linewidth': 0.5,
'alpha': 0.2,
'color': 'gray'
}
ax.xaxis._axinfo["grid"].update(grid_params)
ax.yaxis._axinfo["grid"].update(grid_params)
ax.zaxis._axinfo["grid"].update(grid_params)
# 平面设置
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.pane.set_edgecolor('lightgray')
ax.yaxis.pane.set_edgecolor('lightgray')
ax.zaxis.pane.set_edgecolor('lightgray')
ax.xaxis.pane.set_alpha(0.05)
ax.yaxis.pane.set_alpha(0.05)
ax.zaxis.pane.set_alpha(0.05)
# 9. 视图设置
print("配置轴测图投影...")
ax.set_proj_type('ortho')
ax.view_init(elev=32, azim=42)
ax.set_box_aspect(aspect=(1, 1, 1))
ax.set_xlim(0.5, 5.5)
ax.set_ylim(0.0, 1.1)
ax.set_zlim(z_min, z_max)
# 10. 添加全局颜色条
print("添加全局FsimC颜色条...")
# 关键修改2:颜色条位置不变但用于计算注释框位置
cax = fig.add_axes([0.85, 0.25, 0.02, 0.5])
cbar = ColorbarBase(
cax,
cmap=global_cmap,
norm=global_norm,
orientation='vertical',
label='FsimC Value'
)
cbar.set_label('FsimC Value', fontsize=12, labelpad=15)
cbar.ax.tick_params(labelsize=10)
# 11. 保存和显示
print("保存多曲面轴测图...")
plt.savefig(f'multi_fsimc_surfaces_{len(fsimc_columns)}.png', dpi=350, bbox_inches='tight')
print("渲染完成,显示图表...")
plt.tight_layout(pad=3.0)
plt.show()
if __name__ == "__main__":
generate_multi_fsimc_surfaces()
将上述python代码中曲面标签文字的颜色,调整与该处Z坐标对应的颜色一直,调整python代码