From 0e762310f2d21d07aaf77165aebd66b314bf6ba6 Mon Sep 17 00:00:00 2001 From: Tania Barone <34463965+tansb@users.noreply.github.com> Date: Fri, 29 May 2026 18:35:49 -0400 Subject: [PATCH] Add Visualise all-sky dashboard and select-all query endpoint New sled_visualise app: an interactive all-sky map of selected lenses (Aitoff projection, redshift scatter, survey/galactic overlays with a map-centre toggle). Adds a Visualise button on the query page and a query-all-ids endpoint so actions can target the full result set. --- lenses/templates/lenses/lens_list_query.html | 85 ++- lenses/templates/lenses/lens_query.html | 5 + lenses/urls.py | 1 + lenses/views.py | 29 +- mysite/settings_common.py | 1 + mysite/urls.py | 1 + run_dev_server/settings_development.py | 5 + sled_visualise/__init__.py | 0 sled_visualise/admin.py | 3 + sled_visualise/apps.py | 6 + sled_visualise/migrations/__init__.py | 0 sled_visualise/models.py | 3 + .../sled_visualise/css/lens_visualise.css | 519 +++++++++++++ .../sled_visualise/footprints/_coords.js | 84 +++ .../static/sled_visualise/footprints/des.js | 25 + .../sled_visualise/footprints/euclid.js | 40 + .../sled_visualise/footprints/galactic.js | 12 + .../static/sled_visualise/footprints/sdss.js | 47 ++ .../sled_visualise/js/lens_visualise.js | 708 ++++++++++++++++++ .../sled_visualise/lens_visualise.html | 116 +++ sled_visualise/tests.py | 3 + sled_visualise/urls.py | 7 + sled_visualise/views.py | 63 ++ 23 files changed, 1745 insertions(+), 18 deletions(-) create mode 100644 sled_visualise/__init__.py create mode 100644 sled_visualise/admin.py create mode 100644 sled_visualise/apps.py create mode 100644 sled_visualise/migrations/__init__.py create mode 100644 sled_visualise/models.py create mode 100644 sled_visualise/static/sled_visualise/css/lens_visualise.css create mode 100644 sled_visualise/static/sled_visualise/footprints/_coords.js create mode 100644 sled_visualise/static/sled_visualise/footprints/des.js create mode 100644 sled_visualise/static/sled_visualise/footprints/euclid.js create mode 100644 sled_visualise/static/sled_visualise/footprints/galactic.js create mode 100644 sled_visualise/static/sled_visualise/footprints/sdss.js create mode 100644 sled_visualise/static/sled_visualise/js/lens_visualise.js create mode 100644 sled_visualise/templates/sled_visualise/lens_visualise.html create mode 100644 sled_visualise/tests.py create mode 100644 sled_visualise/urls.py create mode 100644 sled_visualise/views.py diff --git a/lenses/templates/lenses/lens_list_query.html b/lenses/templates/lenses/lens_list_query.html index 370dc0eb..15cf41e5 100755 --- a/lenses/templates/lenses/lens_list_query.html +++ b/lenses/templates/lenses/lens_list_query.html @@ -35,27 +35,84 @@ + + + + +
+ + +
+
+ + + 4 +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + + + +
+
+ +
+ +
+
All-Sky Map
+ +
+
+
Loading lens data...
+
+
+ +
+
+
+ +

How to Use

+

Sky Map (center)

+
    +
  • Hover over any point to view system details
  • +
  • Scroll to zoom in/out, drag to pan
  • +
  • Use controls to change point size, color scheme, and axis labels
  • +
  • Click a point to pin the tooltip (click × to close)
  • +
  • Click a legend item to highlight that subset
  • +
  • Use Overlays to toggle approximate survey footprints (DES, SDSS, Euclid) and the Milky Way galactic plane
  • +
+

Redshift Plot (left)

+
    +
  • Drag to select a region to zoom into
  • +
  • Pan by dragging when zoomed in
  • +
  • Click "Reset" to restore the original view
  • +
+
+
+ + +
+
+
zsource vs zdeflector
+ + +
+
+
+
+
+
+
+ + {{ lens_data|json_script:"lens-data" }} + + + + + + + + diff --git a/sled_visualise/tests.py b/sled_visualise/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/sled_visualise/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/sled_visualise/urls.py b/sled_visualise/urls.py new file mode 100644 index 00000000..f20be389 --- /dev/null +++ b/sled_visualise/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +app_name = 'sled_visualise' +urlpatterns = [ + path('', views.LensVisualiseView.as_view(), name='lens-visualise'), +] diff --git a/sled_visualise/views.py b/sled_visualise/views.py new file mode 100644 index 00000000..f28d2613 --- /dev/null +++ b/sled_visualise/views.py @@ -0,0 +1,63 @@ +from collections import defaultdict + +from django.shortcuts import render +from django.urls import reverse +from django.template.response import TemplateResponse +from django.contrib.auth.decorators import login_required +from django.views.generic import ListView +from django.utils.decorators import method_decorator + +from lenses.models import Lenses, Redshift + + +@method_decorator(login_required, name='dispatch') +class LensVisualiseView(ListView): + model = Lenses + allow_empty = True + template_name = 'sled_visualise/lens_visualise.html' + + def get_queryset(self, ids): + return Lenses.accessible_objects.in_ids(self.request.user, ids) + + def build_data(self, lenses): + lens_ids = [lens.id for lens in lenses] + + # Collect the first LENS and SOURCE redshift value per lens in a single query + z_by_lens = defaultdict(dict) + redshifts = Redshift.accessible_objects.all(self.request.user).filter(lens_id__in=lens_ids) + for z in redshifts: + if z.value is not None and z.tag not in z_by_lens[z.lens_id]: + z_by_lens[z.lens_id][z.tag] = float(z.value) + + data = [] + for lens in lenses: + lens_types = list(lens.lens_type) if lens.lens_type else [] + source_types = list(lens.source_type) if lens.source_type else [] + lt = lens_types[0] if lens_types else '' + st = source_types[0] if source_types else '' + data.append({ + 'id': lens.id, + 'name': lens.name, + 'ra': float(lens.ra), + 'dec': float(lens.dec), + 'lens_z': z_by_lens.get(lens.id, {}).get('LENS'), + 'source_z': z_by_lens.get(lens.id, {}).get('SOURCE'), + 'lens_type': lt, + 'source_type': st, + 'system_type': ('%s-%s' % (lt, st)) if (lt or st) else 'Unknown', + 'flag': lens.flag, + 'detail_url': reverse('lenses:lens-detail', args=[lens.id]), + }) + return data + + def post(self, request, *args, **kwargs): + ids = [pk for pk in self.request.POST.getlist('ids') if pk.isdigit()] + if ids: + lenses = self.get_queryset(ids) + context = {'lens_data': self.build_data(lenses)} + return render(request, self.template_name, context) + else: + return TemplateResponse(request, 'simple_message.html', context={'message': 'No selected lenses to visualise.'}) + + def get(self, request, *args, **kwargs): + return TemplateResponse(request, 'simple_message.html', context={'message': 'You are accessing this page in an unauthorized way.'})