Добавил геофильтры. Теперь нужен рефакторинг.
This commit is contained in:
@@ -18,6 +18,7 @@ from .data_import import (
|
||||
ProcessKubsatView,
|
||||
)
|
||||
from .api import (
|
||||
GeoPointsAPIView,
|
||||
GetLocationsView,
|
||||
LyngsatDataAPIView,
|
||||
SatelliteDataAPIView,
|
||||
@@ -70,6 +71,7 @@ __all__ = [
|
||||
'LinkVchSigmaView',
|
||||
'ProcessKubsatView',
|
||||
# API
|
||||
'GeoPointsAPIView',
|
||||
'GetLocationsView',
|
||||
'LyngsatDataAPIView',
|
||||
'SatelliteDataAPIView',
|
||||
|
||||
@@ -472,6 +472,52 @@ class TransponderDataAPIView(LoginRequiredMixin, View):
|
||||
return JsonResponse({'error': str(e)}, status=500)
|
||||
|
||||
|
||||
class GeoPointsAPIView(LoginRequiredMixin, View):
|
||||
"""API endpoint for getting all geo points for polygon filter visualization."""
|
||||
|
||||
def get(self, request):
|
||||
from ..models import Geo
|
||||
|
||||
try:
|
||||
# Limit to reasonable number of points to avoid performance issues
|
||||
limit = int(request.GET.get('limit', 10000))
|
||||
limit = min(limit, 50000) # Max 50k points
|
||||
|
||||
# Get all Geo objects with coordinates
|
||||
geo_objs = Geo.objects.filter(
|
||||
coords__isnull=False
|
||||
).select_related(
|
||||
'objitem',
|
||||
'objitem__source'
|
||||
)[:limit]
|
||||
|
||||
points = []
|
||||
for geo_obj in geo_objs:
|
||||
if not geo_obj.coords:
|
||||
continue
|
||||
|
||||
# Get source_id if available
|
||||
source_id = None
|
||||
if hasattr(geo_obj, 'objitem') and geo_obj.objitem:
|
||||
if hasattr(geo_obj.objitem, 'source') and geo_obj.objitem.source:
|
||||
source_id = geo_obj.objitem.source.id
|
||||
|
||||
points.append({
|
||||
'id': geo_obj.id,
|
||||
'lat': geo_obj.coords.y,
|
||||
'lng': geo_obj.coords.x,
|
||||
'source_id': source_id or '-'
|
||||
})
|
||||
|
||||
return JsonResponse({
|
||||
'points': points,
|
||||
'total': len(points),
|
||||
'limited': len(points) >= limit
|
||||
})
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': str(e)}, status=500)
|
||||
|
||||
|
||||
class SatelliteDataAPIView(LoginRequiredMixin, View):
|
||||
"""API endpoint for getting Satellite data."""
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ class ShowSourcesMapView(LoginRequiredMixin, View):
|
||||
"""View for displaying selected sources on map."""
|
||||
|
||||
def get(self, request):
|
||||
import json
|
||||
from ..models import Source
|
||||
|
||||
ids = request.GET.get("ids", "")
|
||||
@@ -168,8 +169,18 @@ class ShowSourcesMapView(LoginRequiredMixin, View):
|
||||
else:
|
||||
return redirect("mainapp:home")
|
||||
|
||||
# Get polygon filter from URL if present
|
||||
polygon_coords_str = request.GET.get("polygon", "").strip()
|
||||
polygon_coords = None
|
||||
if polygon_coords_str:
|
||||
try:
|
||||
polygon_coords = json.loads(polygon_coords_str)
|
||||
except (json.JSONDecodeError, ValueError, TypeError):
|
||||
polygon_coords = None
|
||||
|
||||
context = {
|
||||
"groups": groups,
|
||||
"polygon_coords": json.dumps(polygon_coords) if polygon_coords else None,
|
||||
}
|
||||
return render(request, "mainapp/source_map.html", context)
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"""
|
||||
Source related views.
|
||||
"""
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.contrib.gis.geos import Point, Polygon as GEOSPolygon
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Count, Q
|
||||
from django.http import JsonResponse
|
||||
@@ -62,6 +64,23 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
snr_min = request.GET.get("snr_min", "").strip()
|
||||
snr_max = request.GET.get("snr_max", "").strip()
|
||||
|
||||
# Get polygon filter
|
||||
polygon_coords_str = request.GET.get("polygon", "").strip()
|
||||
polygon_coords = None
|
||||
polygon_geom = None
|
||||
|
||||
if polygon_coords_str:
|
||||
try:
|
||||
polygon_coords = json.loads(polygon_coords_str)
|
||||
if polygon_coords and len(polygon_coords) >= 4:
|
||||
# Create GEOS Polygon from coordinates
|
||||
# Coordinates are in [lng, lat] format
|
||||
polygon_geom = GEOSPolygon(polygon_coords, srid=4326)
|
||||
except (json.JSONDecodeError, ValueError, TypeError) as e:
|
||||
# Invalid polygon data, ignore
|
||||
polygon_coords = None
|
||||
polygon_geom = None
|
||||
|
||||
# Get all satellites for filter
|
||||
satellites = (
|
||||
Satellite.objects.filter(parameters__objitem__source__isnull=False)
|
||||
@@ -210,6 +229,11 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
objitem_filter_q &= Q(source_objitems__geo_obj__mirrors__id__in=selected_mirrors)
|
||||
has_objitem_filter = True
|
||||
|
||||
# Add polygon filter
|
||||
if polygon_geom:
|
||||
objitem_filter_q &= Q(source_objitems__geo_obj__coords__within=polygon_geom)
|
||||
has_objitem_filter = True
|
||||
|
||||
# Get all Source objects with query optimization
|
||||
# Using annotate to count ObjItems efficiently (single query with GROUP BY)
|
||||
# Using prefetch_related for reverse ForeignKey relationships to avoid N+1 queries
|
||||
@@ -443,6 +467,12 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
source_objitems__geo_obj__mirrors__id__in=selected_mirrors
|
||||
).distinct()
|
||||
|
||||
# Filter by polygon
|
||||
if polygon_geom:
|
||||
sources = sources.filter(
|
||||
source_objitems__geo_obj__coords__within=polygon_geom
|
||||
).distinct()
|
||||
|
||||
# Apply sorting
|
||||
valid_sort_fields = {
|
||||
"id": "id",
|
||||
@@ -540,6 +570,8 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
objitems_to_display = objitems_to_display.filter(geo_obj__mirrors__id__in=selected_mirrors)
|
||||
if search_by_name:
|
||||
objitems_to_display = objitems_to_display.filter(name__icontains=search_query)
|
||||
if polygon_geom:
|
||||
objitems_to_display = objitems_to_display.filter(geo_obj__coords__within=polygon_geom)
|
||||
|
||||
# Use annotated count (consistent with filtering)
|
||||
objitem_count = source.objitem_count
|
||||
@@ -652,6 +684,7 @@ class SourceListView(LoginRequiredMixin, View):
|
||||
int(x) if isinstance(x, str) else x for x in selected_mirrors if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
|
||||
],
|
||||
'object_infos': object_infos,
|
||||
'polygon_coords': json.dumps(polygon_coords) if polygon_coords else None,
|
||||
'full_width_page': True,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user