import {useParams} from "react-router-dom"
import FormGroup from '@mui/material/FormGroup'
import FormControlLabel from '@mui/material/FormControlLabel'
import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableCell from '@mui/material/TableCell'
import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'
import Paper from '@mui/material/Paper'
import Switch from '@mui/material/Switch'
import * as Highcharts from 'highcharts'
import highchartsItem from 'highcharts/modules/item-series'
import HighchartsReact from 'highcharts-react-official'
import {useEffect, useState} from "react"
import {GeoJSON, MapContainer, Popup, TileLayer, useMap} from "react-leaflet"
import 'leaflet/dist/leaflet.css'
highchartsItem(Highcharts)
function ResultatsTable({blocs, nuances, listes, resultats, siegesParListe}) {
const voix_listes = resultats?.voix_listes ?? {}
const listes_triees = listes.toSorted((l1, l2) => {
return (voix_listes[l2.numero] || 0) - (voix_listes[l1.numero] || 0)
})
return <>
Numéro
Liste
Nuance
Bloc
Voix
% Inscrit⋅es
% Exprimé⋅es
Sièges
{listes_triees.map((liste) => (
))}
>
}
function ListeRow({liste, voix, resultats, siegesParListe, blocs, nuances}) {
const bloc = blocs.filter(bloc => bloc.nom === liste.bloc)[0]
const nuance = nuances.filter(nuance => nuance.code === liste.nuance)[0]
return
{liste.numero}
{liste.nom}
{liste.nuance}
{liste.bloc}
{voix}
{(100 * voix / resultats.inscrits).toFixed(2)} %
{(100 * voix / resultats.exprimes).toFixed(2)} %
{siegesParListe[liste.numero]}
}
function ParticipationTable({resultats}) {
return <>
Nombre
% Inscrit⋅es
% Votant⋅es
Inscrit⋅es
{resultats.inscrits}
Abstention
{resultats.abstentions}
{(100 * resultats.abstentions / resultats.inscrits).toFixed(2)} %
Votant⋅es
{resultats.votants}
{(100 * resultats.votants / resultats.inscrits).toFixed(2)} %
Blancs
{resultats.blancs}
{(100 * resultats.blancs / resultats.inscrits).toFixed(2)} %
{(100 * resultats.blancs / resultats.votants).toFixed(2)} %
Nuls
{resultats.nuls}
{(100 * resultats.nuls / resultats.inscrits).toFixed(2)} %
{(100 * resultats.nuls / resultats.votants).toFixed(2)} %
Exprimés
{resultats.exprimes}
{(100 * resultats.exprimes / resultats.inscrits).toFixed(2)} %
{(100 * resultats.exprimes / resultats.votants).toFixed(2)} %
>
}
function RegionGeoJSON({resultats_region, listes, blocs, nuances, grouperParBloc = false}) {
const voix_listes = resultats_region?.voix_listes ?? {}
const listes_triees = listes.toSorted((l1, l2) => {
return (voix_listes[l2.numero] || 0) - (voix_listes[l1.numero] || 0)
})
const voixParBloc = {}
const voixParNuance = {}
for (let bloc of blocs) {
voixParBloc[bloc.nom] = 0
}
for (let nuance of nuances) {
voixParNuance[nuance.code] = 0
}
for (let liste of listes) {
voixParBloc[liste.bloc] += resultats_region.voix_listes[liste.numero] || 0
voixParNuance[liste.nuance] += resultats_region.voix_listes[liste.numero] || 0
}
let couleur = 'grey'
if (grouperParBloc) {
let maxVoix = 0
for (let bloc of blocs) {
if (voixParBloc[bloc.nom] > maxVoix) {
maxVoix = voixParBloc[bloc.nom]
couleur = bloc.couleur
}
}
}
else {
let maxVoix = 0
for (let nuance of nuances) {
if (voixParNuance[nuance.code] > maxVoix) {
maxVoix = voixParNuance[nuance.code]
couleur = nuance.couleur
}
}
}
return
{resultats_region.region.nom}
{listes_triees.slice(0, 5).map(liste =>
- {liste.nom} : {voix_listes[liste.numero]} ({(100 * voix_listes[liste.numero] / resultats_region.exprimes).toFixed(2)} %)
)}
}
function ContenuCarte({typeResultats, resultats, listes, blocs, nuances, grouperParBloc = false}) {
const [regions, setRegions] = useState([])
useEffect(() => {
if (!resultats || !resultats.france || !resultats.france.regions)
return
setRegions(regions => [])
resultats.france.regions.forEach(region_code => {
fetch(`/data/resultats/europeennes2024/regions/${region_code}.json`).then(response => response.json())
.then(region => setRegions(regions => [...regions, region]))
})
}, [typeResultats, resultats])
const map = useMap()
return <>
{regions.map(region => )}
>
}
function Carte({typeResultats, resultats, listes, blocs, nuances, grouperParBloc = false}) {
const center = [46.603354, 1.888334]
return <>
>
}
export default function Election2024({typeResultats = "france"}) {
const {zoneId} = useParams()
const [zoneName, setZoneName] = useState("France")
const [grouperParBloc, setGrouperParBloc] = useState(false)
const [retirerSeuil, setRetirerSeuil] = useState(false)
const [blocs, setBlocs] = useState([])
const [nuances, setNuances] = useState([])
const [listes, setListes] = useState([])
const [resultats, setResultats] = useState([])
const [siegesParListe, setSiegesParListe] = useState({})
const [voixParBloc, setVoixParBloc] = useState([])
const [voixParNuance, setVoixParNuance] = useState([])
const [siegesParBloc, setSiegesParBloc] = useState([])
const [siegesParNuance, setSiegesParNuance] = useState([])
const [categoriesVoix, setCategoriesVoix] = useState([])
const [dataVoix, setDataVoix] = useState([])
const [dataSieges, setDataSieges] = useState([])
useEffect(() => {
fetch("/data/resultats/europeennes2024/blocs.json").then(response => response.json())
.then(data => setBlocs(data))
fetch("/data/resultats/europeennes2024/nuances.json").then(response => response.json())
.then(data => setNuances(data))
fetch("/data/resultats/europeennes2024/listes.json").then(response => response.json())
.then(data => setListes(data))
if (typeResultats === "france") {
fetch("/data/resultats/europeennes2024/france.json").then(response => response.json())
.then(data => setResultats(data))
}
else {
fetch(`/data/resultats/europeennes2024/${typeResultats}/${zoneId}.json`).then(response => response.json())
.then(data => setResultats(data))
}
}, [typeResultats, zoneId])
useEffect(() => {
if (!resultats || resultats.length === 0)
setZoneName("")
else if (typeResultats === "france")
setZoneName("France")
else if (typeResultats === "regions")
setZoneName(`Région ${resultats.region.nom}`)
else if (typeResultats === "departements")
setZoneName(`Département ${resultats.departement.nom}`)
else if (typeResultats === "circonscriptions")
setZoneName(`Circonscription ${resultats.circonscription.nom}`)
else if (typeResultats === "communes")
setZoneName(`Commune ${resultats.commune.nom}`)
else if (typeResultats === "bureaux_vote")
setZoneName(resultats.bureau_vote.libelle)
}, [typeResultats, resultats])
useEffect(() => {
const parBloc = {}
const parNuance = {}
for (let bloc of blocs) {
parBloc[bloc.nom] = 0
}
for (let nuance of nuances) {
parNuance[nuance.code] = 0
}
for (let liste of listes) {
parBloc[liste.bloc] += resultats.voix_listes[liste.numero] || 0
parNuance[liste.nuance] += resultats.voix_listes[liste.numero] || 0
}
setVoixParBloc(parBloc)
setVoixParNuance(parNuance)
}, [blocs, nuances, listes, resultats])
useEffect(() => {
const categories = []
const data = []
if (grouperParBloc) {
for (let bloc of blocs) {
categories.push(bloc.nom)
data.push([bloc.nom, voixParBloc[bloc.nom], bloc.couleur, bloc.nom])
}
}
else {
for (let nuance of nuances) {
categories.push(nuance.nom)
data.push([nuance.nom, voixParNuance[nuance.code], nuance.couleur, nuance.nom])
}
}
setCategoriesVoix(categories)
setDataVoix(data)
}, [voixParBloc, voixParNuance, grouperParBloc])
useEffect(() => {
const MAX_SIEGES = 81
const sieges = {}
const listesElues = []
let siegesAffectes = 0
let totalVoix = resultats.exprimes
for (let liste of listes) {
const voix = resultats.voix_listes[liste.numero] || 0
if (voix / resultats.exprimes < 0.05 && !retirerSeuil) {
// Barre des 5 % non franchie
totalVoix -= voix
sieges[liste.numero] = 0
}
else {
listesElues.push(liste)
}
}
if (listesElues.length === 0)
return
for (let liste of listesElues) {
const voix = resultats.voix_listes[liste.numero] || 0
sieges[liste.numero] = Math.floor(MAX_SIEGES * voix / totalVoix)
siegesAffectes += sieges[liste.numero]
}
while (siegesAffectes < MAX_SIEGES) {
// Méthode de la plus forte moyenne pour affecter les sièges restants
let maxMoyenne = 0
let listeElue = null
for (let liste of listesElues) {
if (sieges[liste.numero] < MAX_SIEGES) {
const voix = resultats.voix_listes[liste.numero] || 0
const moyenne = voix / (sieges[liste.numero] + 1)
if (moyenne > maxMoyenne) {
maxMoyenne = moyenne
listeElue = liste
}
}
}
sieges[listeElue.numero]++
siegesAffectes++
}
setSiegesParListe(sieges)
}, [listes, resultats, retirerSeuil])
useEffect(() => {
const parBloc = {}
const parNuance = {}
for (let bloc of blocs) {
parBloc[bloc.nom] = 0
}
for (let nuance of nuances) {
parNuance[nuance.code] = 0
}
for (let liste of listes) {
parBloc[liste.bloc] += siegesParListe[liste.numero] || 0
parNuance[liste.nuance] += siegesParListe[liste.numero] || 0
}
setSiegesParBloc(parBloc)
setSiegesParNuance(parNuance)
}, [blocs, nuances, listes, siegesParListe])
useEffect(() => {
const data = []
if (grouperParBloc) {
for (let bloc of blocs) {
data.push([bloc.nom, siegesParBloc[bloc.nom], bloc.couleur, bloc.nom])
}
}
else {
for (let nuance of nuances) {
data.push([nuance.nom, siegesParNuance[nuance.code], nuance.couleur, nuance.nom])
}
}
setDataSieges(data)
}, [blocs, nuances, siegesParBloc, siegesParNuance, grouperParBloc])
const compositonOptions = {
chart: {
type: 'item'
},
title: {
text: 'Projection eurodéputé⋅es français⋅es 2024'
},
legend: {
labelFormat: '{name} {y}'
},
series: [{
name: 'Nombre de sièges',
keys: ['name', 'y', 'color', 'label'],
data: dataSieges,
dataLabels: {
enabled: false,
format: '{point.label}'
},
// Circular options
center: ['50%', '88%'],
size: '170%',
startAngle: -100,
endAngle: 100
}]
}
const scoreOptions = {
chart: {
type: 'column'
},
title: {
text: `Résultats des élections européennes 2024 : ${zoneName}`,
},
tooltip: {
formatter: function () {
return `${this.x} : ${this.y} voix (${(100 * this.y / resultats.exprimes).toFixed(2)} %)
`
}
},
xAxis: {
categories: categoriesVoix,
},
series: [{
name: "Nombre de voix",
keys: ['name', 'y', 'color', 'label'],
data: dataVoix,
}]
}
return <>
setGrouperParBloc(event.target.checked)} inputProps={{ 'aria-label': 'controlled' }} />}
label="Grouper par bloc plutôt que nuance politique" />
setRetirerSeuil(event.target.checked)} inputProps={{ 'aria-label': 'controlled' }} />}
label="Retirer le seuil des 5 %" />
>
}