đã áp dụng file shapefile vào train và predict
This commit is contained in:
+529
-11
@@ -491,10 +491,84 @@
|
||||
|
||||
<!-- NDVI Analysis Content -->
|
||||
<div id="ndviContent" class="content" style="display: none;">
|
||||
<!-- Province Selection for NDVI -->
|
||||
<div class="section" style="grid-column: 1 / -1;">
|
||||
<h2>🗺️ Chọn Khu Vực NDVI Analysis</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="btnNDVI63Provinces" onclick="switchNDVIProvinceList('63')" style="padding: 8px 16px; background: #2ecc71; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">63 Tỉnh (Cũ)</button>
|
||||
<button type="button" id="btnNDVI32Provinces" onclick="switchNDVIProvinceList('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="ndviProvinceListMode" style="color: #2ecc71; font-weight: bold;">Danh sách: 63 tỉnh</span>
|
||||
</div>
|
||||
|
||||
<select id="ndviProvinceSelect" 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 for NDVI -->
|
||||
<div class="form-group" style="margin-bottom: 20px;">
|
||||
<label><strong>🌍 Lọc theo Vùng:</strong></label>
|
||||
<div id="ndviRegionFilterContainer" style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||
<button type="button" class="ndvi-region-filter-btn" data-region="all" style="padding: 8px 16px; background: #2ecc71; 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>
|
||||
|
||||
<!-- Model Selection for NDVI -->
|
||||
<div class="section">
|
||||
<h2>🤖 Chọn Model để Predict</h2>
|
||||
<div class="form-group">
|
||||
<label for="ndviModelSelect">Model đã train:</label>
|
||||
<select id="ndviModelSelect" style="padding: 10px; width: 100%; border: 2px solid #ddd; border-radius: 8px; font-size: 14px; cursor: pointer;">
|
||||
<option value="">Đang tải...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="ndviModelInfo" style="display: none; background: #e8f5e9; padding: 15px; border-radius: 8px; margin-top: 15px;">
|
||||
<h4 style="color: #2e7d32; margin-bottom: 10px;">📊 Thông tin Model</h4>
|
||||
<p><strong>Type:</strong> <span id="ndviModelType">-</span></p>
|
||||
<p><strong>Accuracy:</strong> <span id="ndviModelAccuracy">-</span></p>
|
||||
<p><strong>Training Date:</strong> <span id="ndviModelDate">-</span></p>
|
||||
</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="useGpuNDVI" 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>
|
||||
|
||||
<!-- NDVI Configuration -->
|
||||
<div class="section">
|
||||
|
||||
<h2>⚙️ Cấu hình NDVI để Dự đoán</h2>
|
||||
<h2>⚙️ Cấu hình NDVI để so sánh thay đổi</h2>
|
||||
|
||||
<!-- Quick Presets -->
|
||||
<div class="form-group" style="margin-bottom: 20px; padding: 15px; background: #e8f5e9; border-radius: 8px; border-left: 4px solid #4caf50;">
|
||||
<label style="color: #2e7d32; font-weight: 700; margin-bottom: 10px;">🎯 Quick Presets (Khu vực có dữ liệu tốt)</label>
|
||||
<select id="ndviQuickPreset" onchange="applyNDVIPreset()" style="padding: 10px; width: 100%; border: 2px solid #4caf50; border-radius: 6px; font-size: 14px; cursor: pointer;">
|
||||
<option value="">-- Chọn preset để áp dụng tự động --</option>
|
||||
<option value="mekong_dry">🌾 Đồng bằng Cửu Long - Mùa khô (Jan-Apr 2024)</option>
|
||||
<option value="hanoi_dry">🏙️ Hà Nội - Mùa khô (Feb-Apr 2024)</option>
|
||||
<option value="danang_dry">🏖️ Đà Nẵng - Mùa khô (Jan-Mar 2024)</option>
|
||||
<option value="mekong_2023">🌾 Đồng bằng Cửu Long - Full năm 2023</option>
|
||||
<option value="small_test">⚡ Test nhanh - Khu vực nhỏ (500 points)</option>
|
||||
</select>
|
||||
<div style="font-size: 12px; color: #2e7d32; margin-top: 8px;">💡 Các preset này đã được kiểm tra và có dữ liệu tốt, ít mây</div>
|
||||
</div>
|
||||
|
||||
<!-- NDVI Map for selecting bbox -->
|
||||
<div class="form-group">
|
||||
<label>Chọn bbox trên bản đồ hoặc nhập tọa độ:</label>
|
||||
@@ -524,8 +598,28 @@
|
||||
<input type="number" id="ndviCloudCover" value="30" min="0" max="100">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="calculateNDVI()" id="ndviCalculateBtn">
|
||||
📊 Tính NDVI Time Series
|
||||
<div class="form-group">
|
||||
<label for="ndviMaxScenes">Số scenes tối đa:</label>
|
||||
<input type="number" id="ndviMaxScenes" value="12" min="1" max="100">
|
||||
<div style="font-size: 12px; color: #666; margin-top: 5px;">Giới hạn số lượng ảnh vệ tinh xử lý. Ít scenes = nhanh hơn nhưng ít dữ liệu.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ndviResolution">Resolution:</label>
|
||||
<select id="ndviResolution">
|
||||
<option value="10">10m (Chi tiết cao - Chậm)</option>
|
||||
<option value="20" selected>20m (Cân bằng)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ndviSamplePoints">Số điểm dự đoán (sample):</label>
|
||||
<input type="number" id="ndviSamplePoints" value="1000" min="100" max="10000" step="100">
|
||||
<div style="font-size: 12px; color: #666; margin-top: 5px;">Số điểm ngẫu nhiên để predict và tính NDVI time series. Nhiều điểm = chính xác hơn nhưng chậm hơn.</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="calculateNDVIPrediction()" id="ndviCalculateBtn">
|
||||
📊 Predict & Tính NDVI Time Series
|
||||
</button>
|
||||
|
||||
<button class="btn btn-secondary" onclick="usePredictionBbox()" style="margin-left: 10px;">
|
||||
@@ -1346,6 +1440,258 @@
|
||||
});
|
||||
}
|
||||
|
||||
// === NDVI PROVINCE SELECTION FUNCTIONS ===
|
||||
|
||||
let allNDVIProvinces = {};
|
||||
let allNDVIProvincesMerged = {};
|
||||
let currentNDVIProvinceMode = '63'; // '63' or '32'
|
||||
|
||||
// Switch between 63 and 32 province lists for NDVI
|
||||
function switchNDVIProvinceList(mode) {
|
||||
currentNDVIProvinceMode = mode;
|
||||
|
||||
// Update button styles
|
||||
const btn63 = document.getElementById('btnNDVI63Provinces');
|
||||
const btn32 = document.getElementById('btnNDVI32Provinces');
|
||||
const modeLabel = document.getElementById('ndviProvinceListMode');
|
||||
|
||||
if (mode === '63') {
|
||||
btn63.style.background = '#2ecc71';
|
||||
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 = '#2ecc71';
|
||||
btn32.style.color = 'white';
|
||||
modeLabel.textContent = 'Danh sách: 32 tỉnh (sau sáp nhập)';
|
||||
}
|
||||
|
||||
// Update region filter buttons
|
||||
populateNDVIRegionButtons();
|
||||
|
||||
// Reload province list
|
||||
populateNDVIProvinceSelect();
|
||||
}
|
||||
|
||||
// Populate region filter buttons for NDVI
|
||||
function populateNDVIRegionButtons() {
|
||||
const container = document.getElementById('ndviRegionFilterContainer');
|
||||
|
||||
// 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 = currentNDVIProvinceMode === '63' ? allNDVIProvinces : allNDVIProvincesMerged;
|
||||
const regions = Object.keys(provinceData);
|
||||
|
||||
// Add button for each region
|
||||
regions.forEach(region => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'ndvi-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
|
||||
setupNDVIRegionFilters();
|
||||
}
|
||||
|
||||
// Populate province select dropdown for NDVI
|
||||
function populateNDVIProvinceSelect(filterRegion = 'all') {
|
||||
const select = document.getElementById('ndviProvinceSelect');
|
||||
select.innerHTML = '<option value="">-- Chọn tỉnh thành để tải bbox tự động --</option>';
|
||||
|
||||
// Choose which province list to use
|
||||
const provinceData = currentNDVIProvinceMode === '63' ? allNDVIProvinces : allNDVIProvincesMerged;
|
||||
|
||||
// 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 (currentNDVIProvinceMode === '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 NDVI
|
||||
function onNDVIProvinceSelect(event) {
|
||||
const select = event.target;
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
|
||||
if (!selectedOption.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const provinceName = selectedOption.value;
|
||||
const bbox = JSON.parse(selectedOption.dataset.bbox);
|
||||
|
||||
// Update NDVI bbox inputs
|
||||
document.getElementById('ndviMinLon').value = bbox[0].toFixed(4);
|
||||
document.getElementById('ndviMinLat').value = bbox[1].toFixed(4);
|
||||
document.getElementById('ndviMaxLon').value = bbox[2].toFixed(4);
|
||||
document.getElementById('ndviMaxLat').value = bbox[3].toFixed(4);
|
||||
|
||||
// Draw rectangle on NDVI map
|
||||
const bounds = [[bbox[1], bbox[0]], [bbox[3], bbox[2]]];
|
||||
|
||||
// Remove previous rectangle
|
||||
ndviDrawnItems.clearLayers();
|
||||
|
||||
// Add new rectangle
|
||||
const rectangle = L.rectangle(bounds, {
|
||||
color: '#2ecc71',
|
||||
weight: 3,
|
||||
fillOpacity: 0.2
|
||||
});
|
||||
ndviDrawnItems.addLayer(rectangle);
|
||||
|
||||
// Fit map to bounds
|
||||
ndviMap.fitBounds(bounds, { padding: [50, 50] });
|
||||
|
||||
// Show notification
|
||||
console.log(`✅ NDVI - Đã chọn tỉnh: ${provinceName}`);
|
||||
alert(`✅ Đã chọn tỉnh cho NDVI Analysis: ${provinceName}\n\nBbox: [${bbox.join(', ')}]`);
|
||||
}
|
||||
|
||||
// Handle region filter for NDVI
|
||||
function setupNDVIRegionFilters() {
|
||||
const filterButtons = document.querySelectorAll('.ndvi-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 = '#2ecc71';
|
||||
this.style.color = 'white';
|
||||
this.style.fontWeight = '600';
|
||||
|
||||
// Filter provinces
|
||||
const region = this.dataset.region;
|
||||
populateNDVIProvinceSelect(region);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Load NDVI provinces data
|
||||
async function loadNDVIProvinces() {
|
||||
try {
|
||||
// Reuse data from prediction if already loaded
|
||||
if (Object.keys(allPredProvinces).length > 0) {
|
||||
allNDVIProvinces = allPredProvinces;
|
||||
allNDVIProvincesMerged = allPredProvincesMerged;
|
||||
} else {
|
||||
// Load fresh
|
||||
const response63 = await fetch('/api/provinces/by-region');
|
||||
allNDVIProvinces = await response63.json();
|
||||
|
||||
const response32 = await fetch('/api/provinces-32/by-region');
|
||||
allNDVIProvincesMerged = await response32.json();
|
||||
}
|
||||
|
||||
// Populate NDVI province UI
|
||||
populateNDVIRegionButtons();
|
||||
populateNDVIProvinceSelect();
|
||||
} catch (error) {
|
||||
console.error('Error loading NDVI provinces:', error);
|
||||
document.getElementById('ndviProvinceSelect').innerHTML = '<option value="">Lỗi tải danh sách tỉnh</option>';
|
||||
}
|
||||
}
|
||||
|
||||
// Load models for NDVI
|
||||
async function loadNDVIModels() {
|
||||
try {
|
||||
const response = await fetch('/api/models/list');
|
||||
const data = await response.json();
|
||||
|
||||
const select = document.getElementById('ndviModelSelect');
|
||||
select.innerHTML = '<option value="">Chọn model...</option>';
|
||||
|
||||
// Chỉ lấy các file model thực sự (.joblib), loại bỏ các file có chứa '_info.joblib'
|
||||
data.models
|
||||
.filter(m => m.filename.endsWith('.joblib') && !m.filename.includes('_info.joblib'))
|
||||
.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model.filename;
|
||||
option.textContent = model.filename;
|
||||
if (model.info) {
|
||||
option.dataset.info = JSON.stringify(model.info);
|
||||
}
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
// Auto-select first model
|
||||
const firstJoblib = data.models.find(m => m.filename.endsWith('.joblib') && !m.filename.includes('_info.joblib'));
|
||||
if (firstJoblib) {
|
||||
select.value = firstJoblib.filename;
|
||||
updateNDVIModelInfo();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading NDVI models:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Update NDVI model info display
|
||||
function updateNDVIModelInfo() {
|
||||
const select = document.getElementById('ndviModelSelect');
|
||||
const option = select.options[select.selectedIndex];
|
||||
|
||||
if (option && option.dataset && option.dataset.info) {
|
||||
try {
|
||||
const info = JSON.parse(option.dataset.info);
|
||||
const infoDiv = document.getElementById('ndviModelInfo');
|
||||
|
||||
document.getElementById('ndviModelType').textContent = info.model_type || 'N/A';
|
||||
document.getElementById('ndviModelAccuracy').textContent = info.metrics?.accuracy
|
||||
? (info.metrics.accuracy * 100).toFixed(2) + '%'
|
||||
: 'N/A';
|
||||
document.getElementById('ndviModelDate').textContent = info.training_date || 'N/A';
|
||||
|
||||
infoDiv.style.display = 'block';
|
||||
} catch (e) {
|
||||
console.warn('Error parsing NDVI model info:', e);
|
||||
document.getElementById('ndviModelInfo').style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
document.getElementById('ndviModelInfo').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
window.onload = function() {
|
||||
initMap();
|
||||
@@ -1353,14 +1699,19 @@
|
||||
loadPredictionsList();
|
||||
loadCacheList();
|
||||
loadPredProvinces(); // Load provinces list
|
||||
loadNDVIProvinces(); // Load NDVI provinces list
|
||||
loadNDVIModels(); // Load models for NDVI
|
||||
|
||||
// Add event listener for model selection
|
||||
document.getElementById('modelSelect').addEventListener('change', updateModelInfo);
|
||||
document.getElementById('ndviModelSelect').addEventListener('change', updateNDVIModelInfo);
|
||||
// Add event listener for cache selection
|
||||
document.getElementById('cacheSelect').addEventListener('change', applyCachePreset);
|
||||
// Add event listener for province selection
|
||||
document.getElementById('predProvinceSelect').addEventListener('change', onPredProvinceSelect);
|
||||
document.getElementById('ndviProvinceSelect').addEventListener('change', onNDVIProvinceSelect);
|
||||
setupPredRegionFilters();
|
||||
setupNDVIRegionFilters();
|
||||
};
|
||||
|
||||
// Cleanup on page unload
|
||||
@@ -1379,6 +1730,10 @@
|
||||
document.getElementById('tabPrediction').style.color = 'white';
|
||||
document.getElementById('tabNDVI').style.background = '#ccc';
|
||||
document.getElementById('tabNDVI').style.color = '#666';
|
||||
// Invalidate map size when switching back to prediction tab
|
||||
setTimeout(() => {
|
||||
if (map) map.invalidateSize();
|
||||
}, 100);
|
||||
} else {
|
||||
document.getElementById('predictionContent').style.display = 'none';
|
||||
document.getElementById('ndviContent').style.display = 'grid';
|
||||
@@ -1386,6 +1741,10 @@
|
||||
document.getElementById('tabPrediction').style.color = '#666';
|
||||
document.getElementById('tabNDVI').style.background = '#2ecc71';
|
||||
document.getElementById('tabNDVI').style.color = 'white';
|
||||
// Fix NDVI map rendering issue when tab is shown
|
||||
setTimeout(() => {
|
||||
if (ndviMap) ndviMap.invalidateSize();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1395,14 +1754,88 @@
|
||||
alert('⚠️ Chưa chọn bbox trong phần Prediction. Vui lòng vẽ bbox trên bản đồ trước!');
|
||||
return;
|
||||
}
|
||||
document.getElementById('ndviMinLon').value = selectedBbox[0].toFixed(4);
|
||||
document.getElementById('ndviMinLat').value = selectedBbox[1].toFixed(4);
|
||||
document.getElementById('ndviMaxLon').value = selectedBbox[2].toFixed(4);
|
||||
document.getElementById('ndviMaxLat').value = selectedBbox[3].toFixed(4);
|
||||
document.getElementById('ndviMinLon').value = selectedBbox.min_lon.toFixed(4);
|
||||
document.getElementById('ndviMinLat').value = selectedBbox.min_lat.toFixed(4);
|
||||
document.getElementById('ndviMaxLon').value = selectedBbox.max_lon.toFixed(4);
|
||||
document.getElementById('ndviMaxLat').value = selectedBbox.max_lat.toFixed(4);
|
||||
alert('✅ Đã copy bbox từ Prediction!');
|
||||
}
|
||||
|
||||
// Calculate NDVI time series
|
||||
// Calculate NDVI time series with prediction
|
||||
async function calculateNDVIPrediction() {
|
||||
const minLon = parseFloat(document.getElementById('ndviMinLon').value);
|
||||
const minLat = parseFloat(document.getElementById('ndviMinLat').value);
|
||||
const maxLon = parseFloat(document.getElementById('ndviMaxLon').value);
|
||||
const maxLat = parseFloat(document.getElementById('ndviMaxLat').value);
|
||||
const startDate = document.getElementById('ndviStartDate').value;
|
||||
const endDate = document.getElementById('ndviEndDate').value;
|
||||
const maxCloudCover = parseInt(document.getElementById('ndviCloudCover').value);
|
||||
const maxScenes = parseInt(document.getElementById('ndviMaxScenes').value);
|
||||
const modelFilename = document.getElementById('ndviModelSelect').value;
|
||||
const useGpu = document.getElementById('useGpuNDVI').checked;
|
||||
const resolution = parseInt(document.getElementById('ndviResolution').value);
|
||||
const samplePoints = parseInt(document.getElementById('ndviSamplePoints').value);
|
||||
|
||||
// Validate
|
||||
if (isNaN(minLon) || isNaN(minLat) || isNaN(maxLon) || isNaN(maxLat)) {
|
||||
showNDVIError('Vui lòng nhập đầy đủ tọa độ bbox!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
showNDVIError('Vui lòng chọn khoảng thời gian!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!modelFilename) {
|
||||
showNDVIError('Vui lòng chọn model để predict!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading
|
||||
document.getElementById('ndviLoading').style.display = 'block';
|
||||
document.getElementById('ndviResults').style.display = 'none';
|
||||
document.getElementById('ndviChartSection').style.display = 'none';
|
||||
document.getElementById('ndviError').style.display = 'none';
|
||||
document.getElementById('ndviCalculateBtn').disabled = true;
|
||||
|
||||
const config = {
|
||||
model_filename: modelFilename,
|
||||
bbox: [minLon, minLat, maxLon, maxLat],
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
max_cloud_cover: maxCloudCover,
|
||||
max_scenes: maxScenes,
|
||||
resolution: resolution,
|
||||
sample_points: samplePoints,
|
||||
use_gpu: useGpu
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/ndvi/predict-timeseries', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
ndviData = result;
|
||||
displayNDVIResults(result);
|
||||
} else {
|
||||
throw new Error(result.detail || 'Lỗi khi predict NDVI');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error calculating NDVI with prediction:', error);
|
||||
showNDVIError(error.message);
|
||||
} finally {
|
||||
document.getElementById('ndviLoading').style.display = 'none';
|
||||
document.getElementById('ndviCalculateBtn').disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate NDVI time series (legacy - without prediction)
|
||||
async function calculateNDVI() {
|
||||
const minLon = parseFloat(document.getElementById('ndviMinLon').value);
|
||||
const minLat = parseFloat(document.getElementById('ndviMinLat').value);
|
||||
@@ -1487,7 +1920,7 @@
|
||||
}
|
||||
|
||||
const dates = data.timeseries.map(item => new Date(item.date).toLocaleDateString('vi-VN'));
|
||||
const ndviValues = data.timeseries.map(item => item.ndvi);
|
||||
const ndviValues = data.timeseries.map(item => item.mean_ndvi);
|
||||
|
||||
ndviChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
@@ -1548,9 +1981,9 @@
|
||||
function downloadNDVIData() {
|
||||
if (!ndviData) return;
|
||||
|
||||
let csv = 'Date,NDVI\\n';
|
||||
let csv = 'Date,Mean_NDVI,Min_NDVI,Max_NDVI,Std_NDVI,Valid_Points\\n';
|
||||
ndviData.timeseries.forEach(item => {
|
||||
csv += `${item.date},${item.ndvi}\\n`;
|
||||
csv += `${item.date},${item.mean_ndvi},${item.min_ndvi},${item.max_ndvi},${item.std_ndvi},${item.n_valid_points}\\n`;
|
||||
});
|
||||
|
||||
const blob = new Blob([csv], { type: 'text/csv' });
|
||||
@@ -1578,6 +2011,91 @@
|
||||
document.getElementById('ndviError').style.display = 'block';
|
||||
document.getElementById('ndviErrorMessage').textContent = message;
|
||||
}
|
||||
|
||||
// Apply NDVI preset
|
||||
function applyNDVIPreset() {
|
||||
const preset = document.getElementById('ndviQuickPreset').value;
|
||||
if (!preset) return;
|
||||
|
||||
const presets = {
|
||||
'mekong_dry': {
|
||||
name: 'Đồng bằng Cửu Long - Mùa khô',
|
||||
bbox: [105.6, 9.3, 106.2, 9.8],
|
||||
start_date: '2024-01-01',
|
||||
end_date: '2024-04-30',
|
||||
cloud_cover: 30,
|
||||
max_scenes: 15,
|
||||
sample_points: 1000
|
||||
},
|
||||
'hanoi_dry': {
|
||||
name: 'Hà Nội - Mùa khô',
|
||||
bbox: [105.7, 20.9, 105.9, 21.1],
|
||||
start_date: '2024-02-01',
|
||||
end_date: '2024-04-30',
|
||||
cloud_cover: 40,
|
||||
max_scenes: 12,
|
||||
sample_points: 800
|
||||
},
|
||||
'danang_dry': {
|
||||
name: 'Đà Nẵng - Mùa khô',
|
||||
bbox: [107.9, 15.9, 108.3, 16.2],
|
||||
start_date: '2024-01-01',
|
||||
end_date: '2024-03-31',
|
||||
cloud_cover: 35,
|
||||
max_scenes: 12,
|
||||
sample_points: 800
|
||||
},
|
||||
'mekong_2023': {
|
||||
name: 'Đồng bằng Cửu Long - Full 2023',
|
||||
bbox: [105.6, 9.3, 106.2, 9.8],
|
||||
start_date: '2023-01-01',
|
||||
end_date: '2023-12-31',
|
||||
cloud_cover: 40,
|
||||
max_scenes: 20,
|
||||
sample_points: 1000
|
||||
},
|
||||
'small_test': {
|
||||
name: 'Test nhanh - Khu vực nhỏ',
|
||||
bbox: [105.8, 9.5, 105.9, 9.6],
|
||||
start_date: '2024-01-01',
|
||||
end_date: '2024-03-31',
|
||||
cloud_cover: 50,
|
||||
max_scenes: 10,
|
||||
sample_points: 500
|
||||
}
|
||||
};
|
||||
|
||||
const config = presets[preset];
|
||||
if (!config) return;
|
||||
|
||||
// Apply bbox
|
||||
document.getElementById('ndviMinLon').value = config.bbox[0];
|
||||
document.getElementById('ndviMinLat').value = config.bbox[1];
|
||||
document.getElementById('ndviMaxLon').value = config.bbox[2];
|
||||
document.getElementById('ndviMaxLat').value = config.bbox[3];
|
||||
|
||||
// Apply dates
|
||||
document.getElementById('ndviStartDate').value = config.start_date;
|
||||
document.getElementById('ndviEndDate').value = config.end_date;
|
||||
|
||||
// Apply other params
|
||||
document.getElementById('ndviCloudCover').value = config.cloud_cover;
|
||||
document.getElementById('ndviMaxScenes').value = config.max_scenes;
|
||||
document.getElementById('ndviSamplePoints').value = config.sample_points;
|
||||
|
||||
// Draw bbox on map
|
||||
ndviDrawnItems.clearLayers();
|
||||
const bounds = [[config.bbox[1], config.bbox[0]], [config.bbox[3], config.bbox[2]]];
|
||||
const rectangle = L.rectangle(bounds, {
|
||||
color: '#2ecc71',
|
||||
weight: 3,
|
||||
fillOpacity: 0.2
|
||||
});
|
||||
ndviDrawnItems.addLayer(rectangle);
|
||||
ndviMap.fitBounds(bounds, { padding: [50, 50] });
|
||||
|
||||
alert(`✅ Đã áp dụng preset: ${config.name}\n\nBbox: [${config.bbox.join(', ')}]\nThời gian: ${config.start_date} → ${config.end_date}\nSample points: ${config.sample_points}`);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user