feat: add unified map assemblies and dynamic zoom scaling for mobile assets

This commit is contained in:
kdeterme 2026-09-14 10:05:18 +02:00
parent 3f75b511f2
commit 099043ea8b

View file

@ -341,7 +341,267 @@
box-shadow: 0 2px 6px rgba(0,0,0,0.4); box-shadow: 0 2px 6px rgba(0,0,0,0.4);
} }
/* Marqueur spécifique poteau de signalisation (symbole desktop : socle circulaire + tige verticale) */ /* Assemblage unifié poteau + panneaux (ancre à offset [0, 0] sur la base au sol) */
.asset-map-assembly {
width: 28px;
height: 0px;
position: relative;
cursor: pointer;
pointer-events: none;
background: transparent !important;
border: none !important;
box-shadow: none !important;
padding: 0 !important;
margin: 0 !important;
box-sizing: border-box;
z-index: 5;
transition: none !important;
}
/* Conteneur de mise à l'échelle dynamique des poteaux et panneaux selon le zoom */
.asset-map-assembly .assembly-scaler {
width: 28px;
height: 0px;
position: absolute;
left: 0;
top: 0;
transform-origin: 50% 0;
transform: scale(var(--assembly-scale, 1));
pointer-events: none;
transition: none !important;
}
.asset-map-assembly .pole-base {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #888888;
border: 1.5px solid #333333;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
box-sizing: border-box;
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, -50%);
z-index: 2;
cursor: pointer;
pointer-events: auto;
transition: transform 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}
.asset-map-assembly .pole-base::before {
content: '';
position: absolute;
top: -8px;
bottom: -8px;
left: -8px;
right: -8px;
cursor: pointer;
}
.asset-map-assembly .pole-base:hover,
.asset-map-assembly .pole-base:active {
transform: translate(-50%, -50%) scale(1.3);
background-color: #ca8a04;
border-color: #854d0e;
box-shadow: 0 0 8px rgba(202, 138, 4, 0.6);
}
.asset-map-assembly.selected .pole-base,
.asset-map-assembly .pole-base.selected {
background-color: #fde047 !important;
border-color: #ca8a04 !important;
border-width: 2px !important;
transform: translate(-50%, -50%) scale(1.4);
box-shadow: 0 0 0 4px rgba(250, 204, 21, 0.45), 0 0 12px #eab308 !important;
}
.asset-map-assembly .pole-stem {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 2.5px;
background-color: #555555;
border-left: 0.5px solid #222222;
border-right: 0.5px solid #222222;
border-top: 0.5px solid #222222;
box-sizing: border-box;
z-index: 1;
pointer-events: none;
transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
.asset-map-assembly.selected .pole-stem {
background-color: #eab308 !important;
border-color: #ca8a04 !important;
box-shadow: 0 0 8px rgba(234, 179, 8, 0.7) !important;
}
.asset-map-assembly .assembly-panel-item {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
pointer-events: auto;
box-sizing: border-box;
z-index: 10;
}
.asset-map-assembly .assembly-panel-item::before {
content: '';
position: absolute;
top: -8px;
bottom: -8px;
left: -8px;
right: -8px;
cursor: pointer;
}
.asset-map-assembly .assembly-panel-item img {
width: 20px;
height: 20px;
object-fit: contain;
display: block;
pointer-events: none;
filter: drop-shadow(0 1px 3px rgba(0,0,0,0.45));
transition: transform 0.15s ease;
}
.asset-map-assembly .assembly-panel-item:hover img,
.asset-map-assembly .assembly-panel-item:active img {
transform: scale(1.25);
filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6));
}
.asset-map-assembly .assembly-panel-item i {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 3px;
background: #ffffff;
border: 1.5px solid #2563eb;
color: #2563eb;
font-size: 0.65rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
pointer-events: none;
transition: transform 0.15s ease;
}
.asset-map-assembly .assembly-panel-item:hover i,
.asset-map-assembly .assembly-panel-item:active i {
transform: scale(1.25);
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
.asset-map-assembly .assembly-panel-item.selected img {
transform: scale(1.3);
filter: drop-shadow(0 0 5px #eab308) drop-shadow(0 0 10px #fde047) drop-shadow(0 2px 5px rgba(0,0,0,0.5)) !important;
}
.asset-map-assembly .assembly-panel-item.selected i {
border-color: #ca8a04 !important;
color: #854d0e !important;
transform: scale(1.3);
box-shadow: 0 0 8px rgba(234, 179, 8, 0.85);
}
.asset-map-assembly .assembly-panel-item.selected::after {
content: '';
position: absolute;
top: -3px;
left: -3px;
right: -3px;
bottom: -3px;
border-radius: 4px;
border: 2px solid #eab308;
box-shadow: 0 0 8px rgba(234, 179, 8, 0.85);
pointer-events: none;
animation: marker-selected-pulse 1.8s infinite ease-in-out;
}
.asset-map-assembly .marker-count-badge {
position: absolute;
left: 50%;
top: 0;
transform: translate(6px, -14px);
}
/* Marqueur spécifique panneau compact à faible/moyen zoom (< 16.5) */
.asset-map-marker-compact {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
background: transparent !important;
border: none !important;
box-shadow: none !important;
padding: 0 !important;
box-sizing: border-box;
transition: none !important;
}
.asset-map-marker-compact::before {
content: '';
position: absolute;
top: -4px;
bottom: -4px;
left: -4px;
right: -4px;
cursor: pointer;
}
.asset-map-marker-compact img {
width: 20px;
height: 20px;
object-fit: contain;
display: block;
pointer-events: none;
filter: drop-shadow(0 1px 3px rgba(0,0,0,0.45));
transition: transform 0.15s ease;
}
.asset-map-marker-compact:hover img, .asset-map-marker-compact:active img {
transform: scale(1.2);
filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6));
}
.asset-map-marker-compact i {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 3px;
background: #ffffff;
border: 1.5px solid #2563eb;
color: #2563eb;
font-size: 0.65rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
pointer-events: none;
transition: transform 0.15s ease;
}
.asset-map-marker-compact:hover i, .asset-map-marker-compact:active i {
transform: scale(1.2);
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
.asset-map-marker-compact.selected {
z-index: 50 !important;
}
.asset-map-marker-compact.selected img {
transform: scale(1.25);
filter: drop-shadow(0 0 5px #eab308) drop-shadow(0 0 10px #fde047) drop-shadow(0 2px 5px rgba(0,0,0,0.5)) !important;
}
.asset-map-marker-compact.selected i {
border-color: #ca8a04;
color: #854d0e;
transform: scale(1.2);
box-shadow: 0 0 8px rgba(234, 179, 8, 0.85);
}
.asset-map-marker-compact.selected::after {
content: '';
position: absolute;
top: -4px;
left: -4px;
right: -4px;
bottom: -4px;
border-radius: 50%;
border: 2px solid #eab308;
box-shadow: 0 0 8px rgba(234, 179, 8, 0.85);
pointer-events: none;
animation: marker-selected-pulse 1.8s infinite ease-in-out;
}
/* Marqueur spécifique poteau de signalisation seul (socle circulaire + tige verticale) */
.asset-map-marker-pole { .asset-map-marker-pole {
width: 24px; width: 24px;
height: 24px; height: 24px;
@ -1101,6 +1361,26 @@
return null; return null;
} }
// Mise à l'échelle dynamique des poteaux et panneaux selon le niveau de zoom
function updateAssemblyScale() {
if (!map) return;
const z = map.getZoom();
let scale;
if (z >= 20.5) {
scale = 1.0;
} else if (z <= 12.5) {
scale = 0.25;
} else {
// La réduction commence plus tôt (dès zoom 20.5) et s'adapte régulièrement jusqu'à zoom 12.5
scale = 0.25 + (z - 12.5) * (0.75 / 8.0);
}
scale = Math.round(scale * 100) / 100;
const container = map.getContainer ? map.getContainer() : document.getElementById('assets-map');
if (container) {
container.style.setProperty('--assembly-scale', scale);
}
}
// 1. Initialisation de la carte MapLibre // 1. Initialisation de la carte MapLibre
function initMap() { function initMap() {
if (map) return; if (map) return;
@ -1122,6 +1402,10 @@
map.addControl(new maplibregl.NavigationControl({ showCompass: true }), 'top-left'); map.addControl(new maplibregl.NavigationControl({ showCompass: true }), 'top-left');
// Écouter le zoom pour ajuster dynamiquement et immédiatement la taille des poteaux et panneaux
map.on('zoom', updateAssemblyScale);
updateAssemblyScale();
// Couches vectorielles pour les éléments surfaciques (polygones) et linéaires (lignes) // Couches vectorielles pour les éléments surfaciques (polygones) et linéaires (lignes)
function setupMapLayers() { function setupMapLayers() {
if (!map || !map.isStyleLoaded()) return; if (!map || !map.isStyleLoaded()) return;
@ -1217,6 +1501,8 @@
}, 200); }, 200);
}); });
map.on('load', () => { map.on('load', () => {
layersConfigured = false; layersConfigured = false;
setupMapLayers(); setupMapLayers();
@ -1546,11 +1832,11 @@
let isSameLocation = false; let isSameLocation = false;
if (isSignRelated) { if (isSignRelated) {
// Pour les panneaux et poteaux, regrouper uniquement si même poteau // Pour les panneaux et poteaux, regrouper si même poteau
// ou si coordonnées strictement identiques (< 1m) pour panneaux sans pole_id // ou si coordonnées strictement identiques (< 1.5m) pour éléments sans poteau attribué
if (samePole) { if (samePole) {
isSameLocation = true; isSameLocation = true;
} else if (!asset.pole_id && !other.pole_id && !isAssetPole && !isOtherPole && asset.model_key === 'signpanel' && other.model_key === 'signpanel') { } else if (!asset.pole_id && !other.pole_id) {
const dLon = Math.abs(other.lon - asset.lon); const dLon = Math.abs(other.lon - asset.lon);
const dLat = Math.abs(other.lat - asset.lat); const dLat = Math.abs(other.lat - asset.lat);
isSameLocation = (dLon < 0.000015 && dLat < 0.000015); isSameLocation = (dLon < 0.000015 && dLat < 0.000015);
@ -1584,61 +1870,52 @@
return 10; return 10;
} }
// 4. Créer les marqueurs pour chaque emplacement avec offset vertical pour les panneaux // 4. Créer les marqueurs pour chaque emplacement
updateAssemblyScale();
locationGroups.forEach(group => { locationGroups.forEach(group => {
const signPanels = group.filter(a => a.model_key === 'signpanel'); const signPanels = group.filter(a => a.model_key === 'signpanel');
const otherAssets = group.filter(a => a.model_key !== 'signpanel'); const otherAssets = group.filter(a => a.model_key !== 'signpanel');
// CAS A : Emplacement avec un ou plusieurs panneaux de signalisation // CAS A : Emplacement avec un ou plusieurs panneaux de signalisation
// Rendu systématique en assemblage vertical unifié sans agrégation avec compteur
if (signPanels.length > 0) { if (signPanels.length > 0) {
// Trier pour identifier si un poteau ou support est présent
const poleAsset = otherAssets.find(a => a.model_key === 'signpole' || (a.model_key && a.model_key.includes('pole')) || (a.thematic === 'sign' && a.model_key && a.model_key.includes('pole'))); const poleAsset = otherAssets.find(a => a.model_key === 'signpole' || (a.model_key && a.model_key.includes('pole')) || (a.thematic === 'sign' && a.model_key && a.model_key.includes('pole')));
const baseAsset = poleAsset || (otherAssets.length > 0 ? otherAssets[0] : null); const baseAsset = poleAsset || (otherAssets.length > 0 ? otherAssets[0] : null);
const hasBaseSupport = !!baseAsset; const hasBaseSupport = !!baseAsset;
const refLon = baseAsset ? baseAsset.lon : signPanels[0].lon;
const refLat = baseAsset ? baseAsset.lat : signPanels[0].lat;
// vertical_order = 1 est le panneau le plus bas (au-dessus du dégagement du sol)
// Les ordres supérieurs s'empilent vers le haut le long du mât
signPanels.sort((a, b) => {
const orderA = a.vertical_order != null ? Number(a.vertical_order) : (a.panel_vertical_order != null ? Number(a.panel_vertical_order) : 1);
const orderB = b.vertical_order != null ? Number(b.vertical_order) : (b.panel_vertical_order != null ? Number(b.panel_vertical_order) : 1);
return (orderA - orderB) || (Number(a.id) - Number(b.id));
});
const N = signPanels.length; const N = signPanels.length;
const highestPanelIndex = Math.max(0, N - 1); const itemStep = 21; // Espacement vertical compact entre panneaux
const baseClearance = hasBaseSupport ? 16 : 0; // Dégagement harmonieux au-dessus du trottoir (tige visible ~12px)
const stemHeight = hasBaseSupport ? (baseClearance + (N - 1) * itemStep + 16) : 0;
// Dimensions compactes identiques à la vue desktop : const el = document.createElement('div');
// Centre du panneau le plus bas à -14px au-dessus du sol/socle el.className = 'asset-map-assembly';
// Incrément de 14px pour chaque panneau supplémentaire sur le mât el.style.zIndex = '5';
const stemHeight = hasBaseSupport ? (16 + (highestPanelIndex * 14)) : 0;
const topOffset = -(14 + (highestPanelIndex * 14));
// 1. S'il y a un poteau ou support à la base, le positionner à l'offset [0, 0] const scaler = document.createElement('div');
if (baseAsset) { scaler.className = 'assembly-scaler';
const symbology = (typeof AssetSymbology !== 'undefined') el.appendChild(scaler);
? AssetSymbology.getAssetSymbology(baseAsset)
: { color: THEMATIC_COLORS[baseAsset.thematic] || '#888888', iconClass: baseAsset.thematic_icon || 'bi-signpost-split' };
const color = symbology.color || THEMATIC_COLORS[baseAsset.thematic] || '#888888';
const iconHtml = (typeof AssetSymbology !== 'undefined')
? AssetSymbology.renderIconHtml(symbology)
: `<i class="bi ${symbology.iconClass || 'bi-signpost-split'}"></i>`;
const isSignPole = baseAsset.model_key === 'signpole' || baseAsset.model_key.includes('pole') || (baseAsset.thematic === 'sign' && baseAsset.model_key.includes('pole')); // 1. Socle / base circulaire du poteau (ancré exactement à Y=0 sur les coordonnées GPS)
if (hasBaseSupport) {
const baseDot = document.createElement('div');
baseDot.className = 'pole-base';
const baseFeatId = baseAsset.feature_id || (baseAsset.model_key && baseAsset.id ? `${baseAsset.model_key}_${baseAsset.id}` : null);
if (baseFeatId) baseDot.dataset.featureId = baseFeatId;
baseDot.title = baseAsset.name || baseAsset.code || 'Poteau';
const el = document.createElement('div'); baseDot.addEventListener('click', (e) => {
if (isSignPole) {
el.className = 'asset-map-marker-pole';
el.innerHTML = `<div class="pole-stem" style="height: ${stemHeight}px;"></div><div class="pole-base"></div>`;
el.style.zIndex = '2';
} else {
el.className = 'asset-map-marker';
el.style.borderColor = color;
el.style.color = color;
el.style.zIndex = '5';
el.innerHTML = iconHtml;
}
if (otherAssets.length > 1) {
const badge = document.createElement('span');
badge.className = 'marker-count-badge';
badge.textContent = otherAssets.length > 99 ? '99+' : otherAssets.length;
badge.title = `${otherAssets.length} équipements à la base`;
el.appendChild(badge);
}
el.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
if (e.originalEvent) { if (e.originalEvent) {
e.originalEvent._handledByAssetLayer = true; e.originalEvent._handledByAssetLayer = true;
@ -1650,42 +1927,24 @@
} }
}); });
const marker = new maplibregl.Marker({ element: el, offset: [0, 0] }) if (lastSelectedFeatureId && baseFeatId === lastSelectedFeatureId) {
.setLngLat([baseAsset.lon, baseAsset.lat]) baseDot.classList.add('selected');
.addTo(map);
marker._asset = baseAsset;
marker._group = group;
marker._element = el;
const isBaseSelected = otherAssets.some(a => {
const featId = a.feature_id || (a.model_key && a.id ? `${a.model_key}_${a.id}` : null);
return lastSelectedFeatureId && featId === lastSelectedFeatureId;
});
if (isBaseSelected) {
el.classList.add('selected');
el.style.zIndex = '50'; el.style.zIndex = '50';
selectedMarkerElement = el; selectedMarkerElement = baseDot;
} }
activeMarkers.push(marker); scaler.appendChild(baseDot);
// Tige verticale montant vers le haut depuis la base
const stem = document.createElement('div');
stem.className = 'pole-stem';
stem.style.height = `${stemHeight}px`;
scaler.appendChild(stem);
} }
// 2. Calcul des offsets verticaux pour chaque panneau sur le poteau // 2. Panneaux de signalisation empilés vers le haut
// vertical_order = 1 est le panneau principal placé au SOMMET du mât.
// Les panneaux secondaires / panonceaux (vertical_order > 1) se succèdent vers le bas le long du mât.
signPanels.sort((a, b) => {
const orderA = a.vertical_order != null ? Number(a.vertical_order) : (a.panel_vertical_order != null ? Number(a.panel_vertical_order) : 1);
const orderB = b.vertical_order != null ? Number(b.vertical_order) : (b.panel_vertical_order != null ? Number(b.panel_vertical_order) : 1);
return (orderA - orderB) || (Number(a.id) - Number(b.id));
});
// Coordonnées de référence partagées pour l'alignement parfait sur la tige
const refLon = baseAsset ? baseAsset.lon : signPanels[0].lon;
const refLat = baseAsset ? baseAsset.lat : signPanels[0].lat;
signPanels.forEach((panel, index) => { signPanels.forEach((panel, index) => {
const panelOffset = topOffset + (index * 14); const panelBottom = baseClearance + (index * itemStep);
const symbology = (typeof AssetSymbology !== 'undefined') const symbology = (typeof AssetSymbology !== 'undefined')
? AssetSymbology.getAssetSymbology(panel) ? AssetSymbology.getAssetSymbology(panel)
@ -1694,12 +1953,16 @@
? AssetSymbology.renderIconHtml(symbology) ? AssetSymbology.renderIconHtml(symbology)
: `<i class="bi ${symbology.iconClass || 'bi-sign-stop-fill'}"></i>`; : `<i class="bi ${symbology.iconClass || 'bi-sign-stop-fill'}"></i>`;
const el = document.createElement('div'); const panelEl = document.createElement('div');
el.className = 'asset-map-marker-sign'; panelEl.className = 'assembly-panel-item';
el.innerHTML = iconHtml; panelEl.style.bottom = `${panelBottom}px`;
el.style.zIndex = String(10 + (N - index)); panelEl.style.zIndex = String(10 + index);
panelEl.innerHTML = iconHtml;
el.addEventListener('click', (e) => { const featId = panel.feature_id || (panel.model_key && panel.id ? `${panel.model_key}_${panel.id}` : null);
if (featId) panelEl.dataset.featureId = featId;
panelEl.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
if (e.originalEvent) { if (e.originalEvent) {
e.originalEvent._handledByAssetLayer = true; e.originalEvent._handledByAssetLayer = true;
@ -1707,25 +1970,24 @@
openBottomSheet(panel, false, group); openBottomSheet(panel, false, group);
}); });
const marker = new maplibregl.Marker({ element: el, offset: [0, panelOffset] })
.setLngLat([refLon, refLat])
.addTo(map);
marker.setOffset([0, panelOffset]);
marker._asset = panel;
marker._group = group;
marker._element = el;
const featId = panel.feature_id || (panel.model_key && panel.id ? `${panel.model_key}_${panel.id}` : null);
if (lastSelectedFeatureId && featId === lastSelectedFeatureId) { if (lastSelectedFeatureId && featId === lastSelectedFeatureId) {
el.classList.add('selected'); panelEl.classList.add('selected');
el.style.zIndex = '50'; el.style.zIndex = '50';
selectedMarkerElement = el; selectedMarkerElement = panelEl;
} }
activeMarkers.push(marker); scaler.appendChild(panelEl);
}); });
const marker = new maplibregl.Marker({ element: el, offset: [0, 0] })
.setLngLat([refLon, refLat])
.addTo(map);
marker._asset = baseAsset || signPanels[0];
marker._group = group;
marker._element = el;
activeMarkers.push(marker);
} else { } else {
// CAS B : Emplacement sans panneau (arbres, bancs, corbeilles, poteau seul, etc.) // CAS B : Emplacement sans panneau (arbres, bancs, corbeilles, poteau seul, etc.)
group.sort((a, b) => getDisplayPriority(b) - getDisplayPriority(a)); group.sort((a, b) => getDisplayPriority(b) - getDisplayPriority(a));
@ -1743,17 +2005,63 @@
const el = document.createElement('div'); const el = document.createElement('div');
if (isSignPole) { if (isSignPole) {
el.className = 'asset-map-marker-pole'; el.className = 'asset-map-assembly';
el.innerHTML = '<div class="pole-stem" style="height: 16px;"></div><div class="pole-base"></div>';
el.style.zIndex = '5'; el.style.zIndex = '5';
const scaler = document.createElement('div');
scaler.className = 'assembly-scaler';
el.appendChild(scaler);
const baseDot = document.createElement('div');
baseDot.className = 'pole-base';
const baseFeatId = primaryAsset.feature_id || (primaryAsset.model_key && primaryAsset.id ? `${primaryAsset.model_key}_${primaryAsset.id}` : null);
if (baseFeatId) baseDot.dataset.featureId = baseFeatId;
baseDot.title = primaryAsset.name || primaryAsset.code || 'Poteau';
baseDot.addEventListener('click', (e) => {
e.stopPropagation();
if (e.originalEvent) {
e.originalEvent._handledByAssetLayer = true;
}
if (group.length > 1) {
openMultiBottomSheet(group);
} else {
openBottomSheet(primaryAsset, false, group);
}
});
if (lastSelectedFeatureId && baseFeatId === lastSelectedFeatureId) {
baseDot.classList.add('selected');
el.style.zIndex = '50';
selectedMarkerElement = baseDot;
}
scaler.appendChild(baseDot);
const stem = document.createElement('div');
stem.className = 'pole-stem';
stem.style.height = '18px';
scaler.appendChild(stem);
} else { } else {
el.className = 'asset-map-marker'; el.className = 'asset-map-marker';
el.style.borderColor = color; el.style.borderColor = color;
el.style.color = color; el.style.color = color;
el.innerHTML = iconHtml; el.innerHTML = iconHtml;
el.addEventListener('click', (e) => {
e.stopPropagation();
if (e.originalEvent) {
e.originalEvent._handledByAssetLayer = true;
}
if (group.length > 1) {
openMultiBottomSheet(group);
} else {
openBottomSheet(primaryAsset, false, group);
}
});
} }
if (group.length > 1) { if (!isSignPole && group.length > 1) {
const badge = document.createElement('span'); const badge = document.createElement('span');
badge.className = 'marker-count-badge'; badge.className = 'marker-count-badge';
badge.textContent = group.length > 99 ? '99+' : group.length; badge.textContent = group.length > 99 ? '99+' : group.length;
@ -1761,18 +2069,6 @@
el.appendChild(badge); el.appendChild(badge);
} }
el.addEventListener('click', (e) => {
e.stopPropagation();
if (e.originalEvent) {
e.originalEvent._handledByAssetLayer = true;
}
if (group.length > 1) {
openMultiBottomSheet(group);
} else {
openBottomSheet(primaryAsset, false, group);
}
});
const marker = new maplibregl.Marker({ element: el, offset: [0, 0] }) const marker = new maplibregl.Marker({ element: el, offset: [0, 0] })
.setLngLat([primaryAsset.lon, primaryAsset.lat]) .setLngLat([primaryAsset.lon, primaryAsset.lat])
.addTo(map); .addTo(map);
@ -1955,6 +2251,8 @@
if (selectedMarkerElement) { if (selectedMarkerElement) {
selectedMarkerElement.classList.remove('selected'); selectedMarkerElement.classList.remove('selected');
selectedMarkerElement.style.zIndex = ''; selectedMarkerElement.style.zIndex = '';
const parentAssembly = selectedMarkerElement.closest ? selectedMarkerElement.closest('.asset-map-assembly') : null;
if (parentAssembly) parentAssembly.style.zIndex = '';
selectedMarkerElement = null; selectedMarkerElement = null;
} }
const currentFeatId = asset.feature_id || (asset.model_key && asset.id ? `${asset.model_key}_${asset.id}` : null); const currentFeatId = asset.feature_id || (asset.model_key && asset.id ? `${asset.model_key}_${asset.id}` : null);
@ -1970,9 +2268,17 @@
return m._group && m._group.some(g => String(g.feature_id) === String(currentFeatId) || (g.model_key === asset.model_key && String(g.id) === String(asset.id))); return m._group && m._group.some(g => String(g.feature_id) === String(currentFeatId) || (g.model_key === asset.model_key && String(g.id) === String(asset.id)));
}); });
if (matchingMarker && matchingMarker._element) { if (matchingMarker && matchingMarker._element) {
selectedMarkerElement = matchingMarker._element; const subItem = matchingMarker._element.querySelector ? matchingMarker._element.querySelector(`[data-feature-id="${currentFeatId}"]`) : null;
selectedMarkerElement.classList.add('selected'); if (subItem) {
selectedMarkerElement.style.zIndex = '50'; selectedMarkerElement = subItem;
subItem.classList.add('selected');
subItem.style.zIndex = '50';
matchingMarker._element.style.zIndex = '50';
} else {
selectedMarkerElement = matchingMarker._element;
selectedMarkerElement.classList.add('selected');
selectedMarkerElement.style.zIndex = '50';
}
} }
} }
@ -2033,6 +2339,8 @@
if (selectedMarkerElement) { if (selectedMarkerElement) {
selectedMarkerElement.classList.remove('selected'); selectedMarkerElement.classList.remove('selected');
selectedMarkerElement.style.zIndex = ''; selectedMarkerElement.style.zIndex = '';
const parentAssembly = selectedMarkerElement.closest ? selectedMarkerElement.closest('.asset-map-assembly') : null;
if (parentAssembly) parentAssembly.style.zIndex = '';
selectedMarkerElement = null; selectedMarkerElement = null;
} }
} }
@ -2066,6 +2374,8 @@
if (selectedMarkerElement) { if (selectedMarkerElement) {
selectedMarkerElement.classList.remove('selected'); selectedMarkerElement.classList.remove('selected');
selectedMarkerElement.style.zIndex = ''; selectedMarkerElement.style.zIndex = '';
const parentAssembly = selectedMarkerElement.closest ? selectedMarkerElement.closest('.asset-map-assembly') : null;
if (parentAssembly) parentAssembly.style.zIndex = '';
selectedMarkerElement = null; selectedMarkerElement = null;
} }
if (multiContext && multiContext.length > 1) { if (multiContext && multiContext.length > 1) {