`;
// 2.2 Classification Methodology
html += `
2. Classification Methodology
`;
html += `
The classification was performed using the ${data.classification_method} algorithm. The training and validation process was structured as follows:
`;
html += `
Class Definitions
| Class Name | Definition |
`;
data.class_definitions_parsed.forEach(([name, def]) => {
html += `| ${name} | ${def || 'N/A'} |
`;
});
html += `
`;
html += `
A total of ${data.training_samples} samples were used for model training, and ${data.validation_samples} samples were reserved for validation.
`;
html += `
`;
// 2.3 Results and Accuracy Metrics
html += `
3. Results and Accuracy Metrics
`;
html += `
Overall Accuracy Summary
`;
html += `
| Overall Accuracy | ${data.overall_accuracy}% |
| Kappa Coefficient | ${data.kappa_coefficient} |
`;
html += `
Classified Area Results
`;
html += `
| Class Name | Area (Sq Km) | Percentage (%) |
`;
data.area_results_parsed.forEach(([name, area, percent]) => {
html += `| ${name} | ${area} | ${percent} |
`;
});
html += `
`;
html += `
Confusion Matrix & Notes
`;
html += `
${data.confusion_notes || 'No specific notes on confusion matrix provided.'}
`;
html += `
`;
reviewContent.innerHTML = html;
pdfDownloadBtn.disabled = false;
switchTab(1); // Switch to review tab
}
// --- PDF Generation ---
function downloadPDF() {
const data = getFormData();
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'pt', 'a4');
let currentY = 40;
const margin = 40;
const pageWidth = doc.internal.pageSize.width;
// Helper to add text and increment Y
const addText = (text, size = 11, style = 'normal', indent = 0) => {
const requiredHeight = 15;
if (currentY + requiredHeight > doc.internal.pageSize.height - margin) {
doc.addPage();
currentY = margin;
}
doc.setFontSize(size);
doc.setFont(undefined, style);
const lines = doc.splitTextToSize(text, pageWidth - margin * 2 - indent);
doc.text(lines, margin + indent, currentY);
currentY += (lines.length * (size * 0.5)) + 5;
};
// Helper to add Section Header
const addSectionHeader = (title) => {
currentY += 10;
checkPageBreak(15);
doc.setFontSize(16);
doc.setFont(undefined, 'bold');
doc.text(title, margin, currentY);
currentY += 10;
doc.setLineWidth(0.5);
doc.line(margin, currentY, pageWidth - margin, currentY);
currentY += 10;
};
const checkPageBreak = (spaceNeeded) => {
if (currentY + spaceNeeded > doc.internal.pageSize.height - margin) {
doc.addPage();
currentY = margin;
}
};
// --- PDF Content ---
// 1. Title and Header
doc.setFontSize(24);
doc.setFont(undefined, 'bold');
doc.setTextColor(0, 123, 255); // Primary color
doc.text(data.report_title, pageWidth / 2, currentY, { align: 'center' });
currentY += 15;
doc.setFontSize(11);
doc.setFont(undefined, 'normal');
doc.setTextColor(108, 117, 125); // Secondary color
doc.text(`Prepared by: ${data.analyst_name} | Date: ${data.date_prepared}`, pageWidth / 2, currentY, { align: 'center' });
currentY += 25;
doc.setTextColor(0);
// 2. Project & Data Setup
addSectionHeader("1. Project & Data Setup");
addText(`Data Source: ${data.sensor_name} | Acquired: ${data.data_acquired} | Resolution: ${data.spatial_resolution}m`, 11, 'bold');
addText(`Bands Used: ${data.bands_used}`);
// 3. Classification Methodology
addSectionHeader("2. Classification Methodology");
addText(`Algorithm: ${data.classification_method}`, 11, 'bold');
addText(`Training Samples: ${data.training_samples} | Validation Samples: ${data.validation_samples}`);
// Class Definitions Table
currentY += 10;
addText("Land Cover Class Definitions", 11, 'bold');
const classTableBody = data.class_definitions_parsed.map(([name, def]) => [name, def]);
doc.autoTable({
startY: currentY,
head: [['Class Name', 'Definition']],
body: classTableBody,
theme: 'grid',
headStyles: { fillColor: [230, 230, 230], textColor: [0] },
styles: { fontSize: 9, cellPadding: 4, overflow: 'linebreak' }
});
currentY = doc.autoTable.previous.finalY + 20;
// 4. Results and Accuracy Metrics
addSectionHeader("3. Results and Accuracy Metrics");
// Metrics Table
addText("Overall Accuracy Summary", 11, 'bold');
const metricTableBody = [
['Overall Accuracy', `${data.overall_accuracy}%`],
['Kappa Coefficient', data.kappa_coefficient]
];
doc.autoTable({
startY: currentY,
head: [['Metric', 'Result']],
body: metricTableBody,
theme: 'grid',
headStyles: { fillColor: [0, 123, 255], textColor: [255] },
styles: { fontSize: 10, cellPadding: 4, columnStyles: { 1: { fontStyle: 'bold' } } },
margin: { left: margin, right: pageWidth - (margin + 200) } // Constrain width for metrics
});
currentY = doc.autoTable.previous.finalY + 20;
// Area Results Table
addText("Classified Area Results", 11, 'bold');
const areaTableHead = [['Class Name', 'Area (Sq Km)', 'Percentage (%)']];
const areaTableBody = data.area_results_parsed;
doc.autoTable({
startY: currentY,
head: areaTableHead,
body: areaTableBody,
theme: 'grid',
headStyles: { fillColor: [200, 200, 200], textColor: [0] },
styles: { fontSize: 10, cellPadding: 4 }
});
currentY = doc.autoTable.previous.finalY + 15;
// Confusion Notes
addText("Confusion Matrix & Per-Class Accuracy Notes", 11, 'bold');
addText(data.confusion_notes || 'No specific notes provided.', 11, 'normal', 5);
doc.save(`${data.report_title.replace(/\s/g, '_')}_Report.pdf`);
}
generateBtn.addEventListener('click', generateReport);
pdfDownloadBtn.addEventListener('click', downloadPDF);
// --- Tab Navigation ---
function switchTab(tabIndex) {
tabs.forEach((tab, index) => {
tab.classList.toggle('active', index === tabIndex);
contents[index].classList.toggle('active', index === tabIndex);
});
currentTab = tabIndex;
updateNavButtons();
}
function updateNavButtons() {
prevBtn.disabled = currentTab === 0;
nextBtn.disabled = currentTab === tabs.length - 1;
}
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => switchTab(index));
});
nextBtn.addEventListener('click', () => { if (currentTab < tabs.length - 1) switchTab(currentTab + 1); });
prevBtn.addEventListener('click', () => { if (currentTab > 0) switchTab(currentTab - 1); });
// --- Initial Setup ---
updateNavButtons();
});