bổ sung chức năng load 64 tỉnh thành và 32 tỉnh thành/ bổ sung mô hình Swin-Unet
This commit is contained in:
+251
-10
@@ -370,16 +370,45 @@
|
||||
<form id="trainingForm">
|
||||
<h3 style="margin-bottom: 15px; color: #667eea;">📍 Khu Vực Training</h3>
|
||||
|
||||
<!-- Province Selection Section -->
|
||||
<div class="form-group" style="margin-bottom: 20px;">
|
||||
<label>
|
||||
<strong>🗺️ Chọn theo Tỉnh Thành:</strong>
|
||||
<span style="color: #999; font-size: 13px; font-weight: normal;">(Hoặc vẽ bbox thủ công bên dưới)</span>
|
||||
</label>
|
||||
|
||||
<!-- Toggle between 63 and 32 provinces -->
|
||||
<div style="margin-bottom: 10px; display: flex; gap: 10px; align-items: center;">
|
||||
<button type="button" id="btn63Provinces" onclick="switchProvinceList('63')" style="padding: 8px 16px; background: #667eea; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">63 Tỉnh (Cũ)</button>
|
||||
<button type="button" id="btn32Provinces" onclick="switchProvinceList('32')" style="padding: 8px 16px; background: #f0f0f0; color: #333; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">32 Tỉnh (Sau sáp nhập)</button>
|
||||
<span id="provinceListMode" style="color: #667eea; font-weight: bold;">Danh sách: 63 tỉnh</span>
|
||||
</div>
|
||||
|
||||
<select id="provinceSelect" style="padding: 12px; width: 100%; border: 2px solid #ddd; border-radius: 8px; font-size: 14px; cursor: pointer;">
|
||||
<option value="">-- Chọn tỉnh thành để tải bbox tự động --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Region Filter -->
|
||||
<div class="form-group" style="margin-bottom: 20px;">
|
||||
<label><strong>🌍 Lọc theo Vùng:</strong></label>
|
||||
<div id="regionFilterContainer" style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||
<button type="button" class="region-filter-btn" data-region="all" style="padding: 8px 16px; background: #667eea; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">Tất cả</button>
|
||||
<!-- Dynamic region buttons will be added here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="map-container">
|
||||
<div class="map-instructions">
|
||||
<strong>💡 Hướng dẫn:</strong> Sử dụng công cụ vẽ hình chữ nhật
|
||||
<strong>💡 Hướng dẫn:</strong> Chọn tỉnh thành ở trên để tự động điền bbox, hoặc sử dụng công cụ vẽ hình chữ nhật
|
||||
<span style="display: inline-block; width: 24px; height: 24px; background: white; border: 2px solid #333; vertical-align: middle; margin: 0 5px;">□</span>
|
||||
ở góc trên bên trái của bản đồ để chọn khu vực training
|
||||
ở góc trên bên trái của bản đồ để vẽ khu vực tùy chỉnh
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
<div style="margin-top: 10px; font-size: 13px; color: #666;">
|
||||
<strong>Khu vực đã chọn:</strong>
|
||||
<span id="bboxDisplay">Chưa chọn khu vực</span>
|
||||
<span id="provinceDisplay" style="margin-left: 10px; color: #667eea; font-weight: 600;"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -443,6 +472,7 @@
|
||||
<option value="decision_tree">🌳 Decision Tree (Đơn giản, Nhanh nhất)</option>
|
||||
<option value="svm">🎯 SVM (Chính xác, Chậm với dữ liệu lớn)</option>
|
||||
<option value="cnn">🧠 CNN - Deep Learning (PyTorch, Tốt với ảnh vệ tinh, Hỗ trợ GPU)</option>
|
||||
<option value="swin-unet">🌟 Swin-UNet (Transformer + U-Net, Độ chính xác cao, Hỗ trợ GPU)</option>
|
||||
</select>
|
||||
<div style="margin-top: 8px; padding: 10px; background: #e7f3ff; border-radius: 5px; font-size: 12px;">
|
||||
<span id="modelTypeDesc" style="color: #1976d2;">
|
||||
@@ -463,7 +493,7 @@
|
||||
</div>
|
||||
<div class="form-group" id="learningRateGroup">
|
||||
<label>Learning Rate:</label>
|
||||
<input type="number" step="0.01" id="learningRate" value="0.1" min="0.01" max="1" required>
|
||||
<input type="number" step="0.0001" id="learningRate" value="0.1" min="0.0001" max="1" required>
|
||||
</div>
|
||||
<div class="form-group" id="testSizeGroup">
|
||||
<label>Tỷ lệ dữ liệu test (0-1):</label>
|
||||
@@ -764,8 +794,8 @@
|
||||
<h3>📦 ${model.filename}</h3>
|
||||
<p><strong>Tạo lúc:</strong> ${new Date(model.created).toLocaleString('vi-VN')}</p>
|
||||
<p><strong>Kích thước:</strong> ${model.size_mb} MB</p>
|
||||
${model.info.train_accuracy ? `<p><strong>Train Accuracy:</strong> ${(model.info.train_accuracy * 100).toFixed(2)}%</p>` : ''}
|
||||
${model.info.test_accuracy ? `<p><strong>Test Accuracy:</strong> ${(model.info.test_accuracy * 100).toFixed(2)}%</p>` : ''}
|
||||
${model.info && model.info.train_accuracy ? `<p><strong>Train Accuracy:</strong> ${(model.info.train_accuracy * 100).toFixed(2)}%</p>` : ''}
|
||||
${model.info && model.info.test_accuracy ? `<p><strong>Test Accuracy:</strong> ${(model.info.test_accuracy * 100).toFixed(2)}%</p>` : ''}
|
||||
`;
|
||||
container.appendChild(item);
|
||||
|
||||
@@ -1101,7 +1131,8 @@
|
||||
'random_forest': '✓ Random Forest: Ổn định, không overfitting, phù hợp mọi kích thước dữ liệu',
|
||||
'decision_tree': '✓ Decision Tree: Đơn giản nhất, nhanh nhất, dễ hiểu, phù hợp để test nhanh',
|
||||
'svm': '✓ SVM: Chính xác cao với dữ liệu nhỏ, chậm với dữ liệu lớn',
|
||||
'cnn': '✓ CNN PyTorch: Mạnh nhất với ảnh vệ tinh, tự học features, tương thích GPU tốt, cần pip install torch'
|
||||
'cnn': '✓ CNN PyTorch: Mạnh nhất với ảnh vệ tinh, tự học features, tương thích GPU tốt, cần pip install torch',
|
||||
'swin-unet': '✓ Swin-UNet: Kết hợp Transformer + U-Net, độ chính xác cao nhất, phù hợp dataset lớn, tốc độ training trung bình'
|
||||
};
|
||||
|
||||
desc.textContent = descriptions[modelType];
|
||||
@@ -1130,10 +1161,19 @@
|
||||
document.getElementById('nEstimators').value = 50;
|
||||
learningRateGroup.style.display = 'none';
|
||||
useGpuGroup.style.display = ''; // Show GPU option for CNN
|
||||
} else if (modelType === 'swin-unet') {
|
||||
// Swin-UNet uses n_estimators as epochs and supports GPU
|
||||
nEstimatorsGroup.style.display = '';
|
||||
document.querySelector('#nEstimatorsGroup label').textContent = 'Epochs (số lần training):';
|
||||
document.getElementById('nEstimators').value = 100;
|
||||
learningRateGroup.style.display = ''; // Show learning rate for Swin-UNet
|
||||
document.querySelector('#learningRateGroup label').textContent = 'Learning Rate (mặc định: 0.0005):';
|
||||
document.getElementById('learningRate').value = 0.0005;
|
||||
useGpuGroup.style.display = ''; // Show GPU option for Swin-UNet
|
||||
}
|
||||
|
||||
// Reset n_estimators label for non-CNN
|
||||
if (modelType !== 'cnn' && modelType !== 'decision_tree' && modelType !== 'svm') {
|
||||
// Reset n_estimators label for non-CNN/non-Swin-UNet
|
||||
if (modelType !== 'cnn' && modelType !== 'swin-unet' && modelType !== 'decision_tree' && modelType !== 'svm') {
|
||||
document.querySelector('#nEstimatorsGroup label').textContent = 'N Estimators:';
|
||||
}
|
||||
}
|
||||
@@ -1278,12 +1318,213 @@
|
||||
}
|
||||
}
|
||||
|
||||
// === PROVINCE SELECTION FUNCTIONS ===
|
||||
let allProvinces = {};
|
||||
let allProvincesMerged = {};
|
||||
let currentProvinceName = '';
|
||||
let currentProvinceMode = '63'; // '63' or '32'
|
||||
|
||||
// Load provinces list (both 63 and 32)
|
||||
async function loadProvinces() {
|
||||
try {
|
||||
// Load 63 provinces
|
||||
const response63 = await fetch(`${API_BASE}/provinces/by-region`);
|
||||
allProvinces = await response63.json();
|
||||
|
||||
// Load 32 merged provinces
|
||||
const response32 = await fetch(`${API_BASE}/provinces-32/by-region`);
|
||||
allProvincesMerged = await response32.json();
|
||||
|
||||
// Default to 63 provinces
|
||||
populateRegionButtons();
|
||||
populateProvinceSelect();
|
||||
} catch (error) {
|
||||
console.error('Error loading provinces:', error);
|
||||
document.getElementById('provinceSelect').innerHTML = '<option value="">Lỗi tải danh sách tỉnh</option>';
|
||||
}
|
||||
}
|
||||
|
||||
// Switch between 63 and 32 province lists
|
||||
function switchProvinceList(mode) {
|
||||
currentProvinceMode = mode;
|
||||
|
||||
// Update button styles
|
||||
const btn63 = document.getElementById('btn63Provinces');
|
||||
const btn32 = document.getElementById('btn32Provinces');
|
||||
const modeLabel = document.getElementById('provinceListMode');
|
||||
|
||||
if (mode === '63') {
|
||||
btn63.style.background = '#667eea';
|
||||
btn63.style.color = 'white';
|
||||
btn32.style.background = '#f0f0f0';
|
||||
btn32.style.color = '#333';
|
||||
modeLabel.textContent = 'Danh sách: 63 tỉnh';
|
||||
} else {
|
||||
btn63.style.background = '#f0f0f0';
|
||||
btn63.style.color = '#333';
|
||||
btn32.style.background = '#667eea';
|
||||
btn32.style.color = 'white';
|
||||
modeLabel.textContent = 'Danh sách: 32 tỉnh (sau sáp nhập)';
|
||||
}
|
||||
|
||||
// Update region filter buttons
|
||||
populateRegionButtons();
|
||||
|
||||
// Reload province list
|
||||
populateProvinceSelect();
|
||||
}
|
||||
|
||||
// Populate province select dropdown
|
||||
function populateProvinceSelect(filterRegion = 'all') {
|
||||
const select = document.getElementById('provinceSelect');
|
||||
select.innerHTML = '<option value="">-- Chọn tỉnh thành để tải bbox tự động --</option>';
|
||||
|
||||
// Choose which province list to use
|
||||
const provinceData = currentProvinceMode === '63' ? allProvinces : allProvincesMerged;
|
||||
|
||||
// Get all regions dynamically from data
|
||||
const regions = Object.keys(provinceData);
|
||||
|
||||
regions.forEach(region => {
|
||||
if (filterRegion !== 'all' && filterRegion !== region) {
|
||||
return;
|
||||
}
|
||||
|
||||
const provinces = provinceData[region];
|
||||
if (!provinces || provinces.length === 0) return;
|
||||
|
||||
const optgroup = document.createElement('optgroup');
|
||||
optgroup.label = `${region} (${provinces.length} tỉnh)`;
|
||||
|
||||
provinces.forEach(province => {
|
||||
const option = document.createElement('option');
|
||||
option.value = province.name;
|
||||
|
||||
// For merged provinces, show additional info
|
||||
if (currentProvinceMode === '32' && province.merged_from) {
|
||||
option.textContent = `${province.name} (${province.merged_from.join(', ')})`;
|
||||
} else {
|
||||
option.textContent = `${province.name} - ${province.name_en || ''}`;
|
||||
}
|
||||
|
||||
option.dataset.bbox = JSON.stringify(province.bbox);
|
||||
optgroup.appendChild(option);
|
||||
});
|
||||
|
||||
select.appendChild(optgroup);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle province selection
|
||||
function onProvinceSelect(event) {
|
||||
const select = event.target;
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
|
||||
if (!selectedOption.value) {
|
||||
currentProvinceName = '';
|
||||
document.getElementById('provinceDisplay').textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const provinceName = selectedOption.value;
|
||||
const bbox = JSON.parse(selectedOption.dataset.bbox);
|
||||
|
||||
currentProvinceName = provinceName;
|
||||
|
||||
// Update bbox inputs
|
||||
document.getElementById('minLon').value = bbox[0];
|
||||
document.getElementById('minLat').value = bbox[1];
|
||||
document.getElementById('maxLon').value = bbox[2];
|
||||
document.getElementById('maxLat').value = bbox[3];
|
||||
|
||||
// Update bbox display
|
||||
document.getElementById('bboxDisplay').textContent =
|
||||
`Lon: ${bbox[0]} → ${bbox[2]}, Lat: ${bbox[1]} → ${bbox[3]}`;
|
||||
document.getElementById('provinceDisplay').textContent = `📍 ${provinceName}`;
|
||||
|
||||
// Draw rectangle on map
|
||||
const bounds = [[bbox[1], bbox[0]], [bbox[3], bbox[2]]];
|
||||
|
||||
// Remove previous rectangle
|
||||
if (currentRectangle) {
|
||||
drawnItems.removeLayer(currentRectangle);
|
||||
}
|
||||
|
||||
// Add new rectangle
|
||||
currentRectangle = L.rectangle(bounds, {
|
||||
color: '#667eea',
|
||||
weight: 3,
|
||||
fillOpacity: 0.2
|
||||
});
|
||||
drawnItems.addLayer(currentRectangle);
|
||||
|
||||
// Fit map to bounds
|
||||
map.fitBounds(bounds, { padding: [50, 50] });
|
||||
|
||||
// Show success notification
|
||||
showNotification('success', `Đã chọn tỉnh: ${provinceName}`);
|
||||
}
|
||||
|
||||
// Populate region filter buttons
|
||||
function populateRegionButtons() {
|
||||
const container = document.getElementById('regionFilterContainer');
|
||||
|
||||
// Keep the "Tất cả" button
|
||||
const allButton = container.querySelector('[data-region="all"]');
|
||||
container.innerHTML = '';
|
||||
container.appendChild(allButton);
|
||||
|
||||
// Get regions from current data
|
||||
const provinceData = currentProvinceMode === '63' ? allProvinces : allProvincesMerged;
|
||||
const regions = Object.keys(provinceData);
|
||||
|
||||
// Add button for each region
|
||||
regions.forEach(region => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'region-filter-btn';
|
||||
button.dataset.region = region;
|
||||
button.textContent = region;
|
||||
button.style.cssText = 'padding: 8px 16px; background: #f0f0f0; color: #333; border: none; border-radius: 6px; cursor: pointer;';
|
||||
container.appendChild(button);
|
||||
});
|
||||
|
||||
// Re-setup event listeners
|
||||
setupRegionFilters();
|
||||
}
|
||||
|
||||
// Handle region filter
|
||||
function setupRegionFilters() {
|
||||
const filterButtons = document.querySelectorAll('.region-filter-btn');
|
||||
|
||||
filterButtons.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
// Update active button style
|
||||
filterButtons.forEach(b => {
|
||||
b.style.background = '#f0f0f0';
|
||||
b.style.color = '#333';
|
||||
b.style.fontWeight = 'normal';
|
||||
});
|
||||
this.style.background = '#667eea';
|
||||
this.style.color = 'white';
|
||||
this.style.fontWeight = '600';
|
||||
|
||||
// Filter provinces
|
||||
const region = this.dataset.region;
|
||||
populateProvinceSelect(region);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize map when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initMap();
|
||||
initPredictionMap();
|
||||
loadPredictionsList(); // Load predictions list on page load
|
||||
loadCacheInfo(); // Load cache info
|
||||
loadProvinces(); // Load provinces list
|
||||
|
||||
// Add event listeners
|
||||
document.getElementById('provinceSelect').addEventListener('change', onProvinceSelect);
|
||||
setupRegionFilters();
|
||||
|
||||
// Add model type change listener
|
||||
document.getElementById('modelType').addEventListener('change', updateModelTypeUI);
|
||||
|
||||
Reference in New Issue
Block a user