function createPlot(container, data, column) {
// Extract unique timestamps at the beginning of each month
const monthlyMarkers = data
const currentDate = d.time;
const prevDate = i > 0 ? arr[i - 1].time : null;
currentDate.getMonth() !== (prevDate ? prevDate.getMonth() : -1) ||
currentDate.getFullYear() !== (prevDate ? prevDate.getFullYear() : -1)
// Calculate y-domain with padding
const yValues = data.map((d) => d[column]);
const yMin = Math.min(...yValues);
const yMax = Math.max(...yValues);
const yPadding = (yMax - yMin) * 0.05; // Add 5% vertical padding
// Calculate x-domain with padding
const xExtent = d3.extent(data, (d) => d.time); // Min and max time
const xPadding = (xExtent[1] - xExtent[0]) * 0.02; // Add 2% horizontal padding
const xDomain = [new Date(xExtent[0] - xPadding), new Date(xExtent[1] + xPadding)];
width: container.offsetWidth, // Full width
height: 400, // Adjust height as needed
Plot.frame({ strokeWidth: 2 }), // Frame around the chart
Plot.ruleY([0], { stroke: "red" }), // Horizontal rule at Y=0
Plot.ruleX(monthlyMarkers, { stroke: "blue", strokeWidth: 1 }), // Vertical lines for the start of each month
Plot.line(data, { x: "time", y: column, stroke: "black", strokeWidth: 2 }), // Line chart
tickRotate: -45, // Rotate ticks for better readability
tickValues: monthlyMarkers, // Use monthlyMarkers for tick positions
tickFormat: d3.timeFormat("%b"), // Format ticks as short month names (e.g., "Jan", "Feb")
domain: xDomain, // Add horizontal padding
domain: [yMin - yPadding, yMax + yPadding], // Add vertical padding
grid: true, // Add gridlines
container.appendChild(plot);
function updateCharts(data, columns) {
const chartContainer = document.getElementById("chartContainer");
chartContainer.innerHTML = ""; // Clear existing charts
columns.forEach((column) => {
const chartDiv = document.createElement("div");
chartDiv.className = "chart-container";
chartContainer.appendChild(chartDiv);
createPlot(chartDiv, data, column);
// Save file content to localStorage
function saveFileToLocalStorage(fileContent) {
localStorage.setItem("lastOpenedFile", fileContent);
// Load file content from localStorage
function loadFileFromLocalStorage() {
const fileContent = localStorage.getItem("lastOpenedFile");
processCSV(fileContent); // Process the saved file if it exists
// Process CSV data and update the charts
function processCSV(csvData) {
const data = d3.csvParse(csvData, (d) => {
// Parse "time" as a JavaScript Date object
parsed[key] = new Date(d[key]);
// Attempt to parse other columns as numbers
parsed[key] = isNaN(value) ? d[key] : value;
// Extract column names (ignore "time")
const columns = data.columns.filter(
(col) => col !== "time" && col !== "fid" && col !== "x" && col !== "y"
// Update charts using parsed data
updateCharts(data, columns);
// File input event listener
document.getElementById("csvFileInput").addEventListener("change", function (event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = function (e) {
const csvData = e.target.result;
saveFileToLocalStorage(csvData); // Save file content to localStorage
processCSV(csvData); // Process the CSV file
// Load the last opened file on page load
window.addEventListener("load", () => {
loadFileFromLocalStorage();