fix: wind rose usa vientoDireccion camelCase, muestra vel. media por sector
This commit is contained in:
parent
b457a8d614
commit
d6c9f5e781
|
|
@ -126,6 +126,7 @@
|
||||||
<div class="flex-grow-1 d-flex align-items-center justify-content-center">
|
<div class="flex-grow-1 d-flex align-items-center justify-content-center">
|
||||||
<canvas id="wind-rose" style="max-width:280px; max-height:280px;"></canvas>
|
<canvas id="wind-rose" style="max-width:280px; max-height:280px;"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
<p id="wind-rose-info" class="text-center text-muted small mt-2 mb-0"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@ function renderTrend(data) {
|
||||||
let windRoseChart = null;
|
let windRoseChart = null;
|
||||||
|
|
||||||
function renderWindRose(data) {
|
function renderWindRose(data) {
|
||||||
const sectors = [
|
const sectorDefs = [
|
||||||
{ label: "N", min: 337.5, max: 360 },
|
{ label: "N", min: 337.5, max: 360 },
|
||||||
{ label: "N", min: 0, max: 22.5 },
|
{ label: "N", min: 0, max: 22.5 },
|
||||||
{ label: "NE", min: 22.5, max: 67.5 },
|
{ label: "NE", min: 22.5, max: 67.5 },
|
||||||
|
|
@ -272,36 +272,59 @@ function renderWindRose(data) {
|
||||||
{ label: "NO", min: 292.5, max: 337.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 = {};
|
const byDay = {};
|
||||||
data.forEach(d => {
|
data.forEach(d => {
|
||||||
const date = new Date(d.dia);
|
const date = new Date(d.dia);
|
||||||
if (date.getMonth() === selectedMonth) {
|
if (date.getMonth() !== selectedMonth) return;
|
||||||
if (!byDay[d.dia]) byDay[d.dia] = [];
|
if (!byDay[d.dia]) byDay[d.dia] = { dirs: [], speeds: [] };
|
||||||
byDay[d.dia].push(parseFloat(d.viento_direccion));
|
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
|
// Por cada día: dirección media y velocidad media
|
||||||
const direcciones = Object.values(byDay).map(dirs => {
|
const readings = Object.values(byDay).map(day => ({
|
||||||
return dirs.reduce((a, b) => a + b, 0) / dirs.length;
|
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)
|
// Acumular frecuencia y velocidad media por sector
|
||||||
const counts = { N: 0, NE: 0, E: 0, SE: 0, S: 0, SO: 0, O: 0, NO: 0 };
|
const counts = {};
|
||||||
direcciones.forEach(deg => {
|
const speedSum = {};
|
||||||
for (const s of sectors) {
|
labels.forEach(l => { counts[l] = 0; speedSum[l] = 0; });
|
||||||
if (deg >= s.min && deg < s.max) {
|
|
||||||
|
readings.forEach(({ dir, spd }) => {
|
||||||
|
for (const s of sectorDefs) {
|
||||||
|
if (dir >= s.min && dir < s.max) {
|
||||||
counts[s.label]++;
|
counts[s.label]++;
|
||||||
|
speedSum[s.label] += spd;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = ["N", "NE", "E", "SE", "S", "SO", "O", "NO"];
|
|
||||||
const values = labels.map(l => counts[l]);
|
const values = labels.map(l => counts[l]);
|
||||||
const accent = "rgba(164, 215, 244, 0.75)";
|
const avgSpeed = labels.map(l => counts[l] ? (speedSum[l] / counts[l]).toFixed(1) : 0);
|
||||||
const border = "rgba(164, 215, 244, 1)";
|
|
||||||
|
// 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");
|
const canvas = $("wind-rose");
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
@ -314,8 +337,8 @@ function renderWindRose(data) {
|
||||||
labels,
|
labels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: values,
|
data: values,
|
||||||
backgroundColor: labels.map(() => accent),
|
backgroundColor: "rgba(164, 215, 244, 0.65)",
|
||||||
borderColor: labels.map(() => border),
|
borderColor: "rgba(164, 215, 244, 1)",
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
|
@ -325,15 +348,18 @@ function renderWindRose(data) {
|
||||||
legend: { display: false },
|
legend: { display: false },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
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: {
|
scales: {
|
||||||
r: {
|
r: {
|
||||||
ticks: { color: "#888", font: { size: 10 }, backdropColor: "transparent" },
|
ticks: { color: "#666", font: { size: 10 }, backdropColor: "transparent" },
|
||||||
grid: { color: "rgba(255,255,255,0.1)" },
|
grid: { color: "rgba(255,255,255,0.08)" },
|
||||||
pointLabels: { color: "#a4d7f4", font: { size: 12 } }
|
pointLabels: { color: "#a4d7f4", font: { size: 13, weight: "bold" } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue