function parseCSV(csv, datasetKey) {
const lines = csv.split('\n').filter(line => line.trim() !== '');
const headers = lines[0].split(',');
const timeIndex = headers.indexOf('time');
const dataIndex = headers.findIndex(header => header.trim().startsWith(datasetKey));
if (timeIndex === -1 || dataIndex === -1) {
alert(`The CSV file must contain "time" and "${datasetKey}" columns.`);
return { time: [], data: [] };
lines.slice(1).forEach(line =>
const cols = line.split(',');
time.push(cols[timeIndex].trim()); // Keep the time column as strings
data.push(parseFloat(cols[dataIndex].trim())); // Parse dataset values as floats
// Function to convert Kelvin to Celsius
function kelvinToCelsius(kelvin) {
// Function to convert Kelvin to Fahrenheit
function kelvinToFahrenheit(kelvin) {
return (kelvin - 273.15) * 9 / 5 + 32;
// Toggle for Celsius or Fahrenheit (default is Celsius)
// Function to determine whether a given timestamp is daytime or nighttime
function isDaytime(date, latitude, longitude) {
const times = SunCalc.getTimes(date, latitude, longitude);
return date >= times.sunrise && date <= times.sunset;
function computeDaytimeIntervals(dates, latitude, longitude) {
return dates.map(date => {
const times = SunCalc.getTimes(date, latitude, longitude);
// Chart.js plugin to paint the background
const backgroundPlugin = {
id: 'dayNightBackground',
const { ctx, chartArea, scales } = chart;
const { left, right, top, bottom } = chartArea;
// Save the current canvas state
// Restore the canvas state
let daytimeIntervals = []; // Store precomputed intervals globally
// Function to update the chart with new data
function updateChart(chart, labels, data) {
// Parse the labels (time column) as Date objects
const parsedLabels = labels.map(label => new Date(label.trim() + "Z")); // Add "Z" to indicate UTC
// Precompute daytime intervals
daytimeIntervals = computeDaytimeIntervals(parsedLabels, 49.55, 18.57);
const minValue = Math.min(...data);
const maxValue = Math.max(...data);
chart.options.scales.y.suggestedMin = minValue - 0.1 * (maxValue - minValue); // Add a little padding below
chart.options.scales.y.suggestedMax = maxValue + 0.1 * (maxValue - minValue); // Add a little padding above
// Dynamically adjust the y-axis scale
chart.options.scales.y.min = minValue - 0.1 * (maxValue - minValue); // Add a small padding below the minimum value
chart.options.scales.y.max = maxValue + 0.1 * (maxValue - minValue); // Add a small padding above the maximum value
// Calculate the step size as 1/10th of the range
const range = maxValue - minValue;
const stepSize = range / 10.0;
chart.options.scales.y.ticks.stepSize = stepSize; // Set the step size for horizontal gridlines
chart.data.labels = labels;
chart.data.datasets[0].data = data;
const minDate = new Date(labels[0]).getTime();
const maxDate = new Date(labels[labels.length - 1]).getTime();
function formatTicks(value, index, ticks) {
const date = new Date(this.getLabelForValue(val)); // Chart.js provides timestamp values for time scales
const hours = date.getUTCHours();
const minutes = date.getUTCMinutes();
// Show ticks only for specific hours: 6:00, 12:00, 18:00, and 0:00
if (hours === 0 && minutes === 0) {
return ''; // Hide other ticks
// Initialize an empty chart
const ctx = document.getElementById('lineChart').getContext('2d');
const lineChart = new Chart(ctx, {
label: '2 Meter Temperature [C]',
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
tension: 0.4 // Smooth curves
maintainAspectRatio: false,
label: function(context) {
const kelvin = context.raw;
const convertedValue = isCelsius ? kelvinToCelsius(kelvin) : kelvinToFahrenheit(kelvin);
const unit = isCelsius ? '°C' : '°F';
return `${convertedValue.toFixed(2)} ${unit}`;
unit: 'day', // Set unit to day
tooltipFormat: 'MMMM d, HH:mm', // Tooltips show full date and time
callback: function(value, index) {
const date = new Date(this.getLabelForValue(value)); // Chart.js provides timestamp values for time scales
const hours = date.getUTCHours();
const day = date.getUTCDate();
const month = date.toLocaleString('default', { month: 'long' });
// Show ticks only at mid-day (12:00 UTC)
if (day === 1 && hours === 12) {
return `${month} ${day}`;
} else if (hours === 12) {
return null; // Hide other ticks
drawTicks: true, // Ensure ticks are drawn
drawOnChartArea: false, // Only draw grid lines for displayed ticks
lineWidth: function(ctx) {
if (ctx.tick.index % 360 == 0) {
return 2; // Thicker grid lines for major ticks
return 1; // Normal grid lines for others
const date = new Date(ctx.tick.value);
const day = date.getUTCDate();
// Optionally use a different color for major grid lines
return day === 1 || day === 15 ? 'rgba(0, 0, 0, 0.8)' : 'rgba(0, 0, 0, 0.2)';
stepSize: 5, // Set step size to 5 degrees
<!-- callback: function(value) {-->
<!-- const convertedValue = isCelsius ? kelvinToCelsius(value) : kelvinToFahrenheit(value);-->
<!-- return `${convertedValue.toFixed(1)} ${isCelsius ? '°C' : '°F'}`;-->
plugins: [backgroundPlugin]
// Handle file input and dataset selection
document.getElementById('csvFileInput').addEventListener('change', function(event) {
const file = event.target.files[0];
const datasetKey = document.getElementById('datasetSelector').value;
const reader = new FileReader();
reader.onload = function(e) {
const csv = e.target.result;
const { time, data } = parseCSV(csv, datasetKey);
if (time.length && data.length) {
updateChart(lineChart, time, data, datasetKey);
// Update chart when dataset selection changes
document.getElementById('datasetSelector').addEventListener('change', function() {
const csvInput = document.getElementById('csvFileInput');
if (csvInput.files.length > 0) {
const event = new Event('change');
csvInput.dispatchEvent(event); // Trigger the file input change event