diff --git a/frontend/index.html b/frontend/index.html
index c0a0bd0..92b6a5c 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -126,6 +126,7 @@
+
diff --git a/frontend/js/estadisticas.js b/frontend/js/estadisticas.js
index 839cebc..31211f0 100644
--- a/frontend/js/estadisticas.js
+++ b/frontend/js/estadisticas.js
@@ -260,48 +260,71 @@ function renderTrend(data) {
let windRoseChart = null;
function renderWindRose(data) {
- const sectors = [
- { label: "N", min: 337.5, max: 360 },
- { label: "N", min: 0, max: 22.5 },
- { label: "NE", min: 22.5, max: 67.5 },
- { label: "E", min: 67.5, max: 112.5 },
- { label: "SE", min: 112.5, max: 157.5 },
- { label: "S", min: 157.5, max: 202.5 },
- { label: "SO", min: 202.5, max: 247.5 },
- { label: "O", min: 247.5, max: 292.5 },
- { label: "NO", min: 292.5, max: 337.5 }
+ const sectorDefs = [
+ { label: "N", min: 337.5, max: 360 },
+ { label: "N", min: 0, max: 22.5 },
+ { label: "NE", min: 22.5, max: 67.5 },
+ { label: "E", min: 67.5, max: 112.5 },
+ { label: "SE", min: 112.5, max: 157.5 },
+ { label: "S", min: 157.5, max: 202.5 },
+ { label: "SO", min: 202.5, max: 247.5 },
+ { label: "O", min: 247.5, max: 292.5 },
+ { label: "NO", min: 292.5, max: 337.5 }
];
- // Filtrar por mes seleccionado y agrupar por día para no contar duplicados horarios
+ const labels = ["N", "NE", "E", "SE", "S", "SO", "O", "NO"];
+
+ // Agrupar lecturas por día y mes seleccionado
+ // vientoDireccion viene en camelCase desde Spring Boot (sin @JsonProperty)
const byDay = {};
data.forEach(d => {
const date = new Date(d.dia);
- if (date.getMonth() === selectedMonth) {
- if (!byDay[d.dia]) byDay[d.dia] = [];
- byDay[d.dia].push(parseFloat(d.viento_direccion));
- }
+ if (date.getMonth() !== selectedMonth) return;
+ if (!byDay[d.dia]) byDay[d.dia] = { dirs: [], speeds: [] };
+ const dir = parseFloat(d.vientoDireccion ?? d.viento_direccion ?? 0);
+ const spd = parseFloat(d.viento_velocidad ?? 0);
+ byDay[d.dia].dirs.push(dir);
+ byDay[d.dia].speeds.push(spd);
});
- // Por cada día usar la dirección media del día
- const direcciones = Object.values(byDay).map(dirs => {
- return dirs.reduce((a, b) => a + b, 0) / dirs.length;
- });
+ // Por cada día: dirección media y velocidad media
+ const readings = Object.values(byDay).map(day => ({
+ dir: day.dirs.reduce((a, b) => a + b, 0) / day.dirs.length,
+ spd: day.speeds.reduce((a, b) => a + b, 0) / day.speeds.length
+ }));
- // Contar frecuencia por sector (N agrupa los dos rangos)
- const counts = { N: 0, NE: 0, E: 0, SE: 0, S: 0, SO: 0, O: 0, NO: 0 };
- direcciones.forEach(deg => {
- for (const s of sectors) {
- if (deg >= s.min && deg < s.max) {
+ // Acumular frecuencia y velocidad media por sector
+ const counts = {};
+ const speedSum = {};
+ labels.forEach(l => { counts[l] = 0; speedSum[l] = 0; });
+
+ readings.forEach(({ dir, spd }) => {
+ for (const s of sectorDefs) {
+ if (dir >= s.min && dir < s.max) {
counts[s.label]++;
+ speedSum[s.label] += spd;
break;
}
}
});
- const labels = ["N", "NE", "E", "SE", "S", "SO", "O", "NO"];
- const values = labels.map(l => counts[l]);
- const accent = "rgba(164, 215, 244, 0.75)";
- const border = "rgba(164, 215, 244, 1)";
+ const values = labels.map(l => counts[l]);
+ const avgSpeed = labels.map(l => counts[l] ? (speedSum[l] / counts[l]).toFixed(1) : 0);
+
+ // Dirección dominante
+ const dominant = labels.reduce((best, l) => counts[l] > counts[best] ? l : best, labels[0]);
+ const totalDays = readings.length;
+ const globalAvgSpeed = totalDays
+ ? (readings.reduce((s, r) => s + r.spd, 0) / totalDays).toFixed(1)
+ : 0;
+
+ // Actualizar subtítulo bajo la gráfica
+ const infoEl = $("wind-rose-info");
+ if (infoEl) {
+ infoEl.textContent = totalDays
+ ? `Dominante: ${dominant} · Vel. media: ${globalAvgSpeed} km/h`
+ : "Sin datos";
+ }
const canvas = $("wind-rose");
if (!canvas) return;
@@ -314,8 +337,8 @@ function renderWindRose(data) {
labels,
datasets: [{
data: values,
- backgroundColor: labels.map(() => accent),
- borderColor: labels.map(() => border),
+ backgroundColor: "rgba(164, 215, 244, 0.65)",
+ borderColor: "rgba(164, 215, 244, 1)",
borderWidth: 1
}]
},
@@ -325,15 +348,18 @@ function renderWindRose(data) {
legend: { display: false },
tooltip: {
callbacks: {
- label: ctx => ` ${ctx.label}: ${ctx.raw} días`
+ label: ctx => {
+ const l = ctx.label;
+ return ` ${l}: ${ctx.raw} días · vel. media ${avgSpeed[ctx.dataIndex]} km/h`;
+ }
}
}
},
scales: {
r: {
- ticks: { color: "#888", font: { size: 10 }, backdropColor: "transparent" },
- grid: { color: "rgba(255,255,255,0.1)" },
- pointLabels: { color: "#a4d7f4", font: { size: 12 } }
+ ticks: { color: "#666", font: { size: 10 }, backdropColor: "transparent" },
+ grid: { color: "rgba(255,255,255,0.08)" },
+ pointLabels: { color: "#a4d7f4", font: { size: 13, weight: "bold" } }
}
}
}