diff --git a/loko/mobile/templates/mobile/assets/assets_index_mobile.html b/loko/mobile/templates/mobile/assets/assets_index_mobile.html index cae50c5..dbd4b47 100644 --- a/loko/mobile/templates/mobile/assets/assets_index_mobile.html +++ b/loko/mobile/templates/mobile/assets/assets_index_mobile.html @@ -285,8 +285,8 @@ /* Marqueur spécifique panneau de signalisation (sans cercle bleu/blanc) */ .asset-map-marker-sign { - width: 32px; - height: 32px; + width: 22px; + height: 22px; display: flex; align-items: center; justify-content: center; @@ -299,37 +299,46 @@ box-sizing: border-box; transition: none !important; } + .asset-map-marker-sign::before { + content: ''; + position: absolute; + top: -4px; + bottom: -4px; + left: -4px; + right: -4px; + cursor: pointer; + } .asset-map-marker-sign img { - width: 30px; - height: 30px; + width: 20px; + height: 20px; object-fit: contain; display: block; pointer-events: none; - filter: drop-shadow(0 2px 4px rgba(0,0,0,0.45)); + filter: drop-shadow(0 1px 3px rgba(0,0,0,0.45)); transition: transform 0.15s ease; } .asset-map-marker-sign:hover img, .asset-map-marker-sign:active img { transform: scale(1.2); - filter: drop-shadow(0 3px 8px rgba(0,0,0,0.6)); + filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6)); } .asset-map-marker-sign i { display: inline-flex; align-items: center; justify-content: center; - width: 28px; - height: 28px; - border-radius: 4px; + width: 18px; + height: 18px; + border-radius: 3px; background: #ffffff; - border: 2px solid #0022ff; - color: #0022ff; - font-size: 1rem; - box-shadow: 0 1px 4px rgba(0,0,0,0.35); + 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-sign:hover i, .asset-map-marker-sign:active i { transform: scale(1.2); - box-shadow: 0 2px 8px rgba(0,0,0,0.5); + box-shadow: 0 2px 6px rgba(0,0,0,0.4); } /* Marqueur spécifique poteau de signalisation (symbole desktop : socle circulaire + tige verticale) */ @@ -359,8 +368,8 @@ cursor: pointer; } .asset-map-marker-pole .pole-base { - width: 10px; - height: 10px; + width: 8px; + height: 8px; border-radius: 50%; background-color: #888888; border: 1.5px solid #333333; @@ -378,7 +387,7 @@ bottom: 50%; left: 50%; transform: translateX(-50%); - width: 3.5px; + width: 2.5px; background-color: #555555; border-left: 0.5px solid #222222; border-right: 0.5px solid #222222; @@ -465,24 +474,24 @@ } .asset-map-marker-sign.selected img { transform: scale(1.25); - filter: drop-shadow(0 0 5px #eab308) drop-shadow(0 0 10px #fde047) drop-shadow(0 3px 6px rgba(0,0,0,0.5)) !important; + 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-sign.selected i { border-color: #ca8a04; color: #854d0e; transform: scale(1.2); - box-shadow: 0 0 10px rgba(234, 179, 8, 0.85); + box-shadow: 0 0 8px rgba(234, 179, 8, 0.85); } .asset-map-marker-sign.selected::after { content: ''; position: absolute; - top: -6px; - left: -6px; - right: -6px; - bottom: -6px; + top: -4px; + left: -4px; + right: -4px; + bottom: -4px; border-radius: 50%; - border: 2.5px solid #eab308; - box-shadow: 0 0 10px rgba(234, 179, 8, 0.85); + 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; } @@ -1505,17 +1514,52 @@ const group = [asset]; processedSet.add(key); + const isAssetPole = asset.model_key && asset.model_key.includes('pole'); + punctualAssets.forEach(other => { const otherKey = String(other.feature_id || `${other.model_key}_${other.id}`); if (processedSet.has(otherKey)) return; - const samePole = (other.pole_id && asset.pole_id && String(other.pole_id) === String(asset.pole_id)) - || (asset.model_key && asset.model_key.includes('pole') && String(other.pole_id) === String(asset.id)) - || (other.model_key && other.model_key.includes('pole') && String(asset.pole_id) === String(other.id)); + const isOtherPole = other.model_key && other.model_key.includes('pole'); - const dLon = Math.abs(other.lon - asset.lon); - const dLat = Math.abs(other.lat - asset.lat); - const isSameLocation = samePole || (dLon < 0.00005 && dLat < 0.00005); + // Règle 1 : Deux poteaux distincts ne doivent JAMAIS être fusionnés + if (isAssetPole && isOtherPole && String(asset.id) !== String(other.id)) { + return; + } + // Règle 2 : Deux panneaux rattachés à des poteaux différents ne doivent JAMAIS être fusionnés + if (asset.pole_id && other.pole_id && String(asset.pole_id) !== String(other.pole_id)) { + return; + } + // Règle 3 : Un panneau et un poteau distinct ne doivent JAMAIS être fusionnés + if (isAssetPole && other.pole_id && String(asset.id) !== String(other.pole_id)) { + return; + } + if (isOtherPole && asset.pole_id && String(other.id) !== String(asset.pole_id)) { + return; + } + + const samePole = (other.pole_id && asset.pole_id && String(other.pole_id) === String(asset.pole_id)) + || (isAssetPole && String(other.pole_id) === String(asset.id)) + || (isOtherPole && String(asset.pole_id) === String(other.id)); + + const isSignRelated = isAssetPole || isOtherPole || asset.model_key === 'signpanel' || other.model_key === 'signpanel' || !!asset.pole_id || !!other.pole_id; + + let isSameLocation = false; + if (isSignRelated) { + // Pour les panneaux et poteaux, regrouper uniquement si même poteau + // ou si coordonnées strictement identiques (< 1m) pour panneaux sans pole_id + if (samePole) { + isSameLocation = true; + } else if (!asset.pole_id && !other.pole_id && !isAssetPole && !isOtherPole && asset.model_key === 'signpanel' && other.model_key === 'signpanel') { + const dLon = Math.abs(other.lon - asset.lon); + const dLat = Math.abs(other.lat - asset.lat); + isSameLocation = (dLon < 0.000015 && dLat < 0.000015); + } + } else { + const dLon = Math.abs(other.lon - asset.lon); + const dLat = Math.abs(other.lat - asset.lat); + isSameLocation = (dLon < 0.000015 && dLat < 0.000015); + } if (isSameLocation) { group.push(other); @@ -1547,11 +1591,22 @@ // CAS A : Emplacement avec un ou plusieurs panneaux de signalisation if (signPanels.length > 0) { - // 1. S'il y a un poteau ou support à la base, le positionner à l'offset [0, 0] - if (otherAssets.length > 0) { - otherAssets.sort((a, b) => getDisplayPriority(b) - getDisplayPriority(a)); - const baseAsset = otherAssets[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 baseAsset = poleAsset || (otherAssets.length > 0 ? otherAssets[0] : null); + const hasBaseSupport = !!baseAsset; + const N = signPanels.length; + const highestPanelIndex = Math.max(0, N - 1); + + // Dimensions compactes identiques à la vue desktop : + // Centre du panneau le plus bas à -14px au-dessus du sol/socle + // Incrément de 14px pour chaque panneau supplémentaire sur le mât + 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] + if (baseAsset) { const symbology = (typeof AssetSymbology !== 'undefined') ? AssetSymbology.getAssetSymbology(baseAsset) : { color: THEMATIC_COLORS[baseAsset.thematic] || '#888888', iconClass: baseAsset.thematic_icon || 'bi-signpost-split' }; @@ -1565,8 +1620,6 @@ const el = document.createElement('div'); if (isSignPole) { el.className = 'asset-map-marker-pole'; - const highestPanelIndex = Math.max(0, signPanels.length - 1); - const stemHeight = 36 + (highestPanelIndex * 34); el.innerHTML = `
`; el.style.zIndex = '2'; } else { @@ -1627,22 +1680,12 @@ return (orderA - orderB) || (Number(a.id) - Number(b.id)); }); - const hasBaseSupport = otherAssets.length > 0; - const N = signPanels.length; - const highestPanelIndex = Math.max(0, N - 1); - // Le sommet de la tige du poteau (topOffset) - // Si support à la base : le panneau le plus bas a son centre à -36px (garde de ~20px au-dessus du socle) - // et chaque panneau au-dessus ajoute 34px. - // Le panneau au sommet (index 0, vertical_order = 1) est à topOffset. - // Chaque panneau suivant (index i) est positionné sous le précédent : topOffset + (i * 34). - const topOffset = hasBaseSupport ? -(36 + (highestPanelIndex * 34)) : -(highestPanelIndex * 34); - // Coordonnées de référence partagées pour l'alignement parfait sur la tige - const refLon = hasBaseSupport ? otherAssets[0].lon : (signPanels[0].lon); - const refLat = hasBaseSupport ? otherAssets[0].lat : (signPanels[0].lat); + const refLon = baseAsset ? baseAsset.lon : signPanels[0].lon; + const refLat = baseAsset ? baseAsset.lat : signPanels[0].lat; signPanels.forEach((panel, index) => { - const panelOffset = topOffset + (index * 34); + const panelOffset = topOffset + (index * 14); const symbology = (typeof AssetSymbology !== 'undefined') ? AssetSymbology.getAssetSymbology(panel) @@ -1684,7 +1727,7 @@ activeMarkers.push(marker); }); } else { - // CAS B : Emplacement sans panneau (arbres, bancs, corbeilles, etc.) + // CAS B : Emplacement sans panneau (arbres, bancs, corbeilles, poteau seul, etc.) group.sort((a, b) => getDisplayPriority(b) - getDisplayPriority(a)); const primaryAsset = group[0]; @@ -1701,7 +1744,7 @@ const el = document.createElement('div'); if (isSignPole) { el.className = 'asset-map-marker-pole'; - el.innerHTML = '
'; + el.innerHTML = '
'; el.style.zIndex = '5'; } else { el.className = 'asset-map-marker';