Переделал страницу с ObjItem. Теперь работает корректно.
This commit is contained in:
@@ -8,92 +8,121 @@
|
||||
4. Иначе создать новый Source с coords_average = координаты geo_obj
|
||||
"""
|
||||
|
||||
import os
|
||||
import django
|
||||
# import os
|
||||
# import django
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dbapp.settings")
|
||||
django.setup()
|
||||
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dbapp.settings")
|
||||
# django.setup()
|
||||
|
||||
from mainapp.models import ObjItem, Source, CustomUser
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.contrib.gis.measure import D
|
||||
from django.contrib.gis.db.models.functions import Distance
|
||||
# from mainapp.models import ObjItem, Source, CustomUser
|
||||
# from django.contrib.gis.geos import Point
|
||||
# from django.contrib.gis.measure import D
|
||||
# from django.contrib.gis.db.models.functions import Distance
|
||||
|
||||
|
||||
def calculate_distance_degrees(coord1, coord2):
|
||||
"""Вычисляет расстояние между двумя координатами в градусах."""
|
||||
import math
|
||||
# def calculate_distance_degrees(coord1, coord2):
|
||||
# """Вычисляет расстояние между двумя координатами в градусах."""
|
||||
# import math
|
||||
|
||||
# lon1, lat1 = coord1
|
||||
# lon2, lat2 = coord2
|
||||
|
||||
# return math.sqrt((lon2 - lon1) ** 2 + (lat2 - lat1) ** 2)
|
||||
|
||||
|
||||
# def fix_objitems_without_source():
|
||||
# """Исправляет ObjItems без связи с Source."""
|
||||
|
||||
# # Получаем пользователя по умолчанию
|
||||
# default_user = CustomUser.objects.get(id=1)
|
||||
|
||||
# # Получаем все ObjItems без source
|
||||
# objitems_without_source = ObjItem.objects.filter(source__isnull=True)
|
||||
# total_count = objitems_without_source.count()
|
||||
|
||||
# print(f"Найдено {total_count} ObjItems без source")
|
||||
|
||||
# if total_count == 0:
|
||||
# print("Нечего исправлять!")
|
||||
# return
|
||||
|
||||
# fixed_count = 0
|
||||
# new_sources_count = 0
|
||||
|
||||
# for objitem in objitems_without_source:
|
||||
# # Проверяем, есть ли geo_obj
|
||||
# if not hasattr(objitem, 'geo_obj') or not objitem.geo_obj or not objitem.geo_obj.coords:
|
||||
# print(f"ObjItem {objitem.id} не имеет geo_obj или координат, пропускаем")
|
||||
# continue
|
||||
|
||||
# geo_coords = objitem.geo_obj.coords
|
||||
# coord_tuple = (geo_coords.x, geo_coords.y)
|
||||
|
||||
# # Ищем ближайший Source
|
||||
# sources_with_coords = Source.objects.filter(coords_average__isnull=False)
|
||||
|
||||
# closest_source = None
|
||||
# min_distance = float('inf')
|
||||
|
||||
# for source in sources_with_coords:
|
||||
# source_coord = (source.coords_average.x, source.coords_average.y)
|
||||
# distance = calculate_distance_degrees(coord_tuple, source_coord)
|
||||
|
||||
# if distance < min_distance:
|
||||
# min_distance = distance
|
||||
# closest_source = source
|
||||
|
||||
# # Если нашли близкий Source (расстояние <= 0.5 градуса)
|
||||
# if closest_source and min_distance <= 0.5:
|
||||
# objitem.source = closest_source
|
||||
# objitem.save()
|
||||
# print(f"ObjItem {objitem.id} связан с Source {closest_source.id} (расстояние: {min_distance:.4f}°)")
|
||||
# fixed_count += 1
|
||||
# else:
|
||||
# # Создаем новый Source
|
||||
# new_source = Source.objects.create(
|
||||
# coords_average=Point(coord_tuple, srid=4326),
|
||||
# created_by=default_user
|
||||
# )
|
||||
# objitem.source = new_source
|
||||
# objitem.save()
|
||||
# print(f"ObjItem {objitem.id} связан с новым Source {new_source.id}")
|
||||
# fixed_count += 1
|
||||
# new_sources_count += 1
|
||||
|
||||
# print(f"\nГотово!")
|
||||
# print(f"Исправлено ObjItems: {fixed_count}")
|
||||
# print(f"Создано новых Source: {new_sources_count}")
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# fix_objitems_without_source()
|
||||
|
||||
|
||||
from geographiclib.geodesic import Geodesic
|
||||
|
||||
def calculate_mean_coords(coord1: tuple, coord2: tuple) -> tuple[tuple, float]:
|
||||
"""
|
||||
Вычисляет среднюю точку между двумя координатами с использованием геодезических вычислений (с учётом эллипсоида).
|
||||
|
||||
:param lat1: Широта первой точки в градусах.
|
||||
:param lon1: Долгота первой точки в градусах.
|
||||
:param lat2: Широта второй точки в градусах.
|
||||
:param lon2: Долгота второй точки в градусах.
|
||||
:return: Словарь с ключами 'lat' и 'lon' для средней точки, и расстояние(dist) в КМ.
|
||||
"""
|
||||
lon1, lat1 = coord1
|
||||
lon2, lat2 = coord2
|
||||
geod_inv = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2)
|
||||
azimuth1 = geod_inv['azi1']
|
||||
distance = geod_inv['s12']
|
||||
geod_direct = Geodesic.WGS84.Direct(lat1, lon1, azimuth1, distance / 2)
|
||||
return (geod_direct['lon2'], geod_direct['lat2']), distance/1000
|
||||
|
||||
return math.sqrt((lon2 - lon1) ** 2 + (lat2 - lat1) ** 2)
|
||||
# Пример использования
|
||||
lat1, lon1 = 56.15465080269812, 38.140518028837285
|
||||
lat2, lon2 = 56.0852, 38.0852
|
||||
midpoint = calculate_mean_coords((lat1, lon1), (lat2, lon2)) #56.15465080269812, 38.140518028837285
|
||||
|
||||
|
||||
def fix_objitems_without_source():
|
||||
"""Исправляет ObjItems без связи с Source."""
|
||||
|
||||
# Получаем пользователя по умолчанию
|
||||
default_user = CustomUser.objects.get(id=1)
|
||||
|
||||
# Получаем все ObjItems без source
|
||||
objitems_without_source = ObjItem.objects.filter(source__isnull=True)
|
||||
total_count = objitems_without_source.count()
|
||||
|
||||
print(f"Найдено {total_count} ObjItems без source")
|
||||
|
||||
if total_count == 0:
|
||||
print("Нечего исправлять!")
|
||||
return
|
||||
|
||||
fixed_count = 0
|
||||
new_sources_count = 0
|
||||
|
||||
for objitem in objitems_without_source:
|
||||
# Проверяем, есть ли geo_obj
|
||||
if not hasattr(objitem, 'geo_obj') or not objitem.geo_obj or not objitem.geo_obj.coords:
|
||||
print(f"ObjItem {objitem.id} не имеет geo_obj или координат, пропускаем")
|
||||
continue
|
||||
|
||||
geo_coords = objitem.geo_obj.coords
|
||||
coord_tuple = (geo_coords.x, geo_coords.y)
|
||||
|
||||
# Ищем ближайший Source
|
||||
sources_with_coords = Source.objects.filter(coords_average__isnull=False)
|
||||
|
||||
closest_source = None
|
||||
min_distance = float('inf')
|
||||
|
||||
for source in sources_with_coords:
|
||||
source_coord = (source.coords_average.x, source.coords_average.y)
|
||||
distance = calculate_distance_degrees(coord_tuple, source_coord)
|
||||
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
closest_source = source
|
||||
|
||||
# Если нашли близкий Source (расстояние <= 0.5 градуса)
|
||||
if closest_source and min_distance <= 0.5:
|
||||
objitem.source = closest_source
|
||||
objitem.save()
|
||||
print(f"ObjItem {objitem.id} связан с Source {closest_source.id} (расстояние: {min_distance:.4f}°)")
|
||||
fixed_count += 1
|
||||
else:
|
||||
# Создаем новый Source
|
||||
new_source = Source.objects.create(
|
||||
coords_average=Point(coord_tuple, srid=4326),
|
||||
created_by=default_user
|
||||
)
|
||||
objitem.source = new_source
|
||||
objitem.save()
|
||||
print(f"ObjItem {objitem.id} связан с новым Source {new_source.id}")
|
||||
fixed_count += 1
|
||||
new_sources_count += 1
|
||||
|
||||
print(f"\nГотово!")
|
||||
print(f"Исправлено ObjItems: {fixed_count}")
|
||||
print(f"Создано новых Source: {new_sources_count}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fix_objitems_without_source()
|
||||
print(f"Средняя точка: {midpoint[0]}")
|
||||
print(f"Расстояние: {midpoint[1]} км")
|
||||
@@ -0,0 +1,13 @@
|
||||
{% comment %}
|
||||
Компонент для элемента переключения видимости столбца
|
||||
Использование:
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=0 column_label="Выбрать" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=1 column_label="Имя" checked=True %}
|
||||
{% endcomment %}
|
||||
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="{{ column_index }}" {% if checked %}checked{% endif %}
|
||||
onchange="toggleColumn(this)"> {{ column_label }}
|
||||
</label>
|
||||
</li>
|
||||
@@ -0,0 +1,44 @@
|
||||
{% comment %}
|
||||
Компонент для выпадающего списка видимости столбцов
|
||||
Использование:
|
||||
{% include 'mainapp/components/_column_visibility_dropdown.html' %}
|
||||
{% endcomment %}
|
||||
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle"
|
||||
id="columnVisibilityDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-gear"></i> Колонки
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="columnVisibilityDropdown" style="z-index: 1050; max-height: 300px; overflow-y: auto;">
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" id="select-all-columns" unchecked
|
||||
onchange="toggleAllColumns(this)"> Выбрать всё
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=0 column_label="Выбрать" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=1 column_label="Имя" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=2 column_label="Спутник" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=3 column_label="Част, МГц" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=4 column_label="Полоса, МГц" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=5 column_label="Поляризация" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=6 column_label="Сим. V" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=7 column_label="Модул" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=8 column_label="ОСШ" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=9 column_label="Время ГЛ" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=10 column_label="Местоположение" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=11 column_label="Геолокация" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=12 column_label="Обновлено" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=13 column_label="Кем (обновление)" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=14 column_label="Создано" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=15 column_label="Кем (создание)" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=16 column_label="Комментарий" checked=False %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=17 column_label="Усреднённое" checked=False %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=18 column_label="Стандарт" checked=False %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=19 column_label="Тип источника" checked=True %}
|
||||
{% include 'mainapp/components/_column_toggle_item.html' with column_index=20 column_label="Sigma" checked=True %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -14,7 +14,7 @@
|
||||
{% if user.is_authenticated %}
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:home' %}">Объекты</a>
|
||||
<a class="nav-link" href="{% url 'mainapp:objitem_list' %}">Объекты</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'mainapp:actions' %}">Действия</a>
|
||||
|
||||
@@ -92,179 +92,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Column visibility toggle button -->
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle"
|
||||
id="columnVisibilityDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-gear"></i> Колонки
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="columnVisibilityDropdown" style="z-index: 1050; max-height: 300px; overflow-y: auto;">
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" id="select-all-columns" unchecked
|
||||
onchange="toggleAllColumns(this)"> Выбрать всё
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="0" checked
|
||||
onchange="toggleColumn(this)"> Выбрать
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="1" checked
|
||||
onchange="toggleColumn(this)"> Имя
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="2" checked
|
||||
onchange="toggleColumn(this)"> Спутник
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="3" checked
|
||||
onchange="toggleColumn(this)"> Част, МГц
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="4" checked
|
||||
onchange="toggleColumn(this)"> Полоса, МГц
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="5" checked
|
||||
onchange="toggleColumn(this)"> Поляризация
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="6" checked
|
||||
onchange="toggleColumn(this)"> Сим. V
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="7" checked
|
||||
onchange="toggleColumn(this)"> Модул
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="8" checked
|
||||
onchange="toggleColumn(this)"> ОСШ
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="9" checked
|
||||
onchange="toggleColumn(this)"> Время ГЛ
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="10" checked
|
||||
onchange="toggleColumn(this)"> Местоположение
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="11" checked
|
||||
onchange="toggleColumn(this)"> Геолокация
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="12" checked
|
||||
onchange="toggleColumn(this)"> Кубсат
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="13" checked
|
||||
onchange="toggleColumn(this)"> Опер. отд
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="14" checked
|
||||
onchange="toggleColumn(this)"> Гео-куб, км
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="15" checked
|
||||
onchange="toggleColumn(this)"> Гео-опер, км
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="16" checked
|
||||
onchange="toggleColumn(this)"> Куб-опер, км
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="17" checked
|
||||
onchange="toggleColumn(this)"> Обновлено
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="18" checked
|
||||
onchange="toggleColumn(this)"> Кем (обновление)
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="19" checked
|
||||
onchange="toggleColumn(this)"> Создано
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="20" checked
|
||||
onchange="toggleColumn(this)"> Кем (создание)
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="21" unchecked
|
||||
onchange="toggleColumn(this)"> Комментарий
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="22" unchecked
|
||||
onchange="toggleColumn(this)"> Усреднённое
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="23" unchecked
|
||||
onchange="toggleColumn(this)"> Стандарт
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="24" checked
|
||||
onchange="toggleColumn(this)"> Тип источника
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="dropdown-item">
|
||||
<input type="checkbox" class="column-toggle" data-column="25" checked
|
||||
onchange="toggleColumn(this)"> Sigma
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% include 'mainapp/components/_column_visibility_dropdown.html' %}
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="ms-auto">
|
||||
@@ -497,11 +325,6 @@
|
||||
{% include 'mainapp/components/_table_header.html' with label="Время ГЛ" field="geo_timestamp" sort=sort %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Местоположение" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Геолокация" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Кубсат" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Опер. отд" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Гео-куб, км" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Гео-опер, км" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Куб-опер, км" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Обновлено" field="updated_at" sort=sort %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Кем(обн)" field="" sortable=False %}
|
||||
{% include 'mainapp/components/_table_header.html' with label="Создано" field="created_at" sort=sort %}
|
||||
@@ -531,11 +354,6 @@
|
||||
<td>{{ item.geo_timestamp|date:"d.m.Y H:i" }}</td>
|
||||
<td>{{ item.geo_location}}</td>
|
||||
<td>{{ item.geo_coords }}</td>
|
||||
<td>{{ item.kupsat_coords }}</td>
|
||||
<td>{{ item.valid_coords }}</td>
|
||||
<td>{{ item.distance_geo_kup }}</td>
|
||||
<td>{{ item.distance_geo_valid }}</td>
|
||||
<td>{{ item.distance_kup_valid }}</td>
|
||||
<td>{{ item.obj.updated_at|date:"d.m.Y H:i" }}</td>
|
||||
<td>{{ item.updated_by }}</td>
|
||||
<td>{{ item.obj.created_at|date:"d.m.Y H:i" }}</td>
|
||||
@@ -900,19 +718,8 @@
|
||||
|
||||
// Initialize column visibility - hide creation columns by default
|
||||
function initColumnVisibility() {
|
||||
const creationDateCheckbox = document.querySelector('input[data-column="19"]');
|
||||
const creationUserCheckbox = document.querySelector('input[data-column="20"]');
|
||||
const creationDistanceGOpCheckbox = document.querySelector('input[data-column="15"]');
|
||||
const creationDistanceKubOpCheckbox = document.querySelector('input[data-column="16"]');
|
||||
if (creationDistanceGOpCheckbox) {
|
||||
creationDistanceGOpCheckbox.checked = false;
|
||||
toggleColumn(creationDistanceGOpCheckbox);
|
||||
}
|
||||
if (creationDistanceKubOpCheckbox) {
|
||||
creationDistanceKubOpCheckbox.checked = false;
|
||||
toggleColumn(creationDistanceKubOpCheckbox);
|
||||
}
|
||||
|
||||
const creationDateCheckbox = document.querySelector('input[data-column="12"]');
|
||||
const creationUserCheckbox = document.querySelector('input[data-column="13"]');
|
||||
if (creationDateCheckbox) {
|
||||
creationDateCheckbox.checked = false;
|
||||
toggleColumn(creationDateCheckbox);
|
||||
@@ -924,9 +731,9 @@
|
||||
}
|
||||
|
||||
// Hide comment, is_average, and standard columns by default
|
||||
const commentCheckbox = document.querySelector('input[data-column="21"]');
|
||||
const isAverageCheckbox = document.querySelector('input[data-column="22"]');
|
||||
const standardCheckbox = document.querySelector('input[data-column="23"]');
|
||||
const commentCheckbox = document.querySelector('input[data-column="14"]');
|
||||
const isAverageCheckbox = document.querySelector('input[data-column="15"]');
|
||||
const standardCheckbox = document.querySelector('input[data-column="16"]');
|
||||
|
||||
if (commentCheckbox) {
|
||||
commentCheckbox.checked = false;
|
||||
|
||||
@@ -6,9 +6,9 @@ from . import views
|
||||
app_name = 'mainapp'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.SourceListView.as_view(), name='home'), # Source list page
|
||||
path('objitems/', views.ObjItemListView.as_view(), name='objitem_list'), # Objects list page
|
||||
path('actions/', views.ActionsPageView.as_view(), name='actions'), # Move actions to a separate page
|
||||
path('', views.SourceListView.as_view(), name='home'),
|
||||
path('objitems/', views.ObjItemListView.as_view(), name='objitem_list'),
|
||||
path('actions/', views.ActionsPageView.as_view(), name='actions'),
|
||||
path('excel-data', views.LoadExcelDataView.as_view(), name='load_excel_data'),
|
||||
path('satellites', views.AddSatellitesView.as_view(), name='add_sats'),
|
||||
path('api/locations/<int:sat_id>/geojson/', views.GetLocationsView.as_view(), name='locations_by_id'),
|
||||
|
||||
@@ -10,7 +10,7 @@ from django.db.models import F
|
||||
|
||||
# Third-party imports
|
||||
import pandas as pd
|
||||
|
||||
from geographiclib.geodesic import Geodesic
|
||||
# Local imports
|
||||
from mapsapp.models import Transponders
|
||||
|
||||
@@ -40,6 +40,7 @@ MAX_ITEMS_PER_PAGE = 10000
|
||||
DEFAULT_NUMERIC_VALUE = -1.0
|
||||
MINIMUM_BANDWIDTH_MHZ = 0.08
|
||||
|
||||
RANGE_DISTANCE = 56
|
||||
|
||||
def get_all_constants():
|
||||
sats = [sat.name for sat in Satellite.objects.all()]
|
||||
@@ -160,15 +161,9 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite, current_user=None):
|
||||
|
||||
# Вычислить расстояние от координаты до coords_average
|
||||
current_avg = (source.coords_average.x, source.coords_average.y)
|
||||
distance = calculate_distance_degrees(current_avg, current_coord)
|
||||
|
||||
# Если расстояние <= 0.5 градуса
|
||||
if distance <= 0.5:
|
||||
# Вычислить новое среднее ИНКРЕМЕНТАЛЬНО
|
||||
new_avg = calculate_average_coords_incremental(
|
||||
current_avg, current_coord
|
||||
)
|
||||
new_avg, distance = calculate_mean_coords(current_avg, current_coord)
|
||||
|
||||
if distance <= RANGE_DISTANCE:
|
||||
# Обновить coords_average в Source
|
||||
source.coords_average = Point(new_avg, srid=4326)
|
||||
source.save()
|
||||
@@ -255,7 +250,6 @@ def _create_objitem_from_row(row, sat, source, user_to_use, consts):
|
||||
comment = row["Комментарий"]
|
||||
source_name = row["Объект наблюдения"]
|
||||
|
||||
# Создаем Geo объект (БЕЗ coords_kupsat и coords_valid)
|
||||
geo, _ = Geo.objects.get_or_create(
|
||||
timestamp=timestamp,
|
||||
coords=geo_point,
|
||||
@@ -469,7 +463,7 @@ def get_points_from_csv(file_content, current_user=None):
|
||||
|
||||
for source in existing_sources:
|
||||
source_coord = (source.coords_average.x, source.coords_average.y)
|
||||
distance = calculate_distance_degrees(source_coord, current_coord)
|
||||
new_avg, distance = calculate_mean_coords(source_coord, current_coord)
|
||||
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
@@ -479,7 +473,7 @@ def get_points_from_csv(file_content, current_user=None):
|
||||
if closest_source and min_distance <= 0.5:
|
||||
# Обновить coords_average инкрементально
|
||||
current_avg = (closest_source.coords_average.x, closest_source.coords_average.y)
|
||||
new_avg = calculate_average_coords_incremental(current_avg, current_coord)
|
||||
# new_avg = calculate_average_coords_incremental(current_avg, current_coord)
|
||||
closest_source.coords_average = Point(new_avg, srid=4326)
|
||||
closest_source.save()
|
||||
|
||||
@@ -507,7 +501,7 @@ def get_points_from_csv(file_content, current_user=None):
|
||||
return new_sources_count
|
||||
|
||||
|
||||
def _is_duplicate_objitem(coord_tuple, frequency, freq_range, tolerance=0.001):
|
||||
def _is_duplicate_objitem(coord_tuple, frequency, freq_range, tolerance=0.1):
|
||||
"""
|
||||
Проверяет, существует ли уже ObjItem с такими же координатами и частотой.
|
||||
|
||||
@@ -515,7 +509,7 @@ def _is_duplicate_objitem(coord_tuple, frequency, freq_range, tolerance=0.001):
|
||||
coord_tuple: кортеж (lon, lat) координат
|
||||
frequency: частота в МГц
|
||||
freq_range: полоса частот в МГц
|
||||
tolerance: допуск для сравнения координат в градусах (по умолчанию 0.001 ≈ 100м)
|
||||
tolerance: допуск для сравнения координат в километрах
|
||||
|
||||
Returns:
|
||||
bool: True если дубликат найден, False иначе
|
||||
@@ -531,7 +525,7 @@ def _is_duplicate_objitem(coord_tuple, frequency, freq_range, tolerance=0.001):
|
||||
|
||||
# Проверяем расстояние между координатами
|
||||
geo_coord = (objitem.geo_obj.coords.x, objitem.geo_obj.coords.y)
|
||||
distance = calculate_distance_degrees(coord_tuple, geo_coord)
|
||||
_, distance = calculate_mean_coords(coord_tuple, geo_coord)
|
||||
|
||||
if distance <= tolerance:
|
||||
# Координаты совпадают, проверяем частоту
|
||||
@@ -873,40 +867,24 @@ def kub_report(data_in: io.StringIO) -> pd.DataFrame:
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def calculate_distance_degrees(coord1: tuple, coord2: tuple) -> float:
|
||||
def calculate_mean_coords(coord1: tuple, coord2: tuple) -> tuple[tuple, float]:
|
||||
"""
|
||||
Вычисляет расстояние между двумя координатами в градусах.
|
||||
Вычисляет среднюю точку между двумя координатами с использованием геодезических вычислений (с учётом эллипсоида).
|
||||
|
||||
Использует простую евклидову метрику для малых расстояний.
|
||||
Подходит для определения близости точек в радиусе до нескольких градусов.
|
||||
|
||||
Args:
|
||||
coord1 (tuple): Первая координата в формате (longitude, latitude)
|
||||
coord2 (tuple): Вторая координата в формате (longitude, latitude)
|
||||
|
||||
Returns:
|
||||
float: Расстояние в градусах
|
||||
|
||||
Example:
|
||||
>>> dist = calculate_distance_degrees((37.62, 55.75), (37.63, 55.76))
|
||||
>>> print(f"{dist:.4f}") # ~0.0141 градусов
|
||||
0.0141
|
||||
|
||||
>>> dist = calculate_distance_degrees((37.62, 55.75), (37.62, 55.75))
|
||||
>>> print(dist) # Одинаковые координаты
|
||||
0.0
|
||||
:param lat1: Широта первой точки в градусах.
|
||||
:param lon1: Долгота первой точки в градусах.
|
||||
:param lat2: Широта второй точки в градусах.
|
||||
:param lon2: Долгота второй точки в градусах.
|
||||
:return: Словарь с ключами 'lat' и 'lon' для средней точки, и расстояние(dist) в КМ.
|
||||
"""
|
||||
lon1, lat1 = coord1
|
||||
lon2, lat2 = coord2
|
||||
geod_inv = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2)
|
||||
azimuth1 = geod_inv['azi1']
|
||||
distance = geod_inv['s12']
|
||||
geod_direct = Geodesic.WGS84.Direct(lat1, lon1, azimuth1, distance / 2)
|
||||
return (geod_direct['lon2'], geod_direct['lat2']), distance/1000
|
||||
|
||||
# Простая евклидова метрика для малых расстояний
|
||||
# Для более точных расчетов на больших расстояниях можно использовать формулу гаверсинуса
|
||||
delta_lon = lon2 - lon1
|
||||
delta_lat = lat2 - lat1
|
||||
|
||||
distance = (delta_lon**2 + delta_lat**2) ** 0.5
|
||||
|
||||
return distance
|
||||
|
||||
|
||||
def calculate_average_coords_incremental(
|
||||
|
||||
@@ -915,6 +915,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
objects = (
|
||||
ObjItem.objects.select_related(
|
||||
"geo_obj",
|
||||
"source",
|
||||
"updated_by__user",
|
||||
"created_by__user",
|
||||
"lyngsat_source",
|
||||
@@ -933,6 +934,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
else:
|
||||
objects = ObjItem.objects.select_related(
|
||||
"geo_obj",
|
||||
"source",
|
||||
"updated_by__user",
|
||||
"created_by__user",
|
||||
"lyngsat_source",
|
||||
@@ -1021,14 +1023,14 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
)
|
||||
|
||||
if has_kupsat == "1":
|
||||
objects = objects.filter(geo_obj__coords_kupsat__isnull=False)
|
||||
objects = objects.filter(source__coords_kupsat__isnull=False)
|
||||
elif has_kupsat == "0":
|
||||
objects = objects.filter(geo_obj__coords_kupsat__isnull=True)
|
||||
objects = objects.filter(source__coords_kupsat__isnull=True)
|
||||
|
||||
if has_valid == "1":
|
||||
objects = objects.filter(geo_obj__coords_valid__isnull=False)
|
||||
objects = objects.filter(source__coords_valid__isnull=False)
|
||||
elif has_valid == "0":
|
||||
objects = objects.filter(geo_obj__coords_valid__isnull=True)
|
||||
objects = objects.filter(source__coords_valid__isnull=True)
|
||||
|
||||
# Date filter for geo_obj timestamp
|
||||
date_from = request.GET.get("date_from")
|
||||
@@ -1145,33 +1147,6 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S"
|
||||
geo_coords = f"{lat} {lon}"
|
||||
|
||||
if obj.geo_obj.coords_kupsat:
|
||||
longitude = obj.geo_obj.coords_kupsat.coords[0]
|
||||
latitude = obj.geo_obj.coords_kupsat.coords[1]
|
||||
lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W"
|
||||
lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S"
|
||||
kupsat_coords = f"{lat} {lon}"
|
||||
elif obj.geo_obj.coords_kupsat is not None:
|
||||
kupsat_coords = "-"
|
||||
|
||||
if obj.geo_obj.coords_valid:
|
||||
longitude = obj.geo_obj.coords_valid.coords[0]
|
||||
latitude = obj.geo_obj.coords_valid.coords[1]
|
||||
lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W"
|
||||
lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S"
|
||||
valid_coords = f"{lat} {lon}"
|
||||
elif obj.geo_obj.coords_valid is not None:
|
||||
valid_coords = "-"
|
||||
|
||||
if obj.geo_obj.distance_coords_kup is not None:
|
||||
distance_geo_kup = f"{obj.geo_obj.distance_coords_kup:.3f}"
|
||||
|
||||
if obj.geo_obj.distance_coords_valid is not None:
|
||||
distance_geo_valid = f"{obj.geo_obj.distance_coords_valid:.3f}"
|
||||
|
||||
if obj.geo_obj.distance_kup_valid is not None:
|
||||
distance_kup_valid = f"{obj.geo_obj.distance_kup_valid:.3f}"
|
||||
|
||||
satellite_name = "-"
|
||||
frequency = "-"
|
||||
freq_range = "-"
|
||||
|
||||
Reference in New Issue
Block a user