Поменял усреднение

This commit is contained in:
2025-12-02 11:47:47 +03:00
parent b9e17df32c
commit a18071b7ec
3 changed files with 465 additions and 610 deletions

View File

@@ -1413,27 +1413,28 @@ def kub_report(data_in: io.StringIO) -> pd.DataFrame:
from pyproj import CRS, Transformer
def get_gauss_kruger_zone(longitude: float) -> int:
def get_gauss_kruger_zone(longitude: float) -> int | None:
"""
Определяет номер зоны Гаусса-Крюгера по долготе.
Зоны ГК нумеруются от 1 до 60, каждая зона охватывает 6° долготы.
Центральный меридиан зоны N: (6*N - 3)°
Зоны ГК (Пулково 1942) имеют EPSG коды 28404-28432 (зоны 4-32).
Каждая зона охватывает 6° долготы.
Args:
longitude: Долгота в градусах (от -180 до 180)
Returns:
int: Номер зоны ГК (1-60)
int | None: Номер зоны ГК (4-32) или None если координаты вне зон ГК
"""
# Нормализуем долготу к диапазону 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
# EPSG коды Пулково 1942 существуют только для зон 4-32
if zone < 4 or zone > 32:
return None
return zone
@@ -1441,14 +1442,8 @@ 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)
zone: Номер зоны ГК (4-32)
Returns:
int: EPSG код проекции
@@ -1456,13 +1451,50 @@ def get_gauss_kruger_epsg(zone: int) -> int:
return 28400 + zone
def get_utm_zone(longitude: float) -> int:
"""
Определяет номер зоны UTM по долготе.
UTM зоны нумеруются от 1 до 60, каждая зона охватывает 6° долготы.
Args:
longitude: Долгота в градусах (от -180 до 180)
Returns:
int: Номер зоны UTM (1-60)
"""
zone = int((longitude + 180) / 6) + 1
if zone > 60:
zone = 60
if zone < 1:
zone = 1
return zone
def get_utm_epsg(zone: int, is_northern: bool = True) -> int:
"""
Возвращает EPSG код для зоны UTM (WGS 84 / UTM).
Args:
zone: Номер зоны UTM (1-60)
is_northern: True для северного полушария, False для южного
Returns:
int: EPSG код проекции
"""
if is_northern:
return 32600 + zone
else:
return 32700 + zone
def transform_wgs84_to_gk(coord: tuple, zone: int = None) -> tuple:
"""
Преобразует координаты из WGS84 (EPSG:4326) в проекцию Гаусса-Крюгера.
Преобразует координаты из WGS84 в проекцию Гаусса-Крюгера.
Args:
coord: Координаты в формате (longitude, latitude) в WGS84
zone: Номер зоны ГК (если None, определяется автоматически по долготе)
zone: Номер зоны ГК (если None, определяется автоматически)
Returns:
tuple: Координаты (x, y) в метрах в проекции ГК
@@ -1472,9 +1504,11 @@ def transform_wgs84_to_gk(coord: tuple, zone: int = None) -> tuple:
if zone is None:
zone = get_gauss_kruger_zone(lon)
if zone is None:
raise ValueError(f"Координаты ({lon}, {lat}) вне зон Гаусса-Крюгера (4-32)")
epsg_gk = get_gauss_kruger_epsg(zone)
# Создаём трансформер WGS84 -> GK
transformer = Transformer.from_crs(
CRS.from_epsg(4326),
CRS.from_epsg(epsg_gk),
@@ -1487,7 +1521,7 @@ def transform_wgs84_to_gk(coord: tuple, zone: int = None) -> tuple:
def transform_gk_to_wgs84(coord: tuple, zone: int) -> tuple:
"""
Преобразует координаты из проекции Гаусса-Крюгера в WGS84 (EPSG:4326).
Преобразует координаты из проекции Гаусса-Крюгера в WGS84.
Args:
coord: Координаты (x, y) в метрах в проекции ГК
@@ -1499,7 +1533,6 @@ def transform_gk_to_wgs84(coord: tuple, zone: int) -> tuple:
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),
@@ -1510,37 +1543,126 @@ def transform_gk_to_wgs84(coord: tuple, zone: int) -> tuple:
return (lon, lat)
def calculate_distance_gk(coord1_gk: tuple, coord2_gk: tuple) -> float:
def transform_wgs84_to_utm(coord: tuple, zone: int = None, is_northern: bool = None) -> tuple:
"""
Вычисляет расстояние между двумя точками в проекции ГК (в километрах).
Преобразует координаты из WGS84 в проекцию UTM.
Args:
coord1_gk: Первая точка (x, y) в метрах
coord2_gk: Вторая точка (x, y) в метрах
coord: Координаты в формате (longitude, latitude) в WGS84
zone: Номер зоны UTM (если None, определяется автоматически)
is_northern: Северное полушарие (если None, определяется по широте)
Returns:
float: Расстояние в километрах
tuple: Координаты (x, y) в метрах в проекции UTM
"""
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:
"""
Вычисляет среднее арифметическое координат в проекции Гаусса-Крюгера.
lon, lat = coord
Алгоритм:
1. Определяет зону ГК по первой точке (если не указана)
2. Преобразует все координаты в проекцию ГК
3. Вычисляет среднее арифметическое X и Y
4. Преобразует результат обратно в WGS84
if zone is None:
zone = get_utm_zone(lon)
if is_northern is None:
is_northern = lat >= 0
epsg_utm = get_utm_epsg(zone, is_northern)
transformer = Transformer.from_crs(
CRS.from_epsg(4326),
CRS.from_epsg(epsg_utm),
always_xy=True
)
x, y = transformer.transform(lon, lat)
return (x, y)
def transform_utm_to_wgs84(coord: tuple, zone: int, is_northern: bool = True) -> tuple:
"""
Преобразует координаты из проекции UTM в WGS84.
Args:
coord: Координаты (x, y) в метрах в проекции UTM
zone: Номер зоны UTM
is_northern: Северное полушарие
Returns:
tuple: Координаты (longitude, latitude) в WGS84
"""
x, y = coord
epsg_utm = get_utm_epsg(zone, is_northern)
transformer = Transformer.from_crs(
CRS.from_epsg(epsg_utm),
CRS.from_epsg(4326),
always_xy=True
)
lon, lat = transformer.transform(x, y)
return (lon, lat)
def average_coords_in_gk(coords: list[tuple], zone: int = None) -> tuple[tuple, str]:
"""
Вычисляет среднее арифметическое координат в проекции.
Приоритет:
1. Гаусс-Крюгер (Пулково 1942) для зон 4-32
2. UTM для координат вне зон ГК
3. Геодезическое усреднение как последний fallback
Args:
coords: Список координат в формате [(lon1, lat1), (lon2, lat2), ...]
zone: Номер зоны (если None, определяется по первой точке)
Returns:
tuple: (координаты (lon, lat), тип_усреднения)
тип_усреднения: "ГК" | "UTM" | "Геод"
"""
if not coords:
return (0, 0), "ГК"
if len(coords) == 1:
return coords[0], "ГК"
first_lon, first_lat = coords[0]
# Пытаемся использовать Гаусс-Крюгер
if zone is None:
gk_zone = get_gauss_kruger_zone(first_lon)
else:
gk_zone = zone if 4 <= zone <= 32 else None
# Если координаты в зонах ГК (4-32), используем ГК
if gk_zone is not None:
try:
coords_projected = [transform_wgs84_to_gk(c, gk_zone) for c in coords]
avg_x = sum(c[0] for c in coords_projected) / len(coords_projected)
avg_y = sum(c[1] for c in coords_projected) / len(coords_projected)
return transform_gk_to_wgs84((avg_x, avg_y), gk_zone), "ГК"
except Exception:
pass # Fallback на UTM
# Fallback на UTM для координат вне зон ГК
try:
utm_zone = get_utm_zone(first_lon)
is_northern = first_lat >= 0
coords_utm = [transform_wgs84_to_utm(c, utm_zone, is_northern) for c in coords]
avg_x = sum(c[0] for c in coords_utm) / len(coords_utm)
avg_y = sum(c[1] for c in coords_utm) / len(coords_utm)
return transform_utm_to_wgs84((avg_x, avg_y), utm_zone, is_northern), "UTM"
except Exception:
# Последний fallback - геодезическое усреднение
return _average_coords_geodesic(coords), "Геод"
def _average_coords_geodesic(coords: list[tuple]) -> tuple:
"""
Вычисляет среднее координат через последовательное геодезическое усреднение.
Используется как fallback при ошибках проекции.
Args:
coords: Список координат в формате [(lon1, lat1), (lon2, lat2), ...]
zone: Номер зоны ГК (если None, определяется по первой точке)
Returns:
tuple: Средние координаты (longitude, latitude) в WGS84
@@ -1551,19 +1673,12 @@ def average_coords_in_gk(coords: list[tuple], zone: int = None) -> tuple:
if len(coords) == 1:
return coords[0]
# Определяем зону по первой точке
if zone is None:
zone = get_gauss_kruger_zone(coords[0][0])
# Последовательно усредняем точки
result = coords[0]
for i in range(1, len(coords)):
result, _ = calculate_mean_coords(result, coords[i])
# Преобразуем все координаты в ГК
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)
return result
def calculate_mean_coords(coord1: tuple, coord2: tuple) -> tuple[tuple, float]: