feat: roseta de los vientos con Chart.js polarArea

This commit is contained in:
Tatiana Villa Ema 2026-04-27 01:08:17 +02:00
parent 9ecf4114ba
commit b457a8d614
2 changed files with 102 additions and 1 deletions

View File

@ -117,6 +117,19 @@
</div>
</div>
<!-- ROSETA DE VIENTOS -->
<div class="col-md-6 col-lg-4">
<div class="card project-card h-100">
<div class="card-body d-flex flex-column">
<h5 class="card-title fw-bold">Rosa de los vientos</h5>
<p class="text-muted small mb-2">Frecuencia por dirección — mes seleccionado</p>
<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>
</div>
</div>
</div>
</div>
</div>
</main>
@ -127,6 +140,7 @@
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/estadisticas.js?v=2"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2/dist/chart.umd.min.js"></script>
<script src="js/estadisticas.js?v=3"></script>
</body>
</html>

View File

@ -50,6 +50,7 @@ async function loadStats(options = {}) {
renderMonthStats(data);
renderTrend(data);
renderWindRose(data);
} catch (err) {
console.error(err);
@ -253,6 +254,92 @@ function renderTrend(data) {
container.innerHTML = html;
}
// ====================
// Rosa de los vientos
// ====================
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 }
];
// Filtrar por mes seleccionado y agrupar por día para no contar duplicados horarios
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));
}
});
// 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;
});
// 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) {
counts[s.label]++;
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 canvas = $("wind-rose");
if (!canvas) return;
if (windRoseChart) windRoseChart.destroy();
windRoseChart = new Chart(canvas, {
type: "polarArea",
data: {
labels,
datasets: [{
data: values,
backgroundColor: labels.map(() => accent),
borderColor: labels.map(() => border),
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: ctx => ` ${ctx.label}: ${ctx.raw} días`
}
}
},
scales: {
r: {
ticks: { color: "#888", font: { size: 10 }, backdropColor: "transparent" },
grid: { color: "rgba(255,255,255,0.1)" },
pointLabels: { color: "#a4d7f4", font: { size: 12 } }
}
}
}
});
}
// ====================
// Cargar mes actual según selectedMonth
// ====================