Добавил плавную анимацию для нескольких источников

This commit is contained in:
2025-11-26 23:09:29 +03:00
parent cfaaae9360
commit d832171325
7 changed files with 1106 additions and 496 deletions

View File

@@ -26,6 +26,7 @@ from .api import (
SourceObjItemsAPIView,
LyngsatTaskStatusAPIView,
TransponderDataAPIView,
MultiSourcesPlaybackDataAPIView,
)
from .lyngsat import (
LinkLyngsatSourcesView,
@@ -53,6 +54,7 @@ from .map import (
ShowSourcesMapView,
ShowSourceWithPointsMapView,
ShowSourceAveragingStepsMapView,
MultiSourcesPlaybackMapView,
# ClusterTestView,
)
from .kubsat import (
@@ -93,6 +95,7 @@ __all__ = [
'SourceObjItemsAPIView',
'LyngsatTaskStatusAPIView',
'TransponderDataAPIView',
'MultiSourcesPlaybackDataAPIView',
# LyngSat
'LinkLyngsatSourcesView',
'FillLyngsatDataView',
@@ -122,6 +125,7 @@ __all__ = [
'ShowSourcesMapView',
'ShowSourceWithPointsMapView',
'ShowSourceAveragingStepsMapView',
'MultiSourcesPlaybackMapView',
# 'ClusterTestView',
# Kubsat
'KubsatView',

View File

@@ -622,3 +622,103 @@ class SatelliteDataAPIView(LoginRequiredMixin, View):
return JsonResponse({'error': 'Спутник не найден'}, status=404)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
class MultiSourcesPlaybackDataAPIView(LoginRequiredMixin, View):
"""API endpoint for getting playback data for multiple sources."""
def get(self, request):
from ..models import Source
ids = request.GET.get('ids', '')
if not ids:
return JsonResponse({'error': 'Не указаны ID источников'}, status=400)
try:
id_list = [int(x) for x in ids.split(',') if x.isdigit()]
if not id_list:
return JsonResponse({'error': 'Некорректные ID источников'}, status=400)
sources = Source.objects.filter(id__in=id_list).prefetch_related(
'source_objitems',
'source_objitems__parameter_obj',
'source_objitems__geo_obj',
)
# Collect data for each source
sources_data = []
global_min_time = None
global_max_time = None
# Define colors for different sources
colors = ['red', 'blue', 'green', 'purple', 'orange', 'cyan', 'magenta', 'yellow', 'lime', 'pink']
for idx, source in enumerate(sources):
# Get all ObjItems with geo data and timestamp
objitems = source.source_objitems.filter(
geo_obj__isnull=False,
geo_obj__coords__isnull=False,
geo_obj__timestamp__isnull=False
).select_related('geo_obj', 'parameter_obj').order_by('geo_obj__timestamp')
points = []
for objitem in objitems:
geo = objitem.geo_obj
param = getattr(objitem, 'parameter_obj', None)
timestamp = geo.timestamp
# Update global min/max time
if global_min_time is None or timestamp < global_min_time:
global_min_time = timestamp
if global_max_time is None or timestamp > global_max_time:
global_max_time = timestamp
freq_str = '-'
if param and param.frequency:
freq_str = f"{param.frequency} МГц"
points.append({
'lat': geo.coords.y,
'lng': geo.coords.x,
'timestamp': timestamp.isoformat(),
'timestamp_ms': int(timestamp.timestamp() * 1000),
'name': objitem.name or f'Точка #{objitem.id}',
'frequency': freq_str,
'location': geo.location or '-',
})
if points:
# Get source name from first objitem or use ID
source_name = f"Объект #{source.id}"
if source.source_objitems.exists():
first_objitem = source.source_objitems.first()
if first_objitem and first_objitem.name:
# Extract base name (without frequency info)
source_name = first_objitem.name.split(' ')[0] if first_objitem.name else source_name
sources_data.append({
'source_id': source.id,
'source_name': source_name,
'color': colors[idx % len(colors)],
'points': points,
'points_count': len(points),
})
# Format global time range
time_range = None
if global_min_time and global_max_time:
time_range = {
'min': global_min_time.isoformat(),
'max': global_max_time.isoformat(),
'min_ms': int(global_min_time.timestamp() * 1000),
'max_ms': int(global_max_time.timestamp() * 1000),
}
return JsonResponse({
'sources': sources_data,
'time_range': time_range,
'total_sources': len(sources_data),
})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)

View File

@@ -422,6 +422,28 @@ class ShowSourceAveragingStepsMapView(LoginRequiredMixin, View):
return render(request, "mainapp/source_averaging_map.html", context)
class MultiSourcesPlaybackMapView(LoginRequiredMixin, View):
"""View for displaying animated playback of multiple sources on map."""
def get(self, request):
from ..models import Source
ids = request.GET.get("ids", "")
if not ids:
return redirect("mainapp:source_list")
id_list = [int(x) for x in ids.split(",") if x.isdigit()]
if not id_list:
return redirect("mainapp:source_list")
context = {
"source_ids": ",".join(str(x) for x in id_list),
"source_count": len(id_list),
}
return render(request, "mainapp/multi_sources_playback_map.html", context)
# class ClusterTestView(LoginRequiredMixin, View):
# """Test view for clustering functionality."""