fix(assets): ensure asset geometry layers remain above orthophoto basemap on detail maps

This commit is contained in:
kdeterme 2026-10-10 15:58:12 +02:00
parent 34e4da64a8
commit 5ce7179c19
5 changed files with 78 additions and 10 deletions

View file

@ -94,6 +94,9 @@
addIntersectionAssetsLayer(container); addIntersectionAssetsLayer(container);
addConnectionLinesLayer(); addConnectionLinesLayer();
} }
// Ensure asset layers are stacked above basemaps
bringAssetLayersToTop();
}); });
} }
@ -224,6 +227,8 @@
addLineLayer(); addLineLayer();
addPointLayer(); addPointLayer();
} }
bringAssetLayersToTop();
} }
function addPointLayer() { function addPointLayer() {
@ -651,9 +656,29 @@
'current-asset-icon', 'current-asset-icon',
]; ];
function getBasemapBeforeLayer(m) {
if (!m || !m.getStyle) return undefined;
const styleLayers = (m.getStyle() && m.getStyle().layers) || [];
const drawLayer = styleLayers.find(l => l.id && (l.id.startsWith('gl-draw-') || l.id.startsWith('custom-draw-')));
if (drawLayer) return drawLayer.id;
// Return first existing asset layer so basemap is placed below it
for (const id of ASSET_LAYER_ORDER) {
if (m.getLayer(id)) return id;
}
return undefined;
}
function bringAssetLayersToTop() { function bringAssetLayersToTop() {
if (!map || typeof map.isStyleLoaded !== 'function' || !map.isStyleLoaded()) return;
ASSET_LAYER_ORDER.forEach(id => { ASSET_LAYER_ORDER.forEach(id => {
if (map.getLayer(id)) map.moveLayer(id); if (map.getLayer(id)) {
try {
map.moveLayer(id);
} catch (e) {
// ignore if layer cannot be moved
}
}
}); });
} }
@ -661,16 +686,25 @@
if (typeof LayerSwitcherControl === 'undefined') return; if (typeof LayerSwitcherControl === 'undefined') return;
const layerSwitcher = new LayerSwitcherControl({ const layerSwitcher = new LayerSwitcherControl({
onBasemapChange: function() {
bringAssetLayersToTop();
},
layers: [ layers: [
{ {
id: 'ortho', id: 'ortho',
name: 'Orthophotos', name: 'Orthophotos',
icon: 'satellite', icon: 'satellite',
group: 'ortho', group: 'ortho',
beforeLayer: getBasemapBeforeLayer,
onLayerAdded: () => bringAssetLayersToTop(),
onVisibilityChange: (layerId, isVisible) => {
if (isVisible) bringAssetLayersToTop();
},
layer: { layer: {
type: 'raster', type: 'raster',
paint: { 'raster-opacity': 1 } paint: { 'raster-opacity': 1 }
}, },
basemaps: (window.MAP_BACKGROUNDS && window.MAP_BACKGROUNDS.length > 0) ? window.MAP_BACKGROUNDS : undefined,
sublayers: [ sublayers: [
{ {
id: 'urbis', id: 'urbis',
@ -700,6 +734,12 @@
map.addControl(layerSwitcher, 'top-left'); map.addControl(layerSwitcher, 'top-left');
// Listen for layer switcher events on map to keep asset layers on top
map.on('layerswitcher:layerAdded', bringAssetLayersToTop);
map.on('layerswitcher:visibilityChange', (e) => {
if (e && e.visible) bringAssetLayersToTop();
});
// The LayerSwitcherControl appends sublayer selectors to document.body with // The LayerSwitcherControl appends sublayer selectors to document.body with
// position:fixed and hardcoded coordinates designed for fullscreen maps. // position:fixed and hardcoded coordinates designed for fullscreen maps.
// We move each selector into #asset-map (position:relative) and switch to // We move each selector into #asset-map (position:relative) and switch to
@ -723,8 +763,9 @@
// After any ortho interaction, put asset layers back on top // After any ortho interaction, put asset layers back on top
map.getContainer().addEventListener('click', (e) => { map.getContainer().addEventListener('click', (e) => {
if (e.target.closest('.layer-switcher-btn') || e.target.closest('.layer-switcher-sublayer-btn')) { if (e.target.closest('.layer-switcher-btn') || e.target.closest('.layer-switcher-sublayer-btn') || e.target.closest('.layer-switcher-item')) {
setTimeout(bringAssetLayersToTop, 150); setTimeout(bringAssetLayersToTop, 50);
setTimeout(bringAssetLayersToTop, 200);
} }
}, true); }, true);
} }
@ -761,6 +802,10 @@
}); });
} }
} }
bringAssetLayersToTop();
}; };
window.bringAssetLayersToTop = bringAssetLayersToTop;
})(); })();

