#!/usr/bin/python3
# apt install python3-geopy
# apt install python3-matplotlib
import sys
import math
from geopy.distance import geodesic
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QDoubleSpinBox, QPlainTextEdit, QLabel, QListWidget, QListWidgetItem, QDesktopWidget, QFileDialog
from PyQt5.QtCore import Qt
import datetime
import re
class CoordinateHelper(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("EVSEGN Cord helper")
screen = QDesktopWidget().screenGeometry()
x = (screen.width() - 800) // 2
y = (screen.height() - 800) // 2
self.setGeometry(x, y, 800, 800)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.create_widgets()
def create_widgets(self):
self.main_layout = QVBoxLayout()
self.central_widget.setLayout(self.main_layout)
self.input_layout = QHBoxLayout()
self.main_layout.addLayout(self.input_layout)
self.label = QLabel("Введите координаты (широта, долгота):")
self.input_layout.addWidget(self.label)
self.entry = QLineEdit()
self.input_layout.addWidget(self.entry)
self.distance_threshold_layout = QHBoxLayout()
self.main_layout.addLayout(self.distance_threshold_layout)
self.distance_threshold_label = QLabel("Порог расстояния (м):")
self.distance_threshold_layout.addWidget(self.distance_threshold_label)
self.distance_threshold_spinbox = QDoubleSpinBox()
self.distance_threshold_spinbox.setRange(1, 100)
self.distance_threshold_spinbox.setValue(25)
self.distance_threshold_layout.addWidget(self.distance_threshold_spinbox)
self.add_button = QPushButton("Добавить координаты")
self.add_button.clicked.connect(self.add_coordinate)
self.main_layout.addWidget(self.add_button)
self.import_button = QPushButton("Импорт из файла")
self.import_button.clicked.connect(self.import_coordinates_from_file)
self.main_layout.addWidget(self.import_button)
self.recognize_button = QPushButton("Распознать координаты на фото (В ПРОЦЕССЕ)")
self.main_layout.addWidget(self.recognize_button)
self.coordinates_list_label = QLabel("Добавленные координаты:")
self.main_layout.addWidget(self.coordinates_list_label)
self.coordinates_list = QListWidget()
self.coordinates_list.itemClicked.connect(self.on_item_clicked)
self.main_layout.addWidget(self.coordinates_list)
self.delete_button = QPushButton("Удалить координаты")
self.delete_button.clicked.connect(self.delete_coordinate)
self.main_layout.addWidget(self.delete_button)
self.export_button = QPushButton("Экспортировать в GPX")
self.export_button.clicked.connect(self.export_to_gpx)
self.main_layout.addWidget(self.export_button)
self.fig = Figure(figsize=(10, 6))
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.main_layout.addWidget(self.canvas)
self.coordinates = []
self.distances = {}
def parse_coordinates(self, coord_str):
try:
lat, lon = coord_str.replace(',', ' ').split()
return float(lat), float(lon)
except ValueError:
raise ValueError("Недопустимый формат координат. Используйте 'широта, долгота'")
def calculate_distances(self):
self.distances = {}
for i in range(len(self.coordinates)):
for j in range(i+1, len(self.coordinates)):
start = self.coordinates[i]
end = self.coordinates[j]
distance = geodesic(start, end).m
self.distances[(i, j)] = distance
def draw_map(self):
self.ax.clear()
self.ax.set_xlabel('Долгота')
self.ax.set_ylabel('Широта')
self.ax.set_title('Расстояние между координатами')
for i, (lat, lon) in enumerate(self.coordinates):
self.ax.plot(lon, lat, 'bo', markersize=5)
self.ax.text(lon, lat, f"{i+1}", ha='center', va='center', fontsize=16)
for (i, j), distance in self.distances.items():
start = self.coordinates[i]
end = self.coordinates[j]
if distance < self.distance_threshold_spinbox.value():
line_color = 'r' if distance < self.distance_threshold_spinbox.value() else 'b'
self.ax.plot([start[1], end[1]], [start[0], end[0]], f'{line_color}-')
midx = (start[1] + end[1]) / 2
midy = (start[0] + end[0]) / 2
self.ax.text(midx, midy, f"{distance:.2f} m", ha='center', va='center')
self.canvas.draw()
def add_coordinate(self):
try:
lat, lon = self.parse_coordinates(self.entry.text())
self.coordinates.append((lat, lon))
item = QListWidgetItem(f"{lat}, {lon}")
self.coordinates_list.addItem(item)
self.entry.clear()
self.calculate_distances()
self.draw_map()
except ValueError as e:
print(e)
def delete_coordinate(self):
current_row = self.coordinates_list.currentRow()
if current_row != -1:
self.coordinates_list.takeItem(current_row)
del self.coordinates[current_row]
self.calculate_distances()
self.draw_map()
def on_item_clicked(self, item):
self.coordinates_list.setCurrentItem(item)
def export_to_gpx(self):
if not self.coordinates:
print("Нет координат для экспорта")
return
filename, _ = QFileDialog.getSaveFileName(self, "Экспорт в GPX", "", "GPX files (*.gpx)")
if not filename:
return
with open(filename, "w") as f:
f.write("<?xml version=\"1.0\"?>\n")
f.write("<gpx>\n")
for i, (lat, lon) in enumerate(self.coordinates):
f.write(f" <wpt lat=\"{lat}\" lon=\"{lon}\">\n")
f.write(f" <name>#{i+1}</name>\n")
f.write(f" </wpt>\n")
f.write("</gpx>\n")
print(f"Координаты экспортированы в файл {filename}")
def import_coordinates_from_file(self):
filename, _ = QFileDialog.getOpenFileName(self, "Импорт из файла", "", "Text files (*.txt)")
if not filename:
return
with open(filename, "r", encoding="utf-8") as f:
text = f.read()
coordinates = []
for line in text.splitlines():
matches = re.findall(r"(\d+(?:\.\d+)?)", line)
if len(matches) == 2:
lat, lon = map(float, matches)
coordinates.append((lat, lon))
for coord in coordinates:
item = QListWidgetItem(f"{coord[0]}, {coord[1]}")
self.coordinates_list.addItem(item)
self.coordinates.append(coord)
self.calculate_distances()
self.draw_map()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = CoordinateHelper()
window.show()
sys.exit(app.exec_())