Ajouter le curseur d'intensité du relief, mémorisé et partagé dans le lien
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -45,6 +45,12 @@
|
||||
<button type="button" data-mode="precision" title="Précision géométrique seule : densité de points sol">Précision</button>
|
||||
<button type="button" data-mode="compare" title="Comparer deux couches de part et d'autre d'une barre glissante (touche P : mode suivant)">Comparer</button>
|
||||
</div>
|
||||
<div class="row" id="intensityRow">
|
||||
<label for="reliefIntensity" class="hint">Intensité du relief</label>
|
||||
<input type="range" id="reliefIntensity" min="0.5" max="2" step="0.1" value="1"
|
||||
title="Contraste du relief (1 = rendu standard, le PDF garde le rendu standard)">
|
||||
<span class="panel-stat" id="reliefIntensityVal">1×</span>
|
||||
</div>
|
||||
<div class="row" id="compareCtl" hidden>
|
||||
<select id="compareLeft" class="field" title="Couche à gauche de la barre"></select>
|
||||
<span class="hint">⇆</span>
|
||||
|
||||
@ -217,12 +217,22 @@ function validMain(meta, k) {
|
||||
return mainKeys(meta).includes(k) ? k : null;
|
||||
}
|
||||
|
||||
// Intensité du relief : contraste appliqué au conteneur de la couche
|
||||
// principale (réglage de confort personnel, jamais figé comme défaut serveur
|
||||
// ni appliqué au PDF exporté).
|
||||
function clampIntensity(v) {
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? Math.round(Math.min(2, Math.max(0.5, n)) * 10) / 10 : 1;
|
||||
}
|
||||
const fmtIntensity = (v) => String(v).replace('.', ',') + '×';
|
||||
|
||||
function defaultState(meta) {
|
||||
const b = meta.default_base || {};
|
||||
return {
|
||||
main: validMain(meta, meta.default_main) || mainKeys(meta)[0] || null,
|
||||
mode: validMode(meta.default_mode) || 'relief',
|
||||
compare: validCompare(null),
|
||||
intensity: 1,
|
||||
base: { on: b.on !== undefined ? !!b.on : true,
|
||||
opacity: b.opacity !== undefined ? Number(b.opacity) : 0.85,
|
||||
// null = suit le thème (choix explicite seulement si b.dark est déjà
|
||||
@ -245,6 +255,7 @@ function loadState(meta) {
|
||||
// Ancien mode « les deux » (produit) mémorisé : relief.
|
||||
mode: validMode(s.mode === 'both' ? 'relief' : s.mode) || def.mode,
|
||||
compare: validCompare(s.compare),
|
||||
intensity: clampIntensity(s.intensity),
|
||||
base: Object.assign({}, def.base, sBase) };
|
||||
}
|
||||
// Lien partagé : prime sur l'état local (les anciens liens &L=… de la
|
||||
@ -255,6 +266,7 @@ function loadState(meta) {
|
||||
if (hash.view) st.mode = hash.view.mode;
|
||||
if (hash.compare) st.compare = hash.compare;
|
||||
if (hash.base) st.base = hash.base;
|
||||
if (hash.intensity != null) st.intensity = hash.intensity;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
@ -271,13 +283,14 @@ function parseHash() {
|
||||
const m = /^#([\d.]+)\/(-?[\d.]+)\/(-?[\d.]+)(.*)$/.exec(location.hash || '');
|
||||
if (!m) return null;
|
||||
const out = { zoom: parseFloat(m[1]), lat: parseFloat(m[2]), lng: parseFloat(m[3]),
|
||||
main: null, view: null, compare: null, base: null };
|
||||
main: null, view: null, compare: null, base: null, intensity: null };
|
||||
const seg = {};
|
||||
for (const part of m[4].split('&')) {
|
||||
const i = part.indexOf('=');
|
||||
if (i > 0) seg[part.slice(0, i)] = part.slice(i + 1);
|
||||
}
|
||||
if (seg.M) out.main = seg.M;
|
||||
if (seg.I) out.intensity = clampIntensity(seg.I);
|
||||
if (seg.P) {
|
||||
const f = seg.P.split(':');
|
||||
// Ancien mode « les deux » (produit) : s'ouvre en relief. L'opacité qui
|
||||
@ -311,6 +324,7 @@ function viewHash(full) {
|
||||
const dark = STATE.base.dark === null || STATE.base.dark === undefined ? themeIsDark() : STATE.base.dark;
|
||||
h += '&B=' + (STATE.base.on ? '1' : '0') + ':' + Math.round(STATE.base.opacity * 100)
|
||||
+ ':' + (dark ? '1' : '0');
|
||||
if (STATE.intensity !== 1) h += '&I=' + STATE.intensity;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@ -416,6 +430,13 @@ function applyLayers() {
|
||||
const container = base.getContainer && base.getContainer();
|
||||
const dark = STATE.base.dark === null || STATE.base.dark === undefined ? themeIsDark() : STATE.base.dark;
|
||||
if (container) container.classList.toggle('base-dark', dark);
|
||||
// Intensité (contraste) : posée sur le conteneur de la couche relief
|
||||
// seule — la découpe de comparaison (clipPath) reste indépendante, les
|
||||
// deux propriétés CSS coexistent sur le même conteneur.
|
||||
for (const [key, layer] of LAYERS) {
|
||||
const c = layer.getContainer && layer.getContainer();
|
||||
if (c) c.style.filter = key === STATE.main && STATE.intensity !== 1 ? 'contrast(' + STATE.intensity + ')' : '';
|
||||
}
|
||||
applyCompareClip();
|
||||
updateRose();
|
||||
}
|
||||
@ -528,6 +549,9 @@ function renderPanel() {
|
||||
}
|
||||
const prec = precisionKey();
|
||||
el('precBlock').hidden = !prec;
|
||||
el('reliefIntensity').value = STATE.intensity;
|
||||
el('reliefIntensityVal').textContent = fmtIntensity(STATE.intensity);
|
||||
el('intensityRow').hidden = effectiveMode() === 'precision';
|
||||
if (!prec) return;
|
||||
renderPrecLegend();
|
||||
const mode = effectiveMode();
|
||||
@ -1456,7 +1480,7 @@ window.addEventListener('hashchange', () => {
|
||||
if (!h) return;
|
||||
applyingHash = true;
|
||||
map.setView([h.lat, h.lng], h.zoom);
|
||||
if (h.main || h.view || h.base || h.compare) {
|
||||
if (h.main || h.view || h.base || h.compare || h.intensity != null) {
|
||||
STATE = loadState(META);
|
||||
saveState(); renderPanel(); renderBaseRow(); applyLayers();
|
||||
}
|
||||
@ -1511,6 +1535,12 @@ el('btnDefault').addEventListener('click', () => {
|
||||
|
||||
el('modeSeg').querySelectorAll('button').forEach(b =>
|
||||
b.addEventListener('click', () => setMode(b.dataset.mode)));
|
||||
el('reliefIntensity').addEventListener('input', (e) => {
|
||||
STATE.intensity = clampIntensity(e.target.value);
|
||||
el('reliefIntensityVal').textContent = fmtIntensity(STATE.intensity);
|
||||
applyLayers();
|
||||
});
|
||||
el('reliefIntensity').addEventListener('change', saveState);
|
||||
el('mainSel').addEventListener('change', (e) => {
|
||||
STATE.main = e.target.value;
|
||||
saveState(); renderPanel(); applyLayers();
|
||||
|
||||
Reference in New Issue
Block a user