feat: add multi-asset bottom sheet and selection UI for overlapping map markers
This commit is contained in:
parent
d33b49647b
commit
bd8897499b
1 changed files with 322 additions and 33 deletions
|
|
@ -282,6 +282,63 @@
|
||||||
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
|
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Marqueur ponctuel sélectionné */
|
||||||
|
.asset-map-marker.selected {
|
||||||
|
z-index: 50 !important;
|
||||||
|
border-color: #ca8a04 !important;
|
||||||
|
border-width: 3px !important;
|
||||||
|
background-color: #fef08a !important;
|
||||||
|
box-shadow: 0 0 0 4px rgba(254, 240, 138, 0.9), 0 4px 14px rgba(0, 0, 0, 0.45) !important;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.asset-map-marker.selected::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -8px;
|
||||||
|
left: -8px;
|
||||||
|
right: -8px;
|
||||||
|
bottom: -8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2.5px solid #eab308;
|
||||||
|
box-shadow: 0 0 12px rgba(234, 179, 8, 0.85);
|
||||||
|
pointer-events: none;
|
||||||
|
animation: marker-selected-pulse 1.8s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
@keyframes marker-selected-pulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.4);
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.asset-map-marker.selected i,
|
||||||
|
.asset-map-marker.selected img {
|
||||||
|
color: #854d0e !important;
|
||||||
|
transform: scale(1.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cartes d'assets multiples au même endroit */
|
||||||
|
.multi-asset-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.65rem 0.85rem;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 0.85rem;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
|
||||||
|
}
|
||||||
|
.multi-asset-card:active {
|
||||||
|
background-color: #e2e8f0;
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
/* Volet inférieur (Bottom Sheet) pour prévisualisation */
|
/* Volet inférieur (Bottom Sheet) pour prévisualisation */
|
||||||
.bottom-sheet-preview {
|
.bottom-sheet-preview {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
@ -552,6 +609,9 @@
|
||||||
<!-- VOLET INFÉRIEUR (Bottom Sheet Modal) pour la Carte -->
|
<!-- VOLET INFÉRIEUR (Bottom Sheet Modal) pour la Carte -->
|
||||||
<div id="assetBottomSheet" class="bottom-sheet-preview">
|
<div id="assetBottomSheet" class="bottom-sheet-preview">
|
||||||
<div class="bottom-sheet-drag-handle" id="bottomSheetCloseHandle"></div>
|
<div class="bottom-sheet-drag-handle" id="bottomSheetCloseHandle"></div>
|
||||||
|
<button type="button" id="btnBackToMultiSheet" class="btn btn-sm btn-link text-decoration-none p-0 d-none text-primary mb-2">
|
||||||
|
<i class="bi bi-arrow-left me-1"></i><span id="btnBackToMultiSheetText">{% translate "Retour aux assets à cet endroit" %}</span>
|
||||||
|
</button>
|
||||||
<div class="d-flex align-items-start justify-content-between mb-2">
|
<div class="d-flex align-items-start justify-content-between mb-2">
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<div class="asset-icon-circle" id="sheetIconCircle">
|
<div class="asset-icon-circle" id="sheetIconCircle">
|
||||||
|
|
@ -585,6 +645,27 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- VOLET MULTI-ASSETS (quand plusieurs assets sont au même endroit) -->
|
||||||
|
<div id="multiAssetBottomSheet" class="bottom-sheet-preview">
|
||||||
|
<div class="bottom-sheet-drag-handle" id="multiSheetCloseHandle"></div>
|
||||||
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<div class="rounded-circle bg-primary-subtle text-primary d-inline-flex align-items-center justify-content-center" style="width: 38px; height: 38px; flex-shrink: 0;">
|
||||||
|
<i class="bi bi-layers-half fs-5"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="fw-bold text-dark fs-6" id="multiSheetTitle">{% translate "Plusieurs assets à cet endroit" %}</div>
|
||||||
|
<div class="small text-muted" id="multiSheetCount">2 {% translate "assets trouvés" %}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" id="btnMultiSheetClose" aria-label="{% translate 'Fermer' %}"></button>
|
||||||
|
</div>
|
||||||
|
<div class="text-secondary small mb-2">{% translate "Sélectionnez l'asset à consulter :" %}</div>
|
||||||
|
<div id="multiAssetListContainer" class="d-flex flex-column gap-1.5" style="max-height: 52vh; overflow-y: auto;">
|
||||||
|
<!-- Rempli dynamiquement via JS -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
{% block extra_scripts %}
|
{% block extra_scripts %}
|
||||||
|
|
@ -695,6 +776,18 @@
|
||||||
const sheetDetailLink = document.getElementById('sheetDetailLink');
|
const sheetDetailLink = document.getElementById('sheetDetailLink');
|
||||||
const sheetRouteLink = document.getElementById('sheetRouteLink');
|
const sheetRouteLink = document.getElementById('sheetRouteLink');
|
||||||
|
|
||||||
|
// Éléments du volet Multi-Assets
|
||||||
|
const multiAssetBottomSheet = document.getElementById('multiAssetBottomSheet');
|
||||||
|
const multiSheetCloseHandle = document.getElementById('multiSheetCloseHandle');
|
||||||
|
const btnMultiSheetClose = document.getElementById('btnMultiSheetClose');
|
||||||
|
const multiSheetCount = document.getElementById('multiSheetCount');
|
||||||
|
const multiAssetListContainer = document.getElementById('multiAssetListContainer');
|
||||||
|
const btnBackToMultiSheet = document.getElementById('btnBackToMultiSheet');
|
||||||
|
const btnBackToMultiSheetText = document.getElementById('btnBackToMultiSheetText');
|
||||||
|
|
||||||
|
let lastMultiAssets = null;
|
||||||
|
let selectedMarkerElement = null;
|
||||||
|
|
||||||
// Couleurs par thématique (source unique : AssetSymbology)
|
// Couleurs par thématique (source unique : AssetSymbology)
|
||||||
const THEMATIC_COLORS = (typeof AssetSymbology !== 'undefined')
|
const THEMATIC_COLORS = (typeof AssetSymbology !== 'undefined')
|
||||||
? AssetSymbology.THEMATIC_COLORS
|
? AssetSymbology.THEMATIC_COLORS
|
||||||
|
|
@ -851,15 +944,28 @@
|
||||||
layersConfigured = true;
|
layersConfigured = true;
|
||||||
['assets-fill', 'assets-line'].forEach(layerId => {
|
['assets-fill', 'assets-line'].forEach(layerId => {
|
||||||
map.on('click', layerId, (e) => {
|
map.on('click', layerId, (e) => {
|
||||||
if (e.features && e.features.length > 0) {
|
|
||||||
const feature = e.features[0];
|
|
||||||
const props = feature.properties || {};
|
|
||||||
const featId = feature.id || props.feature_id;
|
|
||||||
const asset = lastResults.find(a => String(a.feature_id) === String(featId) || (a.model_key === props.model_key && String(a.id) === String(props.id))) || props;
|
|
||||||
if (e.originalEvent) {
|
if (e.originalEvent) {
|
||||||
e.originalEvent._handledByAssetLayer = true;
|
e.originalEvent._handledByAssetLayer = true;
|
||||||
}
|
}
|
||||||
openBottomSheet(asset);
|
const bbox = [[e.point.x - 14, e.point.y - 14], [e.point.x + 14, e.point.y + 14]];
|
||||||
|
const rendered = map.queryRenderedFeatures(bbox, { layers: ['assets-fill', 'assets-line'] }) || [];
|
||||||
|
const foundMap = new Map();
|
||||||
|
|
||||||
|
rendered.forEach(feat => {
|
||||||
|
const props = feat.properties || {};
|
||||||
|
const featId = feat.id || props.feature_id;
|
||||||
|
const asset = lastResults.find(a => String(a.feature_id) === String(featId) || (a.model_key === props.model_key && String(a.id) === String(props.id))) || props;
|
||||||
|
const key = String(asset.feature_id || (asset.model_key && asset.id ? `${asset.model_key}_${asset.id}` : asset.code));
|
||||||
|
if (!foundMap.has(key)) {
|
||||||
|
foundMap.set(key, asset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const assetsList = Array.from(foundMap.values());
|
||||||
|
if (assetsList.length > 1) {
|
||||||
|
openMultiBottomSheet(assetsList);
|
||||||
|
} else if (assetsList.length === 1) {
|
||||||
|
openBottomSheet(assetsList[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
map.on('mouseenter', layerId, () => {
|
map.on('mouseenter', layerId, () => {
|
||||||
|
|
@ -876,6 +982,7 @@
|
||||||
map.on('click', (e) => {
|
map.on('click', (e) => {
|
||||||
if (e.originalEvent && e.originalEvent._handledByAssetLayer) return;
|
if (e.originalEvent && e.originalEvent._handledByAssetLayer) return;
|
||||||
closeBottomSheet();
|
closeBottomSheet();
|
||||||
|
closeMultiBottomSheet();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Détection du déplacement ou zoom pour filtrage par zone visible (bbox) et synchronisation de la liste
|
// Détection du déplacement ou zoom pour filtrage par zone visible (bbox) et synchronisation de la liste
|
||||||
|
|
@ -982,6 +1089,10 @@
|
||||||
assetsListContainer.innerHTML = '';
|
assetsListContainer.innerHTML = '';
|
||||||
activeMarkers.forEach(m => m.remove());
|
activeMarkers.forEach(m => m.remove());
|
||||||
activeMarkers = [];
|
activeMarkers = [];
|
||||||
|
selectedMarkerElement = null;
|
||||||
|
lastMultiAssets = null;
|
||||||
|
closeBottomSheet();
|
||||||
|
closeMultiBottomSheet();
|
||||||
lastGeojson = { type: 'FeatureCollection', features: [] };
|
lastGeojson = { type: 'FeatureCollection', features: [] };
|
||||||
if (map && map.getSource('assets')) {
|
if (map && map.getSource('assets')) {
|
||||||
map.getSource('assets').setData(lastGeojson);
|
map.getSource('assets').setData(lastGeojson);
|
||||||
|
|
@ -1168,6 +1279,7 @@
|
||||||
// Supprimer les anciens marqueurs
|
// Supprimer les anciens marqueurs
|
||||||
activeMarkers.forEach(m => m.remove());
|
activeMarkers.forEach(m => m.remove());
|
||||||
activeMarkers = [];
|
activeMarkers = [];
|
||||||
|
selectedMarkerElement = null;
|
||||||
|
|
||||||
(results || []).forEach(asset => {
|
(results || []).forEach(asset => {
|
||||||
if (asset.lon === null || asset.lat === null) return;
|
if (asset.lon === null || asset.lat === null) return;
|
||||||
|
|
@ -1192,23 +1304,161 @@
|
||||||
|
|
||||||
el.addEventListener('click', (e) => {
|
el.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
if (e.originalEvent) {
|
||||||
|
e.originalEvent._handledByAssetLayer = true;
|
||||||
|
}
|
||||||
|
const matchingAssets = findAssetsAtMarker(asset);
|
||||||
|
if (matchingAssets.length > 1) {
|
||||||
|
openMultiBottomSheet(matchingAssets);
|
||||||
|
} else {
|
||||||
openBottomSheet(asset);
|
openBottomSheet(asset);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const marker = new maplibregl.Marker({ element: el })
|
const marker = new maplibregl.Marker({ element: el })
|
||||||
.setLngLat([asset.lon, asset.lat])
|
.setLngLat([asset.lon, asset.lat])
|
||||||
.addTo(map);
|
.addTo(map);
|
||||||
|
|
||||||
|
marker._asset = asset;
|
||||||
|
marker._element = el;
|
||||||
|
|
||||||
|
const featId = asset.feature_id || (asset.model_key && asset.id ? `${asset.model_key}_${asset.id}` : null);
|
||||||
|
if (lastSelectedFeatureId && featId === lastSelectedFeatureId) {
|
||||||
|
el.classList.add('selected');
|
||||||
|
el.style.zIndex = '50';
|
||||||
|
selectedMarkerElement = el;
|
||||||
|
}
|
||||||
|
|
||||||
activeMarkers.push(marker);
|
activeMarkers.push(marker);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ne jamais modifier la visualisation (zoom ou pan) de manière automatique lors d'une action de filtre ou de recherche
|
// Ne jamais modifier la visualisation (zoom ou pan) de manière automatique lors d'une action de filtre ou de recherche
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Volet d'aperçu inférieur (Bottom Sheet)
|
// Trouver tous les assets situés au même endroit ou à proximité immédiate (tap sur écran)
|
||||||
function openBottomSheet(asset) {
|
function findAssetsAtMarker(targetAsset) {
|
||||||
|
if (!map || !targetAsset) return [targetAsset];
|
||||||
|
const targetPoint = (targetAsset.lon !== null && targetAsset.lat !== null)
|
||||||
|
? map.project([targetAsset.lon, targetAsset.lat])
|
||||||
|
: null;
|
||||||
|
const tolerancePx = 24; // Rayon de tolérance pour le tap tactile
|
||||||
|
const foundMap = new Map();
|
||||||
|
const targetKey = String(targetAsset.feature_id || (targetAsset.model_key && targetAsset.id ? `${targetAsset.model_key}_${targetAsset.id}` : targetAsset.code));
|
||||||
|
foundMap.set(targetKey, targetAsset);
|
||||||
|
|
||||||
|
(lastResults || []).forEach(a => {
|
||||||
|
if (a.lon === null || a.lat === null) return;
|
||||||
|
const key = String(a.feature_id || (a.model_key && a.id ? `${a.model_key}_${a.id}` : a.code));
|
||||||
|
if (foundMap.has(key)) return;
|
||||||
|
|
||||||
|
// 1. Coordonnées identiques ou quasi-identiques
|
||||||
|
const dLon = Math.abs(a.lon - targetAsset.lon);
|
||||||
|
const dLat = Math.abs(a.lat - targetAsset.lat);
|
||||||
|
if (dLon < 0.00005 && dLat < 0.00005) {
|
||||||
|
foundMap.set(key, a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Distance à l'écran (marqueurs superposés au zoom actuel)
|
||||||
|
if (targetPoint) {
|
||||||
|
const p = map.project([a.lon, a.lat]);
|
||||||
|
const dist = Math.hypot(p.x - targetPoint.x, p.y - targetPoint.y);
|
||||||
|
if (dist <= tolerancePx) {
|
||||||
|
foundMap.set(key, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(foundMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5a. Volet Multi-Assets (quand plusieurs éléments se trouvent au même endroit)
|
||||||
|
function openMultiBottomSheet(assetsList) {
|
||||||
|
if (!assetsList || assetsList.length === 0) return;
|
||||||
|
lastMultiAssets = assetsList;
|
||||||
|
|
||||||
|
// Fermer le bottom sheet simple
|
||||||
|
bottomSheet.classList.remove('active');
|
||||||
|
|
||||||
|
if (multiSheetCount) {
|
||||||
|
multiSheetCount.textContent = `${assetsList.length} {% translate "assets trouvés à cet endroit" %}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
multiAssetListContainer.innerHTML = '';
|
||||||
|
assetsList.forEach(asset => {
|
||||||
|
const symbology = (typeof AssetSymbology !== 'undefined')
|
||||||
|
? AssetSymbology.getAssetSymbology(asset)
|
||||||
|
: { color: THEMATIC_COLORS[asset.thematic] || '#2f3e8b', iconClass: asset.thematic_icon || 'bi-pin-map' };
|
||||||
|
const color = symbology.color || THEMATIC_COLORS[asset.thematic] || '#2f3e8b';
|
||||||
|
const iconHtml = (typeof AssetSymbology !== 'undefined')
|
||||||
|
? AssetSymbology.renderIconHtml(symbology)
|
||||||
|
: `<i class="bi ${symbology.iconClass || 'bi-pin-map'}"></i>`;
|
||||||
|
|
||||||
|
const statusClass = `badge-status badge-status-${asset.status || 'active'}`;
|
||||||
|
const distanceBadge = (asset.distance_m !== null && asset.distance_m !== undefined)
|
||||||
|
? `<span class="badge bg-primary-subtle text-primary ms-auto me-1 text-nowrap"><i class="bi bi-geo-alt me-1"></i>${formatDistance(asset.distance_m)}</span>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'multi-asset-card';
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="asset-icon-circle" style="background-color: ${color}18; color: ${color}; width: 36px; height: 36px; font-size: 1.1rem; flex-shrink: 0;">
|
||||||
|
${iconHtml}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1 text-truncate">
|
||||||
|
<div class="d-flex align-items-center gap-1.5 flex-wrap">
|
||||||
|
<span class="fw-bold text-dark text-truncate">${escapeHtml(asset.code)}</span>
|
||||||
|
<span class="${statusClass}">${escapeHtml(asset.status_display || asset.status || 'Actif')}</span>
|
||||||
|
</div>
|
||||||
|
<div class="small text-secondary text-truncate">${escapeHtml(asset.name || asset.element_type || asset.thematic_name || '')}</div>
|
||||||
|
</div>
|
||||||
|
${distanceBadge}
|
||||||
|
<i class="bi bi-chevron-right text-muted flex-shrink-0"></i>
|
||||||
|
`;
|
||||||
|
|
||||||
|
card.addEventListener('click', (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
closeMultiBottomSheet(true); // Conserve le contexte multi pour le bouton retour
|
||||||
|
openBottomSheet(asset, true); // fromMulti = true
|
||||||
|
});
|
||||||
|
|
||||||
|
multiAssetListContainer.appendChild(card);
|
||||||
|
});
|
||||||
|
|
||||||
|
multiAssetBottomSheet.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeMultiBottomSheet(keepMultiContext = false) {
|
||||||
|
if (multiAssetBottomSheet) {
|
||||||
|
multiAssetBottomSheet.classList.remove('active');
|
||||||
|
}
|
||||||
|
if (!keepMultiContext) {
|
||||||
|
lastMultiAssets = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5b. Volet d'aperçu individuel (Bottom Sheet)
|
||||||
|
function openBottomSheet(asset, fromMulti = false) {
|
||||||
if (!asset) return;
|
if (!asset) return;
|
||||||
|
|
||||||
|
// Fermer le volet multi-assets
|
||||||
|
if (multiAssetBottomSheet) {
|
||||||
|
multiAssetBottomSheet.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bouton retour vers la liste multi si ouvert depuis une sélection multiple
|
||||||
|
if (btnBackToMultiSheet) {
|
||||||
|
if (fromMulti && lastMultiAssets && lastMultiAssets.length > 1) {
|
||||||
|
btnBackToMultiSheet.classList.remove('d-none');
|
||||||
|
if (btnBackToMultiSheetText) {
|
||||||
|
btnBackToMultiSheetText.textContent = `{% translate "Retour aux" %} ${lastMultiAssets.length} {% translate "assets à cet endroit" %}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
btnBackToMultiSheet.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mettre en surbrillance sur la carte (pour surfaces polygonales et polylignes)
|
// Mettre en surbrillance sur la carte (pour surfaces polygonales et polylignes)
|
||||||
if (map && map.getSource('assets')) {
|
if (map && map.getSource('assets')) {
|
||||||
if (lastSelectedFeatureId) {
|
if (lastSelectedFeatureId) {
|
||||||
|
|
@ -1225,6 +1475,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Symbologie de sélection pour marqueur ponctuel
|
||||||
|
if (selectedMarkerElement) {
|
||||||
|
selectedMarkerElement.classList.remove('selected');
|
||||||
|
selectedMarkerElement.style.zIndex = '';
|
||||||
|
selectedMarkerElement = null;
|
||||||
|
}
|
||||||
|
const currentFeatId = asset.feature_id || (asset.model_key && asset.id ? `${asset.model_key}_${asset.id}` : null);
|
||||||
|
if (currentFeatId) {
|
||||||
|
const matchingMarker = activeMarkers.find(m => m._asset && (
|
||||||
|
String(m._asset.feature_id) === String(currentFeatId) ||
|
||||||
|
(m._asset.model_key === asset.model_key && String(m._asset.id) === String(asset.id))
|
||||||
|
));
|
||||||
|
if (matchingMarker && matchingMarker._element) {
|
||||||
|
selectedMarkerElement = matchingMarker._element;
|
||||||
|
selectedMarkerElement.classList.add('selected');
|
||||||
|
selectedMarkerElement.style.zIndex = '50';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sheetCode.textContent = asset.code || '-';
|
sheetCode.textContent = asset.code || '-';
|
||||||
sheetName.textContent = asset.name || asset.element_type || '';
|
sheetName.textContent = asset.name || asset.element_type || '';
|
||||||
sheetThematicName.textContent = asset.thematic_name || '';
|
sheetThematicName.textContent = asset.thematic_name || '';
|
||||||
|
|
@ -1263,30 +1532,44 @@
|
||||||
|
|
||||||
bottomSheet.classList.add('active');
|
bottomSheet.classList.add('active');
|
||||||
|
|
||||||
// Cadrage ou recentrage sur la carte
|
// NOTE : Le centrage et le zoom ont été supprimés lors du tap pour préserver la vue choisie par l'utilisateur.
|
||||||
if (map) {
|
|
||||||
let bbox = asset.bbox;
|
|
||||||
if (typeof bbox === 'string') {
|
|
||||||
try { bbox = JSON.parse(bbox); } catch (e) { bbox = null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(bbox) && bbox.length === 4 &&
|
|
||||||
isFinite(bbox[0]) && isFinite(bbox[1]) && isFinite(bbox[2]) && isFinite(bbox[3]) &&
|
|
||||||
(bbox[2] > bbox[0] || bbox[3] > bbox[1])) {
|
|
||||||
// Surface ou ligne : zoomer sur son emprise
|
|
||||||
isProgrammaticMove = true;
|
|
||||||
map.fitBounds(
|
|
||||||
[[bbox[0], bbox[1]], [bbox[2], bbox[3]]],
|
|
||||||
{ padding: { top: 60, bottom: 220, left: 40, right: 40 }, maxZoom: 18 }
|
|
||||||
);
|
|
||||||
} else if (asset.lon !== null && asset.lat !== null && isFinite(asset.lon) && isFinite(asset.lat)) {
|
|
||||||
isProgrammaticMove = true;
|
|
||||||
map.easeTo({ center: [asset.lon, asset.lat], zoom: Math.max(map.getZoom(), 16), offset: [0, 80] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeBottomSheet() {
|
function closeBottomSheet() {
|
||||||
|
bottomSheet.classList.remove('active');
|
||||||
|
closeMultiBottomSheet();
|
||||||
|
if (map && map.getSource('assets') && lastSelectedFeatureId) {
|
||||||
|
try {
|
||||||
|
map.setFeatureState({ source: 'assets', id: lastSelectedFeatureId }, { selected: false });
|
||||||
|
} catch (e) {}
|
||||||
|
lastSelectedFeatureId = null;
|
||||||
|
}
|
||||||
|
if (selectedMarkerElement) {
|
||||||
|
selectedMarkerElement.classList.remove('selected');
|
||||||
|
selectedMarkerElement.style.zIndex = '';
|
||||||
|
selectedMarkerElement = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
btnSheetClose.addEventListener('click', () => {
|
||||||
|
closeBottomSheet();
|
||||||
|
lastMultiAssets = null;
|
||||||
|
});
|
||||||
|
bottomSheetCloseHandle.addEventListener('click', () => {
|
||||||
|
closeBottomSheet();
|
||||||
|
lastMultiAssets = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (btnMultiSheetClose) {
|
||||||
|
btnMultiSheetClose.addEventListener('click', () => closeMultiBottomSheet());
|
||||||
|
}
|
||||||
|
if (multiSheetCloseHandle) {
|
||||||
|
multiSheetCloseHandle.addEventListener('click', () => closeMultiBottomSheet());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnBackToMultiSheet) {
|
||||||
|
btnBackToMultiSheet.addEventListener('click', () => {
|
||||||
|
const multiContext = lastMultiAssets;
|
||||||
bottomSheet.classList.remove('active');
|
bottomSheet.classList.remove('active');
|
||||||
if (map && map.getSource('assets') && lastSelectedFeatureId) {
|
if (map && map.getSource('assets') && lastSelectedFeatureId) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1294,10 +1577,16 @@
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
lastSelectedFeatureId = null;
|
lastSelectedFeatureId = null;
|
||||||
}
|
}
|
||||||
|
if (selectedMarkerElement) {
|
||||||
|
selectedMarkerElement.classList.remove('selected');
|
||||||
|
selectedMarkerElement.style.zIndex = '';
|
||||||
|
selectedMarkerElement = null;
|
||||||
|
}
|
||||||
|
if (multiContext && multiContext.length > 1) {
|
||||||
|
openMultiBottomSheet(multiContext);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
btnSheetClose.addEventListener('click', closeBottomSheet);
|
|
||||||
bottomSheetCloseHandle.addEventListener('click', closeBottomSheet);
|
|
||||||
|
|
||||||
// 6. Gestion du GPS et localisation
|
// 6. Gestion du GPS et localisation
|
||||||
function locateUser() {
|
function locateUser() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue