Files
lidar_rendu/lidar_pipeline/web/map.css
Antoine fb892ea9f2 Translate the whole project to English and fix outdated comments and help
Comments, docstrings, logs, CLI help, map UI, legends, PDF sheet, scripts,
compose files and AGENTS.md are now English. Data keys stay unchanged
(relief_oriente, densite_sol, visualisations/, API JSON keys, link params).
Wrong comments and help defaults found along the way are corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:16:45 +02:00

278 lines
18 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* LiDAR map — XYZ tiles, minimal consumer-map style chrome. */
/* Shared settings: every rule draws from them (colours, text, radii,
spacing, shadows, stacking). Two palettes: light by default, dark when
the system asks for it (unless light is forced) or when dark is forced. */
:root {
color-scheme: light;
--bg: #eef0f3;
--surface: rgba(251, 251, 250, 0.96);
--surface-2: #eef0f3; /* fields, segments, neutral buttons */
--border: #d6d9de;
--text: #1d232b;
--muted: #5b6573;
--accent: #1f6fb2;
--on-accent: #ffffff;
--track: #cfd4da;
--print: #d99a00;
--gen-running: #d9730d;
--gen-pending: #0b8fc0;
--gen-failed: #c8452c;
--gen-sel: var(--accent);
/* Tile selection outline: darker in the light theme (readable on the
light OSM base map), bright yellow in the dark theme. */
--tile-sel: #b8860b;
--gps: #39d98a;
--fs-xs: 11px; --fs-sm: 12px; --fs-md: 13px; --fs-lg: 16px;
--r-sm: 6px; --r-md: 10px; --r-full: 999px;
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.18);
--shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.28);
--z-map-ui: 800; --z-panel: 900; --z-popup: 950;
--z-loading: 960; --z-toast: 1000; --z-modal: 1100;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--bg: #0b0e13; --surface: rgba(16, 20, 27, 0.95); --surface-2: rgba(255, 255, 255, 0.06);
--border: rgba(255, 255, 255, 0.12); --text: #d9dee6; --muted: #8b94a3;
--accent: #5aa9e6; --on-accent: #0b0e13; --track: rgba(255, 255, 255, 0.18);
--print: #ffd166; --gen-running: #f4a261; --gen-pending: #4cc9f0; --gen-failed: #e76f51;
--tile-sel: #ffd24a;
--shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.4); --shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.45);
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--bg: #0b0e13; --surface: rgba(16, 20, 27, 0.95); --surface-2: rgba(255, 255, 255, 0.06);
--border: rgba(255, 255, 255, 0.12); --text: #d9dee6; --muted: #8b94a3;
--accent: #5aa9e6; --on-accent: #0b0e13; --track: rgba(255, 255, 255, 0.18);
--print: #ffd166; --gen-running: #f4a261; --gen-pending: #4cc9f0; --gen-failed: #e76f51;
--tile-sel: #ffd24a;
--shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.4); --shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.45);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
html, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text);
font: var(--fs-md)/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
#map { position: absolute; inset: 0; background: var(--bg); }
/* Compare mode: sliding divider between the two layers. */
#compareBar { position: fixed; top: 0; bottom: 0; width: 4px; margin-left: -2px; z-index: var(--z-map-ui);
background: var(--surface); box-shadow: var(--shadow-sm); cursor: ew-resize; touch-action: none; }
/* Handle in the middle of the visible map, above the bottom sheet (phone): at
half screen height it fell under the Leaflet scale sitting on the sheet. */
#compareBar span { position: absolute; top: calc((100% - var(--sheet-h)) / 2); left: 50%; transform: translate(-50%, -50%);
width: 36px; height: 36px; border-radius: var(--r-full); display: flex; align-items: center;
justify-content: center; background: var(--surface); color: var(--text); border: 1px solid var(--border);
box-shadow: var(--shadow-sm); font-size: var(--fs-lg); }
.leaflet-container { background: var(--bg); font: inherit; }
/* Dimmed OSM base map so the LiDAR renderings stand out. The filter sits on
the layer container, not on each tile: same result (per-pixel operations),
but a single filter pass instead of one per tile — −30% CPU (renderer +
GPU process) while panning, measured in Chrome. */
.leaflet-tile-pane .leaflet-layer.base-dark { filter: brightness(0.38) contrast(1.1) saturate(0.7); }
.info-sec { margin-top: 10px; }
.info-sec h4 { margin: 0 0 4px; font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em;
text-transform: uppercase; color: var(--muted); }
.info-kv { display: grid; grid-template-columns: max-content 1fr; gap: 2px 10px; font-size: var(--fs-sm); }
.info-kv dt { color: var(--muted); }
.info-kv dd { margin: 0; overflow-wrap: anywhere; }
.info-dl { display: inline-block; margin-top: 6px; padding: 6px 10px; border-radius: var(--r-sm);
background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text); text-decoration: none; font-weight: 600; }
.info-dl:hover { background: color-mix(in srgb, var(--accent) 30%, transparent); }
.rose-cap { font-size: var(--fs-xs); color: var(--text); line-height: 1.3; }
.rose-cap span { color: var(--muted); font-size: var(--fs-xs); }
.tile-head .sec-title { margin: 0; flex: 1; }
/* --- shared components ---------------------------------------------------- */
.btn, button { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
min-height: 32px; padding: 0 var(--sp-3); border-radius: var(--r-sm); border: 1px solid var(--border);
background: var(--surface-2); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.btn:hover, button:hover { border-color: var(--accent); }
.btn:disabled, button:disabled { opacity: 0.5; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.btn-primary:hover { filter: brightness(1.08); }
.btn.accent, .btn.busy { border-color: var(--accent); color: var(--accent); }
.btn-ghost { background: transparent; }
.btn-icon { min-height: 0; padding: var(--sp-1) var(--sp-2); border: none; background: none;
color: var(--muted); font-size: var(--fs-lg); }
.btn-icon:hover { color: var(--text); }
.row { display: flex; gap: var(--sp-2); align-items: center; }
.row > .btn, .row > .field { flex: 1; min-width: 0; }
.field { height: 32px; min-width: 0; width: 100%; padding: 0 var(--sp-2); border-radius: var(--r-sm);
border: 1px solid var(--border); background: var(--surface-2); color: var(--text);
font: inherit; font-size: var(--fs-sm); }
select.field[multiple] { height: auto; padding: var(--sp-1); }
.field option { background-color: var(--surface); color: var(--text); }
.seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; border: 1px solid var(--border);
border-radius: var(--r-sm); overflow: hidden; }
.seg button { min-height: 32px; border: none; border-radius: 0; background: var(--surface-2); color: var(--muted); }
.seg button + button { border-left: 1px solid var(--border); }
.seg button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); font-weight: 600; }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.hint { font-size: var(--fs-sm); color: var(--muted); margin: var(--sp-1) 0; }
input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 20px;
background: transparent; cursor: pointer; }
input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: var(--track); }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px;
margin-top: -5px; border-radius: 50%; background: var(--accent); border: 2px solid var(--surface); }
input[type="range"]::-moz-range-track { height: 4px; border-radius: 2px; background: var(--track); }
input[type="range"]::-moz-range-thumb { width: 10px; height: 10px; border-radius: 50%;
background: var(--accent); border: 2px solid var(--surface); }
@media (max-width: 720px) {
.btn, button, .field, .seg button { min-height: 40px; }
.field { height: 40px; }
}
/* Base map row: eye, name, opacity. */
.base-row { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; align-items: center;
gap: var(--sp-2); padding: var(--sp-1) 0; }
.base-row .base-ctl { grid-column: 2 / -1; display: grid; grid-template-columns: minmax(0, 1fr) 40px;
gap: var(--sp-2); align-items: center; }
/* Display: one main layer, precision alone or in comparison. */
.view-row { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
gap: 8px; padding: 8px 4px 4px; border-top: 1px solid var(--border); margin-top: 4px; }
.view-label { font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.view-row .layer-name { color: var(--text); }
.prec-legend { padding: 8px 4px 2px; }
.prec-bar { display: grid; grid-template-columns: repeat(16, 1fr); height: 10px;
border: 1px solid var(--border); border-radius: 3px; overflow: hidden; }
.prec-ticks { display: flex; justify-content: space-between; font-size: var(--fs-xs); color: var(--muted);
font-variant-numeric: tabular-nums; margin-top: 2px; }
.reading summary { cursor: pointer; font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.reading ul { margin: var(--sp-2) 0 0; padding-left: var(--sp-4); }
.reading li { font-size: var(--fs-sm); color: var(--muted); line-height: 1.5; }
.reading li + li { margin-top: var(--sp-1); }
.eye { width: 24px; height: 24px; padding: 0; border: none; background: none; color: var(--muted);
display: flex; align-items: center; justify-content: center; }
.eye.on { color: var(--accent); }
.eye svg { width: 16px; height: 16px; }
.layer-name { font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.base-row .layer-name { color: var(--text); cursor: pointer; }
.base-row:not(.on) .layer-name { color: var(--muted); }
.pct { font-size: var(--fs-xs); color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
#tools { position: fixed; right: var(--sp-3); bottom: calc(var(--sheet-h) + var(--sp-3));
z-index: var(--z-map-ui); display: flex; flex-direction: column; gap: var(--sp-2); }
@media (max-width: 720px) {
#tools { top: var(--sp-3); bottom: auto; }
}
/* The Leaflet attribution stays bottom right: the tools sit above it. */
@media (min-width: 721px) { #tools { bottom: 44px; } }
#tools button { width: 38px; height: 38px; padding: 0; font-size: var(--fs-lg); background: var(--surface);
border: 1px solid var(--border); box-shadow: var(--shadow-sm); }
/* Loading indicator: tiles left to finish the current view (base map +
LiDAR), progress bar of the current batch. */
.tileload { position: fixed; left: calc(var(--panel-w) + (100vw - var(--panel-w)) / 2); top: auto;
bottom: calc(var(--sheet-h) + var(--sp-3)); transform: translateX(-50%); z-index: var(--z-loading);
display: flex; align-items: center; gap: 8px; padding: 5px 12px; border-radius: var(--r-full);
background: var(--surface); border: 1px solid var(--border); font-size: var(--fs-sm);
box-shadow: var(--shadow-sm); pointer-events: none;
transition: opacity 0.3s ease; white-space: nowrap; }
.tileload.done { opacity: 0; }
.tl-spin { width: 12px; height: 12px; border-radius: var(--r-full); flex: none;
border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent); border-top-color: var(--accent);
animation: tlspin 0.8s linear infinite; }
.tileload.done .tl-spin { animation: none; border-color: var(--accent); }
@keyframes tlspin { to { transform: rotate(360deg); } }
.tl-bar { width: 60px; height: 4px; border-radius: var(--r-sm); background: var(--track);
overflow: hidden; flex: none; }
.tl-bar span { display: block; height: 100%; width: 0; background: var(--accent);
transition: width 0.2s ease; }
.toast { position: fixed; left: calc(var(--panel-w) + (100vw - var(--panel-w)) / 2);
bottom: calc(var(--sheet-h) + 56px); transform: translateX(-50%); z-index: var(--z-toast);
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
padding: 7px 12px; font-size: var(--fs-sm); color: var(--text); max-width: 80vw; text-align: center; }
.meta { font-size: var(--fs-xs); color: var(--muted); margin: 2px 0; }
.meta b { color: var(--text); font-weight: 600; }
.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: var(--fs-xs);
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
padding: 6px 8px; word-break: break-all; margin: 4px 0 8px; }
.use-note { font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }
.leaflet-control-attribution { background: var(--surface) !important; color: var(--muted) !important; }
.leaflet-control-attribution a { color: var(--accent) !important; }
/* --- PDF export and tile generation --------------------------------------- */
.print-msg { font-size: var(--fs-sm); margin-left: 8px; }
/* #1b1b1b (glyph + border) is hard-coded on purpose: --print is a similar
yellow/amber in both themes (#d99a00 light, #ffd166 dark), and a fixed dark text
keeps good contrast on it in both cases — a --text/--on-accent token would flip
(light in the dark theme) and break that contrast. */
.print-handle { display: flex; align-items: center; justify-content: center; cursor: move;
font-size: var(--fs-lg); color: #1b1b1b; background: var(--print); border: 2px solid #1b1b1b;
border-radius: var(--r-full); box-shadow: 0 1px 6px rgba(0,0,0,0.5); }
body.drawing { cursor: crosshair !important; }
body.drawing .leaflet-container { cursor: crosshair !important; }
.gen-row { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 6px;
align-items: center; margin: 4px 0; font-size: var(--fs-xs); color: var(--muted); }
.chk { display: flex; gap: 7px; align-items: center; font-size: var(--fs-xs);
color: var(--muted); margin: 4px 0; cursor: pointer; }
.chk input { accent-color: var(--accent); }
.gen-log { font: var(--fs-xs)/1.5 ui-monospace, "SF Mono", Menlo, monospace; color: var(--text);
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
padding: 6px 8px; max-height: 130px; overflow-y: auto; white-space: pre-wrap;
word-break: break-all; margin: 6px 0; }
#tools button.busy { color: var(--accent); border-color: var(--accent); }
/* Frames of the run's tiles: pulsing orange = rendering, blue = pending,
red = failed. Finished tiles have no frame. */
@keyframes genpulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.gen-frame-running { stroke: var(--gen-running); fill: color-mix(in srgb, var(--gen-running) 13%, transparent); animation: genpulse 1.4s ease-in-out infinite; }
.gen-frame-pending { stroke: var(--gen-pending); fill: color-mix(in srgb, var(--gen-pending) 8%, transparent); stroke-dasharray: 6 4; }
.gen-frame-failed { stroke: var(--gen-failed); fill: color-mix(in srgb, var(--gen-failed) 20%, transparent); }
.gen-sel { stroke: var(--accent); fill: color-mix(in srgb, var(--accent) 12%, transparent); cursor: pointer; }
/* --- single tabbed panel ------------------------------------------------------
Desktop: left column, collapsible into an icon strip.
Phone: bottom sheet with three heights (closed / half / full). */
#panel { position: fixed; z-index: var(--z-panel); display: flex; flex-direction: column;
background: var(--surface); color: var(--text); border: 1px solid var(--border);
box-shadow: var(--shadow-lg); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
left: var(--sp-3); top: var(--sp-3); bottom: var(--sp-3); width: 340px; border-radius: var(--r-md); }
.panel-head { display: flex; align-items: center; gap: var(--sp-1); padding: var(--sp-2);
border-bottom: 1px solid var(--border); }
.panel-tabs { display: flex; gap: var(--sp-1); flex: 1; min-width: 0; }
.panel-tabs button { flex: 1; min-width: 0; flex-direction: column; gap: 0; min-height: 44px;
padding: var(--sp-1); border: none; background: transparent; color: var(--muted); }
.panel-tabs button[aria-selected="true"] { background: var(--surface-2); color: var(--accent); }
.tab-ico { font-size: var(--fs-lg); line-height: 1.1; }
.tab-lbl { font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.panel-body { flex: 1; overflow-y: auto; padding: var(--sp-3); overscroll-behavior: contain; }
.panel-body section > * + * { margin-top: var(--sp-2); }
.panel-stat { font-size: var(--fs-sm); color: var(--muted); }
.sec-title { font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
color: var(--muted); margin-top: var(--sp-4); }
.legend { display: flex; align-items: center; gap: var(--sp-3); }
.legend canvas { width: 96px; height: 96px; flex: none; }
.sheet-grip { display: none; }
#panel.collapsed { width: 60px; }
#panel.collapsed .panel-body, #panel.collapsed .tab-lbl { display: none; }
#panel.collapsed .panel-head { flex-direction: column; border-bottom: none; }
#panel.collapsed .panel-tabs { flex-direction: column; align-self: stretch; }
#panel.collapsed #panelCollapse { transform: rotate(180deg); }
/* The map, the scale and the messages shift by the panel width. */
body { --panel-w: 364px; --sheet-h: 0px; }
body.panel-collapsed { --panel-w: 84px; }
.leaflet-bottom.leaflet-left { left: var(--panel-w); }
@media (max-width: 720px) {
body, body.panel-collapsed { --panel-w: 0px; }
#panel, #panel.collapsed { left: 0; right: 0; top: auto; bottom: 0; width: auto;
border-radius: var(--r-md) var(--r-md) 0 0; border-bottom: none; transition: height 0.2s ease; }
#panel[data-sheet="closed"] { height: 72px; }
#panel[data-sheet="half"] { height: 45vh; }
#panel[data-sheet="full"] { height: calc(100vh - var(--sp-4)); }
#panel[data-sheet="full"] { height: calc(100dvh - var(--sp-4)); }
#panel[data-sheet="closed"] .panel-body { display: none; }
#panelCollapse { display: none; }
.sheet-grip { display: flex; justify-content: center; padding: var(--sp-2) 0 0; cursor: grab; touch-action: none; }
.sheet-grip span { width: 36px; height: 4px; border-radius: var(--r-full); background: var(--muted); }
.panel-head { padding: var(--sp-1) var(--sp-2); }
.leaflet-bottom { bottom: var(--sheet-h); }
/* Full-width attribution: the scale sits above it, the loading indicator
at the top of the screen (left of the tools). */
.leaflet-bottom.leaflet-left .leaflet-control-scale { margin-bottom: 28px; }
.tileload { top: var(--sp-3); bottom: auto; }
}