refactor: implement strict map layer stacking order for intersection and cross plan visualizations
This commit is contained in:
parent
1e7f8123de
commit
4424c2d9c4
3 changed files with 243 additions and 5 deletions
|
|
@ -42,6 +42,104 @@ class CustomPlansControl {
|
|||
}
|
||||
map.addControl(new CustomPlansControl(), 'top-right');
|
||||
|
||||
// ── Helper to determine where to place the orthophoto layer ──
|
||||
function getOrthoBeforeLayer(m) {
|
||||
if (!m || !m.getStyle) return undefined;
|
||||
const style = m.getStyle();
|
||||
const styleLayers = (style && style.layers) || [];
|
||||
|
||||
// 1. Bottom-most plan layer: first georef image layer
|
||||
const imageLayer = styleLayers.find(l => l.id && l.id.startsWith('georef-image-overlay-layer-'));
|
||||
if (imageLayer) return imageLayer.id;
|
||||
|
||||
// 2. Or first CAD layer
|
||||
if (m.getLayer('dxf-layout-fills')) return 'dxf-layout-fills';
|
||||
if (m.getLayer('dxf-layout-lines')) return 'dxf-layout-lines';
|
||||
if (m.getLayer('dxf-layout-symbols')) return 'dxf-layout-symbols';
|
||||
|
||||
// 3. Or traffic light / marker layer
|
||||
if (m.getLayer('traffic-light-outline')) return 'traffic-light-outline';
|
||||
if (m.getLayer('traffic-light')) return 'traffic-light';
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// ── Ensure strict layer stacking order ──
|
||||
// 1. Base vector tiles
|
||||
// 2. Orthophotos (layer-switcher-ortho)
|
||||
// 3. Georeferenced image plans (georef-image-overlay-layer-*)
|
||||
// 4. CAD / DXF layers (fills -> lines -> symbols)
|
||||
// 5. Traffic light marker (traffic-light-outline -> traffic-light)
|
||||
// 6. Assets (cables, poles, radars, cabinets, equipment)
|
||||
function reorderIntersectionLayers() {
|
||||
if (!map || typeof map.isStyleLoaded !== 'function' || !map.isStyleLoaded()) return;
|
||||
|
||||
const markerBottomId = map.getLayer('traffic-light-outline')
|
||||
? 'traffic-light-outline'
|
||||
: (map.getLayer('traffic-light') ? 'traffic-light' : undefined);
|
||||
|
||||
const style = map.getStyle();
|
||||
const styleLayers = (style && style.layers) || [];
|
||||
const imageLayerIds = styleLayers
|
||||
.filter(l => l.id && l.id.startsWith('georef-image-overlay-layer-'))
|
||||
.map(l => l.id);
|
||||
|
||||
const firstCadId = map.getLayer('dxf-layout-fills')
|
||||
? 'dxf-layout-fills'
|
||||
: (map.getLayer('dxf-layout-lines') ? 'dxf-layout-lines' : (map.getLayer('dxf-layout-symbols') ? 'dxf-layout-symbols' : undefined));
|
||||
|
||||
const firstPlanId = (imageLayerIds.length > 0 ? imageLayerIds[0] : null) || firstCadId;
|
||||
|
||||
// 1. Move orthophoto under any plan layer (or under marker)
|
||||
const orthoBeforeId = firstPlanId || markerBottomId;
|
||||
if (map.getLayer('layer-switcher-ortho') && orthoBeforeId && map.getLayer(orthoBeforeId)) {
|
||||
try {
|
||||
map.moveLayer('layer-switcher-ortho', orthoBeforeId);
|
||||
} catch (e) {
|
||||
console.warn("Could not move layer-switcher-ortho:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Georeferenced image layers under CAD layers (or under marker)
|
||||
const imageBeforeId = firstCadId || markerBottomId;
|
||||
if (imageBeforeId && map.getLayer(imageBeforeId)) {
|
||||
imageLayerIds.forEach(imgId => {
|
||||
if (map.getLayer(imgId)) {
|
||||
try {
|
||||
map.moveLayer(imgId, imageBeforeId);
|
||||
} catch (e) {
|
||||
console.warn("Could not move image overlay layer:", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. DXF CAD layers: fills -> lines -> symbols, before markerBottomId
|
||||
if (markerBottomId && map.getLayer(markerBottomId)) {
|
||||
['dxf-layout-fills', 'dxf-layout-lines', 'dxf-layout-symbols'].forEach(cadId => {
|
||||
if (map.getLayer(cadId)) {
|
||||
try {
|
||||
map.moveLayer(cadId, markerBottomId);
|
||||
} catch (e) {
|
||||
console.warn("Could not move CAD layer:", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Ensure assets stay on top of the intersection marker and plans
|
||||
const assetLayerIds = ['assets-cables', 'assets-poles', 'assets-radars', 'assets-cabinets', 'assets-equipment'];
|
||||
assetLayerIds.forEach(assetId => {
|
||||
if (map.getLayer(assetId)) {
|
||||
try {
|
||||
map.moveLayer(assetId);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- Contrôle photo aérienne (LayerSwitcherControl) --- (second, below Plans button)
|
||||
if (typeof LayerSwitcherControl !== 'undefined') {
|
||||
const layerSwitcher = new LayerSwitcherControl({
|
||||
|
|
@ -51,7 +149,11 @@ if (typeof LayerSwitcherControl !== 'undefined') {
|
|||
name: 'Orthophotos',
|
||||
icon: 'satellite',
|
||||
group: 'ortho',
|
||||
beforeLayer: 'traffic-light',
|
||||
beforeLayer: getOrthoBeforeLayer,
|
||||
onLayerAdded: () => reorderIntersectionLayers(),
|
||||
onVisibilityChange: (layerId, isVisible) => {
|
||||
if (isVisible) reorderIntersectionLayers();
|
||||
},
|
||||
layer: {
|
||||
type: 'raster',
|
||||
paint: { 'raster-opacity': 1 }
|
||||
|
|
@ -440,11 +542,14 @@ map.on('load', () => {
|
|||
});
|
||||
});
|
||||
|
||||
reorderIntersectionLayers();
|
||||
|
||||
}).catch(e => {
|
||||
console.error('Failed to load asset icons:', e);
|
||||
});
|
||||
}
|
||||
|
||||
reorderIntersectionLayers();
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -704,7 +809,9 @@ function ensureDxfSourcesAndLayers() {
|
|||
data: { type: "FeatureCollection", features: [] }
|
||||
});
|
||||
|
||||
const beforeId = map.getLayer('traffic-light') ? 'traffic-light' : undefined;
|
||||
const beforeId = map.getLayer('traffic-light-outline')
|
||||
? 'traffic-light-outline'
|
||||
: (map.getLayer('traffic-light') ? 'traffic-light' : undefined);
|
||||
|
||||
map.addLayer({
|
||||
'id': 'dxf-layout-fills',
|
||||
|
|
@ -762,6 +869,7 @@ function ensureDxfSourcesAndLayers() {
|
|||
}
|
||||
}, beforeId);
|
||||
}
|
||||
reorderIntersectionLayers();
|
||||
}
|
||||
|
||||
// ── Georeferenced Plans & DXF Background Loader ──
|
||||
|
|
@ -792,6 +900,7 @@ function showGeorefImage(doc, active, opacity) {
|
|||
if (map.getLayer(lyrId)) {
|
||||
map.setLayoutProperty(lyrId, 'visibility', 'visible');
|
||||
map.setPaintProperty(lyrId, 'raster-opacity', opacity);
|
||||
reorderIntersectionLayers();
|
||||
}
|
||||
} else {
|
||||
map.addSource(srcId, {
|
||||
|
|
@ -805,6 +914,8 @@ function showGeorefImage(doc, active, opacity) {
|
|||
beforeId = 'dxf-layout-fills';
|
||||
} else if (map.getLayer('dxf-layout-lines')) {
|
||||
beforeId = 'dxf-layout-lines';
|
||||
} else if (map.getLayer('traffic-light-outline')) {
|
||||
beforeId = 'traffic-light-outline';
|
||||
} else if (map.getLayer('traffic-light')) {
|
||||
beforeId = 'traffic-light';
|
||||
}
|
||||
|
|
@ -816,6 +927,8 @@ function showGeorefImage(doc, active, opacity) {
|
|||
'layout': { 'visibility': 'visible' },
|
||||
'paint': { 'raster-opacity': opacity }
|
||||
}, beforeId);
|
||||
|
||||
reorderIntersectionLayers();
|
||||
}
|
||||
|
||||
// Zoom/fitBounds to document
|
||||
|
|
@ -873,6 +986,7 @@ function applyDxfVectorFilters() {
|
|||
map.setLayoutProperty(lId, 'visibility', 'visible');
|
||||
}
|
||||
});
|
||||
reorderIntersectionLayers();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1195,6 +1195,83 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
}
|
||||
map.addControl(new CustomPlansControl(), 'top-right');
|
||||
|
||||
// ── Helper to determine where to place the orthophoto layer ──
|
||||
function getOrthoBeforeLayer(m) {
|
||||
if (!m || !m.getStyle) return undefined;
|
||||
const style = m.getStyle();
|
||||
const styleLayers = (style && style.layers) || [];
|
||||
|
||||
// 1. Bottom-most plan layer: first georef image layer
|
||||
const imageLayer = styleLayers.find(l => l.id && l.id.startsWith('georef-image-overlay-layer-'));
|
||||
if (imageLayer) return imageLayer.id;
|
||||
|
||||
// 2. Or first CAD layer
|
||||
if (m.getLayer('dxf-layout-fills')) return 'dxf-layout-fills';
|
||||
if (m.getLayer('dxf-layout-lines')) return 'dxf-layout-lines';
|
||||
if (m.getLayer('dxf-layout-symbols')) return 'dxf-layout-symbols';
|
||||
|
||||
// 3. Or existing poles layer
|
||||
if (m.getLayer('existing-poles-layer')) return 'existing-poles-layer';
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// ── Ensure strict layer stacking order for cross plan import ──
|
||||
function reorderCrossPlanLayers() {
|
||||
if (!map || typeof map.isStyleLoaded !== 'function' || !map.isStyleLoaded()) return;
|
||||
|
||||
const markerBottomId = map.getLayer('existing-poles-layer') ? 'existing-poles-layer' : undefined;
|
||||
|
||||
const style = map.getStyle();
|
||||
const styleLayers = (style && style.layers) || [];
|
||||
const imageLayerIds = styleLayers
|
||||
.filter(l => l.id && l.id.startsWith('georef-image-overlay-layer-'))
|
||||
.map(l => l.id);
|
||||
|
||||
const firstCadId = map.getLayer('dxf-layout-fills')
|
||||
? 'dxf-layout-fills'
|
||||
: (map.getLayer('dxf-layout-lines') ? 'dxf-layout-lines' : (map.getLayer('dxf-layout-symbols') ? 'dxf-layout-symbols' : undefined));
|
||||
|
||||
const firstPlanId = (imageLayerIds.length > 0 ? imageLayerIds[0] : null) || firstCadId;
|
||||
|
||||
// 1. Move orthophoto under any plan layer (or under marker)
|
||||
const orthoBeforeId = firstPlanId || markerBottomId;
|
||||
if (map.getLayer('layer-switcher-ortho') && orthoBeforeId && map.getLayer(orthoBeforeId)) {
|
||||
try {
|
||||
map.moveLayer('layer-switcher-ortho', orthoBeforeId);
|
||||
} catch (e) {
|
||||
console.warn("Could not move layer-switcher-ortho:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Georeferenced image layers under CAD layers (or under marker)
|
||||
const imageBeforeId = firstCadId || markerBottomId;
|
||||
if (imageBeforeId && map.getLayer(imageBeforeId)) {
|
||||
imageLayerIds.forEach(imgId => {
|
||||
if (map.getLayer(imgId)) {
|
||||
try {
|
||||
map.moveLayer(imgId, imageBeforeId);
|
||||
} catch (e) {
|
||||
console.warn("Could not move image overlay layer:", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. DXF CAD layers: fills -> lines -> symbols, before markerBottomId
|
||||
if (markerBottomId && map.getLayer(markerBottomId)) {
|
||||
['dxf-layout-fills', 'dxf-layout-lines', 'dxf-layout-symbols'].forEach(cadId => {
|
||||
if (map.getLayer(cadId)) {
|
||||
try {
|
||||
map.moveLayer(cadId, markerBottomId);
|
||||
} catch (e) {
|
||||
console.warn("Could not move CAD layer:", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- Contrôle photo aérienne (LayerSwitcherControl) --- (second, below Plans button)
|
||||
if (typeof LayerSwitcherControl !== 'undefined') {
|
||||
const layerSwitcher = new LayerSwitcherControl({
|
||||
|
|
@ -1204,7 +1281,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
name: 'Orthophotos',
|
||||
icon: 'satellite',
|
||||
group: 'ortho',
|
||||
beforeLayer: 'existing-poles-layer',
|
||||
beforeLayer: getOrthoBeforeLayer,
|
||||
onLayerAdded: () => reorderCrossPlanLayers(),
|
||||
onVisibilityChange: (layerId, isVisible) => {
|
||||
if (isVisible) reorderCrossPlanLayers();
|
||||
},
|
||||
layer: {
|
||||
type: 'raster',
|
||||
paint: { 'raster-opacity': 1 }
|
||||
|
|
@ -1783,6 +1864,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
if (map.getLayer(lyrId)) {
|
||||
map.setLayoutProperty(lyrId, 'visibility', 'visible');
|
||||
map.setPaintProperty(lyrId, 'raster-opacity', opacity);
|
||||
reorderCrossPlanLayers();
|
||||
}
|
||||
} else {
|
||||
map.addSource(srcId, {
|
||||
|
|
@ -1807,6 +1889,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
'layout': { 'visibility': 'visible' },
|
||||
'paint': { 'raster-opacity': opacity }
|
||||
}, beforeId);
|
||||
|
||||
reorderCrossPlanLayers();
|
||||
}
|
||||
|
||||
// Zoom/fitBounds to document
|
||||
|
|
@ -1864,6 +1948,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
map.setLayoutProperty(lId, 'visibility', 'visible');
|
||||
}
|
||||
});
|
||||
reorderCrossPlanLayers();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1932,6 +2017,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
}
|
||||
}, beforeId);
|
||||
}
|
||||
reorderCrossPlanLayers();
|
||||
}
|
||||
|
||||
function updateButtonActiveState() {
|
||||
|
|
|
|||
|
|
@ -425,7 +425,12 @@
|
|||
this.map.addSource(sourceId, sublayer.source);
|
||||
|
||||
// Determine where to insert the layer
|
||||
let beforeLayer = layerConfig.beforeLayer;
|
||||
let beforeLayer = typeof layerConfig.beforeLayer === 'function'
|
||||
? layerConfig.beforeLayer(this.map)
|
||||
: layerConfig.beforeLayer;
|
||||
if (beforeLayer && !this.map.getLayer(beforeLayer)) {
|
||||
beforeLayer = undefined;
|
||||
}
|
||||
if (!beforeLayer) {
|
||||
const layers = this.map.getStyle().layers;
|
||||
for (const layer of layers) {
|
||||
|
|
@ -447,6 +452,13 @@
|
|||
};
|
||||
|
||||
this.map.addLayer(mapLayerConfig, beforeLayer);
|
||||
|
||||
if (layerConfig.onLayerAdded && typeof layerConfig.onLayerAdded === 'function') {
|
||||
layerConfig.onLayerAdded(mapLayerId, this.map);
|
||||
}
|
||||
if (this.map && typeof this.map.fire === 'function') {
|
||||
this.map.fire('layerswitcher:layerAdded', { layerId: mapLayerId, config: layerConfig });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -501,8 +513,14 @@
|
|||
}
|
||||
|
||||
// Determine where to insert the layer
|
||||
let beforeLayer = layerConfig.beforeLayer;
|
||||
let beforeLayer = typeof layerConfig.beforeLayer === 'function'
|
||||
? layerConfig.beforeLayer(this.map)
|
||||
: layerConfig.beforeLayer;
|
||||
|
||||
if (beforeLayer && !this.map.getLayer(beforeLayer)) {
|
||||
beforeLayer = undefined;
|
||||
}
|
||||
|
||||
// If no beforeLayer specified, try to insert before draw layers
|
||||
if (!beforeLayer) {
|
||||
const layers = this.map.getStyle().layers;
|
||||
|
|
@ -531,6 +549,13 @@
|
|||
state.layerAdded = true;
|
||||
state.visible = true;
|
||||
}
|
||||
|
||||
if (layerConfig.onLayerAdded && typeof layerConfig.onLayerAdded === 'function') {
|
||||
layerConfig.onLayerAdded(layerId, this.map);
|
||||
}
|
||||
if (this.map && typeof this.map.fire === 'function') {
|
||||
this.map.fire('layerswitcher:layerAdded', { layerId: layerId, config: layerConfig });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -578,6 +603,13 @@
|
|||
if (layerConfig.sublayers && layerConfig.sublayers.length > 0) {
|
||||
this._hideSublayerSelector(layerId);
|
||||
}
|
||||
|
||||
if (layerConfig.onVisibilityChange && typeof layerConfig.onVisibilityChange === 'function') {
|
||||
layerConfig.onVisibilityChange(mapLayerId, false, this.map);
|
||||
}
|
||||
if (this.map && typeof this.map.fire === 'function') {
|
||||
this.map.fire('layerswitcher:visibilityChange', { layerId: mapLayerId, visible: false });
|
||||
}
|
||||
} else {
|
||||
// Handle exclusive layers
|
||||
if (layerConfig.exclusive && layerConfig.group) {
|
||||
|
|
@ -587,6 +619,12 @@
|
|||
// Show layer
|
||||
if (this.map.getLayer(mapLayerId)) {
|
||||
this.map.setLayoutProperty(mapLayerId, 'visibility', 'visible');
|
||||
if (layerConfig.onVisibilityChange && typeof layerConfig.onVisibilityChange === 'function') {
|
||||
layerConfig.onVisibilityChange(mapLayerId, true, this.map);
|
||||
}
|
||||
if (this.map && typeof this.map.fire === 'function') {
|
||||
this.map.fire('layerswitcher:visibilityChange', { layerId: mapLayerId, visible: true });
|
||||
}
|
||||
} else {
|
||||
this._addLayer(layerConfig);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue