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:
+266
-15
@@ -313,12 +313,43 @@
|
||||
|
||||
<!-- Prediction Content -->
|
||||
<div id="predictionContent" class="content">
|
||||
<!-- Province Selection Section -->
|
||||
<div class="section" style="grid-column: 1 / -1;">
|
||||
<h2>🗺️ Chọn Khu Vực Prediction</h2>
|
||||
|
||||
<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="btnPred63Provinces" onclick="switchPredProvinceList('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="btnPred32Provinces" onclick="switchPredProvinceList('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="predProvinceListMode" style="color: #667eea; font-weight: bold;">Danh sách: 63 tỉnh</span>
|
||||
</div>
|
||||
|
||||
<select id="predProvinceSelect" 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="predRegionFilterContainer" style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||
<button type="button" class="pred-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>
|
||||
|
||||
<!-- Map Section -->
|
||||
<div class="map-container">
|
||||
<div class="map-instructions">
|
||||
<h3>📍 Chọn khu vực để predict</h3>
|
||||
<p>✏️ Click vào nút hình vuông bên phải để vẽ bbox</p>
|
||||
<p>🖱️ Kéo và thả để tạo vùng muốn phân loại</p>
|
||||
<h3>💡 Hướng dẫn:</h3>
|
||||
<p>📍 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 trên bản đồ</p>
|
||||
<p>🔄 Có thể chỉnh sửa sau khi vẽ</p>
|
||||
</div>
|
||||
<div id="predictMap"></div>
|
||||
@@ -382,6 +413,16 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top: 15px; padding: 15px; background: #fff3e0; border-radius: 8px; border-left: 4px solid #ff9800;">
|
||||
<label style="display: flex; align-items: center; cursor: pointer; margin: 0;">
|
||||
<input type="checkbox" id="useGpuPred" checked style="width: 18px; height: 18px; margin-right: 10px;">
|
||||
<span style="font-weight: 600; color: #e65100;">🚀 Sử dụng GPU (Deep Learning Models)</span>
|
||||
</label>
|
||||
<div style="font-size: 12px; color: #e65100; margin-top: 8px; margin-left: 28px;">
|
||||
⚡ Tăng tốc prediction cho CNN/Swin-UNet models (yêu cầu GPU khả dụng)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top: 20px; padding: 15px; background: #e7f3ff; border-radius: 8px; border-left: 4px solid #2196F3;">
|
||||
<label style="display: flex; align-items: center; cursor: pointer; margin: 0;">
|
||||
<input type="checkbox" id="exportNDVI" checked style="width: 18px; height: 18px; margin-right: 10px;">
|
||||
@@ -453,7 +494,7 @@
|
||||
<!-- NDVI Configuration -->
|
||||
<div class="section">
|
||||
|
||||
<h2>⚙️ Cấu hình NDVI</h2>
|
||||
<h2>⚙️ Cấu hình NDVI để Dự đoán</h2>
|
||||
<!-- NDVI Map for selecting bbox -->
|
||||
<div class="form-group">
|
||||
<label>Chọn bbox trên bản đồ hoặc nhập tọa độ:</label>
|
||||
@@ -559,6 +600,12 @@
|
||||
let ndviData = null;
|
||||
let ndviChart = null;
|
||||
|
||||
// Province data
|
||||
let allPredProvinces = {};
|
||||
let allPredProvincesMerged = {};
|
||||
let currentPredProvinceMode = '63'; // '63' or '32'
|
||||
let currentPredProvinceName = '';
|
||||
|
||||
// Initialize map
|
||||
function initMap() {
|
||||
map = L.map('predictMap').setView([9.5, 105.9], 9);
|
||||
@@ -724,17 +771,22 @@
|
||||
const select = document.getElementById('modelSelect');
|
||||
const option = select.options[select.selectedIndex];
|
||||
|
||||
if (option.dataset.info) {
|
||||
const info = JSON.parse(option.dataset.info);
|
||||
const infoDiv = document.getElementById('modelInfo');
|
||||
|
||||
document.getElementById('modelType').textContent = info.model_type || 'N/A';
|
||||
document.getElementById('modelAccuracy').textContent = info.metrics?.accuracy
|
||||
? (info.metrics.accuracy * 100).toFixed(2) + '%'
|
||||
: 'N/A';
|
||||
document.getElementById('modelDate').textContent = info.training_date || 'N/A';
|
||||
|
||||
infoDiv.style.display = 'block';
|
||||
if (option && option.dataset && option.dataset.info) {
|
||||
try {
|
||||
const info = JSON.parse(option.dataset.info);
|
||||
const infoDiv = document.getElementById('modelInfo');
|
||||
|
||||
document.getElementById('modelType').textContent = info.model_type || 'N/A';
|
||||
document.getElementById('modelAccuracy').textContent = info.metrics?.accuracy
|
||||
? (info.metrics.accuracy * 100).toFixed(2) + '%'
|
||||
: 'N/A';
|
||||
document.getElementById('modelDate').textContent = info.training_date || 'N/A';
|
||||
|
||||
infoDiv.style.display = 'block';
|
||||
} catch (e) {
|
||||
console.warn('Error parsing model info:', e);
|
||||
document.getElementById('modelInfo').style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
document.getElementById('modelInfo').style.display = 'none';
|
||||
}
|
||||
@@ -765,6 +817,7 @@
|
||||
|
||||
// Check if export NDVI is enabled
|
||||
const exportNDVI = document.getElementById('exportNDVI').checked;
|
||||
const useGpu = document.getElementById('useGpuPred').checked;
|
||||
|
||||
const config = {
|
||||
model_filename: modelFilename,
|
||||
@@ -777,6 +830,7 @@
|
||||
max_scenes: parseInt(document.getElementById('predMaxScenes').value),
|
||||
cloud_cover: parseInt(document.getElementById('predCloudCover').value),
|
||||
resolution: parseInt(document.getElementById('predResolution').value),
|
||||
use_gpu: useGpu,
|
||||
export_ndvi: exportNDVI,
|
||||
export_classification: true
|
||||
};
|
||||
@@ -1100,16 +1154,213 @@
|
||||
alert(`✅ Đã áp dụng cache preset!\n\nBBox: [${meta.bbox?.join(', ') || 'N/A'}]\nTime: ${meta.start_date || '?'} → ${meta.end_date || '?'}\n\nBạn có thể predict ngay mà không cần vẽ bbox!`);
|
||||
}
|
||||
|
||||
// === PROVINCE SELECTION FUNCTIONS FOR PREDICTION ===
|
||||
|
||||
// Load provinces list (both 63 and 32)
|
||||
async function loadPredProvinces() {
|
||||
try {
|
||||
// Load 63 provinces
|
||||
const response63 = await fetch('/api/provinces/by-region');
|
||||
allPredProvinces = await response63.json();
|
||||
|
||||
// Load 32 merged provinces
|
||||
const response32 = await fetch('/api/provinces-32/by-region');
|
||||
allPredProvincesMerged = await response32.json();
|
||||
|
||||
// Default to 63 provinces
|
||||
populatePredRegionButtons();
|
||||
populatePredProvinceSelect();
|
||||
} catch (error) {
|
||||
console.error('Error loading provinces:', error);
|
||||
document.getElementById('predProvinceSelect').innerHTML = '<option value="">Lỗi tải danh sách tỉnh</option>';
|
||||
}
|
||||
}
|
||||
|
||||
// Switch between 63 and 32 province lists
|
||||
function switchPredProvinceList(mode) {
|
||||
currentPredProvinceMode = mode;
|
||||
|
||||
// Update button styles
|
||||
const btn63 = document.getElementById('btnPred63Provinces');
|
||||
const btn32 = document.getElementById('btnPred32Provinces');
|
||||
const modeLabel = document.getElementById('predProvinceListMode');
|
||||
|
||||
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
|
||||
populatePredRegionButtons();
|
||||
|
||||
// Reload province list
|
||||
populatePredProvinceSelect();
|
||||
}
|
||||
|
||||
// Populate region filter buttons
|
||||
function populatePredRegionButtons() {
|
||||
const container = document.getElementById('predRegionFilterContainer');
|
||||
|
||||
// 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 = currentPredProvinceMode === '63' ? allPredProvinces : allPredProvincesMerged;
|
||||
const regions = Object.keys(provinceData);
|
||||
|
||||
// Add button for each region
|
||||
regions.forEach(region => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'pred-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
|
||||
setupPredRegionFilters();
|
||||
}
|
||||
|
||||
// Populate province select dropdown
|
||||
function populatePredProvinceSelect(filterRegion = 'all') {
|
||||
const select = document.getElementById('predProvinceSelect');
|
||||
select.innerHTML = '<option value="">-- Chọn tỉnh thành để tải bbox tự động --</option>';
|
||||
|
||||
// Choose which province list to use
|
||||
const provinceData = currentPredProvinceMode === '63' ? allPredProvinces : allPredProvincesMerged;
|
||||
|
||||
// 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 (currentPredProvinceMode === '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 for prediction
|
||||
function onPredProvinceSelect(event) {
|
||||
const select = event.target;
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
|
||||
if (!selectedOption.value) {
|
||||
currentPredProvinceName = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const provinceName = selectedOption.value;
|
||||
const bbox = JSON.parse(selectedOption.dataset.bbox);
|
||||
|
||||
currentPredProvinceName = provinceName;
|
||||
|
||||
// Update selectedBbox
|
||||
selectedBbox = {
|
||||
min_lon: bbox[0],
|
||||
min_lat: bbox[1],
|
||||
max_lon: bbox[2],
|
||||
max_lat: bbox[3]
|
||||
};
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('prediction_bbox', JSON.stringify(selectedBbox));
|
||||
|
||||
// Draw rectangle on map
|
||||
const bounds = [[bbox[1], bbox[0]], [bbox[3], bbox[2]]];
|
||||
|
||||
// Remove previous rectangle
|
||||
drawnItems.clearLayers();
|
||||
|
||||
// Add new rectangle
|
||||
const rectangle = L.rectangle(bounds, {
|
||||
color: '#667eea',
|
||||
weight: 3,
|
||||
fillOpacity: 0.2
|
||||
});
|
||||
drawnItems.addLayer(rectangle);
|
||||
|
||||
// Fit map to bounds
|
||||
map.fitBounds(bounds, { padding: [50, 50] });
|
||||
|
||||
// Show notification
|
||||
console.log(`✅ Đã chọn tỉnh: ${provinceName}`);
|
||||
alert(`✅ Đã chọn tỉnh: ${provinceName}\n\nBbox: [${bbox.join(', ')}]`);
|
||||
}
|
||||
|
||||
// Handle region filter for prediction
|
||||
function setupPredRegionFilters() {
|
||||
const filterButtons = document.querySelectorAll('.pred-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;
|
||||
populatePredProvinceSelect(region);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
window.onload = function() {
|
||||
initMap();
|
||||
loadModels();
|
||||
loadPredictionsList();
|
||||
loadCacheList();
|
||||
loadPredProvinces(); // Load provinces list
|
||||
|
||||
// Add event listener for model selection
|
||||
document.getElementById('modelSelect').addEventListener('change', updateModelInfo);
|
||||
// Add event listener for cache selection
|
||||
document.getElementById('cacheSelect').addEventListener('change', applyCachePreset);
|
||||
// Add event listener for province selection
|
||||
document.getElementById('predProvinceSelect').addEventListener('change', onPredProvinceSelect);
|
||||
setupPredRegionFilters();
|
||||
};
|
||||
|
||||
// Cleanup on page unload
|
||||
|
||||
Reference in New Issue
Block a user