From 79673367f00d28d0590728329169231ca27de444 Mon Sep 17 00:00:00 2001 From: kdeterme Date: Sat, 8 Aug 2026 10:04:55 +0200 Subject: [PATCH] feat: implement asset preselection in quick inspection map with coordinate and filter auto-focus --- .../assets/static/assets/inspection_map.js | 49 +++++++++++- .../assets/components/asset_inspections.html | 57 ++++++++++++- .../assets/quick_inspection_map.html | 6 ++ streetup/assets/views/inspections.py | 79 +++++++++++++++++++ 4 files changed, 185 insertions(+), 6 deletions(-) diff --git a/streetup/assets/static/assets/inspection_map.js b/streetup/assets/static/assets/inspection_map.js index 3c39cef..910f267 100644 --- a/streetup/assets/static/assets/inspection_map.js +++ b/streetup/assets/static/assets/inspection_map.js @@ -22,9 +22,19 @@ if (mapContainer.dataset.mapInitialized === 'true') return; mapContainer.dataset.mapInitialized = 'true'; - // Centrage et style depuis les settings globaux (window.MAP_STYLE_LIGHT / window.DEFAULT_MAP_CENTER) - const defaultCenter = window.DEFAULT_MAP_CENTER || [4.3517, 50.8503]; - const defaultZoom = window.DEFAULT_MAP_ZOOM || 15; + // Centrage et style depuis les settings globaux ou pré-sélection d'asset + let defaultCenter = window.DEFAULT_MAP_CENTER || [4.3517, 50.8503]; + let defaultZoom = window.DEFAULT_MAP_ZOOM || 15; + + if (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined') { + const preLon = parseFloat(window.PRESELECT_LON); + const preLat = parseFloat(window.PRESELECT_LAT); + if (!isNaN(preLon) && !isNaN(preLat) && preLon >= -180 && preLon <= 180 && preLat >= -90 && preLat <= 90 && !(preLon === 0 && preLat === 0)) { + defaultCenter = [preLon, preLat]; + defaultZoom = 19; + } + } + const styleUrl = window.MAP_STYLE_LIGHT || 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json'; map = new maplibregl.Map({ @@ -304,6 +314,34 @@ map.getCanvas().style.cursor = ''; }); + // Prise en compte de la pré-sélection d'un asset (zoom, thématique, catégorie) + if (window.PRESELECT_THEMATIC_CODE) { + const tileBtn = document.querySelector(`.thematic-tile[data-thematic="${window.PRESELECT_THEMATIC_CODE}"]`); + if (tileBtn) { + document.querySelectorAll('.thematic-tile').forEach(t => t.classList.remove('active')); + tileBtn.classList.add('active'); + } + } + + if (window.PRESELECT_CATEGORY_ID) { + const catSelect = document.getElementById('filter-category-select'); + if (catSelect) { + catSelect.value = String(window.PRESELECT_CATEGORY_ID); + } + } + + if (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined') { + const lon = parseFloat(window.PRESELECT_LON); + const lat = parseFloat(window.PRESELECT_LAT); + if (!isNaN(lon) && !isNaN(lat) && lon >= -180 && lon <= 180 && lat >= -90 && lat <= 90 && !(lon === 0 && lat === 0)) { + map.flyTo({ + center: [lon, lat], + zoom: 19, + duration: 1200 + }); + } + } + // Charger les assets et l'historique d'inspections au premier affichage loadAssets(); loadInspectionsHistory(); @@ -383,7 +421,10 @@ gpsBtn.addEventListener('click', locateUser); } - locateUser(); + const hasPreselect = (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined'); + if (!hasPreselect) { + locateUser(); + } } function locateUser() { diff --git a/streetup/assets/templates/assets/components/asset_inspections.html b/streetup/assets/templates/assets/components/asset_inspections.html index 21a96ca..dd4f0d1 100644 --- a/streetup/assets/templates/assets/components/asset_inspections.html +++ b/streetup/assets/templates/assets/components/asset_inspections.html @@ -10,9 +10,18 @@ {% endif %} {% if can_inspect %} - + {% endif %}
@@ -149,3 +158,47 @@
{% endfor %} + +{% if can_inspect %} +{% include "assets/components/inspection_form_modal.html" %} + + +{% endif %} diff --git a/streetup/assets/templates/assets/quick_inspection_map.html b/streetup/assets/templates/assets/quick_inspection_map.html index 0947348..29204b3 100644 --- a/streetup/assets/templates/assets/quick_inspection_map.html +++ b/streetup/assets/templates/assets/quick_inspection_map.html @@ -436,6 +436,12 @@ {% endblock %} diff --git a/streetup/assets/views/inspections.py b/streetup/assets/views/inspections.py index 2a16b66..d44ff95 100644 --- a/streetup/assets/views/inspections.py +++ b/streetup/assets/views/inspections.py @@ -126,6 +126,54 @@ def inspections_list(request): return render(request, 'assets/inspections_list.html', context) +def _get_asset_wgs84_lon_lat(obj): + if hasattr(obj, 'lon') and hasattr(obj, 'lat') and obj.lon is not None and obj.lat is not None: + try: + lon_f = float(obj.lon) + lat_f = float(obj.lat) + if -180 <= lon_f <= 180 and -90 <= lat_f <= 90 and not (lon_f == 0 and lat_f == 0): + return lon_f, lat_f + except (ValueError, TypeError): + pass + + if hasattr(obj, 'geojson') and obj.geojson: + try: + import json + data = json.loads(obj.geojson) if isinstance(obj.geojson, str) else obj.geojson + geom_data = data.get('geometry', {}) if data.get('type') == 'Feature' else data + g_type = geom_data.get('type') + coords = geom_data.get('coordinates') + if g_type == 'Point' and coords: + return float(coords[0]), float(coords[1]) + elif g_type == 'Polygon' and coords and len(coords) > 0: + ring = coords[0] + avg_lon = sum(pt[0] for pt in ring) / len(ring) + avg_lat = sum(pt[1] for pt in ring) / len(ring) + return avg_lon, avg_lat + elif g_type == 'MultiPolygon' and coords and len(coords) > 0: + all_pts = [pt for poly in coords for ring in poly for pt in ring] + if all_pts: + avg_lon = sum(pt[0] for pt in all_pts) / len(all_pts) + avg_lat = sum(pt[1] for pt in all_pts) / len(all_pts) + return avg_lon, avg_lat + except Exception: + pass + + if hasattr(obj, 'geom') and obj.geom: + try: + g = obj.geom + srid = getattr(g, 'srid', None) or 3812 + from django.contrib.gis.geos import GEOSGeometry + g_copy = GEOSGeometry(g.wkt, srid=srid) + g_4326 = g_copy.transform(4326, clone=True) + centroid = g_4326.centroid + return float(centroid.x), float(centroid.y) + except Exception: + pass + + return None, None + + @login_required def quick_inspection_map(request): """ @@ -143,12 +191,43 @@ def quick_inspection_map(request): from assets.models import ParkingSpot parking_spot_ct_id = ContentType.objects.get_for_model(ParkingSpot).id + preselect_ct_id = request.GET.get('content_type_id') + preselect_obj_id = request.GET.get('object_id') + preselect_lon = None + preselect_lat = None + preselect_thematic_code = None + preselect_category_id = None + + if preselect_ct_id and preselect_obj_id: + try: + ct = ContentType.objects.get(pk=preselect_ct_id) + obj = ct.get_object_for_this_type(pk=preselect_obj_id) + if obj: + from assets.permissions import _get_asset_thematic + th = _get_asset_thematic(obj) + if th: + preselect_thematic_code = th.code + if hasattr(obj, 'category') and obj.category: + preselect_category_id = obj.category.id + + preselect_lon, preselect_lat = _get_asset_wgs84_lon_lat(obj) + except Exception: + pass + + import json + context = { 'base_template': base_template, 'is_mobile': is_mobile, 'thematics': thematics, 'categories': categories, 'parking_spot_ct_id': parking_spot_ct_id, + 'preselect_ct_id': preselect_ct_id or "null", + 'preselect_obj_id': preselect_obj_id or "null", + 'preselect_lon': json.dumps(preselect_lon) if preselect_lon is not None else "null", + 'preselect_lat': json.dumps(preselect_lat) if preselect_lat is not None else "null", + 'preselect_thematic_code': json.dumps(preselect_thematic_code) if preselect_thematic_code else "null", + 'preselect_category_id': preselect_category_id or "null", } return render(request, 'assets/quick_inspection_map.html', context)