Улучшение и добавления
This commit is contained in:
@@ -51,6 +51,45 @@ def get_all_constants():
|
||||
return sats, standards, pols, mirrors, modulations
|
||||
|
||||
|
||||
def find_mirror_satellites(mirror_names: list) -> list:
|
||||
"""
|
||||
Находит спутники, которые соответствуют именам зеркал.
|
||||
|
||||
Алгоритм:
|
||||
1. Для каждого имени зеркала:
|
||||
- Обрезать пробелы и привести к нижнему регистру
|
||||
- Найти все спутники, в имени которых содержится это имя
|
||||
2. Вернуть список найденных спутников
|
||||
|
||||
Args:
|
||||
mirror_names: список имен зеркал
|
||||
|
||||
Returns:
|
||||
list: список объектов Satellite
|
||||
"""
|
||||
found_satellites = []
|
||||
|
||||
for mirror_name in mirror_names:
|
||||
if not mirror_name or mirror_name == "-":
|
||||
continue
|
||||
|
||||
# Обрезаем пробелы и приводим к нижнему регистру
|
||||
mirror_name_clean = mirror_name.strip().lower()
|
||||
|
||||
if not mirror_name_clean:
|
||||
continue
|
||||
|
||||
# Ищем спутники, в имени которых содержится имя зеркала
|
||||
satellites = Satellite.objects.filter(
|
||||
name__icontains=mirror_name_clean
|
||||
)
|
||||
|
||||
found_satellites.extend(satellites)
|
||||
|
||||
# Убираем дубликаты
|
||||
return list(set(found_satellites))
|
||||
|
||||
|
||||
def coords_transform(coords: str):
|
||||
lat_part, lon_part = coords.strip().split()
|
||||
sign_map = {"N": 1, "E": 1, "S": -1, "W": -1}
|
||||
@@ -83,24 +122,31 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite, current_user=None):
|
||||
"""
|
||||
Импортирует данные из DataFrame с группировкой близких координат.
|
||||
|
||||
Алгоритм:
|
||||
Улучшенный алгоритм с учетом существующих Source:
|
||||
1. Извлечь все координаты и данные строк из DataFrame
|
||||
2. Создать список необработанных записей (координата + данные строки)
|
||||
3. Пока список не пуст:
|
||||
3. Получить все существующие Source из БД
|
||||
4. Для каждой необработанной записи:
|
||||
a. Найти ближайший существующий Source (расстояние <= 56 км)
|
||||
b. Если найден:
|
||||
- Обновить coords_average этого Source (инкрементально)
|
||||
- Создать ObjItem и связать с этим Source
|
||||
- Удалить запись из списка необработанных
|
||||
5. Пока список необработанных записей не пуст:
|
||||
a. Взять первую запись из списка
|
||||
b. Создать новый Source с coords_average = эта координата
|
||||
c. Создать ObjItem для этой записи и связать с Source
|
||||
d. Удалить запись из списка
|
||||
e. Для каждой оставшейся записи в списке:
|
||||
- Вычислить расстояние от её координаты до coords_average
|
||||
- Если расстояние <= 0.5 градуса:
|
||||
- Если расстояние <= 56 км:
|
||||
* Вычислить новое среднее ИНКРЕМЕНТАЛЬНО:
|
||||
new_avg = (coords_average + current_coord) / 2
|
||||
* Обновить coords_average в Source
|
||||
* Создать ObjItem для этой записи и связать с Source
|
||||
* Удалить запись из списка
|
||||
- Иначе: пропустить и проверить следующую запись
|
||||
4. Сохранить все изменения в БД
|
||||
6. Сохранить все изменения в БД
|
||||
|
||||
Важно: Среднее вычисляется инкрементально - каждая новая точка
|
||||
усредняется с текущим средним, а не со всеми точками кластера.
|
||||
@@ -137,23 +183,66 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite, current_user=None):
|
||||
|
||||
user_to_use = current_user if current_user else CustomUser.objects.get(id=1)
|
||||
source_count = 0
|
||||
added_to_existing_count = 0
|
||||
|
||||
# Шаг 3: Цикл обработки пока список не пуст
|
||||
# Шаг 3: Получить все существующие Source из БД
|
||||
existing_sources = list(Source.objects.filter(coords_average__isnull=False))
|
||||
|
||||
# Шаг 4: Попытка добавить записи к существующим Source
|
||||
records_to_remove = []
|
||||
|
||||
for i, record in enumerate(unprocessed_records):
|
||||
current_coord = record["coord"]
|
||||
|
||||
# Найти ближайший существующий Source
|
||||
closest_source = None
|
||||
min_distance = float('inf')
|
||||
best_new_avg = None
|
||||
|
||||
for source in existing_sources:
|
||||
source_coord = (source.coords_average.x, source.coords_average.y)
|
||||
new_avg, distance = calculate_mean_coords(source_coord, current_coord)
|
||||
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
closest_source = source
|
||||
best_new_avg = new_avg
|
||||
|
||||
# Если найден близкий Source (расстояние <= 56 км)
|
||||
if closest_source and min_distance <= RANGE_DISTANCE:
|
||||
# Обновить coords_average инкрементально
|
||||
closest_source.coords_average = Point(best_new_avg, srid=4326)
|
||||
closest_source.save()
|
||||
|
||||
# Создать ObjItem и связать с существующим Source
|
||||
_create_objitem_from_row(
|
||||
record["row"], sat, closest_source, user_to_use, consts
|
||||
)
|
||||
added_to_existing_count += 1
|
||||
|
||||
# Пометить запись для удаления
|
||||
records_to_remove.append(i)
|
||||
|
||||
# Удалить обработанные записи из списка (в обратном порядке, чтобы не сбить индексы)
|
||||
for i in reversed(records_to_remove):
|
||||
unprocessed_records.pop(i)
|
||||
|
||||
# Шаг 5: Цикл обработки оставшихся записей - создание новых Source
|
||||
while unprocessed_records:
|
||||
# Шаг 3a: Взять первую запись из списка
|
||||
# Шаг 5a: Взять первую запись из списка
|
||||
first_record = unprocessed_records.pop(0)
|
||||
first_coord = first_record["coord"]
|
||||
|
||||
# Шаг 3b: Создать новый Source с coords_average = эта координата
|
||||
# Шаг 5b: Создать новый Source с coords_average = эта координата
|
||||
source = Source.objects.create(
|
||||
coords_average=Point(first_coord, srid=4326), created_by=user_to_use
|
||||
)
|
||||
source_count += 1
|
||||
|
||||
# Шаг 3c: Создать ObjItem для этой записи и связать с Source
|
||||
# Шаг 5c: Создать ObjItem для этой записи и связать с Source
|
||||
_create_objitem_from_row(first_record["row"], sat, source, user_to_use, consts)
|
||||
|
||||
# Шаг 3e: Для каждой оставшейся записи в списке
|
||||
# Шаг 5e: Для каждой оставшейся записи в списке
|
||||
records_to_remove = []
|
||||
|
||||
for i, record in enumerate(unprocessed_records):
|
||||
@@ -180,6 +269,9 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite, current_user=None):
|
||||
for i in reversed(records_to_remove):
|
||||
unprocessed_records.pop(i)
|
||||
|
||||
print(f"Импорт завершен: создано {source_count} новых источников, "
|
||||
f"добавлено {added_to_existing_count} точек к существующим источникам")
|
||||
|
||||
return source_count
|
||||
|
||||
|
||||
@@ -225,26 +317,22 @@ def _create_objitem_from_row(row, sat, source, user_to_use, consts):
|
||||
time_ = time(0, 0, 0)
|
||||
timestamp = datetime.combine(date, time_)
|
||||
|
||||
# Обработка зеркал
|
||||
current_mirrors = []
|
||||
# Обработка зеркал - теперь это спутники
|
||||
mirror_names = []
|
||||
mirror_1 = row["Зеркало 1"].strip().split("\n")
|
||||
mirror_2 = row["Зеркало 2"].strip().split("\n")
|
||||
|
||||
if len(mirror_1) > 1:
|
||||
for mir in mirror_1:
|
||||
Mirror.objects.get_or_create(name=mir.strip())
|
||||
current_mirrors.append(mir.strip())
|
||||
elif mirror_1[0] not in consts[3]:
|
||||
Mirror.objects.get_or_create(name=mirror_1[0].strip())
|
||||
current_mirrors.append(mirror_1[0].strip())
|
||||
# Собираем все имена зеркал
|
||||
for mir in mirror_1:
|
||||
if mir.strip() and mir.strip() != "-":
|
||||
mirror_names.append(mir.strip())
|
||||
|
||||
for mir in mirror_2:
|
||||
if mir.strip() and mir.strip() != "-":
|
||||
mirror_names.append(mir.strip())
|
||||
|
||||
if len(mirror_2) > 1:
|
||||
for mir in mirror_2:
|
||||
Mirror.objects.get_or_create(name=mir.strip())
|
||||
current_mirrors.append(mir.strip())
|
||||
elif mirror_2[0] not in consts[3]:
|
||||
Mirror.objects.get_or_create(name=mirror_2[0].strip())
|
||||
current_mirrors.append(mirror_2[0].strip())
|
||||
# Находим спутники-зеркала
|
||||
mirror_satellites = find_mirror_satellites(mirror_names)
|
||||
|
||||
location = row["Местоопределение"].strip()
|
||||
comment = row["Комментарий"]
|
||||
@@ -260,7 +348,10 @@ def _create_objitem_from_row(row, sat, source, user_to_use, consts):
|
||||
},
|
||||
)
|
||||
geo.save()
|
||||
geo.mirrors.set(Mirror.objects.filter(name__in=current_mirrors))
|
||||
|
||||
# Устанавливаем связи с спутниками-зеркалами
|
||||
if mirror_satellites:
|
||||
geo.mirrors.set(mirror_satellites)
|
||||
|
||||
# Проверяем, существует ли уже ObjItem с таким же geo
|
||||
existing_obj_item = ObjItem.objects.filter(geo_obj=geo).first()
|
||||
@@ -380,7 +471,7 @@ def get_points_from_csv(file_content, current_user=None):
|
||||
4. Для каждой записи:
|
||||
a. Проверить, существует ли дубликат (координаты + частота)
|
||||
b. Если дубликат найден, пропустить запись
|
||||
c. Найти ближайший существующий Source (расстояние <= 0.5 градуса)
|
||||
c. Найти ближайший существующий Source (расстояние <= 56 км)
|
||||
d. Если найден:
|
||||
- Обновить coords_average этого Source (инкрементально)
|
||||
- Создать ObjItem и связать с этим Source
|
||||
@@ -460,6 +551,7 @@ def get_points_from_csv(file_content, current_user=None):
|
||||
# Шаг 4c: Найти ближайший существующий Source
|
||||
closest_source = None
|
||||
min_distance = float('inf')
|
||||
best_new_avg = None
|
||||
|
||||
for source in existing_sources:
|
||||
source_coord = (source.coords_average.x, source.coords_average.y)
|
||||
@@ -468,13 +560,12 @@ def get_points_from_csv(file_content, current_user=None):
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
closest_source = source
|
||||
best_new_avg = new_avg
|
||||
|
||||
# Шаг 4d: Если найден близкий Source (расстояние <= 0.5 градуса)
|
||||
if closest_source and min_distance <= 0.5:
|
||||
# Шаг 4d: Если найден близкий Source (расстояние <= 56 км)
|
||||
if closest_source and min_distance <= RANGE_DISTANCE:
|
||||
# Обновить coords_average инкрементально
|
||||
current_avg = (closest_source.coords_average.x, closest_source.coords_average.y)
|
||||
# new_avg = calculate_average_coords_incremental(current_avg, current_coord)
|
||||
closest_source.coords_average = Point(new_avg, srid=4326)
|
||||
closest_source.coords_average = Point(best_new_avg, srid=4326)
|
||||
closest_source.save()
|
||||
|
||||
# Создать ObjItem и связать с существующим Source
|
||||
@@ -565,14 +656,20 @@ def _create_objitem_from_csv_row(row, source, user_to_use):
|
||||
sat_obj, _ = Satellite.objects.get_or_create(
|
||||
name=row["sat"], defaults={"norad": row["norad_id"]}
|
||||
)
|
||||
mir_1_obj, _ = Mirror.objects.get_or_create(name=row["mir_1"])
|
||||
mir_2_obj, _ = Mirror.objects.get_or_create(name=row["mir_2"])
|
||||
mir_lst = [row["mir_1"], row["mir_2"]]
|
||||
if not pd.isna(row["mir_3"]):
|
||||
mir_3_obj, _ = Mirror.objects.get_or_create(name=row["mir_3"])
|
||||
mir_lst.append(row["mir_3"])
|
||||
|
||||
# Обработка зеркал - теперь это спутники
|
||||
mirror_names = []
|
||||
if not pd.isna(row["mir_1"]) and row["mir_1"].strip() != "-":
|
||||
mirror_names.append(row["mir_1"])
|
||||
if not pd.isna(row["mir_2"]) and row["mir_2"].strip() != "-":
|
||||
mirror_names.append(row["mir_2"])
|
||||
if not pd.isna(row["mir_3"]) and row["mir_3"].strip() != "-":
|
||||
mirror_names.append(row["mir_3"])
|
||||
|
||||
# Находим спутники-зеркала
|
||||
mirror_satellites = find_mirror_satellites(mirror_names)
|
||||
|
||||
# Создаем Geo объект (БЕЗ coords_kupsat и coords_valid)
|
||||
# Создаем Geo объект
|
||||
geo_obj, _ = Geo.objects.get_or_create(
|
||||
timestamp=row["time"],
|
||||
coords=Point(row["lon"], row["lat"], srid=4326),
|
||||
@@ -580,7 +677,10 @@ def _create_objitem_from_csv_row(row, source, user_to_use):
|
||||
"is_average": False,
|
||||
},
|
||||
)
|
||||
geo_obj.mirrors.set(Mirror.objects.filter(name__in=mir_lst))
|
||||
|
||||
# Устанавливаем связи с спутниками-зеркалами
|
||||
if mirror_satellites:
|
||||
geo_obj.mirrors.set(mirror_satellites)
|
||||
|
||||
# Проверяем, существует ли уже ObjItem с таким же geo
|
||||
existing_obj_item = ObjItem.objects.filter(geo_obj=geo_obj).first()
|
||||
|
||||
Reference in New Issue
Block a user