Skip to content
Snippets Groups Projects
Commit 6234fa02 authored by RABEHI AMIRA p2312013's avatar RABEHI AMIRA p2312013
Browse files

started fixing the 4th visualisation

parent 929b2ba7
No related branches found
No related tags found
No related merge requests found
...@@ -999,8 +999,7 @@ function renderCaloriesVisualization() { ...@@ -999,8 +999,7 @@ function renderCaloriesVisualization() {
} }
}); });
} }
// Visu4 - Découpe en 4 visualisations distinctes avec légende, modal et gestion des valeurs manquantes
// Visu4
function renderSleepVisualization() { function renderSleepVisualization() {
fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire
.then((response) => response.json()) .then((response) => response.json())
...@@ -1012,85 +1011,58 @@ function renderSleepVisualization() { ...@@ -1012,85 +1011,58 @@ function renderSleepVisualization() {
const members = ["Corentin", "Maya", "Anis", "Amira"]; const members = ["Corentin", "Maya", "Anis", "Amira"];
data.forEach((d) => (d.date = parseDate(d.date))); data.forEach((d) => (d.date = parseDate(d.date)));
// Dimensions et marges // Dimensions et marges pour chaque sous-graphe
const margin = { top: 50, right: 230, bottom: 150, left: 70 }; const margin = { top: 30, right: 30, bottom: 50, left: 50 };
const width = 800 - margin.left - margin.right; const fullWidth = 600;
const height = 500 - margin.top - margin.bottom; const fullHeight = 400;
const width = (fullWidth - margin.left - margin.right) / 2;
const height = (fullHeight - margin.top - margin.bottom) / 2;
// Créer le conteneur SVG principal // Créer le conteneur SVG principal
const svg = d3 const svg = d3
.select("#sleep-visualization") .select("#sleep-visualization")
.append("svg") .append("svg")
.attr("width", width + margin.left + margin.right) .attr("width", fullWidth)
.attr("height", height + margin.top + margin.bottom) .attr("height", fullHeight);
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Créer un conteneur pour le graphique détaillé // Tooltip
const detailContainer = d3 const tooltip = d3
.select("#sleep-visualization") .select("body")
.append("div") .append("div")
.attr("id", "detail-container") .attr("class", "tooltip-sleep")
.style("margin-top", "20px"); .style("opacity", 0);
// Définir les années et la plage d'affichage // Modal pour afficher les détails
const years = [2021, 2022, 2023, 2024]; const modal = d3.select("#modal");
// Créer le slider modal.select(".close").on("click", () => {
modal.style("display", "none");
d3.select("#detail-visualization").selectAll("*").remove();
});
// Navigation temporelle
const prevYearBtn = document.getElementById("sleepPrevYear"); const prevYearBtn = document.getElementById("sleepPrevYear");
const nextYearBtn = document.getElementById("sleepNextYear"); const nextYearBtn = document.getElementById("sleepNextYear");
const currentYearDisplay = document.getElementById("sleepCurrentYear"); const currentYearDisplay = document.getElementById("sleepCurrentYear");
const years = [2021, 2022, 2023, 2024];
// Gestion des événements des boutons
prevYearBtn.addEventListener("click", () => { prevYearBtn.addEventListener("click", () => {
const currentYearIndex = years.indexOf( const currentYearIndex = years.indexOf(parseInt(currentYearDisplay.textContent));
parseInt(currentYearDisplay.textContent)
);
if (currentYearIndex > 0) { if (currentYearIndex > 0) {
const newYear = years[currentYearIndex - 1]; const newYear = years[currentYearIndex - 1];
currentYearDisplay.textContent = newYear; currentYearDisplay.textContent = newYear;
updateVisualization(newYear); // Affiche la visualisation pour 2024 (par défaut) updateVisualization(newYear);
updateAnalysis("sleep", newYear); // Assure que l'analyse correspondante est également affichée
// Activer/désactiver les boutons
nextYearBtn.disabled = false;
if (currentYearIndex - 1 === 0) {
prevYearBtn.disabled = true;
}
} }
}); });
nextYearBtn.addEventListener("click", () => { nextYearBtn.addEventListener("click", () => {
const currentYearIndex = years.indexOf( const currentYearIndex = years.indexOf(parseInt(currentYearDisplay.textContent));
parseInt(currentYearDisplay.textContent)
);
if (currentYearIndex < years.length - 1) { if (currentYearIndex < years.length - 1) {
const newYear = years[currentYearIndex + 1]; const newYear = years[currentYearIndex + 1];
currentYearDisplay.textContent = newYear; currentYearDisplay.textContent = newYear;
updateVisualization(newYear); // Affiche la visualisation pour 2024 (par défaut) updateVisualization(newYear);
updateAnalysis("sleep", newYear); // Assure que l'analyse correspondante est également affichée
// Activer/désactiver les boutons
prevYearBtn.disabled = false;
if (currentYearIndex + 1 === years.length - 1) {
nextYearBtn.disabled = true;
}
} }
}); });
// Initialisation des boutons
prevYearBtn.disabled =
years.indexOf(parseInt(currentYearDisplay.textContent)) === 0;
nextYearBtn.disabled =
years.indexOf(parseInt(currentYearDisplay.textContent)) ===
years.length - 1;
// Tooltip
const tooltip = d3
.select("body")
.append("div")
.attr("class", "tooltip-sleep")
.style("opacity", 0);
// Fonction de mise à jour de la visualisation // Fonction de mise à jour de la visualisation
function updateVisualization(selectedYear) { function updateVisualization(selectedYear) {
const filteredData = data.filter( const filteredData = data.filter(
...@@ -1110,303 +1082,113 @@ function renderSleepVisualization() { ...@@ -1110,303 +1082,113 @@ function renderSleepVisualization() {
members.forEach((member) => { members.forEach((member) => {
aggregated[`Sleep_${member}`] = d3.mean( aggregated[`Sleep_${member}`] = d3.mean(
records, records,
(d) => d[`Sleep_${member}`] || 0 (d) => d[`Sleep_${member}`] || -1
); );
}); });
return aggregated; return aggregated;
}); });
svg.selectAll("*").remove(); svg.selectAll("g.member-group").remove();
svg // Créer un graphe pour chaque membre
.append("text") members.forEach((member, memberIndex) => {
.attr("x", width / 2) // Centrer horizontalement const row = Math.floor(memberIndex / 2);
.attr("y", margin.top - 60) // Positionner légèrement au-dessus du graphique const col = memberIndex % 2;
.attr("text-anchor", "middle") // Centrer le texte
.style("font-size", "16px") // Taille de police
.style("font-weight", "bold") // Gras
.text("Analyse des heures de sommeil par mois");
const xScale = d3 const memberGroup = svg
.scaleBand() .append("g")
.domain(aggregatedData.map((d) => d.month)) .attr("class", "member-group")
.range([0, width]) .attr(
.padding(0.2); "transform",
`translate(${margin.left + col * (width + margin.right)}, ${
margin.top + row * (height + margin.bottom)
})`
);
const yScale = d3 const memberData = aggregatedData.map((d) => ({
.scaleLinear() month: d.month,
.domain([ sleep: d[`Sleep_${member}`],
0, }));
d3.max(aggregatedData, (d) =>
Math.max(...members.map((member) => d[`Sleep_${member}`]))
),
])
.nice()
.range([height, 0]);
const colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(members); const xScale = d3
.scaleBand()
.domain(memberData.map((d) => d.month))
.range([0, width])
.padding(0.2);
// Axe X const yScale = d3
svg .scaleLinear()
.domain([0, d3.max(memberData, (d) => (d.sleep === -1 ? 10 : d.sleep))])
.nice()
.range([height, 0]);
// Axe X avec rotation des labels
memberGroup
.append("g") .append("g")
.attr("transform", `translate(0, ${height})`) .attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(xScale)) .call(d3.axisBottom(xScale))
.selectAll("text") .selectAll("text")
.attr("transform", "rotate(-45)") .attr("transform", "rotate(-45)") // Rotation pour éviter les chevauchements
.style("text-anchor", "end"); .style("text-anchor", "end") // Alignement à droite pour plus de clarté
.style("font-size", "10px"); // Taille des labels ajustée pour s'adapter à l'espace
// Légende de l'axe X
svg
.append("text")
.attr("x", width / 2)
.attr("y", height + 70)
.attr("text-anchor", "middle")
.style("font-size", "14px")
.text("Mois");
// Axe Y
svg.append("g").call(d3.axisLeft(yScale));
// Légende de l'axe Y
svg
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", -50)
.attr("text-anchor", "middle")
.style("font-size", "14px")
.text("Sommeil moyen (heures)");
// Légende des membres (rectangles colorés)
// Créer un conteneur pour la légende // Axe Y
const legend = svg memberGroup.append("g").call(d3.axisLeft(yScale));
.append("g")
.attr(
"transform",
`translate(${width / 3}, ${height + margin.bottom - 50})`
) // Positionner la légende en bas, centrée
.attr("text-anchor", "middle");
// Ajouter les éléments de la légende
members.forEach((member, i) => {
legend
.append("rect")
.attr("x", i * 100 - members.length * 50) // Espacement horizontal entre les rectangles
.attr("y", 0)
.attr("width", 15)
.attr("height", 15)
.attr("fill", colorMap[member]);
legend // Titre du sous-graphe
memberGroup
.append("text") .append("text")
.attr("x", i * 100 - members.length * 50 + 20) // Texte à côté du rectangle .attr("x", width / 2)
.attr("y", 12) .attr("y", -10)
.text(member) .attr("text-anchor", "middle")
.style("font-size", "12px") .style("font-size", "14px")
.attr("text-anchor", "start"); .style("font-weight", "bold")
}); .text(`Sommeil de ${member}`);
// Ajouter un élément pour "Pas de données" // Barres pour le membre
legend memberGroup
.append("rect") .selectAll(".bar-sleep")
.attr("x", members.length * 100 - members.length * 50) // Position pour le rectangle gris .data(memberData)
.attr("y", 0)
.attr("width", 15)
.attr("height", 15)
.attr("fill", "lightgrey");
legend
.append("text")
.attr("x", members.length * 100 - members.length * 50 + 20) // Texte à côté du rectangle gris
.attr("y", 12)
.text("Pas de données")
.style("font-size", "12px")
.attr("text-anchor", "start");
// Barres pour chaque membre
members.forEach((member, i) => {
svg
.selectAll(`.bar-sleep-${member}`)
.data(aggregatedData)
.enter() .enter()
.append("rect") .append("rect")
.attr( .attr("x", (d) => xScale(d.month))
"x", .attr("y", (d) => (d.sleep === -1 ? yScale(yScale.domain()[1]) : yScale(d.sleep)))
(d) => xScale(d.month) + i * (xScale.bandwidth() / members.length) .attr("width", xScale.bandwidth())
.attr("height", (d) =>
d.sleep === -1 ? height - yScale(10) : height - yScale(d.sleep)
) )
.attr("y", (d) => { .attr("fill", (d) => (d.sleep === -1 ? "#D3D3D3" : colorMap[member]))
const value = d[`Sleep_${member}`];
return value === -1.0 ? yScale(2) : yScale(value); // Placer les -1.0 à une hauteur fixe, ici 2 heures
})
.attr("width", xScale.bandwidth() / members.length)
.attr("height", (d) => {
const value = d[`Sleep_${member}`];
return value === -1.0
? height - yScale(2)
: height - yScale(value); // Barres grisées si -1.0
})
.attr("fill", (d) => {
const value = d[`Sleep_${member}`];
return value === -1.0 ? "#D3D3D3" : colorMap[member]; // Gris pour -1.0
})
.on("mouseover", function (event, d) { .on("mouseover", function (event, d) {
tooltip.transition().duration(200).style("opacity", 0.9); // Transition d'apparition du tooltip tooltip.transition().duration(200).style("opacity", 1);
tooltip.html(`
<div style="text-align: center;">
<strong>Mois :</strong> ${d.month}<br>
<strong>Sommeil moyen :</strong> ${
d.sleep === -1 ? "Pas de données" : d.sleep.toFixed(2)
} heures
</div>
`);
})
.on("mousemove", function (event) {
tooltip tooltip
.html( .style("left", event.pageX + 15 + "px")
`Mois : ${d.month}<br>Sommeil moyen : ${ .style("top", event.pageY + 15 + "px");
d[`Sleep_${member}`] === -1.0
? "Pas de données"
: d[`Sleep_${member}`].toFixed(2)
} heures`
) // Affichage du tooltip
.style("left", event.pageX + 5 + "px")
.style("top", event.pageY - 28 + "px");
}) })
.on("mouseout", function () { .on("mouseout", function () {
tooltip.transition().duration(500).style("opacity", 0); // Transition de disparition du tooltip tooltip.transition().duration(500).style("opacity", 0);
})
.on("click", (event, d) => {
const memberColor = colorMap[member]; // Récupérer la couleur du membre
showDetailChart(d.month, member, selectedYear, memberColor); // Passer la couleur à la fonction showDetailChart
}); });
}); });
} }
// Fonction pour afficher les détails
function showDetailChart(month, member, year, memberColor) {
// Affiche le modal
const modal = d3.select("#modal");
modal.style("display", "block");
// Fermer le modal
modal.select(".close").on("click", () => {
modal.style("display", "none");
d3.select("#detail-visualization").selectAll("*").remove();
});
const detailContainer = d3.select("#detail-visualization");
detailContainer.selectAll("*").remove();
const filteredData = data.filter(
(d) =>
formatYear(d.date) === year.toString() &&
formatMonth(d.date) === month
);
const dailyData = d3
.groups(filteredData, (d) => formatDay(d.date))
.map(([day, records]) => ({
day: day,
value: d3.mean(records, (d) => d[`Sleep_${member}`] || 0),
}));
const detailSvg = detailContainer
.append("svg")
.attr("width", 600)
.attr("height", 400)
.append("g")
.attr("transform", "translate(50, 50)");
const xScale = d3
.scaleBand()
.domain(dailyData.map((d) => d.day))
.range([0, 500])
.padding(0.1);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(dailyData, (d) => d.value)])
.nice()
.range([300, 0]);
// Ajout de l'axe X
detailSvg
.append("g")
.attr("transform", "translate(0, 300)")
.call(d3.axisBottom(xScale));
// Ajout de l'axe Y
detailSvg.append("g").call(d3.axisLeft(yScale));
// Titre du graphique
detailSvg
.append("text")
.attr("x", 250)
.attr("y", -20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.text(`${member} - Sommeil du mois de ${month} (${year})`);
// Ajouter la légende de l'axe X (Jours)
detailSvg
.append("text")
.attr("x", 250)
.attr("y", 340)
.attr("text-anchor", "middle")
.style("font-size", "12px")
.text("Jours du mois");
// Ajouter la légende de l'axe Y (Heures de sommeil)
detailSvg
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -200)
.attr("y", -40)
.attr("text-anchor", "middle")
.style("font-size", "12px")
.text("Heures de sommeil moyen");
// Dessin des barres
detailSvg
.selectAll(".bar")
.data(dailyData)
.enter()
.append("rect")
.attr("x", (d) => xScale(d.day))
.attr("y", (d) => {
const value = d.value;
return value < 0 || value === null ? yScale(2) : yScale(value);
})
.attr("width", xScale.bandwidth())
.attr("height", (d) => {
const value = d.value;
return value < 0 || value === null
? 300 - yScale(2)
: 300 - yScale(value);
})
.attr("fill", (d) => {
const value = d.value;
return value < 0 || value === null ? "lightgrey" : memberColor;
})
.on("mouseover", function (event, d) {
tooltip.transition().duration(200).style("opacity", 1);
tooltip.html(`
<div style="text-align: center;">
<strong>Jour :</strong> ${d.day}<br>
<strong>Sommeil :</strong> ${
d.value === -1.0 || d.value === null
? "Pas de données"
: d.value.toFixed(2)
} heures
</div>
`);
})
.on("mousemove", function (event) {
tooltip
.style("left", event.pageX + 15 + "px") // Décalage pour positionner le tooltip
.style("top", event.pageY + 15 + "px");
})
.on("mouseout", function () {
tooltip.transition().duration(500).style("opacity", 0);
});
}
// Initialisation de la visualisation // Initialisation de la visualisation
updateVisualization(years[3]); updateVisualization(2024);
}); });
} }
// Visu 5 // Visu 5
function renderSleepActivityVisualization() { function renderSleepActivityVisualization() {
fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment