document.querySelector(« #dec_deuxieme_para »).innerHTML = asterisque + textes_dec.texte_asterique;
function extraireTitre(civilite) {
const titre = civilite.split( » « )[0];
if (titre === « M. ») {
return textes_dec.maire_elu;
} else if (titre === « Mme ») {
return textes_dec.maire_elue;
} else {
return textes_dec.maire_elu;
}
}
function initTooltipBrt(selector) {
let currentTooltip = null;
function removeCurrentTooltip() {
if (currentTooltip) {
currentTooltip.remove();
currentTooltip = null;
}
}
function showTooltip(elmt) {
removeHoverTooltip();
removeCurrentTooltip();
let tt_fixe = document.createElement(« div »);
tt_fixe.classList.add(« tooltipbrt »);
tt_fixe.innerHTML = `
×
${elmt.getAttribute(« data-tt »)}
`;
document.body.appendChild(tt_fixe);
// Positionnement du tooltip
if (window.innerWidth > 500) {
const rect = elmt.getBoundingClientRect();
tt_fixe.style.top = rect.top – 14 + window.scrollY – tt_fixe.offsetHeight + « px »;
tt_fixe.style.left = rect.left + rect.width / 2 – tt_fixe.offsetWidth / 2 + 2 + « px »;
}
// Gestionnaire pour fermer le tooltip
tt_fixe.querySelector(« .fermer-tooltip »).addEventListener(« click », function () {
removeCurrentTooltip();
reset_func();
d3.selectAll(« #dec_mun circle.bubulle »).classed(« opacified », false);
d3.selectAll(« .dec_nuance »).classed(« choisi », false);
d3.selectAll(« .dec_nuance.latotale »).classed(« choisi », true);
});
currentTooltip = tt_fixe;
return tt_fixe;
}
// Sélectionne les éléments
let tt_fixe_elmts = getA(selector);
// Ajoute les événements de clic
forEach(tt_fixe_elmts, function (elmt) {
elmt.addEventListener(« click », function (e) {
e.stopPropagation();
showTooltip(this);
});
});
// Fonction pour afficher le tooltip manuellement
function showTooltipManually(elmt) {
forEach(tt_fixe_elmts, function (e) {
e.classList.remove(« selected-commune »);
});
elmt.classList.add(« selected-commune »);
// on poireaute un peu
setTimeout(() => showTooltip(elmt), 25);
}
// Retourne les fonctions utiles
return {
showTooltipManually,
showTooltip,
};
}
function showHoverTooltip(elmt) {
removeHoverTooltip();
const tt = document.createElement(« div »);
tt.classList.add(« minitt », « tooltipbrt »);
tt.innerHTML = elmt.getAttribute(« data-tt ») + »;
document.body.appendChild(tt);
const rect = elmt.getBoundingClientRect();
tt.style.top = rect.top – 14 + window.scrollY – tt.offsetHeight + « px »;
tt.style.left = rect.left + 2 + rect.width / 2 – tt.offsetWidth / 2 + « px »;
}
function removeHoverTooltip() {
const existing = document.querySelector(« .minitt »);
if (existing) existing.remove();
}
function dessinerBubulles(langue) {
// on modifie quelques textes en dur
if (langue != « fr ») {
d3.select(« #search_mun input »).attr(« placeholder », « Search for a town or a city »);
}
// Variables globales
const largeur = document.getElementById(« dec_mun »).offsetWidth;
const mobileDec = document.getElementById(« contenant_carte »).offsetWidth < 600;
const ratioMun = mobileDec ? 1.4 : 1.3;
const hauteur = largeur * ratioMun;
const is_dark = document.querySelector(« html »).getAttribute(« data-color-mode ») === « dark »;
// Initialisation du SVG
const svg = d3
.select(« #dec_mun »)
.append(« svg »)
.attr(« width », « 100% »)
.attr(« viewBox », `${largeur * -0.025} ${largeur * ratioMun * -0.05} ${largeur} ${largeur * ratioMun}`),
dec_dept = svg.append(« g »),
dec_ligne = svg.append(« g »),
dec_communes = svg.append(« g »),
dec_textes = svg.append(« g »);
// Chargement des données
// URL finale : https://assets-decodeurs.lemonde.fr/decodeurs/municipales_2026_snippets/municipales/exports/2026/T2/listes_en_tete.json
// test : https://assets-decodeurs.lemonde.fr/decodeurs/municipales_2026_snippets/municipales/exports/2026test2/T2/listes_en_tete.json
Promise.all([d3.json(« //assets-decodeurs.lemonde.fr/decodeurs/assets/municipales2026/brt_3500.json »), d3.csv(« //assets-decodeurs.lemonde.fr/decodeurs/assets/municipales2026/commplus3500.csv »), d3.json(« //assets-decodeurs.lemonde.fr/decodeurs/municipales_2026_snippets/municipales/exports/2026/T2/listes_en_tete.json »), d3.csv(« //assets-decodeurs.lemonde.fr/sheets/wAn2GJcj3n7zw3Ivl7sP8tBTw6KHCg_3917.csv »), d3.csv(« //assets-decodeurs.lemonde.fr/sheets/wAn2GJcj3n7zw3Ivl7sP8tBTw6KHCg_3918.csv »), d3.csv(« //assets-decodeurs.lemonde.fr/sheets/wAn2GJcj3n7zw3Ivl7sP8tBTw6KHCg_3985.csv »)]).then(
function ([geoData, popData, resultData, nuancesMin, nuancesLM, blocsData]) {
const dicoPop = {},
listeComm = [],
listeBlocs = [],
listeNuancesLM = [];
let decompteBlocs = {},
decompteNuances = {},
decompteTours = {},
dataGraphe = {},
dataGraphePop = {},
totalBubulles = 0,
resultBubulles = 0,
totalPop = 0,
resultPop = 0;
// si on a des résultats
if (resultData.length > 0) {
document.querySelector(« #titre_tetesliste »).innerHTML = textes_dec.leg_tetesliste + asterisque;
document.querySelector(« #titre_nuanceliste »).innerHTML = textes_dec.leg_blocs;
} else {
document.querySelector(« #titre_tetesliste »).innerHTML = textes_dec.pasresultat;
d3.selectAll(« #titre_nuanceliste, #dec_deuxieme_para »).style(« display », « none »);
}
// mes dicos
const resultDict = resultData.reduce((acc, current) => {
acc[current.code_circo] = current;
return acc;
}, {}),
dicoMin = nuancesMin.reduce((acc, e) => {
e.n = +e.n;
acc[e.code] = e;
return acc;
}, {}),
dicoLM = nuancesLM.reduce((acc, e) => {
e.n = +e.n;
acc[e.code] = e;
return acc;
}, {}),
dicoBloc = blocsData.reduce((acc, e) => {
e.n = +e.n;
acc[e.bloc] = e;
return acc;
}, {});
function listerLesListes(circonscription, maxListes) {
if (!maxListes) {
maxListes = 10;
}
var html= »
« ;
return html;
}
// Remplir le dictionnaire de population
popData.forEach((row) => (dicoPop[row.code_insee] = +row[« PMUN »]));
// Paramètres pour les cercles
const minPop = 3000,
maxPop = d3.max(popData, (d) => +d[« PMUN »]),
valeursCercles = mobileDec ? [1, largeur / 38] : [1.5, largeur / 36],
unite = valeursCercles[1],
echelleRayon = d3.scaleSqrt().domain([minPop, maxPop]).range([valeursCercles[0], valeursCercles[1]]).clamp(true);
// Extraction des points et départements
const points = topojson.feature(geoData, geoData.objects.p_com),
departements = topojson.feature(geoData, geoData.objects.a_dept);
// Projection et path
const projection = d3
.geoIdentity()
.reflectY(true)
.fitSize([largeur * 0.95, largeur * ratioMun * 0.9], points),
path = d3.geoPath().projection(projection);
// Dessiner les départements/la Fronce
// dec_dept.selectAll(« .departement »).data(departements.features)tnter().append(« path »).attr(« class », « departement »).attr(« d », path);
dec_dept
.append(« path »)
.attr(« class », « departement »)
.attr(« d », path(topojson.merge(geoData, geoData.objects.a_dept.geometries)));
// la légende
const legende = svg
.append(« g »)
.attr(« class », « legende passelect »)
.attr(« transform », « translate( » + largeur * 0.075 + « , » + hauteur * 0.075 + « ) »);
const valeursLeg = mobileDec ? [200000, 2000000] : [100000, 700000, 2000000],
xLabel = largeur * 0.08;
legende
.selectAll(« circle.leg_stroke »)
.data(valeursLeg)
.join(« circle »)
.attr(« class », « leg_stroke »)
.attr(« cx », 0)
.attr(« cy », (d) => -echelleRayon(d))
.attr(« r », (d) => echelleRayon(d))
.style(« fill », « none »);
legende
.selectAll(« line.leg_stroke »)
.data(valeursLeg)
.join(« line »)
.attr(« class », « leg_stroke »)
.attr(« x1 », 0)
.attr(« x2 », xLabel)
.attr(« y1 », (d) => -echelleRayon(d) * 2)
.attr(« y2 », (d, i) => -echelleRayon(d) * 2)
.style(« stroke-dasharray », « 2,2 »);
legende
.selectAll(« text.texte_leg »)
.data(valeursLeg)
.join(« text »)
.attr(« class », « texte_leg »)
.attr(« x », xLabel * 1.1)
.attr(« y », (d) => 3 + -echelleRayon(d) * 2)
.text(function (d) {
if (langue == « fr ») {
return milliers(d) + (d >= 1000000 ? » d’hab. » : « »);
} else {
return thousands(d) + (d >= 1000000 ? » inhab. » : « »);
}
});
// Préparation des données pour les cercles
// + la liste pour le menu déroulant
const circlesData = points.features
.filter((d, i) => dicoPop[d.properties.c] !== undefined /*&& i < 600*/)
.map((d) => {
listeComm.push({
label: { name: d.properties.l + » ( » + d.properties.d + « ) » },
data: { name: d.properties.l, dept: d.properties.d, c: d.properties.c },
});
const pop = dicoPop[d.properties.c] || 0;
var radiusTemp = 1;
// variables
if (pop) {
radiusTemp = echelleRayon(pop);
}
cXtemp = projection(d.geometry.coordinates)[0];
cYtemp = projection(d.geometry.coordinates)[1];
if (dicoPosition[d.properties.c]) {
tempProj = projection([dicoPosition[d.properties.c][0], dicoPosition[d.properties.c][1]]);
cXtemp = tempProj[0];
cYtemp = tempProj[1];
}
return {
cx: cXtemp,
cy: cYtemp,
r: radiusTemp,
l: d.properties.l,
d: d.properties.d,
id: d.properties.c,
p: dicoPop[d.properties.c],
};
});
const simulation = d3
.forceSimulation(circlesData)
.force(« x », d3.forceX((d) => d.cx).strength(0.5))
.force(« y », d3.forceY((d) => d.cy).strength(0.5))
.force(« collide », d3.forceCollide((d) => d.r + 0.2).strength(0.5))
.on(« tick », ticked)
.on(« end », () => {
simulation.stop();
});
// Dessin des cercles
const circles = dec_communes
.selectAll(« circle »)
.data(circlesData)
.enter()
.append(« circle »)
.attr(« class », function (d, i) {
if (resultDict[d.id]) {
resultBubulles++;
resultPop += d.p;
}
totalBubulles++;
totalPop += d.p;
return resultDict[d.id] ? « bubulle resultat » : « bubulle sansresultat »;
})
.attr(« data-insee », (d) => d.id)
.attr(« r », (d) => d.r)
.attr(« data-tt », function (d) {
if (resultDict[d.id]) {
e = resultDict[d.id];
let html= »
» + d.l + » ( » + d.d + « )
« ;
if (e) {
html += listerLesListes(e, 5);
} else {
html += ‘
‘ + textes_dec.pasresultat + «
« ;
}
html += »;
html += ‘ »;
return html;
} else {
let html= »
» + d.l + » ( » + d.d + « )
« ;
html += «
» + textes_dec.noresult + «
« ;
html += »;
html += ‘ »;
return html;
}
})
.attr(« data-status », function (d) {
if (resultDict[d.id]) {
// je fais le compte
if (!decompteTours[resultDict[d.id].status]) {
decompteTours[resultDict[d.id].status] = 0;
}
decompteTours[resultDict[d.id].status]++;
// je remplis
return resultDict[d.id] ? resultDict[d.id].status : « »;
}
})
.attr(« data-bloc », function (d) {
if (resultDict[d.id]) {
if (dicoLM[resultDict[d.id].liste_1_nuance]) {
if (dicoLM[resultDict[d.id].liste_1_nuance].bloc) {
// on crée la dataGraphe
if (!dataGraphe[dicoLM[resultDict[d.id].liste_1_nuance].bloc]) {
dataGraphe[dicoLM[resultDict[d.id].liste_1_nuance].bloc] = { T1: 0, T2: 0 };
dataGraphePop[dicoLM[resultDict[d.id].liste_1_nuance].bloc] = { T1: 0, T2: 0 };
}
// on ajoute la population
// T2
if (resultDict[d.id].status == « Elue ») {
dataGraphe[dicoLM[resultDict[d.id].liste_1_nuance].bloc][« T2 »]++;
dataGraphePop[dicoLM[resultDict[d.id].liste_1_nuance].bloc][« T2 »] += d.p;
}
// T1
if (resultDict[d.id].status == « Elue en T1 ») {
dataGraphe[dicoLM[resultDict[d.id].liste_1_nuance].bloc][« T1 »]++;
dataGraphePop[dicoLM[resultDict[d.id].liste_1_nuance].bloc][« T1 »] += d.p;
}
// on remplit le décompte
if (!decompteBlocs[dicoLM[resultDict[d.id].liste_1_nuance].bloc]) {
decompteBlocs[dicoLM[resultDict[d.id].liste_1_nuance].bloc] = 0;
}
decompteBlocs[dicoLM[resultDict[d.id].liste_1_nuance].bloc]++;
return dicoLM[resultDict[d.id].liste_1_nuance].bloc;
}
} else if (dicoMin[resultDict[d.id].liste_1_nuance]) {
if (dicoMin[resultDict[d.id].liste_1_nuance].bloc) {
// on crée la dataGraphe
if (!dataGraphe[dicoMin[resultDict[d.id].liste_1_nuance].bloc]) {
dataGraphe[dicoMin[resultDict[d.id].liste_1_nuance].bloc] = { T1: 0, T2: 0 };
dataGraphePop[dicoMin[resultDict[d.id].liste_1_nuance].bloc] = { T1: 0, T2: 0 };
}
// on ajoute la population
// T2
if (resultDict[d.id].status == « Elue ») {
dataGraphe[dicoMin[resultDict[d.id].liste_1_nuance].bloc][« T2 »]++;
dataGraphePop[dicoMin[resultDict[d.id].liste_1_nuance].bloc][« T2 »] += d.p;
}
// T1
if (resultDict[d.id].status == « Elue en T1 ») {
dataGraphe[dicoMin[resultDict[d.id].liste_1_nuance].bloc][« T1 »]++;
dataGraphePop[dicoMin[resultDict[d.id].liste_1_nuance].bloc][« T1 »] += d.p;
}
// on remplit le décompte ministère
if (!decompteBlocs[dicoMin[resultDict[d.id].liste_1_nuance].bloc]) {
decompteBlocs[dicoMin[resultDict[d.id].liste_1_nuance].bloc] = 0;
}
decompteBlocs[dicoMin[resultDict[d.id].liste_1_nuance].bloc]++;
return dicoMin[resultDict[d.id].liste_1_nuance].bloc;
}
}
}
})
.attr(« data-nuance-lm », function (d) {
if (resultDict[d.id] && resultDict[d.id].liste_1_nuance_lemonde) {
// on remplit le décompte LM
if (!decompteNuances[resultDict[d.id].liste_1_nuance_lemonde]) {
decompteNuances[resultDict[d.id].liste_1_nuance_lemonde] = 0;
}
decompteNuances[resultDict[d.id].liste_1_nuance_lemonde]++;
return resultDict[d.id].liste_1_nuance_lemonde;
} else if (resultDict[d.id] && resultDict[d.id].liste_1_nuance) {
if (!decompteNuances[resultDict[d.id].liste_1_nuance]) {
decompteNuances[resultDict[d.id].liste_1_nuance] = 0;
}
decompteNuances[resultDict[d.id].liste_1_nuance]++;
return resultDict[d.id].liste_1_nuance;
}
})
.attr(« style », function (d) {
if (resultDict[d.id]) {
// je stocke les blocs depuis les nuances LM
if (dicoLM[resultDict[d.id].liste_1_nuance]) {
if (listeBlocs.indexOf(dicoLM[resultDict[d.id].liste_1_nuance].bloc) == -1) {
listeBlocs.push(dicoLM[resultDict[d.id].liste_1_nuance].bloc);
}
} else if (dicoMin[resultDict[d.id].liste_1_nuance]) {
// les blocs depuis les nuances ministère
if (listeBlocs.indexOf(dicoMin[resultDict[d.id].liste_1_nuance].bloc) == -1) {
listeBlocs.push(dicoMin[resultDict[d.id].liste_1_nuance].bloc);
}
} else {
console.warn(« ouille, pas de nuance « intérieur » », d.id, resultDict[d.id].liste_1_nuance);
}
if (resultDict[d.id].liste_1_nuance_lemonde) {
if (listeNuancesLM.indexOf(resultDict[d.id].liste_1_nuance_lemonde) == -1) {
listeNuancesLM.push(resultDict[d.id].liste_1_nuance_lemonde);
}
}
if (dicoLM[resultDict[d.id].liste_1_nuance_lemonde]) {
styleCercle = « –color: » + dicoLM[resultDict[d.id].liste_1_nuance_lemonde].couleur + « ; –dark-color: » + dicoLM[resultDict[d.id].liste_1_nuance_lemonde].couleur_dark + « ;–stroke-color: » + d3.rgb(dicoLM[resultDict[d.id].liste_1_nuance_lemonde].couleur).darker(1) + « ;–stroke-dark-color: » + d3.rgb(dicoLM[resultDict[d.id].liste_1_nuance_lemonde].couleur_dark).darker(1) + « ; »;
} else if (dicoMin[resultDict[d.id].liste_1_nuance]) {
styleCercle = « –color: » + dicoMin[resultDict[d.id].liste_1_nuance].couleur + « ; –dark-color: » + dicoMin[resultDict[d.id].liste_1_nuance].couleur_dark + « ;–stroke-color: » + d3.rgb(dicoMin[resultDict[d.id].liste_1_nuance].couleur).darker(1) + « ;–stroke-dark-color: » + d3.rgb(dicoMin[resultDict[d.id].liste_1_nuance].couleur_dark).darker(1) + « ; »;
}
return styleCercle;
} else {
return « –color: #323232; –dark-color: #A4A4A4;–stroke-color:#636363;–stroke-dark-color:#cdcdcd; »;
}
})
.style(« stroke-width », (d) => (resultDict[d.id] ? 0 : 0.25))
.on(« mouseover », function () {
d3.select(this).style(« stroke-width », 1.5);
if (!mobileDec) {
showHoverTooltip(this);
}
})
.on(« mouseout », function () {
d3.select(this).style(« stroke-width », (d) => (resultDict[d.id] ? 0 : 0.25));
removeHoverTooltip();
});
// on lance la création du graphe
dessinerGraphe(« #graphe_t1t2 », dataGraphe, dataGraphePop, dicoBloc);
// j’ajoute le texte pour les DROM
if (langue == « fr ») {
var listeDrom = [
// ligne du bas
{ nom: « S.-Pierre-et-M. », nom_court: « SPM », hauteur: 0.93, largeur: 0.2 },
{ nom: « Nouvelle-Calédonie », nom_court: « N.-Calédonie », hauteur: 0.93, largeur: 0.39 },
{ nom: « Polynésie fr. », nom_court: « Poly. fr. », hauteur: 0.93, largeur: 0.57 },
{ nom: « Mayotte », nom_court: « Mayotte », hauteur: 0.93, largeur: 0.74 },
// ligne du haut ligne
{ nom: « Guadeloupe », nom_court: « Guadeloupe », hauteur: 0.73, largeur: 0.2 },
{ nom: « Martinique », nom_court: « Martinique », hauteur: 0.73, largeur: 0.37 },
{ nom: « Guyane », nom_court: « Guyane », hauteur: 0.73, largeur: 0.55 },
{ nom: « La Réunion », nom_court: « Réunion », hauteur: 0.73, largeur: 0.74 },
];
} else {
var listeDrom = [
// ligne du bas
{ nom: « St. Pierre and M. », nom_court: « SPM », hauteur: 0.93, largeur: 0.2 },
{ nom: « New Caledonia », nom_court: « N-Caledonia », hauteur: 0.93, largeur: 0.39 },
{ nom: « French Polynesia », nom_court: « Fr Polynesia », hauteur: 0.93, largeur: 0.57 },
{ nom: « Mayotte », nom_court: « Mayotte », hauteur: 0.93, largeur: 0.74 },
// ligne du haut ligne
{ nom: « Guadeloupe », nom_court: « Guadeloupe », hauteur: 0.73, largeur: 0.2 },
{ nom: « Martinique », nom_court: « Martinique », hauteur: 0.73, largeur: 0.37 },
{ nom: « French Guiana », nom_court: « Fr Guiana », hauteur: 0.73, largeur: 0.55 },
{ nom: « La Réunion », nom_court: « Réunion », hauteur: 0.73, largeur: 0.74 },
];
}
dec_textes
.selectAll(« text.ile »)
.data(listeDrom)
.enter()
.append(« text »)
.attr(« class », « ile passelect »)
.attr(« x », (d) => largeur * d.largeur)
.attr(« y », (d) => hauteur * (mobileDec ? d.hauteur – 0.02 : d.hauteur))
.text((d) => (mobileDec ? d.nom_court : d.nom));
// après la création des bubulles, on peut remplir
// le premier para avec quelques infos au long
if (langue == « fr » && resultData.length > 0) {
texte_html = « Cette carte de France affiche les résultats des élections municipales dans les » + milliers(resultBubulles) + » communes françaises dont les listes se voient attribuer une couleur politique par les préfectures. Elles représentent » + milliers(resultPop) + (resultPop < 1000000 ? » » : » d’ ») + « habitants. »;
if (resultBubulles < decompteTours[« Elue en T1″]) {
texte_html += » Parmi elles, » + milliers(decompteTours[« Elue en T1″]) + » ont déjà une élu une liste avec plus de 50 % des voix au premier tour. »;
}
document.querySelector(« #dec_premier_para »).innerHTML = texte_html;
} else {
document.getElementById(« dec_premier_para »).remove();
}
/*██ /██ /██
| ██ /██__/ | ██
| ██ /██████ /██████ /██████ /███████ /███████ /██████
| ██ /██__ ██ /██__ ██ /██__ ██| ██__ ██ /██__ ██ /██__ ██
| ██| ████████| ██ ██| ████████| ██ ██| ██ | ██| ████████
| ██| ██_____/| ██ | ██| ██_____/| ██ | ██| ██ | ██| ██_____/
| ██| ███████| ███████| ███████| ██ | ██| ███████| ███████
|__/ _______/ ____ ██ _______/|__/ |__/ _______/ _______/
/██ ██
| ██████/ *INTERACTIVE
_____*/
if (resultData.length > 0) {
var legendeBlocs= »
» + textes_dec.toutafficher + «
« ;
listeBlocs.sort((a, b) => {
const valA = dicoBloc[a] ? dicoBloc[a].n : Infinity;
const valB = dicoBloc[b] ? dicoBloc[b].n : Infinity;
return valA – valB;
});
for (const [i, d] of listeBlocs.entries()) {
if (dicoBloc[d]) {
e = dicoBloc[d];
if (decompteBlocs[e.bloc] >= minLegende) {
legendeBlocs += ‘
‘ + (langue == « fr » ? e.bloc : e.bloc_en) + ‘ ‘ + decompteBlocs[e.bloc] + «
« ;
}
}
}
legendeCouleurs = d3.select(« #dec_leg_min »).html(legendeBlocs);
// on va faire aussi une légende avec les nuances LM
var legendeNuancesLM = ‘
‘ + textes_dec.toutafficher + «
« ;
listeNuancesLM.sort((a, b) => {
const valA = dicoLM[a] ? dicoLM[a].n : Infinity;
const valB = dicoLM[b] ? dicoLM[b].n : Infinity;
return valA – valB;
});
for (const [i, d] of listeNuancesLM.entries()) {
if (dicoLM[d]) {
e = dicoLM[d];
if (decompteNuances[e.code] >= minLegende) {
legendeNuancesLM += ‘
‘ + (langue == « fr » ? e.nom_court : e.nom_court_en) + ‘ ‘ + decompteNuances[e.code] + «
« ;
}
}
}
legendeCouleursLM = d3.select(« #dec_leg_lm »).html(legendeNuancesLM);
var leg_tours= »
» + textes_dec.toutafficher + «
« ;
leg_tours += ‘
‘ + textes_dec.deuxiemetour + » » + (langue == « fr » ? milliers(decompteTours[« Elue »]) : thousands(decompteTours[« Elue »])) + «
« ;
leg_tours += ‘
‘ + textes_dec.liste_elue + » » + (langue == « fr » ? milliers(decompteTours[« Elue en T1 »]) : thousands(decompteTours[« Elue en T1 »])) + «
« ;
var legendeStatus = d3.select(« #dec_leg_elu »).html(leg_tours);
// Variables pour stocker les sélections
var selectedStatus = « tout »;
var selectedBloc = « tout »;
var selectedNuanceLM = « tout »;
// Fonction de filtrage combiné
function filtrerLesCercles() {
svg.selectAll(« circle.bubulle »).classed(« opacified », function () {
var currentStatus = d3.select(this).attr(« data-status »);
var currentNuance = d3.select(this).attr(« data-bloc »);
var currentNuanceLM = d3.select(this).attr(« data-nuance-lm »);
// Condition pour vérifier si le cercle doit être affiché
var isVisible = (currentStatus === selectedStatus || selectedStatus === « tout ») && (currentNuance === selectedBloc || selectedBloc === « tout ») && (currentNuanceLM === selectedNuanceLM || selectedNuanceLM === « tout »);
return !isVisible;
});
}
// Interaction pour le status
legendeStatus.selectAll(« .dec_status »).on(« click », function () {
d3.selectAll(« .tooltipbrt »).remove();
d3.selectAll(« #dec_leg_elu .dec_status »).classed(« choisi », false);
d3.select(this).classed(« choisi », true);
selectedStatus = d3.select(this).attr(« data-status »);
filtrerLesCercles();
});
// Interaction pour les nuances
legendeCouleurs.selectAll(« .dec_nuance_min »).on(« click », function () {
d3.selectAll(« .tooltipbrt »).remove();
d3.selectAll(« .dec_nuance_min »).classed(« choisi », false);
d3.select(this).classed(« choisi », true);
selectedBloc = d3.select(this).attr(« data-bloc »);
selectedNuanceLM = « tout »;
d3.selectAll(« .dec_nuance_lm »).classed(« choisi », false);
d3.selectAll(« .dec_nuance_lm.latotale »).classed(« choisi », true);
d3.select(this).classed(« choisi », true);
filtrerLesCercles();
});
// Interaction pour nuance LM
legendeCouleursLM.selectAll(« .dec_nuance_lm »).on(« click », function () {
d3.selectAll(« .tooltipbrt »).remove();
d3.selectAll(« #dec_leg_lm .dec_nuance_lm »).classed(« choisi », false);
d3.select(this).classed(« choisi », true);
selectedNuanceLM = d3.select(this).attr(« data-nuance-lm »);
selectedBloc = « tout »;
d3.selectAll(« .dec_nuance_min »).classed(« choisi », false);
d3.selectAll(« .dec_nuance_min.latotale »).classed(« choisi », true);
filtrerLesCercles();
});
}
// fin de la gestion de la légende interactive
// Mise à jour des positions des cercles
function ticked() {
circles.attr(« cx », (d) => d.x).attr(« cy », (d) => d.y);
}
// Initialisation du tooltip
const { showTooltipManually, showTooltip } = initTooltipBrt(« #dec_mun .bubulle »);
// la fonction qui traite les résultats
const func_to_treat_result_donnees = (result) => {
setTimeout(() => d3.selectAll(« #dec_mun circle.bubulle »).classed(« opacified », (d) => d.id !== result.data.c), 25);
const selectedCircle = document.querySelector(`#dec_mun circle.bubulle[data-insee= »${result.data.c} »]`);
if (selectedCircle) {
showTooltipManually(selectedCircle);
selectedCircle.scrollIntoView({ behavior: « smooth », block: « center » });
}
};
// Initialisation de l’autocomplete
if (langue == « fr ») {
autocomplete_decodeurs(« search_mun », listeComm, func_to_treat_result_donnees, reset_func, (x) => `${x.label.name}`, 6, 3, slugify, « Aucune commune avec ce nom »);
} else {
autocomplete_decodeurs(« search_mun », listeComm, func_to_treat_result_donnees, reset_func, (x) => `${x.label.name}`, 6, 3, slugify, « No town or city with that name »);
}
// quand on tape échap
document.addEventListener(« keydown », function (event) {
if (event.key === « Escape ») {
reset_func();
const circles = getA(« #dec_mun circle.bubulle »);
var selectedStatus = « tout »;
var selectedBloc = « tout »;
var selectedNuanceLM = « tout »;
forEach(circles, function (circle) {
circle.classList.remove(« selected-commune »);
});
d3.selectAll(« .tooltipbrt »).remove();
}
});
// Gestionnaire pour fermer le tooltip en cliquant ailleurs
document.addEventListener(« click », function (e) {
if (!e.target.closest(« #dec_mun circle ») && !e.target.closest(« #search_mun »)) {
const circles = getA(« #dec_mun circle.bubulle »);
forEach(circles, function (circle) {
circle.classList.remove(« selected-commune »);
});
}
});
}
);
}
// Fonction pour réinitialiser la carte
const reset_func = () => {
document.querySelector(« #search_mun input »).value = « »;
d3.selectAll(« #dec_mun circle.bubulle »).classed(« opacified », false);
d3.select(« #search_mun .lmui-search__button »).style(« visibility », « visible »);
d3.select(« #search_mun .lmui-search__reset-button »).style(« visibility », « hidden »);
// on remet à zéro les filtres
d3.selectAll(« .dec_nuance »).classed(« choisi », false);
d3.selectAll(« .dec_nuance.latotale »).classed(« choisi », true);
var selectedBloc = « tout »;
var selectedNuanceLM = « tout »;
};
dessinerBubulles(langue);
Les résultats des principaux partis et blocs politiques
`;
});
toFill += `
${textsMap[`color_note_${currentBloc}`]}
${textsMap.precision_methodo}
`;
htmlContainer.innerHTML = toFill;
}
function inittooltipdecodeurs(selector) {
let currentTooltip = null;
function removeCurrentTooltip() {
if (currentTooltip) {
currentTooltip.remove();
currentTooltip = null;
}
}
function showTooltip(elmt) {
removeCurrentTooltip();
let ttFixe = document.createElement(« div »);
ttFixe.classList.add(« tooltipdecodeurs »);
ttFixe.innerHTML = elmt.getAttribute(« data-tt ») + « ;
document.body.appendChild(ttFixe);
// Ne pas appliquer de positionnement en JavaScript en mobile
if (!isMobile) {
const rect = elmt.getBoundingClientRect();
ttFixe.style.top = rect.top – 14 + window.scrollY – ttFixe.offsetHeight + « px »;
ttFixe.style.left = rect.left + rect.width / 2 – ttFixe.offsetWidth / 2 + 2 + « px »;
}
// Ajouter un gestionnaire pour fermer le tooltip en cliquant sur la croix en mobile
ttFixe.addEventListener(« click », function (e) {
if (e.target.tagName === « DIV » && e.target.classList.contains(« tooltipdecodeurs ») && window.innerWidth <= 600) {
removeCurrentTooltip();
reset_func();
}
});
currentTooltip = ttFixe;
return ttFixe;
}
// Sélectionne les éléments
let ttFixe_elmts = getA(selector);
// Ajoute les événements
forEach(ttFixe_elmts, function (elmt) {
elmt.addEventListener(« mouseover », function () {
showTooltip(this);
});
elmt.addEventListener(« mouseout », function () {
// Ne supprime pas le tooltip si on a cliqué sur une commune
if (!this.classList.contains(« selected-commune »)) {
removeCurrentTooltip();
}
});
});
// Retourne la fonction pour afficher le tooltip manuellement
return function (elmt) {
// Marque le cercle comme sélectionné
forEach(ttFixe_elmts, function (e) {
e.classList.remove(« selected-commune »);
});
elmt.classList.add(« selected-commune »);
return showTooltip(elmt);
};
}
const formatNb = (number, locale= »fr-FR », round = 2, style= »decimal ») => {
return `${number.toLocaleString(locale, { maximumFractionDigits: round, style })}`;
};
async function drawMap(langue) {
// Variables globales
const largeur = document.getElementById(`dec_mun_prct_${snippetId}`).offsetWidth;
const isMobile = largeur < 600;
const ratioMun = isMobile ? 1.4 : 1.2;
const hauteur = largeur * ratioMun;
const isDark = document.querySelector(« html »).getAttribute(« data-color-mode ») === « dark »;
// Initialisation du SVG
const svg = d3
.select(`#dec_mun_prct_${snippetId}`)
.append(« svg »)
.attr(« width », « 100% »)
.attr(« viewBox », `${largeur * -0.025} ${largeur * ratioMun * -0.05} ${largeur} ${largeur * ratioMun}`);
const decDept = svg.append(« g »);
const decCommunes = svg.append(« g »);
const decTextes = svg.append(« g »);
// Données
const geoData = await d3.json(« //assets-decodeurs.lemonde.fr/decodeurs/assets/municipales2026/brt_3500.json »);
const popData = await d3.csv(« //assets-decodeurs.lemonde.fr/decodeurs/assets/municipales2026/commplus3500.csv »);
const dicoPop = {};
const listeComm = [];
function agregerResultats(datasetsArray) {
const dictGlobal = {};
(datasetsArray || []).forEach(dataset => {
if (!dataset?.listes_communes) return;
const nuanceCode = dataset.code;
dataset.listes_communes.forEach(current => {
if (!current || current.code_circo == null) return;
const id = current.code_circo;
const statusCommune = current.status;
if (!dictGlobal[id]) {
dictGlobal[id] = {
…current,
voix: 0,
status: null,
listes: [],
detailListes: [],
};
}
const isEnAttente = statusCommune === « En attente »;
if (isEnAttente) {
if (dictGlobal[id].status !== « disponible ») {
dictGlobal[id].status = « en_attente »;
}
return;
}
// Résultats disponibles : agrégation bloc
const voixListe = +current.voix || 0;
dictGlobal[id].voix += voixListe;
dictGlobal[id].status = « disponible »;
if (nuanceCode && !dictGlobal[id].listes.includes(nuanceCode)) {
dictGlobal[id].listes.push(nuanceCode);
}
// Détail par liste pour cette commune
const exprimés = current.mentions?.exprimes;
const hasExprimes = exprimés != null && +exprimés > 0;
const prctListe = hasExprimes && voixListe > 0
? (voixListe / +exprimés) * 100
: 0;
const tete = current.tete || {};
const eluPremierTour = current.ordre === 1 && current.pourvu === « T1 »;
const eluDeuxiemeTour = current.ordre === 1 && current.pourvu === « T2 »;
dictGlobal[id].detailListes.push({
nuance: nuanceCode,
nuance_lemonde: current.nuance_lemonde ?? null,
voix: voixListe,
prct: prctListe,
tete: {
nom: tete.nom || null,
prenom: tete.prenom || null,
civ: tete.civ || null,
},
eluPremierTour,
eluDeuxiemeTour,
});
});
});
// Calcul du % global bloc par commune
Object.values(dictGlobal).forEach(commune => {
const exprimés = commune.mentions?.exprimes;
const hasExprimes = exprimés != null && +exprimés > 0;
if (commune.status === « disponible » && commune.voix != null && hasExprimes) {
commune.prct = (+commune.voix / +exprimés) * 100;
} else if (commune.status === « en_attente ») {
commune.prct = null;
} else {
commune.prct = 0;
}
});
return dictGlobal;
}
const datasetsT1init = (dataByBlocT1[currentBloc] || []).map(d => d.nuanceData).filter(Boolean);
const result2026DictT1 = agregerResultats(datasetsT1init);
const datasetsT2init = (dataByBlocT2[currentBloc] || []).map(d => d.nuanceData).filter(Boolean);
const result2026DictT2 = agregerResultats(datasetsT2init);
const result2026Dict = currentTour === « T2 » ? result2026DictT2 : result2026DictT1;
// console.log(« ——result2026Dict », result2026Dict);
const allPrctValues = [
…Object.values(result2026Dict).map(d => d.prct),
];
const maxPrct = typeof d3.max(allPrctValues) == ‘undefined’ || d3.max(allPrctValues) < 10 ? 10 : d3.max(allPrctValues);
// Adapter la couleur en fonction du bloc
function getBlocColorMax(bloc) {
const colors = blocColors[bloc];
const couleur = colors?.couleur;
const couleurDark = colors?.couleur_dark;
if (!couleur && !couleurDark) return colorMax;
return isDark ? (d3.rgb(couleurDark).darker(1) || d3.rgb(couleur).darker(1)) : (d3.rgb(couleur).darker(1) || d3.rgb(couleurDark).darker(1));
}
let colorScale = d3.scaleSequential(
d3.interpolate(colorMin, getBlocColorMax(currentBloc))
).domain([0, maxPrct]);
// Création de la légende colorée
const legendWidth = isMobile ? largeur * 0.7 : largeur * 0.5;
const legendHeight = isMobile ? 10 : 12;
const legendMargin = { top: isMobile ? 18 : 15, right: isMobile ? 40 : 20, bottom: 18, left: isMobile ? 12 : 20 };
const legendSvg = d3.select(« .legend_color »)
.append(« svg »)
.attr(« width », « 100% »)
.attr(« height », legendHeight + legendMargin.top + legendMargin.bottom);
const legendContainerEl = document.querySelector(`.dec_mun_prct_container[data-attr=${snippetId}] .legend_color`);
const legendContainerWidth = legendContainerEl ? legendContainerEl.offsetWidth : largeur;
const legendRoot = legendSvg.append(« g »)
.attr(« transform », `translate(${(legendContainerWidth / 2) – (legendWidth / 2)}, ${legendMargin.top})`);
const legendScore = legendRoot.append(« g »)
.attr(« id », « legend_score »);
// dégradé score
const gradientId = `score-gradient-${snippetId}`;
const gradient = legendSvg.append(« defs »)
.append(« linearGradient »)
.attr(« id », gradientId)
.attr(« x1 », « 0% »)
.attr(« x2 », « 100% »);
// Ajout des stops de couleur
const numStops = 10;
for (let i = 0; i <= numStops; i++) {
const value = (i / numStops) * maxPrct;
gradient.append(« stop »)
.attr(« offset », `${(i / numStops) * 100}%`)
.attr(« stop-color », colorScale(value));
}
// Barre de dégradé
legendScore.append(« rect »)
.attr(« class », « legend_color_bar »)
.attr(« width », legendWidth)
.attr(« height », legendHeight)
.style(« fill », `url(#${gradientId})`);
// Labels min et max
const legendScale = d3.scaleLinear()
.domain([0, maxPrct])
.range([0, legendWidth]);
const legendAxis = d3.axisBottom(legendScale)
.ticks(6)
.tickFormat((d) => Math.round(d) + » % »);
legendScore.append(« g »)
.attr(« class », « legend_color_axis »)
.attr(« transform », `translate(0, ${legendHeight})`)
.call(legendAxis)
.call(g => g.select(« .domain »).remove());
// Titre de la légende
legendScore.append(« text »)
.attr(« class », « legend_color_title »)
.attr(« x », legendWidth / 2)
.attr(« y », -5)
.attr(« text-anchor », « middle »)
.text(`${textsMap.titre_legende_score} ${textsMap.prct}`);
function buildListeRows(detailBloc) {
let html= »
« ;
return html;
}
function makeTooltipContent(d) {
const blocName = langue === « fr-FR » ? currentBloc : blocColors[currentBloc]?.bloc_en ?? currentBloc;
let html = `
${d.l} (${d.d})
`;
// Tour 2
html += `
${textsMap.tour2 ?? « Tour 2 »}
`;
const s2 = d.statusBlocT2 || « aucune_liste »;
if (s2 === « aucune_liste ») {
if (d.pasDeTourDeux) {
html += `
${textsMap.pas_de_second_tour ?? « Pas de second tour : une liste a été élue dès le premier tour »}
`;
} else {
html += `
${textsMap.aucune_liste_bloc}
`;
}
} else if (s2 === « en_attente ») {
html += `
${textsMap.en_attente}
`;
} else {
html += `
${d.value2026T2 != null ? `${formatNb(d.value2026T2, langue, 1)}${textsMap.suffrages} ${blocName}` : textsMap.pasresultat}
`;
if (d.detailBlocT2.length > 0) {
html += « ;
html += buildListeRows(d.detailBlocT2);
}
}
html += « ;
// Tour 1
html += `
${textsMap.tour1 ?? « Tour 1 »}
`;
const s1 = d.statusBlocT1 || « aucune_liste »;
if (s1 === « en_attente ») {
html += `
${textsMap.en_attente}
`;
} else if (s1 === « aucune_liste ») {
html += `
${textsMap.aucune_liste_bloc}
`;
} else {
html += `
${d.value2026T1 != null ? `${formatNb(d.value2026T1, langue, 1)}${textsMap.suffrages} ${blocName}` : textsMap.pasresultat}
`;
if (d.detailBlocT1.length > 0) {
html += « ;
html += buildListeRows(d.detailBlocT1);
}
}
html += `
${textsMap.pop_mun} ${formatNb(d.p, langue, 0)} ${textsMap.hab}
`;
return html;
}
// Remplir le dictionnaire de population
popData.forEach((row) => (dicoPop[row.code_insee] = +row[« PMUN »]));
// Paramètres pour les cercles
const minPop = 3000;
const maxPop = d3.max(popData, (d) => +d[« PMUN »]);
const valeursCercles = isMobile ? [1, largeur / 38] : [1.5, largeur / 36];
// Échelle pour le rayon des cercles
const echelleRayon = d3
.scaleSqrt()
.domain([minPop, maxPop])
.range([valeursCercles[0], valeursCercles[1]])
.clamp(true);
// Extraction des points et départements
const points = topojson.feature(geoData, geoData.objects.p_com);
// const departements = topojson.feature(geoData, geoData.objects.a_dept);
// Projection et path
const projection = d3
.geoIdentity()
.reflectY(true)
.fitSize([largeur * 0.95, largeur * ratioMun * 0.9], points);
const path = d3.geoPath().projection(projection);
// Dessin des départements
decDept.append(« path »).attr(« class », « departement »).attr(« d », path(topojson.merge(geoData, geoData.objects.a_dept.geometries)));
// la légende taille des cercles
const legende = svg
.append(« g »)
.attr(« class », « legende »)
.attr(« transform », « translate( » + largeur * 0.075 + « , » + hauteur * 0.075 + « ) »);
const valeursLeg = isMobile ? [200000, 2000000] : [100000, 700000, 2000000];
const xLabel = largeur * 0.08;
legende
.selectAll(« circle.leg_stroke »)
.data(valeursLeg)
.join(« circle »)
.attr(« class », « leg_stroke »)
.attr(« cx », 0)
.attr(« cy », (d) => -echelleRayon(d))
.attr(« r », (d) => echelleRayon(d))
.style(« fill », « none »);
legende
.selectAll(« line.leg_stroke »)
.data(valeursLeg)
.join(« line »)
.attr(« class », « leg_stroke »)
.attr(« x1 », 0)
.attr(« x2 », xLabel)
.attr(« y1 », (d) => -echelleRayon(d) * 2)
.attr(« y2 », (d, i) => -echelleRayon(d) * 2)
.style(« stroke-dasharray », « 2,2 »);
legende
.selectAll(« text.texte_leg »)
.data(valeursLeg)
.join(« text »)
.attr(« class », « texte_leg »)
.attr(« x », xLabel * 1.1)
.attr(« y », (d) => 3 + -echelleRayon(d) * 2)
.text(function (d) {
if (d >= 1000000) {
const millions = Math.round(d / 1000000);
if (langue === « fr-FR ») {
return `${millions} million${millions > 1 ? « s » : « »} d’hab.`;
}
return `${millions} million inhab.`;
}
return formatNb(d, langue, 0);
});
// Préparation des données pour les cercles
const circlesData = points.features
.filter((d) => dicoPop[d.properties.c] !== undefined)
.map((d) => {
listeComm.push({
label: { name: d.properties.l + » ( » + d.properties.d + « ) » },
data: { name: d.properties.l, dept: d.properties.d, c: d.properties.c },
});
const id = d.properties.c;
const pop = dicoPop[id] || 0;
let radiusTemp = pop ? echelleRayon(pop) : 1;
let cXtemp = projection(d.geometry.coordinates)[0];
let cYtemp = projection(d.geometry.coordinates)[1];
if (dicoPosition[id]) {
const tempProj = projection([dicoPosition[id][0], dicoPosition[id][1]]);
cXtemp = tempProj[0];
cYtemp = tempProj[1];
}
const resultEntryT1 = result2026DictT1[id];
const resultEntryT2 = result2026DictT2[id];
const pasDeTourDeux = (resultEntryT1?.detailListes ?? []).some(l => l.eluPremierTour);
return {
cx: cXtemp,
cy: cYtemp,
r: radiusTemp,
l: d.properties.l,
d: d.properties.d,
id,
p: dicoPop[id],
// Données T1
value2026T1: resultEntryT1 ? resultEntryT1.prct : null,
statusBlocT1: resultEntryT1?.status ?? « aucune_liste »,
detailBlocT1: resultEntryT1?.detailListes ?? [],
// Données T2
value2026T2: resultEntryT2 ? resultEntryT2.prct : null,
statusBlocT2: resultEntryT2?.status ?? « aucune_liste »,
detailBlocT2: resultEntryT2?.detailListes ?? [],
pasDeTourDeux,
};
});
// Dessin des cercles
const circles = decCommunes
.selectAll(« circle »)
.data(circlesData)
.enter()
.append(« circle »)
.attr(« data-insee », d => d.id)
.attr(« class », « bubulle »)
.attr(« r », d => d.r)
.attr(« data-tt », d => makeTooltipContent(d))
.attr(« fill », d => {
const value = currentTour === « T1 » ? d.value2026T1 : d.value2026T2;
if (value === null) return `var(–prct-no-result)`;
return colorScale(value);
})
.attr(« stroke », d => {
const value = currentTour === « T1 » ? d.value2026T1 : d.value2026T2;
return value === null
? `var(–prct-no-result-stroke)`
: d3.rgb(
colorScale(value)
).darker(1);
})
.style(« stroke-width », d =>
(currentTour === « T1 » ? d.value2026T1 : d.value2026T2) === null ? 0 : 0.25
)
.on(« mouseover », function () {
d3.select(this).style(« stroke-width », 1.5);
// terre brûlée, on vire la classe .opacified partout
d3.selectAll(`#dec_mun_prct_${snippetId} circle.bubulle`).classed(« opacified », false);
})
.on(« mouseout », function () {
d3.select(this).style(« stroke-width », (d) => ((currentTour === « T1 » ? d.value2026T1 : d.value2026T2) === null ? 0 : 0.25));
});
decTextes
.selectAll(« text.ile »)
.data(textDrom)
.enter()
.append(« text »)
.attr(« class », « ile »)
.attr(« x », (d) => largeur * d.largeur)
.attr(« y », (d) => hauteur * (isMobile ? d.hauteur – 0.02 : d.hauteur))
.text((d) => (isMobile ? d.nom_court : d.nom));
// Mise à jour des positions des cercles
function ticked() {
circles.attr(« cx », (d) => d.x).attr(« cy », (d) => d.y);
}
const simulation = d3
.forceSimulation(circlesData)
.force(« x », d3.forceX((d) => d.cx).strength(0.5))
.force(« y », d3.forceY((d) => d.cy).strength(0.5))
.force(« collide », d3.forceCollide((d) => d.r + 0.1).strength(0.5))
.on(« tick », ticked)
.on(« end », () => {
simulation.stop();
});
// Watch bloc update
const blocDivs = document.querySelectorAll(`.dec_mun_prct_container[data-attr=${snippetId}] .legend_bloc`);
blocDivs.forEach(bloc => {
bloc.addEventListener(‘click’, () => {
const blocClicked = bloc.dataset.bloc;
blocDivs.forEach(bloc => {
bloc.classList.remove(‘legend_bloc__active’);
});
bloc.classList.add(‘legend_bloc__active’);
switchBloc(blocClicked);
});
});
function switchBloc(bloc) {
currentBloc = bloc;
const dsT1 = (dataByBlocT1[bloc] || []).map(d => d.nuanceData).filter(Boolean);
const dsT2 = (dataByBlocT2[bloc] || []).map(d => d.nuanceData).filter(Boolean);
const newDictT1 = agregerResultats(dsT1);
const newDictT2 = agregerResultats(dsT2);
const newDictCurrent = currentTour === « T2 » ? newDictT2 : newDictT1;
circlesData.forEach(d => {
const e1 = newDictT1[d.id];
const e2 = newDictT2[d.id];
d.value2026T1 = e1 ? e1.prct : null;
d.statusBlocT1 = e1?.status ?? « aucune_liste »;
d.detailBlocT1 = e1?.detailListes ?? [];
d.value2026T2 = e2 ? e2.prct : null;
d.statusBlocT2 = e2?.status ?? « aucune_liste »;
d.detailBlocT2 = e2?.detailListes ?? [];
d.pasDeTourDeux = (e1?.detailListes ?? []).some(l => l.eluPremierTour);
const eCurrent = currentTour === « T2 » ? e2 : e1;
d.value2026 = eCurrent ? eCurrent.prct : null;
});
circles.attr(« data-tt », d => makeTooltipContent(d));
updateLegend(newDictCurrent, bloc);
updateMap();
}
function updateLegend(result2026Dict, bloc) {
const allPrctValues = Object.values(result2026Dict).map(d => d.prct);
const newMaxPrct = typeof d3.max(allPrctValues) == ‘undefined’ || d3.max(allPrctValues) < 10 ? 10 : d3.max(allPrctValues);
const blocColorMax = getBlocColorMax(bloc);
// Nouvelle échelle avec la couleur du bloc
colorScale = d3.scaleSequential(
d3.interpolate(colorMin, blocColorMax)
).domain([0, newMaxPrct]);
// Mettre à jour les stops du dégradé
legendSvg.select(`#${gradientId}`)
.selectAll(« stop »)
.attr(« stop-color », (d, i) => colorScale((i / numStops) * newMaxPrct));
legendScale.domain([0, newMaxPrct]);
legendRoot.select(« .legend_color_axis »)
.call(legendAxis)
.call(g => g.select(« .domain »).remove());
legendRoot.select(« .legend_color_title »)
.text(`${textsMap.titre_legende_score} ${textsMap.prct}`);
// color note
const noteEl = document.querySelector(
`.dec_mun_prct_container[data-attr=${snippetId}] .legend_color_note`
);
if (!noteEl) return;
const key = `color_note_${bloc}`;
noteEl.innerHTML = textsMap[key] || « »;
}
// update cercles
function updateMap() {
circles
.on(« mouseover », function () {
d3.select(this).style(« stroke-width », 1.5);
d3.selectAll(`#dec_mun_prct_${snippetId} circle.bubulle`).classed(« opacified », false);
})
.on(« mouseout », function () {
d3.select(this).style(« stroke-width », (d) => (d.value2026 === null ? 0 : 0.25));
})
.transition()
.duration(600)
.attr(« fill », d => {
const value = currentTour === « T1 » ? d.value2026T1 : d.value2026T2;
if (value === null) return `var(–prct-no-result)`;
return colorScale(value);
})
.attr(« stroke », d => {
const value = currentTour === « T1 » ? d.value2026T1 : d.value2026T2;
return value === null
? `var(–prct-no-result-stroke)`
: d3.rgb(
colorScale(value)
).darker(1);
})
.style(« stroke-width », (d) => ((currentTour === « T1 » ? d.value2026T1 : d.value2026T2) === null ? 0 : 0.25));
}
// Fonction pour traiter la sélection d’une commune
const func_to_treat_result_donnees = (result) => {
// Désopacifie tous les cercles
d3.selectAll(`#dec_mun_prct_${snippetId} circle.bubulle`).classed(« opacified », false);
// Opacifie tous les cercles sauf celui sélectionné
d3.selectAll(`#dec_mun_prct_${snippetId} circle.bubulle`)
.filter((d) => d.id !== result.data.c)
.classed(« opacified », true);
// Trouve le cercle correspondant
const selectedCircle = document.querySelector(`#dec_mun_prct_${snippetId} circle[data-insee= »${result.data.c} »]`);
if (selectedCircle) {
showTooltipManually(selectedCircle);
}
};
// Initialisation de l’autocomplete
if (langue == « fr-FR ») {
autocomplete_decodeurs(`search_mun_prct_${snippetId}`, listeComm, func_to_treat_result_donnees, reset_func, (x) => `${x.label.name}`, 6, 3, slugify, « Aucune commune avec ce nom »);
} else {
autocomplete_decodeurs(`search_mun_prct_${snippetId}`, listeComm, func_to_treat_result_donnees, reset_func, (x) => `${x.label.name}`, 6, 3, slugify, « No town or city with that name »);
}
// quand on tape échap
document.addEventListener(« keydown », function (event) {
if (event.key === « Escape ») {
reset_func();
const circles = getA(`#dec_mun_prct_${snippetId} circle.bubulle`);
forEach(circles, function (circle) {
circle.classList.remove(« selected-commune »);
});
d3.selectAll(« .tooltipdecodeurs »).remove();
}
});
// Initialisation du tooltip
const showTooltipManually = inittooltipdecodeurs(`#dec_mun_prct_${snippetId} .bubulle`);
// Gestionnaire pour fermer le tooltip en cliquant ailleurs
document.addEventListener(« click », function (e) {
if (!e.target.closest(`#dec_mun_prct_${snippetId} circle`) && !e.target.closest(`#search_mun_prct_${snippetId}`)) {
const circles = getA(`#dec_mun_prct_${snippetId} circle.bubulle`);
forEach(circles, function (circle) {
circle.classList.remove(« selected-commune »);
});
}
});
// Gérer les onglets par tour
const tourTabs = document.querySelectorAll(
`.dec_mun_prct_container[data-attr=${snippetId}] [data-tour]`
);
tourTabs.forEach(btn => {
btn.addEventListener(« click », () => {
const newTour = btn.dataset.tour;
currentTour = newTour;
tourTabs.forEach(b => b.classList.remove(« lmui-tab_enabled »));
btn.classList.add(« lmui-tab_enabled »);
switchBloc(currentBloc);
});
});
}
// Fonction pour réinitialiser la carte
const reset_func = () => {
document.querySelector(`#search_mun_prct_${snippetId} input`).value = « »;
d3.selectAll(`#dec_mun_prct_${snippetId} circle.bubulle`).classed(« opacified », false);
};
// Start
fillHtml();
drawMap(langue);
}
initPercentMap();
Il vous reste 0% de cet article à lire. La suite est réservée aux abonnés.




