[Python 原创] 经纬度坐标转换器及距离坐标计算器

[Python 原创] 经纬度坐标转换器及距离坐标计算器

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

运行图:

image

image

© 版权声明
THE END
喜欢就支持一下吧
点赞9赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容