làm mịn các điểm ảnh

This commit is contained in:
Victor Phan
2026-01-06 12:25:42 +07:00
parent ae3e5fffdd
commit d6ba6d8db0
4 changed files with 975 additions and 202 deletions
+171
View File
@@ -353,6 +353,20 @@
<p>🔄 Có thể chỉnh sửa sau khi vẽ</p>
</div>
<div id="predictMap"></div>
<!-- Prediction Cache Section -->
<div class="section" style="margin-top: 20px;">
<h3>💾 Cache Predictions (Tự động lưu)</h3>
<p style="color: #666; font-size: 0.9em; margin-bottom: 10px;">Cache được tự động tạo sau mỗi lần predict thành công</p>
<div style="display: flex; gap: 10px; margin-bottom: 15px;">
<select id="predCacheSelect" style="flex: 1; padding: 10px; border: 2px solid #e0e0e0; border-radius: 5px;">
<option value="">-- Chọn cache để phục hồi cấu hình --</option>
</select>
<button onclick="loadPredictionCache()" class="btn btn-success">📂 Phục hồi</button>
<button onclick="refreshPredCache()" class="btn btn-secondary" title="Làm mới danh sách">🔄</button>
</div>
<div id="predCacheList" style="max-height: 200px; overflow-y: auto; background: white; border-radius: 5px; padding: 10px;"></div>
</div>
</div>
<!-- Model Selection -->
@@ -1174,6 +1188,162 @@
}
}
// ============ PREDICTION CACHE FUNCTIONS (Auto-save) ============
// Load prediction cache list
async function loadPredCacheList() {
try {
const response = await fetch('/api/prediction/cache/list');
const data = await response.json();
const select = document.getElementById('predCacheSelect');
const listDiv = document.getElementById('predCacheList');
select.innerHTML = '<option value="">-- Chọn cache để phục hồi cấu hình --</option>';
listDiv.innerHTML = '';
if (data.caches && data.caches.length > 0) {
data.caches.forEach(cache => {
// Add to select dropdown
const option = document.createElement('option');
option.value = cache.filename;
option.textContent = cache.display_name;
option.dataset.config = JSON.stringify(cache.config);
select.appendChild(option);
// Add to list
const item = document.createElement('div');
item.style.cssText = 'background: #f8f9fa; padding: 10px; border-radius: 5px; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; border-left: 3px solid #4facfe;';
const cfg = cache.config;
item.innerHTML = `
<div style="flex: 1;">
<strong style="color: #667eea;">📍 [${cfg.bbox[0].toFixed(2)}, ${cfg.bbox[1].toFixed(2)}${cfg.bbox[2].toFixed(2)}, ${cfg.bbox[3].toFixed(2)}]</strong><br>
<small style="color: #666;">📅 ${cfg.start_date}${cfg.end_date}</small><br>
<small style="color: #999;">🔧 Resolution: ${cfg.resolution}m | Scenes: ${cfg.max_scenes} | Cloud: ${cfg.cloud_cover}%</small><br>
<small style="color: #999;">⏰ ${cache.created || 'N/A'}</small>
</div>
<div style="display: flex; gap: 5px;">
<button onclick="applyPredCache('${cache.filename}')" class="btn btn-success" style="padding: 5px 10px; font-size: 0.9em;">📂</button>
<button onclick="deletePredCache('${cache.filename}')" class="btn btn-secondary" style="padding: 5px 10px; font-size: 0.9em;">🗑️</button>
</div>
`;
listDiv.appendChild(item);
});
} else {
listDiv.innerHTML = '<p style="color: #999; text-align: center; padding: 20px;">Chưa có cache nào. Cache sẽ tự động được tạo sau khi predict thành công.</p>';
}
} catch (error) {
console.error('Error loading prediction cache:', error);
}
}
// Refresh prediction cache list
function refreshPredCache() {
loadPredCacheList();
}
// Apply prediction cache from filename
function applyPredCache(filename) {
const select = document.getElementById('predCacheSelect');
// Find option by filename
for (let i = 0; i < select.options.length; i++) {
if (select.options[i].value === filename) {
select.selectedIndex = i;
loadPredictionCache();
return;
}
}
}
// Load prediction cache and restore all parameters
function loadPredictionCache() {
const select = document.getElementById('predCacheSelect');
const selectedOption = select.options[select.selectedIndex];
if (!selectedOption || !selectedOption.value || !selectedOption.dataset.config) {
alert('⚠️ Vui lòng chọn cache từ danh sách!');
return;
}
const config = JSON.parse(selectedOption.dataset.config);
// Update selectedBbox
selectedBbox = {
min_lon: config.min_lon,
min_lat: config.min_lat,
max_lon: config.max_lon,
max_lat: config.max_lat
};
// Save to localStorage
localStorage.setItem('prediction_bbox', JSON.stringify(selectedBbox));
// Update ALL form fields
if (config.start_date) document.getElementById('predStartDate').value = config.start_date;
if (config.end_date) document.getElementById('predEndDate').value = config.end_date;
if (config.max_scenes) document.getElementById('predMaxScenes').value = config.max_scenes;
if (config.cloud_cover) document.getElementById('predCloudCover').value = config.cloud_cover;
if (config.resolution) document.getElementById('predResolution').value = config.resolution;
// Select model if available
if (config.model_filename) {
const modelSelect = document.getElementById('modelSelect');
for (let i = 0; i < modelSelect.options.length; i++) {
if (modelSelect.options[i].value === config.model_filename) {
modelSelect.selectedIndex = i;
updateModelInfo();
break;
}
}
}
// Draw rectangle on map
const bbox = config.bbox;
const bounds = [[bbox[1], bbox[0]], [bbox[3], bbox[2]]];
// Remove previous rectangle
drawnItems.clearLayers();
// Add new rectangle
const rectangle = L.rectangle(bounds, {
color: '#4facfe',
weight: 3,
fillOpacity: 0.2
});
drawnItems.addLayer(rectangle);
// Fit map to bounds
map.fitBounds(bounds, { padding: [50, 50] });
alert(`✅ Đã phục hồi cấu hình từ cache!\n\n📍 Bbox: [${bbox.map(n => n.toFixed(4)).join(', ')}]\n📅 Time: ${config.start_date}${config.end_date}\n🔧 Resolution: ${config.resolution}m, Scenes: ${config.max_scenes}, Cloud: ${config.cloud_cover}%`);
}
// Delete prediction cache
async function deletePredCache(filename) {
if (!confirm(`Bạn có chắc muốn xóa cache này?\n\nFile: ${filename}`)) {
return;
}
try {
const response = await fetch(`/api/prediction/cache/delete/${filename}`, {
method: 'DELETE'
});
const result = await response.json();
if (result.success) {
alert(`${result.message}`);
loadPredCacheList(); // Reload list
} else {
alert(`❌ Lỗi: ${result.detail || 'Unknown error'}`);
}
} catch (error) {
console.error('Error deleting cache:', error);
alert(`❌ Lỗi khi xóa cache: ${error.message}`);
}
}
// Apply cache preset - auto fill bbox and other params
function applyCachePreset() {
const selectValue = document.getElementById('cacheSelect').value;
@@ -1698,6 +1868,7 @@
loadModels();
loadPredictionsList();
loadCacheList();
loadPredCacheList(); // Load prediction cache list
loadPredProvinces(); // Load provinces list
loadNDVIProvinces(); // Load NDVI provinces list
loadNDVIModels(); // Load models for NDVI