import pandas as pd
import numpy as np
from geopy.distance import geodesic
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
class CoordinateProcessor:
def __init__(self):
self.root = tk.Tk()
self.root.title("坐标处理工具")
self.root.geometry("750x500")
self.create_widgets()
def create_widgets(self):
# 创建标签页
self.tab_control = ttk.Notebook(self.root)
# 坐标转换标签页
self.tab_conversion = ttk.Frame(self.tab_control)
self.tab_control.add(self.tab_conversion, text="坐标转换")
self.setup_conversion_tab()
# 距离计算标签页
self.tab_distance = ttk.Frame(self.tab_control)
self.tab_control.add(self.tab_distance, text="距离计算")
self.setup_distance_tab()
self.tab_control.pack(expand=1, fill="both")
# 状态栏
self.status_var = tk.StringVar()
self.status_bar = tk.Label(self.root, textvariable=self.status_var, bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
self.update_status("就绪")
def setup_conversion_tab(self):
# 输入文件选择
tk.Label(self.tab_conversion, text="全站仪数据文件:").grid(row=0, column=0, padx=10, pady=10, sticky="e")
self.conv_input_entry = tk.Entry(self.tab_conversion, width=50)
self.conv_input_entry.grid(row=0, column=1, padx=5, pady=5, sticky="we")
tk.Button(self.tab_conversion, text="浏览...", command=lambda: self.browse_file(self.conv_input_entry)).grid(
row=0, column=2, padx=5)
# 输出文件选择
tk.Label(self.tab_conversion, text="输出Excel文件:").grid(row=1, column=0, padx=10, pady=10, sticky="e")
self.conv_output_entry = tk.Entry(self.tab_conversion, width=50)
self.conv_output_entry.grid(row=1, column=1, padx=5, pady=5, sticky="we")
tk.Button(self.tab_conversion, text="浏览...",
command=lambda: self.browse_save_file(self.conv_output_entry, "xlsx")).grid(row=1, column=2, padx=5)
# 转换按钮
self.convert_btn = tk.Button(self.tab_conversion, text="开始转换", command=self.convert_coordinates, bg="#4CAF50",
fg="white", height=2)
self.convert_btn.grid(row=2, column=1, pady=15)
# 网格配置
self.tab_conversion.columnconfigure(1, weight=1)
def setup_distance_tab(self):
# 全站仪文件选择
tk.Label(self.tab_distance, text="全站仪数据文件:").grid(row=0, column=0, padx=10, pady=10, sticky="e")
self.dist_ts_entry = tk.Entry(self.tab_distance, width=50)
self.dist_ts_entry.grid(row=0, column=1, padx=5, pady=5, sticky="we")
tk.Button(self.tab_distance, text="浏览...", command=lambda: self.browse_file(self.dist_ts_entry)).grid(row=0,
column=2,
padx=5)
# RTK文件选择
tk.Label(self.tab_distance, text="RTK数据文件:").grid(row=1, column=0, padx=10, pady=10, sticky="e")
self.dist_rtk_entry = tk.Entry(self.tab_distance, width=50)
self.dist_rtk_entry.grid(row=1, column=1, padx=5, pady=5, sticky="we")
tk.Button(self.tab_distance, text="浏览...", command=lambda: self.browse_file(self.dist_rtk_entry)).grid(row=1,
column=2,
padx=5)
# 输出文件选择
tk.Label(self.tab_distance, text="结果Excel文件:").grid(row=2, column=0, padx=10, pady=10, sticky="e")
self.dist_output_entry = tk.Entry(self.tab_distance, width=50)
self.dist_output_entry.grid(row=2, column=1, padx=5, pady=5, sticky="we")
tk.Button(self.tab_distance, text="浏览...",
command=lambda: self.browse_save_file(self.dist_output_entry, "xlsx")).grid(row=2, column=2, padx=5)
# 计算按钮
self.calc_btn = tk.Button(self.tab_distance, text="开始计算", command=self.calculate_distances, bg="#2196F3",
fg="white", height=2)
self.calc_btn.grid(row=3, column=1, pady=15)
# 网格配置
self.tab_distance.columnconfigure(1, weight=1)
def browse_file(self, entry_widget):
file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")])
if file_path:
entry_widget.delete(0, tk.END)
entry_widget.insert(0, file_path)
def browse_save_file(self, entry_widget, ext):
file_path = filedialog.asksaveasfilename(
defaultextension=f".{ext}",
filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")]
)
if file_path:
entry_widget.delete(0, tk.END)
entry_widget.insert(0, file_path)
def update_status(self, message):
self.status_var.set(message)
self.root.update_idletasks()
def dms_to_decimal(self, dms_str):
"""将度分秒格式(DD:MM:SS.SSSS方向)转换为十进制度,保留原始精度"""
direction_char = dms_str[-1]
dms_str = dms_str[:-1].strip()
parts = dms_str.split(':')
if len(parts) != 3:
raise ValueError(f"无效的坐标格式: {dms_str}")
degrees = float(parts[0])
minutes = float(parts[1])
seconds = float(parts[2])
decimal_value = degrees + minutes / 60 + seconds / 3600
if direction_char in ['S', 'W']:
decimal_value = -decimal_value
return decimal_value
def convert_coordinates(self):
input_path = self.conv_input_entry.get()
output_path = self.conv_output_entry.get()
if not input_path or not output_path:
messagebox.showerror("错误", "请选择输入和输出文件路径")
return
try:
self.update_status("正在转换坐标...")
data = []
with open(input_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
parts = [part.strip() for part in line.split(',')]
if len(parts) < 3:
continue
point_id = parts[0]
lat_dms = parts[1]
lon_dms = parts[2]
try:
# 转换坐标并保留原始数据
lat_decimal = self.dms_to_decimal(lat_dms)
lon_decimal = self.dms_to_decimal(lon_dms)
data.append({
'点号': point_id,
'原始纬度': lat_dms,
'原始经度': lon_dms,
'十进制纬度': lat_decimal,
'十进制经度': lon_decimal
})
except Exception as e:
print(f"处理点 {point_id} 时出错: {str(e)}")
# 创建DataFrame并保存
df = pd.DataFrame(data)
# 使用ExcelWriter保留原始数据精度
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
df.to_excel(writer, index=False)
# 设置列宽
worksheet = writer.sheets['Sheet1']
worksheet.column_dimensions['A'].width = 15
worksheet.column_dimensions['B'].width = 25
worksheet.column_dimensions['C'].width = 25
worksheet.column_dimensions['D'].width = 20
worksheet.column_dimensions['E'].width = 20
messagebox.showinfo("成功", f"坐标转换完成!\n文件已保存至: {output_path}")
self.update_status("转换完成")
except Exception as e:
messagebox.showerror("错误", f"处理文件时出错:\n{str(e)}")
self.update_status(f"错误: {str(e)}")
def calculate_distances(self):
ts_file = self.dist_ts_entry.get()
rtk_file = self.dist_rtk_entry.get()
output_path = self.dist_output_entry.get()
if not all([ts_file, rtk_file, output_path]):
messagebox.showerror("错误", "请选择所有文件路径")
return
try:
self.update_status("正在计算距离...")
# 解析全站仪数据(度分秒格式)
ts_points = []
with open(ts_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
parts = [part.strip() for part in line.split(',')]
if len(parts) < 3:
continue
point_id = parts[0]
lat_dms = parts[1]
lon_dms = parts[2]
try:
lat = self.dms_to_decimal(lat_dms)
lon = self.dms_to_decimal(lon_dms)
ts_points.append({
'id': point_id,
'lat_dms': lat_dms,
'lon_dms': lon_dms,
'lat': lat,
'lon': lon
})
except Exception as e:
print(f"处理全站仪点 {point_id} 时出错: {str(e)}")
# 解析RTK数据(十进制格式)
rtk_points = []
with open(rtk_file, 'r', encoding='utf-8') as f:
for idx, line in enumerate(f):
line = line.strip().rstrip(',')
if not line:
continue
coords = line.split()
if len(coords) < 2:
continue
try:
lon = float(coords[0])
lat = float(coords[1])
rtk_points.append({
'index': idx,
'lat': lat,
'lon': lon
})
except Exception as e:
print(f"处理RTK点 {idx} 时出错: {str(e)}")
# 计算最小距离
results = []
for ts_point in ts_points:
min_dist = float('inf')
min_rtk_point = None
for rtk_point in rtk_points:
# 计算球面距离(单位:米)
distance_m = geodesic(
(ts_point['lat'], ts_point['lon']),
(rtk_point['lat'], rtk_point['lon'])
).meters
# 转换为厘米
distance_cm = distance_m * 100
if distance_cm < min_dist:
min_dist = distance_cm
min_rtk_point = rtk_point
results.append({
'全站仪点号': ts_point['id'],
'原始纬度': ts_point['lat_dms'],
'原始经度': ts_point['lon_dms'],
'全站仪纬度': ts_point['lat'],
'全站仪经度': ts_point['lon'],
'最近RTK点索引': min_rtk_point['index'],
'RTK纬度': min_rtk_point['lat'],
'RTK经度': min_rtk_point['lon'],
'最小距离(cm)': min_dist
})
# 创建结果DataFrame
df = pd.DataFrame(results)
# 使用ExcelWriter保留原始数据精度
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
df.to_excel(writer, index=False)
# 设置列宽
worksheet = writer.sheets['Sheet1']
for col in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']:
worksheet.column_dimensions[col].width = 15
messagebox.showinfo("成功", f"距离计算完成!\n文件已保存至: {output_path}")
self.update_status("计算完成")
except Exception as e:
messagebox.showerror("错误", f"处理过程中出错:\n{str(e)}")
self.update_status(f"错误: {str(e)}")
def run(self):
self.root.mainloop()
if __name__ == "__main__":
app = CoordinateProcessor()
app.run()
运行图:
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容