View file

@ -278,6 +278,10 @@
} }
}); });
if (typeof window.bringAssetLayersToTop === 'function') {
window.bringAssetLayersToTop();
}
// Remove draw control and listeners // Remove draw control and listeners
if (drawInstance && drawControlAdded) { if (drawInstance && drawControlAdded) {
mapInstance.off('draw.create', handleDrawCreate); mapInstance.off('draw.create', handleDrawCreate);

View file

@ -1852,11 +1852,11 @@
{% block script %} {% block script %}
<!-- MapLibre GL 5.7.0 --> <!-- MapLibre GL 5.7.0 -->
<script src="{% static 'common/maplibre-gl.js' %}"></script> <script src="{% static 'common/maplibre-gl.js' %}"></script>
<script src="{% static 'common/layer-switcher-control.js' %}"></script> <script src="{% static 'common/layer-switcher-control.js' %}?v=20261010_order"></script>
<script src="{% static 'assets/asset_cms_edit_mode.js' %}"></script> <script src="{% static 'assets/asset_cms_edit_mode.js' %}"></script>
<script src="{% static 'assets/asset_location_edit.js' %}"></script> <script src="{% static 'assets/asset_location_edit.js' %}"></script>
<!-- MapLibre map initialization for asset detail --> <!-- MapLibre map initialization for asset detail -->
<script src="{% static 'assets/asset_generic_map.js' %}"></script> <script src="{% static 'assets/asset_generic_map.js' %}?v=20261010_order"></script>
<!-- MapboxDraw for geometry editing (Point, LineString, Polygon) --> <!-- MapboxDraw for geometry editing (Point, LineString, Polygon) -->
<script src="{% static 'common/mapbox-gl-draw.js' %}"></script> <script src="{% static 'common/mapbox-gl-draw.js' %}"></script>
<script src="{% static 'assets/asset_geometry_edit.js' %}?v=1.1"></script> <script src="{% static 'assets/asset_geometry_edit.js' %}?v=1.1"></script>
@ -1865,11 +1865,11 @@
{% block extra_scripts %} {% block extra_scripts %}
<!-- MapLibre GL 5.7.0 --> <!-- MapLibre GL 5.7.0 -->
<script src="{% static 'common/maplibre-gl.js' %}"></script> <script src="{% static 'common/maplibre-gl.js' %}"></script>
<script src="{% static 'common/layer-switcher-control.js' %}"></script> <script src="{% static 'common/layer-switcher-control.js' %}?v=20261010_order"></script>
<script src="{% static 'assets/asset_cms_edit_mode.js' %}"></script> <script src="{% static 'assets/asset_cms_edit_mode.js' %}"></script>
<script src="{% static 'assets/asset_location_edit.js' %}"></script> <script src="{% static 'assets/asset_location_edit.js' %}"></script>
<!-- MapLibre map initialization for asset detail --> <!-- MapLibre map initialization for asset detail -->
<script src="{% static 'assets/asset_generic_map.js' %}"></script> <script src="{% static 'assets/asset_generic_map.js' %}?v=20261010_order"></script>
<!-- MapboxDraw for geometry editing (Point, LineString, Polygon) --> <!-- MapboxDraw for geometry editing (Point, LineString, Polygon) -->
<script src="{% static 'common/mapbox-gl-draw.js' %}"></script> <script src="{% static 'common/mapbox-gl-draw.js' %}"></script>
<script src="{% static 'assets/asset_geometry_edit.js' %}?v=1.1"></script> <script src="{% static 'assets/asset_geometry_edit.js' %}?v=1.1"></script>

View file

@ -26,7 +26,7 @@ class NatureLocationsGeoJSONTest(TestCase):
# Create a user # Create a user
self.user = User.objects.create_user(username="testuser", password="password123") self.user = User.objects.create_user(username="testuser", password="password123")
self.config = UserConfig.objects.create(user=self.user) self.config = UserConfig.objects.create(user=self.user, is_intern=True, limit_assets_to_contracts=False)
# Give thematic access to user # Give thematic access to user
UserThematics.objects.create( UserThematics.objects.create(
@ -5720,6 +5720,8 @@ class NatureLocationDetailViewTest(TestCase):
self.assertTemplateNotUsed(response, 'assets/naturegreensurface_detail.html') self.assertTemplateNotUsed(response, 'assets/naturegreensurface_detail.html')
self.assertContains(response, "GS-001") self.assertContains(response, "GS-001")
self.assertContains(response, 'id="asset-map"') self.assertContains(response, 'id="asset-map"')
self.assertContains(response, 'asset_generic_map.js?v=20261010_order')
self.assertContains(response, 'layer-switcher-control.js?v=20261010_order')

View file

@ -748,6 +748,9 @@
if (mainLayer.onVisibilityChange && typeof mainLayer.onVisibilityChange === 'function') { if (mainLayer.onVisibilityChange && typeof mainLayer.onVisibilityChange === 'function') {
mainLayer.onVisibilityChange(layerId, false, this.map); mainLayer.onVisibilityChange(layerId, false, this.map);
} }
if (typeof this.options.onVisibilityChange === 'function') {
this.options.onVisibilityChange(layerId, false, this.map);
}
if (typeof this.map.fire === 'function') { if (typeof this.map.fire === 'function') {
this.map.fire('layerswitcher:visibilityChange', { layerId, visible: false }); this.map.fire('layerswitcher:visibilityChange', { layerId, visible: false });
} }
@ -776,7 +779,7 @@
const mainLayer = this.layers[0] || {}; const mainLayer = this.layers[0] || {};
let beforeLayer = typeof mainLayer.beforeLayer === 'function' let beforeLayer = typeof mainLayer.beforeLayer === 'function'
? mainLayer.beforeLayer(this.map) ? mainLayer.beforeLayer(this.map)
: mainLayer.beforeLayer; : (mainLayer.beforeLayer || (typeof this.options.beforeLayer === 'function' ? this.options.beforeLayer(this.map) : this.options.beforeLayer));
if (this.map.getLayer('territory-mask')) { if (this.map.getLayer('territory-mask')) {
beforeLayer = 'territory-mask'; beforeLayer = 'territory-mask';
@ -788,7 +791,15 @@
beforeLayer = undefined; beforeLayer = undefined;
const layers = (this.map.getStyle() && this.map.getStyle().layers) || []; const layers = (this.map.getStyle() && this.map.getStyle().layers) || [];
for (const l of layers) { for (const l of layers) {
if (l.id.startsWith('gl-draw-') || l.id.startsWith('custom-draw-') || l.id.startsWith('polygon-')) { if (
l.id.startsWith('gl-draw-') ||
l.id.startsWith('custom-draw-') ||
l.id.startsWith('polygon-') ||
l.id.startsWith('asset-') ||
l.id.startsWith('nature-') ||
l.id.startsWith('layer-') ||
l.id.startsWith('intersection-')
) {
beforeLayer = l.id; beforeLayer = l.id;
break; break;
} }
@ -827,9 +838,15 @@
if (mainLayer.onLayerAdded && typeof mainLayer.onLayerAdded === 'function') { if (mainLayer.onLayerAdded && typeof mainLayer.onLayerAdded === 'function') {
mainLayer.onLayerAdded(layerId, this.map); mainLayer.onLayerAdded(layerId, this.map);
} }
if (typeof this.options.onLayerAdded === 'function') {
this.options.onLayerAdded(layerId, this.map);
}
if (mainLayer.onVisibilityChange && typeof mainLayer.onVisibilityChange === 'function') { if (mainLayer.onVisibilityChange && typeof mainLayer.onVisibilityChange === 'function') {
mainLayer.onVisibilityChange(layerId, true, this.map); mainLayer.onVisibilityChange(layerId, true, this.map);
} }
if (typeof this.options.onVisibilityChange === 'function') {
this.options.onVisibilityChange(layerId, true, this.map);
}
if (typeof this.map.fire === 'function') { if (typeof this.map.fire === 'function') {
this.map.fire('layerswitcher:layerAdded', { layerId, config: bg }); this.map.fire('layerswitcher:layerAdded', { layerId, config: bg });
this.map.fire('layerswitcher:visibilityChange', { layerId, visible: true }); this.map.fire('layerswitcher:visibilityChange', { layerId, visible: true });