diff --git a/loko/assets/static/assets/unified_index.js b/loko/assets/static/assets/unified_index.js index c2b8ade..2218f18 100644 --- a/loko/assets/static/assets/unified_index.js +++ b/loko/assets/static/assets/unified_index.js @@ -748,6 +748,14 @@ function applyInitialPreferences() { window._restoredMapCenter = prefs.map_center; window._restoredMapZoom = prefs.map_zoom; } + + // Basemap preference + if (prefs.basemap) { + window._restoredBasemap = prefs.basemap; + try { + localStorage.setItem('loko_active_basemap', prefs.basemap); + } catch (e) {} + } } else if (DEFAULT_THEMATIC_CODE && DEFAULT_THEMATIC_CODE in THEMATIC_CONFIG) { // First visit: pre-select the user's default thematic activeThematics = new Set([DEFAULT_THEMATIC_CODE]); @@ -775,6 +783,9 @@ function savePreferences() { ).map(cb => cb.value); const includeNoGeomCheckbox = document.getElementById('include-no-geom'); const includeNoGeom = includeNoGeomCheckbox ? includeNoGeomCheckbox.checked : true; + const activeBasemap = (window.unifiedLayerSwitcher && window.unifiedLayerSwitcher.activeBasemapId) + || localStorage.getItem('loko_active_basemap') + || 'default'; const prefs = { thematics: Array.from(activeThematics), categories: Array.from(activeCategories), @@ -787,6 +798,7 @@ function savePreferences() { include_no_geom: includeNoGeom, map_center: (typeof map !== 'undefined' && map.getCenter) ? [map.getCenter().lng, map.getCenter().lat] : null, map_zoom: (typeof map !== 'undefined' && map.getZoom) ? map.getZoom() : null, + basemap: activeBasemap, }; fetch(SAVE_ASSETS_PREFS_URL, { method: 'POST', @@ -2929,7 +2941,12 @@ maploader.addEventListener('mapLoaded', () => { // Layer switcher — Urbis & Vlaanderen orthos (top-right, topmost) if (typeof LayerSwitcherControl !== 'undefined') { + const initialBasemap = window._restoredBasemap || localStorage.getItem('loko_active_basemap') || 'default'; const layerSwitcher = new LayerSwitcherControl({ + initialBasemapId: initialBasemap, + onBasemapChange: function (basemapId) { + schedulePreferencesSave(); + }, layers: [{ id: 'basemap', name: 'Fonds de plan & Orthophotos', @@ -2937,7 +2954,10 @@ maploader.addEventListener('mapLoaded', () => { beforeLayer: (map && map.getLayer('territory-mask') ? 'territory-mask' : 'locations'), basemaps: window.MAP_BACKGROUNDS || [], }], - });// Reset view — fly back to initial Brussels Region view + }); + window.unifiedLayerSwitcher = layerSwitcher; + + // Reset view — fly back to initial Brussels Region view map.addControl({ onAdd(m) { this._map = m; diff --git a/loko/assets/templates/assets/unified_index.html b/loko/assets/templates/assets/unified_index.html index e7dea0c..ebe2085 100644 --- a/loko/assets/templates/assets/unified_index.html +++ b/loko/assets/templates/assets/unified_index.html @@ -1315,7 +1315,7 @@ {% block script %} - + - + {% endblock script %} \ No newline at end of file diff --git a/loko/common/locale/en/LC_MESSAGES/django.mo b/loko/common/locale/en/LC_MESSAGES/django.mo index 7eb40fc..a64342f 100644 Binary files a/loko/common/locale/en/LC_MESSAGES/django.mo and b/loko/common/locale/en/LC_MESSAGES/django.mo differ diff --git a/loko/common/locale/en/LC_MESSAGES/django.po b/loko/common/locale/en/LC_MESSAGES/django.po index cdbbd5c..5c1f710 100644 --- a/loko/common/locale/en/LC_MESSAGES/django.po +++ b/loko/common/locale/en/LC_MESSAGES/django.po @@ -553,3 +553,8 @@ msgstr "Zoom in" #: common/templates/common/components/document_viewer.html msgid "Réinitialiser" msgstr "Reset" + +#: common/views.py:683 +msgid "Format de fond de plan invalide" +msgstr "Invalid basemap format" + diff --git a/loko/common/locale/fr/LC_MESSAGES/django.mo b/loko/common/locale/fr/LC_MESSAGES/django.mo index 8d040b2..94e12ae 100644 Binary files a/loko/common/locale/fr/LC_MESSAGES/django.mo and b/loko/common/locale/fr/LC_MESSAGES/django.mo differ diff --git a/loko/common/locale/fr/LC_MESSAGES/django.po b/loko/common/locale/fr/LC_MESSAGES/django.po index 0b12f73..b125da3 100644 --- a/loko/common/locale/fr/LC_MESSAGES/django.po +++ b/loko/common/locale/fr/LC_MESSAGES/django.po @@ -539,3 +539,8 @@ msgstr "Zoomer" #: common/templates/common/components/document_viewer.html msgid "Réinitialiser" msgstr "Réinitialiser" + +#: common/views.py:683 +msgid "Format de fond de plan invalide" +msgstr "Format de fond de plan invalide" + diff --git a/loko/common/locale/nl/LC_MESSAGES/django.mo b/loko/common/locale/nl/LC_MESSAGES/django.mo index e47b6d7..d8192e4 100644 Binary files a/loko/common/locale/nl/LC_MESSAGES/django.mo and b/loko/common/locale/nl/LC_MESSAGES/django.mo differ diff --git a/loko/common/locale/nl/LC_MESSAGES/django.po b/loko/common/locale/nl/LC_MESSAGES/django.po index dd7466b..bc848c5 100644 --- a/loko/common/locale/nl/LC_MESSAGES/django.po +++ b/loko/common/locale/nl/LC_MESSAGES/django.po @@ -539,3 +539,8 @@ msgstr "Inzoomen" #: common/templates/common/components/document_viewer.html msgid "Réinitialiser" msgstr "Herstellen" + +#: common/views.py:683 +msgid "Format de fond de plan invalide" +msgstr "Ongeldig basiskaartformaat" + diff --git a/loko/common/static/common/layer-switcher-control.js b/loko/common/static/common/layer-switcher-control.js index 5e37a1d..495b05f 100644 --- a/loko/common/static/common/layer-switcher-control.js +++ b/loko/common/static/common/layer-switcher-control.js @@ -381,7 +381,8 @@ // Extract or build basemaps list this.basemaps = this._resolveBasemaps(); - this.activeBasemapId = localStorage.getItem('loko_active_basemap') || 'default'; + this.activeBasemapId = options.initialBasemapId || localStorage.getItem('loko_active_basemap') || 'default'; + this.onBasemapChange = typeof options.onBasemapChange === 'function' ? options.onBasemapChange : null; this._injectStyles(); this._handleOutsideClick = this._handleOutsideClick.bind(this); @@ -442,13 +443,26 @@ this.popover = this._createPopover(); map.getContainer().appendChild(this.popover); - // Initialize active basemap on map load - if (map.loaded()) { + // Initialize active basemap on map load (checking map._loaded, isStyleLoaded, or loaded) + const isMapReady = Boolean( + map._loaded || + (typeof map.isStyleLoaded === 'function' && map.isStyleLoaded()) || + map.loaded() + ); + + if (isMapReady) { this._applyInitialBasemap(); } else { - map.on('load', () => this._applyInitialBasemap()); + map.once('load', () => this._applyInitialBasemap()); } + // Re-apply if the style reloads dynamically + map.on('style.load', () => { + if (this.activeBasemapId && this.activeBasemapId !== 'default') { + this._applyInitialBasemap(); + } + }); + document.addEventListener('click', this._handleOutsideClick); return this.container; @@ -660,8 +674,11 @@ } selectBasemap(id) { + const previousId = this.activeBasemapId; this.activeBasemapId = id; - localStorage.setItem('loko_active_basemap', id); + try { + localStorage.setItem('loko_active_basemap', id); + } catch (e) {} // Update UI list items if (this.popover) { @@ -682,9 +699,20 @@ this._applyRasterBasemap(bgConfig); } } + + if (this.onBasemapChange && previousId !== id) { + try { + this.onBasemapChange(id); + } catch (err) { + console.error('[LayerSwitcherControl] onBasemapChange error:', err); + } + } } _applyInitialBasemap() { + if (!this.basemaps || this.basemaps.length === 0) { + this.basemaps = this._resolveBasemaps(); + } if (this.activeBasemapId && this.activeBasemapId !== 'default') { const bgConfig = this.basemaps.find(b => b.id === this.activeBasemapId); if (bgConfig) { @@ -693,13 +721,15 @@ return; } } - // Check if any basemap has is_default - const defaultBg = this.basemaps.find(b => b.is_default); - if (defaultBg) { - this.selectBasemap(defaultBg.id); - } else { - this._updateButtonState(); + // Check if any basemap has is_default only if activeBasemapId is not set + if (!this.activeBasemapId) { + const defaultBg = this.basemaps.find(b => b.is_default); + if (defaultBg) { + this.selectBasemap(defaultBg.id); + return; + } } + this._updateButtonState(); } _removeCurrentRasterLayer() { diff --git a/loko/common/tests.py b/loko/common/tests.py index f0ab794..48fbb8e 100644 --- a/loko/common/tests.py +++ b/loko/common/tests.py @@ -324,6 +324,35 @@ class AssetsIndexPreferencesTests(TestCase): self.assertEqual(response.status_code, 400) self.assertIn("error", response.json()) + def test_save_assets_index_preferences_with_basemap(self): + self.client.force_login(self.superuser) + save_url = reverse("common:save_assets_index_preferences") + payload = { + "thematics": ["sign"], + "basemap": "urbis_ortho", + } + response = self.client.post(save_url, json.dumps(payload), content_type="application/json") + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json().get("success")) + + from common.models import UserConfig + config = UserConfig.objects.get(user=self.superuser) + self.assertEqual( + config.assets_index_preferences.get("basemap"), + "urbis_ortho" + ) + + def test_save_assets_index_preferences_invalid_basemap_returns_400(self): + self.client.force_login(self.superuser) + save_url = reverse("common:save_assets_index_preferences") + payload = { + "basemap": 9999, + } + response = self.client.post(save_url, json.dumps(payload), content_type="application/json") + self.assertEqual(response.status_code, 400) + self.assertIn("error", response.json()) + + class UserConfigCopyTests(TestCase): diff --git a/loko/common/views.py b/loko/common/views.py index 1683737..c5c7cec 100644 --- a/loko/common/views.py +++ b/loko/common/views.py @@ -663,6 +663,7 @@ def save_assets_index_preferences(request): include_no_geom = data.get('include_no_geom', True) map_center = data.get('map_center') map_zoom = data.get('map_zoom') + basemap = data.get('basemap') if not isinstance(thematics, list): return JsonResponse({'error': _('Format de thématiques invalide')}, status=400) @@ -680,13 +681,18 @@ def save_assets_index_preferences(request): return JsonResponse({'error': _('Format de filtres statut invalide')}, status=400) if not isinstance(validation_status_filters, list): return JsonResponse({'error': _('Format de filtres statut de validation invalide')}, status=400) + if basemap is not None and not isinstance(basemap, str): + return JsonResponse({'error': _('Format de fond de plan invalide')}, status=400) try: user_config, _created = UserConfig.objects.get_or_create(user=request.user) existing_prefs = user_config.assets_index_preferences or {} - existing_visible_columns = existing_prefs.get('visible_columns', []) if isinstance(existing_prefs, dict) else [] + if not isinstance(existing_prefs, dict): + existing_prefs = {} + existing_visible_columns = existing_prefs.get('visible_columns', []) if not isinstance(existing_visible_columns, list): existing_visible_columns = [] + existing_basemap = existing_prefs.get('basemap', 'default') user_config.assets_index_preferences = { 'thematics': thematics, @@ -700,6 +706,7 @@ def save_assets_index_preferences(request): 'include_no_geom': include_no_geom, 'map_center': map_center, 'map_zoom': map_zoom, + 'basemap': basemap if basemap is not None else existing_basemap, } user_config.save(update_fields=['assets_index_preferences']) return JsonResponse({'success': True})