Усредение точек в проекции ГК

This commit is contained in:
2025-12-01 09:54:22 +03:00
parent d521b6baad
commit 01871c3e13
7 changed files with 437 additions and 154 deletions

View File

@@ -7,6 +7,7 @@ from datetime import datetime, time
# Django imports
from django.contrib.gis.geos import Point
from django.db.models import F
from django.utils import timezone
# Third-party imports
import pandas as pd
@@ -691,6 +692,10 @@ def get_points_from_csv(file_content, current_user=None, is_automatic=False):
"mir_1",
"mir_2",
"mir_3",
"mir_4",
"mir_5",
"mir_6",
"mir_7",
],
)
df[["lat", "lon", "freq", "f_range"]] = (
@@ -719,7 +724,7 @@ def get_points_from_csv(file_content, current_user=None, is_automatic=False):
sat_name = row["sat"]
# Извлекаем время для проверки дубликатов
timestamp = row["time"]
timestamp = timezone.make_aware(row["time"])
# Проверяем дубликаты по координатам и времени
if _is_duplicate_by_coords_and_time(coord_tuple, timestamp):
@@ -727,10 +732,13 @@ def get_points_from_csv(file_content, current_user=None, is_automatic=False):
continue
# Получаем или создаем объект спутника
sat_obj, _ = Satellite.objects.get_or_create(
name=sat_name, defaults={"norad": row["norad_id"]}
)
# sat_obj, _ = Satellite.objects.get_or_create(
# name=sat_name, defaults={"norad": row["norad_id"]}
# )
sat_obj, _ = Satellite.objects.get_or_create(
norad=row["norad_id"], defaults={"name": sat_name}
)
source = None
# Если is_automatic=False, работаем с Source
@@ -784,7 +792,7 @@ def get_points_from_csv(file_content, current_user=None, is_automatic=False):
return new_sources_count
def _is_duplicate_by_coords_and_time(coord_tuple, timestamp, tolerance_km=0.1):
def _is_duplicate_by_coords_and_time(coord_tuple, timestamp, tolerance_km=0.001):
"""
Проверяет, существует ли уже ObjItem с такими же координатами и временем ГЛ.
@@ -934,7 +942,7 @@ def _create_objitem_from_csv_row(row, source, user_to_use, is_automatic=False):
# Создаем Geo объект
geo_obj, _ = Geo.objects.get_or_create(
timestamp=row["time"],
timestamp=timezone.make_aware(row["time"]),
coords=Point(row["lon"], row["lat"], srid=4326),
defaults={
"is_average": False,
@@ -1259,6 +1267,162 @@ def kub_report(data_in: io.StringIO) -> pd.DataFrame:
# Утилиты для работы с координатами
# ============================================================================
# Импорт pyproj для работы с проекциями
from pyproj import CRS, Transformer
def get_gauss_kruger_zone(longitude: float) -> int:
"""
Определяет номер зоны Гаусса-Крюгера по долготе.
Зоны ГК нумеруются от 1 до 60, каждая зона охватывает 6° долготы.
Центральный меридиан зоны N: (6*N - 3)°
Args:
longitude: Долгота в градусах (от -180 до 180)
Returns:
int: Номер зоны ГК (1-60)
"""
# Нормализуем долготу к диапазону 0-360
lon_normalized = longitude if longitude >= 0 else longitude + 360
# Вычисляем номер зоны (1-60)
zone = int((lon_normalized + 6) / 6)
if zone > 60:
zone = 60
if zone < 1:
zone = 1
return zone
def get_gauss_kruger_epsg(zone: int) -> int:
"""
Возвращает EPSG код для зоны Гаусса-Крюгера (Pulkovo 1942 / Gauss-Kruger).
EPSG коды для Pulkovo 1942 GK зон:
- Зона 4: EPSG:28404
- Зона 5: EPSG:28405
- ...
- Зона N: EPSG:28400 + N
Args:
zone: Номер зоны ГК (1-60)
Returns:
int: EPSG код проекции
"""
return 28400 + zone
def transform_wgs84_to_gk(coord: tuple, zone: int = None) -> tuple:
"""
Преобразует координаты из WGS84 (EPSG:4326) в проекцию Гаусса-Крюгера.
Args:
coord: Координаты в формате (longitude, latitude) в WGS84
zone: Номер зоны ГК (если None, определяется автоматически по долготе)
Returns:
tuple: Координаты (x, y) в метрах в проекции ГК
"""
lon, lat = coord
if zone is None:
zone = get_gauss_kruger_zone(lon)
epsg_gk = get_gauss_kruger_epsg(zone)
# Создаём трансформер WGS84 -> GK
transformer = Transformer.from_crs(
CRS.from_epsg(4326),
CRS.from_epsg(epsg_gk),
always_xy=True
)
x, y = transformer.transform(lon, lat)
return (x, y)
def transform_gk_to_wgs84(coord: tuple, zone: int) -> tuple:
"""
Преобразует координаты из проекции Гаусса-Крюгера в WGS84 (EPSG:4326).
Args:
coord: Координаты (x, y) в метрах в проекции ГК
zone: Номер зоны ГК
Returns:
tuple: Координаты (longitude, latitude) в WGS84
"""
x, y = coord
epsg_gk = get_gauss_kruger_epsg(zone)
# Создаём трансформер GK -> WGS84
transformer = Transformer.from_crs(
CRS.from_epsg(epsg_gk),
CRS.from_epsg(4326),
always_xy=True
)
lon, lat = transformer.transform(x, y)
return (lon, lat)
def calculate_distance_gk(coord1_gk: tuple, coord2_gk: tuple) -> float:
"""
Вычисляет расстояние между двумя точками в проекции ГК (в километрах).
Args:
coord1_gk: Первая точка (x, y) в метрах
coord2_gk: Вторая точка (x, y) в метрах
Returns:
float: Расстояние в километрах
"""
import math
x1, y1 = coord1_gk
x2, y2 = coord2_gk
distance_m = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
return distance_m / 1000
def average_coords_in_gk(coords: list[tuple], zone: int = None) -> tuple:
"""
Вычисляет среднее арифметическое координат в проекции Гаусса-Крюгера.
Алгоритм:
1. Определяет зону ГК по первой точке (если не указана)
2. Преобразует все координаты в проекцию ГК
3. Вычисляет среднее арифметическое X и Y
4. Преобразует результат обратно в WGS84
Args:
coords: Список координат в формате [(lon1, lat1), (lon2, lat2), ...]
zone: Номер зоны ГК (если None, определяется по первой точке)
Returns:
tuple: Средние координаты (longitude, latitude) в WGS84
"""
if not coords:
return (0, 0)
if len(coords) == 1:
return coords[0]
# Определяем зону по первой точке
if zone is None:
zone = get_gauss_kruger_zone(coords[0][0])
# Преобразуем все координаты в ГК
coords_gk = [transform_wgs84_to_gk(c, zone) for c in coords]
# Вычисляем среднее арифметическое
avg_x = sum(c[0] for c in coords_gk) / len(coords_gk)
avg_y = sum(c[1] for c in coords_gk) / len(coords_gk)
# Преобразуем обратно в WGS84
return transform_gk_to_wgs84((avg_x, avg_y), zone)
def calculate_mean_coords(coord1: tuple, coord2: tuple) -> tuple[tuple, float]:
"""
@@ -1279,6 +1443,23 @@ def calculate_mean_coords(coord1: tuple, coord2: tuple) -> tuple[tuple, float]:
return (geod_direct['lon2'], geod_direct['lat2']), distance/1000
def calculate_distance_wgs84(coord1: tuple, coord2: tuple) -> float:
"""
Вычисляет расстояние между двумя точками в WGS84 (в километрах).
Args:
coord1: Первая точка (longitude, latitude)
coord2: Вторая точка (longitude, latitude)
Returns:
float: Расстояние в километрах
"""
lon1, lat1 = coord1
lon2, lat2 = coord2
geod_inv = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2)
return geod_inv['s12'] / 1000
def calculate_average_coords_incremental(
current_average: tuple, new_coord: tuple