From 1e1b1d1f3f394c8421508848bd2f46995e284a4b Mon Sep 17 00:00:00 2001
From: Helge Jung <hej@c3pb.de>
Date: Sun, 22 Dec 2024 01:53:19 +0100
Subject: [PATCH] assemblyteam positioning: show all current assemblies in edit
 view

---
 src/backoffice/urls.py                      |  1 +
 src/backoffice/views/assemblies/__init__.py |  4 ++
 src/backoffice/views/assemblies/map.py      | 51 +++++++++++++++++++++
 src/core/templates/core/map.html            | 44 +++++++++++++++++-
 4 files changed, 99 insertions(+), 1 deletion(-)
 create mode 100644 src/backoffice/views/assemblies/map.py

diff --git a/src/backoffice/urls.py b/src/backoffice/urls.py
index b3ceb9af6..c7f1201d8 100644
--- a/src/backoffice/urls.py
+++ b/src/backoffice/urls.py
@@ -62,6 +62,7 @@ urlpatterns = [
     path('wiki/page/<uuid:pk>/delete', wiki.PageDeleteView.as_view(), name='wiki-page-delete'),
     path('wiki/page/<uuid:pk>/delete-revision', wiki.PageRevisionDeleteView.as_view(), name='wiki-page-revision-delete'),
     path('assemblies', assemblyteam.AssembliesView.as_view(), name='assemblies'),
+    path('assemblies/floor.geojson', assemblies.AssemblyGeoJsonExportView.as_view(), name='assemblies-floor-geojson'),
     path('assemblies/list/<str:variant>', assemblyteam.AssembliesListsView.as_view(), name='assemblieslist'),
     path('assemblyteam/<uuid:pk>', assemblyteam.AssemblyView.as_view(), name='assemblyteam-detail'),
     path('assemblyteam/<uuid:pk>/state', assemblyteam.AssemblyEditStateView.as_view(), name='assemblyteam-editstate'),
diff --git a/src/backoffice/views/assemblies/__init__.py b/src/backoffice/views/assemblies/__init__.py
index 3cc45e5da..718271192 100644
--- a/src/backoffice/views/assemblies/__init__.py
+++ b/src/backoffice/views/assemblies/__init__.py
@@ -13,6 +13,9 @@ from backoffice.views.assemblies.auth import (
     AuthView,
     VouchersView,
 )
+from backoffice.views.assemblies.map import (
+    AssemblyGeoJsonExportView,
+)
 from backoffice.views.assemblies.members import (
     MemberCreateView,
     MemberListView,
@@ -31,6 +34,7 @@ __all__ = [
     'AssemblyChildrenUpdateView',
     'AssemblyCreateView',
     'AssemblyDetailView',
+    'AssemblyGeoJsonExportView',
     'AssemblyLinksUpdateView',
     'AssemblyListView',
     'AssemblyParentLeaveView',
diff --git a/src/backoffice/views/assemblies/map.py b/src/backoffice/views/assemblies/map.py
new file mode 100644
index 000000000..27f563ae6
--- /dev/null
+++ b/src/backoffice/views/assemblies/map.py
@@ -0,0 +1,51 @@
+from django.http import HttpResponseBadRequest, JsonResponse
+from django.views import View
+
+from core.models import Assembly
+
+from backoffice.views.mixins import ConferenceLoginRequiredMixin
+
+
+def flip_coordinates(coordinates):
+    """Flips the coordinates' tuples' items as latitude/longitude in GeoJSON represents to Y/X instead of X/Y."""
+    result = []
+    for polygon in coordinates:
+        p = []
+        for coord in polygon:
+            p.append((coord[1], coord[0]))
+        result.append(p)
+    return result
+
+
+class AssemblyGeoJsonExportView(ConferenceLoginRequiredMixin, View):
+    def get(self, request):
+        floor = request.GET.get('floor')
+        if floor is None:
+            return HttpResponseBadRequest('Missing "floor" parameter.')
+
+        features = []
+
+        qs = Assembly.objects.conference_accessible(self.conference, self.conferencemember).filter(location_floor__index=floor)
+        qs = qs.exclude(location_data__point=None, location_data__boundaries=None)
+
+        for assembly in qs:
+            if coords := assembly.location_data.get('boundaries'):
+                feature = {
+                    'type': 'Feature',
+                    'geometry': {
+                        'type': 'Polygon',
+                        'coordinates': flip_coordinates(coords),
+                    },
+                    'properties': {
+                        'name': assembly.name,
+                        'slug': assembly.slug,
+                        'published': assembly.is_placed,
+                    },
+                }
+                features.append(feature)
+
+        result = {
+            'type': 'FeatureCollection',
+            'features': features,
+        }
+        return JsonResponse(result, content_type='application/vnd.geo+json', json_dumps_params={'indent': 2})
diff --git a/src/core/templates/core/map.html b/src/core/templates/core/map.html
index 8b976f4f8..0d2b5aa1d 100644
--- a/src/core/templates/core/map.html
+++ b/src/core/templates/core/map.html
@@ -45,7 +45,15 @@
           "{{ floor.name }}": floor{{ floor.code_slug }},
         {% endfor %}
       };
-      var overlays = {};
+
+      var assemblies = L.geoJSON().addTo(map);
+
+      var overlays = {
+        assemblies,
+      };
+
+      loadAssemblies(initial_floor_idx);
+
       const layerOptions = {
         collapsed: false,
         hideSingleBase: true,
@@ -123,10 +131,44 @@
         }
       {% endif %}
 
+      function styleAssemblies(feature) {
+        return {color: 'red'};
+      }
+
+      function handleAssemblyFeature (feature, layer) {
+        layer.bindTooltip('<b>' + feature.properties.slug + '</b><br>' + feature.properties.name, {permanent: false, opacity: 0.75})
+      }
+
+      function loadAssemblies(activeFloor) {
+        map.removeLayer(assemblies);
+        //overlays = overlays.filter(item => item !== assemblies);  // overlays.remove(assemblies);
+
+        var url = "{% url "backoffice:assemblies-floor-geojson" %}?floor=" + activeFloor
+        fetch(url).then((response) => {
+          if (!response.ok) {
+            throw new Error(`HTTP error! Status: ${response.status}`);
+          }
+          return response.json();
+        })
+          .then((data) => {
+            console.log('got GeoJSON for assemblies on floor', activeFloor, '=', data);
+            assemblies = L.geoJSON(data, {
+              onEachFeature: handleAssemblyFeature,
+              style: styleAssemblies,
+            }).addTo(map);
+            console.log('new assemblies layer:', assemblies);
+            overlays.push(assemblies);
+
+            boundariesLayer.bringToFront();
+            poiLayer.bringToFront();
+          });
+      }
+
       function updateFloor(idx) {
         activeFloor = idx;
         const floor_elem = document.getElementById('{{ floor_id }}');
         floor_elem.value = idx != null ? idx.toString() : "";
+        loadAssemblies(activeFloor);
       }
 
       function updatePoiPosition(latLng) {
-- 
GitLab