document.addEventListener('DOMContentLoaded', () => { updateStatistics(); collectFrequenciesData(); const inputs = document.querySelectorAll('#tables tbody td:nth-child(2) input'); inputs.forEach(input => { input.addEventListener('input', updateStatistics); input.addEventListener('click', () => { currentInput = input; }); }); }); function showTab(tabId) { document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); const activeTab = document.getElementById(tabId); activeTab.classList.add('active'); document.querySelectorAll('.tab').forEach(tab => { tab.classList.remove('active'); }); const activeTabElement = document.querySelector(`.tab[onclick*="${tabId}"]`); if (activeTabElement) { activeTabElement.classList.add('active'); } } let currentInput = null; document.addEventListener('DOMContentLoaded', () => { const inputs = document.querySelectorAll('#tables tbody input[type="text"]'); inputs.forEach(input => { input.addEventListener('input', updateStatistics); }); }); function updateStatistics() { const inputs = document.querySelectorAll('#tables tbody input[type="text"]'); let sum = 0, count = 0, min = Infinity, max = -Infinity; let varianceSum = 0; let validCount = 0; inputs.forEach(input => { const value = parseFloat(input.value); if (!isNaN(value) && value >= 1 && value <= 200) { sum += value; count++; validCount++; min = Math.min(min, value); max = Math.max(max, value); } }); const average = count > 0 ? sum / count : 0; inputs.forEach(input => { const value = parseFloat(input.value); if (!isNaN(value) && value >= 1 && value <= 200) { varianceSum += Math.pow(value - average, 2); } }); const stdDeviation = count > 1 ? Math.sqrt(varianceSum / (count - 1)).toFixed(1) : 0; document.getElementById('stdDeviation').textContent = stdDeviation; document.getElementById('avgSpeed').textContent = average.toFixed(1); document.getElementById('minSpeed').textContent = min === Infinity ? 0 : min; document.getElementById('maxSpeed').textContent = max === -Infinity ? 0 : max; document.getElementById('freqTotal').textContent = validCount; document.getElementById('stdDeviation1').textContent = stdDeviation; document.getElementById('avgSpeed1').textContent = average.toFixed(1); document.getElementById('minSpeed1').textContent = min === Infinity ? 0 : min; document.getElementById('maxSpeed1').textContent = max === -Infinity ? 0 : max; document.getElementById('freqTotal1').textContent = validCount; generateFreqDistTable(); } function importSpeeds() { const confirmed = confirm("Do you want to delete current values and import new ones?"); if (!confirmed) { return; } const inputs = document.querySelectorAll('#tables tbody td:nth-child(2) input'); inputs.forEach(input => { input.value = ''; }); const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; if (!file) { alert("Please, select a file."); return; } const reader = new FileReader(); reader.onload = function (event) { const lines = event.target.result.split('\n') .map(line => line.trim()) .filter(line => line !== ''); let insertedCount = 0; let currentRow = 0; const inputs = document.querySelectorAll('#tables tbody input[type="text"]'); lines.forEach(line => { const value = parseFloat(line); if (!isNaN(value) && value >= 30 && value <= 70) { while (currentRow < inputs.length && inputs[currentRow].value !== '') { currentRow++; } if (currentRow < inputs.length) { inputs[currentRow].value = value; insertedCount++; } } }); if (insertedCount === 0) { alert("Valid values not found."); } updateStatistics(); }; reader.readAsText(file); } function insertSpeed(value = null) { const pSpeedInput = document.getElementById('pSpeed'); const speedValue = value !== null ? value : (pSpeedInput.value ? parseInt(pSpeedInput.value) : 50); if (currentInput) { const currentRow = parseInt(currentInput.id.split('-')[1]); const inputs = document.querySelectorAll('#tables tbody input[type="text"]'); for (let i = inputs.length - 1; i > currentRow - 1; i--) { inputs[i].value = inputs[i - 1].value; } inputs[currentRow - 1].value = speedValue; inputs[currentRow - 1].focus(); currentInput = inputs[currentRow - 1]; } else { alert("Please, select a cell to insert the value."); } updateStatistics(); } function deleteSpeed() { if (currentInput) { const currentRow = parseInt(currentInput.id.split('-')[1]); const inputs = document.querySelectorAll('#tables tbody input[type="text"]'); inputs[currentRow - 1].value = ''; for (let i = currentRow; i < inputs.length; i++) { inputs[i - 1].value = inputs[i].value; } inputs[inputs.length - 1].value = ''; const nextFocus = document.querySelector(`#speed-${currentRow - 1}`); if (nextFocus) { nextFocus.focus(); currentInput = nextFocus; } else { const firstInput = document.querySelector('input[type="text"]'); if (firstInput) { firstInput.focus(); currentInput = firstInput; } } updateStatistics(); } else { alert("Please, select a cell to delete."); } } function deleteAll() { const confirmed = confirm("Do you confirm to clear the table?"); if (confirmed) { const inputs = document.querySelectorAll('#tables tbody td:nth-child(2) input'); inputs.forEach(input => { if (input.value !== '') { input.value = ''; } }); updateStatistics(); } } function generateFreqDistTable() { const minSpeed = Math.floor(parseFloat(document.getElementById("minSpeed").innerText)); const classInterval = parseFloat(document.getElementById("classInterval").value); const tablesValues = Array.from(document.querySelectorAll("#tables input")) .map(input => parseFloat(input.value)) .filter(value => !isNaN(value)); const totalValues = tablesValues.length; let cumulativePercentage = 0; const tableBody = document.getElementById("freqDistTable").querySelector("tbody"); tableBody.innerHTML = ""; const maxSpeed = Math.max(...tablesValues); const numClasses = Math.ceil((maxSpeed - minSpeed) / classInterval); document.getElementById("numClasses").textContent = numClasses; for (let i = 0; i < numClasses; i++) { const lowerBound = minSpeed + i * classInterval; const upperBound = lowerBound + classInterval; const classMidValue = (lowerBound + upperBound) / 2; const frequency = tablesValues.filter(value => value > lowerBound && value <= upperBound).length; const percentage = (frequency / totalValues) * 100; cumulativePercentage += percentage; const row = document.createElement("tr"); const classCell = document.createElement("td"); classCell.textContent = `${lowerBound} - ${upperBound}`; const midValueCell = document.createElement("td"); midValueCell.textContent = classMidValue.toFixed(1); const frequencyCell = document.createElement("td"); frequencyCell.textContent = frequency; const percentageCell = document.createElement("td"); percentageCell.textContent = percentage.toFixed(1); const cumulativeCell = document.createElement("td"); cumulativeCell.textContent = cumulativePercentage.toFixed(1); row.appendChild(classCell); row.appendChild(midValueCell); row.appendChild(frequencyCell); row.appendChild(percentageCell); row.appendChild(cumulativeCell); tableBody.appendChild(row); } generateFreqDistChart(); generateChiSquareTable(); collectFrequenciesData(); } function collectFrequenciesData() { const frequenciesData = Array.from(document.querySelectorAll("#freqDistTable tbody tr")).map(row => { return { classRange: row.children[0].textContent, observed: parseInt(row.children[2].textContent) }; }); return frequenciesData; } let frequencyChart; function generateFreqDistChart() { const tableRows = document.querySelectorAll("#freqDistTable tbody tr"); const labels = []; const frequencies = []; tableRows.forEach(row => { const midValue = row.children[1].textContent; const frequency = row.children[2].textContent; labels.push(midValue); frequencies.push(parseInt(frequency) || 0); }); const ctx = document.getElementById('frequencyHistogram').getContext('2d'); if (frequencyChart) { frequencyChart.destroy(); } frequencyChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Frequency histogram', data: frequencies, backgroundColor: '#4CAF50', borderColor: '#175500', borderWidth: 2 }] }, options: { scales: { y: { beginAtZero: true, title: { display: true, text: 'Frequency' } }, x: { title: { display: true, text: 'Speed (km/h)' } } } } }); } function generateChiSquareTable() { const tableBody = document.getElementById("chiSquareTable").querySelector("tbody"); tableBody.innerHTML = ""; const totalValues = parseInt(document.getElementById("freqTotal").textContent); const mean = parseFloat(document.getElementById("avgSpeed").textContent); const stdDev = parseFloat(document.getElementById("stdDeviation").textContent); const minSpeed = Math.floor(parseFloat(document.getElementById("minSpeed").innerText)); const classInterval = parseFloat(document.getElementById("classInterval").value); const numClasses = parseInt(document.getElementById("numClasses").value); const frequenciesData = Array.from(document.querySelectorAll("#freqDistTable tbody tr")).map(row => { return { classRange: row.children[0].textContent, observed: parseInt(row.children[2].textContent) }; }); const probabilities = []; const expectedFrequencies = []; const chiSquareValues = []; frequenciesData.forEach((data, index) => { const lowerBound = minSpeed + index * classInterval; const upperBound = lowerBound + classInterval; const probLower = jStat.normal.cdf(lowerBound, mean, stdDev); const probUpper = jStat.normal.cdf(upperBound, mean, stdDev); const classProbability = probUpper - probLower; probabilities.push(classProbability); expectedFrequencies.push(classProbability * totalValues); }); for (let i = 0; i < frequenciesData.length; i++) { const row = document.createElement("tr"); const classCell = document.createElement("td"); classCell.textContent = frequenciesData[i].classRange; const observedCell = document.createElement("td"); observedCell.textContent = frequenciesData[i].observed; const probCell = document.createElement("td"); probCell.textContent = probabilities[i].toFixed(4); const expectedCell = document.createElement("td"); expectedCell.textContent = expectedFrequencies[i].toFixed(2); const chiSquareCell = document.createElement("td"); const chiSquareValue = (Math.pow(frequenciesData[i].observed - expectedFrequencies[i], 2) / expectedFrequencies[i]).toFixed(2); chiSquareCell.textContent = chiSquareValue; chiSquareValues.push(parseFloat(chiSquareValue)); row.appendChild(classCell); row.appendChild(observedCell); row.appendChild(probCell); row.appendChild(expectedCell); row.appendChild(chiSquareCell); tableBody.appendChild(row); } const totalChiSquare = chiSquareValues.reduce((sum, value) => sum + value, 0).toFixed(2); document.getElementById("chiSquareCalc").textContent = totalChiSquare; const degreesOfFreedom = frequenciesData.length - 3; const criticalValue = jStat.chisquare.inv(0.95, degreesOfFreedom).toFixed(2); document.getElementById("chiSquareCrit").textContent = criticalValue; document.getElementById("chiSquareCalc1").textContent = totalChiSquare; document.getElementById("chiSquareCrit1").textContent = criticalValue; compareChiSquare(); } function compareChiSquare() { const frequenciesData = collectFrequenciesData(); const chiSquareCalc = parseFloat(document.getElementById("chiSquareCalc").textContent); const chiSquareCrit = parseFloat(document.getElementById("chiSquareCrit").textContent); const resultContainer = document.getElementById("chiSquareResult"); resultContainer.innerHTML = ""; if (chiSquareCalc < chiSquareCrit) { resultContainer.innerHTML = `

${chiSquareCalc} <= ${chiSquareCrit}

The normal assumption cannot be rejected.

`; } else { resultContainer.innerHTML = `

${chiSquareCalc} >= ${chiSquareCrit}

The normal assumption is rejected.

`; } generateFrequencyDistributionChart(frequenciesData); generateCumulativeFrequencyChart(frequenciesData); } let speedFrequencyChart; function generateFrequencyDistributionChart(frequenciesData) { const totalValues = frequenciesData.reduce((sum, data) => sum + data.observed, 0); const mean = parseFloat(document.getElementById("avgSpeed").textContent); const stdDev = parseFloat(document.getElementById("stdDeviation").textContent); const minSpeed = Math.floor(parseFloat(document.getElementById("minSpeed").innerText)); const classInterval = parseFloat(document.getElementById("classInterval").value); const numClasses = frequenciesData.length; const percentages = frequenciesData.map(data => (data.observed / totalValues * 100).toFixed(2)); const normalCurve = []; const classBoundaries = []; for (let i = 0; i < numClasses; i++) { const lowerBound = minSpeed + i * classInterval; const upperBound = lowerBound + classInterval; const midPoint = (lowerBound + upperBound) / 2; const normalProbability = jStat.normal.pdf(midPoint, mean, stdDev) * classInterval * 100; normalCurve.push(normalProbability); classBoundaries.push(midPoint.toFixed(2)); } const ctx = document.getElementById('frequencyChart').getContext('2d'); if (speedFrequencyChart) { speedFrequencyChart.destroy(); } speedFrequencyChart = new Chart(ctx, { type: 'scatter', data: { datasets: [ { label: 'Frequency', data: classBoundaries.map((label, index) => ({ x: parseFloat(label), y: parseFloat(percentages[index]) })), backgroundColor: '#FFFF00', borderColor: '#000000', pointRadius: 3, showLine: false }, { label: 'Normal approximation', data: classBoundaries.map((label, index) => ({ x: parseFloat(label), y: normalCurve[index] })), type: 'line', borderColor: '#175500', backgroundColor: '#2c8c0a', fill: false, tension: 0.1, pointRadius: 1 } ] }, options: { scales: { y: { beginAtZero: true, title: { display: true, text: 'Frequency (%)' } }, x: { title: { display: true, text: 'Speed(km/h)' } } }, plugins: { legend: { display: true } } } }); } let cumulativeFrequencyChart; function generateCumulativeFrequencyChart(frequenciesData) { const totalValues = frequenciesData.reduce((sum, data) => sum + data.observed, 0); const mean = parseFloat(document.getElementById("avgSpeed").textContent); const stdDev = parseFloat(document.getElementById("stdDeviation").textContent); const minSpeed = Math.floor(parseFloat(document.getElementById("minSpeed").innerText)); const classInterval = parseFloat(document.getElementById("classInterval").value); const numClasses = frequenciesData.length; const cumulativeFrequencies = []; let cumulativeTotal = 0; frequenciesData.forEach(data => { cumulativeTotal += data.observed; cumulativeFrequencies.push((cumulativeTotal / totalValues * 100).toFixed(2)); }); const normalCurve = []; const classBoundaries = []; for (let i = 0; i < numClasses; i++) { const lowerBound = minSpeed + i * classInterval; const upperBound = lowerBound + classInterval; const midPoint = (lowerBound + upperBound) / 2; const normalProbability = jStat.normal.cdf(midPoint, mean, stdDev) * 100; normalCurve.push(normalProbability); classBoundaries.push(midPoint.toFixed(2)); } const ctx = document.getElementById('cumulativeFrequencyChart').getContext('2d'); if (cumulativeFrequencyChart) { cumulativeFrequencyChart.destroy(); } cumulativeFrequencyChart = new Chart(ctx, { type: 'line', data: { labels: classBoundaries, datasets: [ { label: 'Cumulative frequency', data: cumulativeFrequencies, borderColor: '#000000', backgroundColor: '#FFFF00', fill: false, tension: 0.1, pointRadius: 3, showLine: false }, { label: 'Normal approximation', data: normalCurve, borderColor: '#175500', backgroundColor: '#2c8c0a', fill: false, tension: 0.1, pointRadius: 1 } ] }, options: { scales: { y: { beginAtZero: true, title: { display: true, text: 'Cumulative Percentage (%)' } }, x: { title: { display: true, text: 'Speed Class (km/h)' } } }, plugins: { legend: { display: true } }, } }); calculateAndDisplayPercentiles(mean, stdDev); } function calculateAndDisplayPercentiles(mean, stdDev) { const percentiles = [15, 50, 85]; const percentileValues = percentiles.map(p => jStat.normal.inv(p / 100, mean, stdDev)); document.getElementById('percentile15').textContent = percentileValues[0].toFixed(0); document.getElementById('percentile50').textContent = percentileValues[1].toFixed(0); document.getElementById('percentile85').textContent = percentileValues[2].toFixed(0); } function printConstants() { const { jsPDF } = window.jspdf; const pdf = new jsPDF(); const accentColor = '#2c8c0a'; pdf.setFontSize(18); pdf.setTextColor(accentColor); pdf.setFont('helvetica', 'bold'); pdf.text('SPOT SPEED STUDY', 10, 10); pdf.setDrawColor(accentColor); pdf.setLineWidth(0.5); pdf.line(10, 15, 200, 15); pdf.setFontSize(14); pdf.setTextColor(accentColor); pdf.setFont('helvetica', 'bold'); pdf.text('DATA', 10, 25); const location = document.getElementById('location').value || 'N/A'; const direction = document.getElementById('direction').value || 'N/A'; const date = document.getElementById('date').value || 'N/A'; const weather = document.getElementById('weather').value || 'N/A'; const postedSpeed = document.getElementById('pSpeed').value || 'N/A'; const startHour = document.getElementById('sHour').value || 'N/A'; const endHour = document.getElementById('eHour').value || 'N/A'; const analyst = document.getElementById('analyst').value || 'N/A'; const minSpeed = document.getElementById('minSpeed1').innerText.trim() || 'N/A'; const maxSpeed = document.getElementById('maxSpeed1').innerText.trim() || 'N/A'; const avgSpeed = document.getElementById('avgSpeed1').innerText.trim() || 'N/A'; const stdDeviation = document.getElementById('stdDeviation1').innerText.trim() || 'N/A'; const ttlObs = document.getElementById('freqTotal1').innerText.trim() || 'N/A'; const chiSquareCalc = document.getElementById('chiSquareCalc1').innerText.trim() || 'N/A'; const chiSquareCrit = document.getElementById('chiSquareCrit1').innerText.trim() || 'N/A'; pdf.setFontSize(10); pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); const dataFields = [ `Location: ${location}`, `Traffic direction: ${direction}`, `Date: ${date}`, `Weather: ${weather}`, `Posted speed: ${postedSpeed}`, `Start hour: ${startHour}`, `End hour: ${endHour}`, `Analyst: ${analyst}` ]; let yPosition = 35; dataFields.forEach(field => { pdf.text(field, 10, yPosition); yPosition += 5; }); pdf.setDrawColor(accentColor); pdf.line(10, yPosition, 200, yPosition); yPosition += 10; pdf.setFontSize(14); pdf.setTextColor(accentColor); pdf.setFont('helvetica', 'bold'); pdf.text('RESULTS', 10, yPosition); yPosition += 10; const resultFields = [ `Minimum speed: ${minSpeed}`, `Maximum speed: ${maxSpeed}`, `Average speed: ${avgSpeed}`, `Standard deviation: ${stdDeviation}`, `Total observations: ${ttlObs}`, `Chi-square calculated: ${chiSquareCalc}`, `Chi-square critical: ${chiSquareCrit}` ]; pdf.setFontSize(10); pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); resultFields.forEach(field => { pdf.text(field, 10, yPosition); yPosition += 5; }); pdf.save('Spot_Speed_Study_Constants.pdf'); } function printTableData() { const { jsPDF } = window.jspdf; const pdf = new jsPDF(); const accentColor = '#2c8c0a'; pdf.setFontSize(18); pdf.setTextColor(accentColor); pdf.setFont('helvetica', 'bold'); pdf.text('SPEED DATA TABLE STUDY', 10, 10); pdf.setDrawColor(accentColor); pdf.setLineWidth(0.5); pdf.line(10, 15, 200, 15); pdf.setFontSize(14); pdf.setTextColor(accentColor); pdf.text('TABLE DATA', 10, 25); const minSpeed = document.getElementById('minSpeed1').innerText.trim() || 'N/A'; const maxSpeed = document.getElementById('maxSpeed1').innerText.trim() || 'N/A'; const avgSpeed = document.getElementById('avgSpeed1').innerText.trim() || 'N/A'; const stdDeviation = document.getElementById('stdDeviation1').innerText.trim() || 'N/A'; const freqTotal = document.getElementById('freqTotal1').innerText.trim() || 'N/A'; const additionalFields = [ `Minimum speed: ${minSpeed} km/h`, `Maximum speed: ${maxSpeed} km/h`, `Average speed: ${avgSpeed} km/h`, `Standard deviation: ${stdDeviation} km/h`, `Total observations: ${freqTotal}` ]; pdf.setFontSize(10); pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); let yPosition = 35; additionalFields.forEach(field => { pdf.text(field, 10, yPosition); yPosition += 5; }); pdf.setDrawColor(accentColor); pdf.line(10, yPosition, 200, yPosition); yPosition += 10; pdf.setFontSize(14); pdf.setTextColor(accentColor); pdf.setFont('helvetica', 'bold'); pdf.text('SPEED DATA TABLE', 10, yPosition); yPosition += 10; const colWidth = 22; const startX = 10; let currentX = startX; yPosition += 5; pdf.setFont('helvetica', 'normal'); pdf.setFontSize(10); let columnCount = 0; currentX = startX; for (let i = 1; i <= 249; i++) { const speedValue = document.getElementById(`speed-${i}`).value.trim(); if (speedValue) { pdf.text(speedValue, currentX, yPosition); currentX += colWidth; columnCount++; if (columnCount === 8) { columnCount = 0; currentX = startX; yPosition += 5; if (yPosition > 280) { pdf.addPage(); yPosition = 20; } } } } pdf.save('Spot_Speed_Study_Table.pdf'); } function printFreqDistTable() { const { jsPDF } = window.jspdf; const pdf = new jsPDF(); const tableBody = document.getElementById("freqDistTable").querySelector("tbody"); const rows = Array.from(tableBody.querySelectorAll("tr")); const accentColor = '#2c8c0a'; const minSpeed = document.getElementById("minSpeed").innerText || 'N/A'; const maxSpeed = document.getElementById("maxSpeed").innerText || 'N/A'; const avgSpeed = document.getElementById("avgSpeed").innerText || 'N/A'; const stdDeviation = document.getElementById("stdDeviation").innerText || 'N/A'; const freqTotal = document.getElementById("freqTotal").innerText || 'N/A'; const numClasses = document.getElementById("numClasses").innerText || 'N/A'; const classInterval = document.getElementById("classInterval").value || 'N/A'; pdf.setFontSize(18); pdf.setFont('helvetica', 'bold'); pdf.setTextColor('#2c8c0a'); pdf.text('FREQUENCY DISTRIBUTION STUDY', 10, 10); pdf.setDrawColor(accentColor); pdf.setLineWidth(0.5); pdf.line(10, 15, 200, 15); pdf.setFontSize(12); pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); pdf.text(`Min Speed: ${minSpeed}`, 10, 25); pdf.text(`Max Speed: ${maxSpeed}`, 10, 30); pdf.text(`Avg Speed: ${avgSpeed}`, 10, 35); pdf.text(`Std Deviation: ${stdDeviation}`, 10, 40); pdf.text(`Total Observations: ${freqTotal}`, 10, 45); pdf.text(`Number of Classes: ${numClasses}`, 10, 50); pdf.text(`Class Interval: ${classInterval}`, 10, 55); pdf.setDrawColor(accentColor); pdf.setLineWidth(0.5); pdf.line(10, 60, 200, 60); const headers = ["Class Range", "Class Mid-Value", "Frequency", "Percentage (%)", "Cumulative Percentage (%)"]; const lineHeight = 8; const cellWidth = 35; const startX = 10; let y = 70; pdf.setFillColor(44, 140, 10); pdf.setTextColor(0, 0, 0); pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); headers.forEach((header, index) => { const xPos = startX + index * cellWidth; pdf.rect(xPos, y, cellWidth, lineHeight * 2); const splitText = pdf.splitTextToSize(header, cellWidth - 5); pdf.text(splitText, xPos + cellWidth / 2, y + 5, { align: "center" }); }); y += lineHeight * 2; pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); rows.forEach((row) => { if (y > 280) { pdf.addPage(); y = 20; pdf.setFillColor(44, 140, 10); pdf.setTextColor(255, 255, 255); pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); headers.forEach((header, index) => { const xPos = startX + index * cellWidth; pdf.rect(xPos, y, cellWidth, lineHeight * 2); const splitText = pdf.splitTextToSize(header, cellWidth - 5); pdf.text(splitText, xPos + 2, y + 5); }); y += lineHeight * 2; } const cells = row.querySelectorAll("td"); cells.forEach((cell, i) => { const xPosition = startX + i * cellWidth; pdf.rect(xPosition, y, cellWidth, lineHeight); pdf.text(cell.textContent, xPosition + cellWidth / 2, y + 5, { align: "center" }); }); y += lineHeight; }); pdf.save('Frequency_Distribution_Table.pdf'); } function printChiSquareTable() { const { jsPDF } = window.jspdf; const pdf = new jsPDF(); const tableBody = document.getElementById("chiSquareTable").querySelector("tbody"); const rows = Array.from(tableBody.querySelectorAll("tr")); const accentColor = '#2c8c0a'; const chiCalc = document.getElementById("chiSquareCalc").innerText || 'N/A'; const chiCrit = document.getElementById("chiSquareCrit").innerText || 'N/A'; const chiRes = document.getElementById("chiSquareResult").innerText || 'N/A'; pdf.setFontSize(18); pdf.setFont('helvetica', 'bold'); pdf.setTextColor('#2c8c0a'); pdf.text('CHI-SQUARE DISTRIBUTION STUDY', 10, 10); pdf.setDrawColor(accentColor); pdf.setLineWidth(0.5); pdf.line(10, 15, 200, 15); pdf.setFontSize(12); pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); pdf.text(`Chi-square calculated: ${chiCalc}`, 10, 25); pdf.text(`Chi-square critical: ${chiCrit}`, 10, 30); pdf.text(`${chiRes}`, 10, 40); pdf.setDrawColor(accentColor); pdf.setLineWidth(0.5); pdf.line(10, 55, 200, 55); const headers = ["Speed class (km/h)", "Observed frequency (ni)", "Class probability (normal)", "Expected frequency (ei)", "Chi-square calculated"]; const lineHeight = 8; const cellWidth = 35; const startX = 10; let y = 65; pdf.setFillColor(44, 140, 10); pdf.setTextColor(0, 0, 0); pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); headers.forEach((header, index) => { const xPos = startX + index * cellWidth; pdf.rect(xPos, y, cellWidth, lineHeight * 2); const splitText = pdf.splitTextToSize(header, cellWidth - 5); pdf.text(splitText, xPos + cellWidth / 2, y + 5, { align: "center" }); }); y += lineHeight * 2; pdf.setTextColor(0, 0, 0); pdf.setFont('helvetica', 'normal'); rows.forEach((row) => { if (y > 280) { pdf.addPage(); y = 20; pdf.setFillColor(44, 140, 10); pdf.setTextColor(255, 255, 255); pdf.setFontSize(10); pdf.setFont('helvetica', 'bold'); headers.forEach((header, index) => { const xPos = startX + index * cellWidth; pdf.rect(xPos, y, cellWidth, lineHeight * 2); const splitText = pdf.splitTextToSize(header, cellWidth - 5); pdf.text(splitText, xPos + 2, y + 5); }); y += lineHeight * 2; } const cells = row.querySelectorAll("td"); cells.forEach((cell, i) => { const xPosition = startX + i * cellWidth; pdf.rect(xPosition, y, cellWidth, lineHeight); pdf.text(cell.textContent, xPosition + cellWidth / 2, y + 5, { align: "center" }); }); y += lineHeight; }); pdf.save('ChiSquare_Table.pdf'); }