Files
UAV-CO2/3Dview.py

371 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
3D可视化脚本 - 显示经纬度高程数据
读取CSV文件中的经度、纬度、融合高程数据
生成可交互的三维点图输出为HTML格式。
数据格式要求:
经度 纬度 高程 融合高程
经纬度坐标需要除以10^-7进行单位转换。
"""
import pandas as pd
import plotly.graph_objects as go
import plotly.io as pio
import argparse
import sys
import numpy as np
from pathlib import Path
def load_and_process_data(file_path, lon_col=0, lat_col=1, alt_col=3, delimiter=None, sheet_name=0):
"""
加载并处理CSV或Excel数据
Args:
file_path: 文件路径 (支持.csv, .xlsx, .xls)
lon_col: 经度列索引 (默认0)
lat_col: 纬度列索引 (默认1)
alt_col: 高程列索引 (默认3为融合高程)
delimiter: CSV分隔符None表示自动检测
sheet_name: Excel工作表名称或索引 (默认0)
Returns:
pandas.DataFrame: 处理后的数据
"""
try:
file_path_obj = Path(file_path)
# 根据文件扩展名选择读取方式
if file_path_obj.suffix.lower() in ['.xlsx', '.xls']:
# 读取Excel文件
print(f"检测到Excel文件: {file_path}")
df = pd.read_excel(file_path, sheet_name=sheet_name, header=None)
else:
# 读取CSV文件
print(f"检测到CSV文件: {file_path}")
df = pd.read_csv(file_path, delimiter=delimiter, header=None)
# 验证数据列数
if df.shape[1] < 4:
raise ValueError(f"数据文件至少需要4列经度、纬度、高程、融合高程。当前只有{df.shape[1]}列。")
# 显示前几行数据以便调试
print(f"数据预览 (前3行):")
print(df.head(3))
print()
# 提取所需列
lon_raw = df.iloc[:, lon_col] # 经度(原始格式)
lat_raw = df.iloc[:, lat_col] # 纬度(原始格式)
altitude = df.iloc[:, alt_col] # 融合高程
# 数据类型转换和清理
try:
lon_raw = pd.to_numeric(lon_raw, errors='coerce')
lat_raw = pd.to_numeric(lat_raw, errors='coerce')
altitude = pd.to_numeric(altitude, errors='coerce')
except Exception as e:
print(f"数据类型转换失败: {e}")
print("尝试手动转换...")
# 手动转换函数
def safe_convert(value):
try:
return float(value)
except (ValueError, TypeError):
return np.nan
lon_raw = lon_raw.apply(safe_convert)
lat_raw = lat_raw.apply(safe_convert)
altitude = altitude.apply(safe_convert)
# 检查转换结果
nan_count_lon = lon_raw.isna().sum()
nan_count_lat = lat_raw.isna().sum()
nan_count_alt = altitude.isna().sum()
if nan_count_lon > 0 or nan_count_lat > 0 or nan_count_alt > 0:
print(f"警告:发现无效数据点 - 经度: {nan_count_lon}, 纬度: {nan_count_lat}, 高程: {nan_count_alt}")
print("无效数据点将被移除")
# 移除NaN值
valid_mask = ~(lon_raw.isna() | lat_raw.isna() | altitude.isna())
lon_raw = lon_raw[valid_mask]
lat_raw = lat_raw[valid_mask]
altitude = altitude[valid_mask]
if len(lon_raw) == 0:
raise ValueError("没有有效的坐标数据")
# 经纬度单位转换 (除以10^7)
longitude = lon_raw / 1e7
latitude = lat_raw / 1e7
# 创建处理后的DataFrame
processed_df = pd.DataFrame({
'longitude': longitude,
'latitude': latitude,
'altitude': altitude
})
print(f"成功加载数据:{len(processed_df)} 个数据点")
print(f"经度范围: {longitude.min():.6f} - {longitude.max():.6f}")
print(f"纬度范围: {latitude.min():.6f} - {latitude.max():.6f}")
print(f"高程范围: {altitude.min():.2f} - {altitude.max():.2f}")
return processed_df
except Exception as e:
print(f"数据加载失败: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
def create_3d_plot(df, title="3D轨迹可视化", output_file="3d_view.html"):
"""
创建三维散点图
Args:
df: 包含longitude, latitude, altitude列的DataFrame
title: 图表标题
output_file: 输出HTML文件路径
"""
# 创建3D散点图
fig = go.Figure(data=[go.Scatter3d(
x=df['longitude'],
y=df['latitude'],
z=df['altitude'],
mode='markers',
marker=dict(
size=3,
color=df['altitude'], # 颜色映射到高程
colorscale='Viridis', # 颜色方案
colorbar=dict(title="高程 (m)"),
opacity=0.8
),
hovertemplate=(
'经度: %{x:.6f}<br>' +
'纬度: %{y:.6f}<br>' +
'高程: %{z:.2f} m<br>' +
'<extra></extra>'
)
)])
# 设置布局
fig.update_layout(
title=title,
scene=dict(
xaxis_title='经度',
yaxis_title='纬度',
zaxis_title='高程 (m)',
xaxis=dict(showgrid=True, gridcolor='lightgray'),
yaxis=dict(showgrid=True, gridcolor='lightgray'),
zaxis=dict(showgrid=True, gridcolor='lightgray'),
# 设置视角
camera=dict(
eye=dict(x=1.5, y=1.5, z=1.5) # 初始视角位置
)
),
margin=dict(l=0, r=0, b=0, t=40),
paper_bgcolor='white',
plot_bgcolor='white'
)
# 添加交互说明
fig.add_annotation(
text="鼠标拖拽旋转视角,滚轮缩放,双击重置",
xref="paper", yref="paper",
x=0.02, y=0.98,
showarrow=False,
bgcolor="rgba(255,255,255,0.8)",
bordercolor="black",
borderwidth=1
)
# 保存为HTML文件
try:
pio.write_html(fig, file=output_file, auto_open=False)
print(f"3D可视化已保存到: {output_file}")
print("在浏览器中打开该文件即可查看可交互的3D图表")
except Exception as e:
print(f"保存HTML文件失败: {e}")
sys.exit(1)
return fig
def main(input_file, output_file='3d_view.html', lon_col=2, lat_col=3, alt_col=5,
delimiter=None, title='3D轨迹可视化', sheet_name=0):
"""
直接调用函数生成3D可视化
Args:
input_file: 输入文件路径 (支持.csv, .xlsx, .xls)
output_file: 输出HTML文件路径
lon_col: 经度列索引 (默认: 0)
lat_col: 纬度列索引 (默认: 1)
alt_col: 融合高程列索引 (默认: 3)
delimiter: CSV分隔符 (默认: 自动检测)
title: 图表标题 (默认: '3D轨迹可视化')
sheet_name: Excel工作表名称或索引 (默认: 0)
"""
# 检查输入文件
input_path = Path(input_file)
if not input_path.exists():
print(f"错误:输入文件不存在: {input_path}")
return False
print(f"正在处理文件: {input_path}")
print(f"输出文件: {output_file}")
print("-" * 50)
try:
# 加载和处理数据
df = load_and_process_data(
file_path=input_path,
lon_col=lon_col,
lat_col=lat_col,
alt_col=alt_col,
delimiter=delimiter,
sheet_name=sheet_name
)
# 创建3D图表
fig = create_3d_plot(
df=df,
title=title,
output_file=output_file
)
print("-" * 50)
print("处理完成!")
print(f"\n✅ 3D可视化已保存到: {output_file}")
print("\n使用说明:")
print("- 在浏览器中打开生成的HTML文件")
print("- 鼠标左键拖拽:旋转视角")
print("- 鼠标滚轮:缩放视图")
print("- 双击:重置到初始视角")
print("- 右键拖拽:平移视图")
return True
except Exception as e:
print(f"❌ 处理失败: {e}")
import traceback
traceback.print_exc()
return False
# 命令行接口(保持兼容性)
def cli_main():
"""命令行接口"""
parser = argparse.ArgumentParser(
description="3D轨迹可视化工具 - 将CSV数据转换为可交互的3D点图",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
# CSV文件
python 3Dview.py data.csv
python 3Dview.py data.csv -o custom_output.html
python 3Dview.py data.csv --lon-col 1 --lat-col 2 --alt-col 4
# Excel文件
python 3Dview.py trajectory.xlsx
python 3Dview.py data.xlsx --sheet-name "Sheet1"
数据格式:
文件应包含至少4列经度 纬度 高程 融合高程
支持格式: CSV (.csv), Excel (.xlsx, .xls)
经纬度将自动除以10^7进行单位转换
直接调用示例:
from 3Dview import main
main('data.xlsx', sheet_name='Sheet1') # Excel文件
main('data.csv', lon_col=0, lat_col=1, alt_col=3) # CSV文件
"""
)
parser.add_argument('input_file', help='输入CSV文件路径')
parser.add_argument('-o', '--output', default='3d_view.html',
help='输出HTML文件路径 (默认: 3d_view.html)')
parser.add_argument('--lon-col', type=int, default=0,
help='经度列索引 (默认: 0)')
parser.add_argument('--lat-col', type=int, default=1,
help='纬度列索引 (默认: 1)')
parser.add_argument('--alt-col', type=int, default=3,
help='融合高程列索引 (默认: 3)')
parser.add_argument('--delimiter', default=None,
help='CSV分隔符 (默认: 自动检测)')
parser.add_argument('--title', default='3D轨迹可视化',
help='图表标题 (默认: 3D轨迹可视化)')
parser.add_argument('--sheet-name', default=0,
help='Excel工作表名称或索引 (默认: 0)')
args = parser.parse_args()
success = main(
input_file=args.input_file,
output_file=args.output,
lon_col=args.lon_col,
lat_col=args.lat_col,
alt_col=args.alt_col,
delimiter=args.delimiter,
title=args.title,
sheet_name=args.sheet_name
)
if not success:
sys.exit(1)
# 示例用法
if __name__ == "__main__":
# 检查是否有命令行参数
if len(sys.argv) > 1:
# 使用命令行接口
cli_main()
else:
# 直接调用示例
print("3D可视化工具使用示例")
print("=" * 50)
# 示例1: 处理Excel文件
print("\n示例1: 处理Excel数据文件")
excel_file = r"C:\Users\HL\Documents\xwechat_files\wxid_rdrswaol1qsr21_737e\msg\file\2025-12\08_51_15_间隔高度10m.xlsx"
if Path(excel_file).exists():
success = main(
input_file=excel_file,
output_file='./10m_3d_view.html',
title='无人机轨迹3D可视化示例'
)
else:
print(f"示例文件不存在: {excel_file}")
print("请提供正确的Excel文件路径")
success = False
if success:
print("\n示例代码:")
print("""
# 直接调用示例
from 3Dview import main
# 处理Excel文件
main('data.xlsx', sheet_name=0) # 指定工作表
# 处理CSV文件
main('data.csv')
# 自定义参数
main(
input_file='trajectory.xlsx',
output_file='flight_path.html',
lon_col=0, # 经度列索引
lat_col=1, # 纬度列索引
alt_col=3, # 融合高程列索引
sheet_name=0, # Excel工作表
title='飞行轨迹可视化'
)
""")