đã áp dụng file shapefile vào train và predict
This commit is contained in:
+281
-13
@@ -413,10 +413,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Hidden inputs to store bbox values -->
|
||||
<input type="hidden" id="minLon" value="105.6" required>
|
||||
<input type="hidden" id="minLat" value="9.3" required>
|
||||
<input type="hidden" id="maxLon" value="106.2" required>
|
||||
<input type="hidden" id="maxLat" value="9.8" required>
|
||||
<input type="hidden" id="minLon" value="105.5" required>
|
||||
<input type="hidden" id="minLat" value="9.2" required>
|
||||
<input type="hidden" id="maxLon" value="106.4" required>
|
||||
<input type="hidden" id="maxLat" value="10.0" required>
|
||||
|
||||
<h3 style="margin: 20px 0 15px; color: #667eea;">📅 Thời Gian</h3>
|
||||
<div class="form-row">
|
||||
@@ -426,11 +426,45 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ngày kết thúc:</label>
|
||||
<input type="date" id="endDate" value="2023-05-31" required>
|
||||
<input type="date" id="endDate" value="2023-12-31" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin: 20px 0 15px; color: #667eea;">� Dataset Cache Preset</h3>
|
||||
<h3 style="margin: 20px 0 15px; color: #667eea;">📊 Training Data (Shapefile)</h3>
|
||||
<div class="form-group">
|
||||
<label><strong>🗂️ Chọn file training shapefile:</strong></label>
|
||||
<select id="trainingShapefile" style="font-size: 14px; font-weight: 600;">
|
||||
<option value="">Đang tải...</option>
|
||||
</select>
|
||||
<div style="font-size: 12px; color: #666; margin-top: 5px;">
|
||||
💡 Chọn shapefile chứa dữ liệu training points với labels
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Training Shapefile Info Display -->
|
||||
<div id="shapefileInfo" style="display: none; margin-top: 15px; padding: 15px; background: #e7f3ff; border-radius: 8px; border-left: 4px solid #2196F3;">
|
||||
<h4 style="color: #1976d2; margin-bottom: 10px;">📋 Thông tin Shapefile</h4>
|
||||
<div style="font-size: 13px;">
|
||||
<p><strong>📍 Số điểm:</strong> <span id="shapefilePoints">-</span></p>
|
||||
<p><strong>🏷️ Label column:</strong> <span id="shapefileLabelCol">-</span></p>
|
||||
<p><strong>📊 Số lớp:</strong> <span id="shapefileLabelCount">-</span></p>
|
||||
<p><strong>🗺️ Bbox:</strong> <span id="shapefileBbox">-</span></p>
|
||||
</div>
|
||||
|
||||
<!-- Labels Distribution -->
|
||||
<div id="labelsDistribution" style="margin-top: 10px;">
|
||||
<h5 style="color: #1976d2; margin-bottom: 8px;">🎯 Phân bố Labels:</h5>
|
||||
<div id="labelsList" style="font-size: 12px; max-height: 200px; overflow-y: auto;">
|
||||
<!-- Labels will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" onclick="applyShapefileBbox()" style="margin-top: 10px; padding: 8px 16px; background: #2196F3; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">
|
||||
📍 Áp dụng Bbox từ Shapefile
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h3 style="margin: 20px 0 15px; color: #667eea;">💾 Dataset Cache Preset</h3>
|
||||
<div class="form-group">
|
||||
<label>Chọn Dataset đã cache:</label>
|
||||
<select id="cachePreset" style="font-size: 14px;">
|
||||
@@ -579,17 +613,245 @@
|
||||
failed: 0,
|
||||
times: []
|
||||
};
|
||||
let trainingFiles = []; // Store training shapefile data
|
||||
let currentShapefileData = null; // Currently selected shapefile data
|
||||
|
||||
// Load presets and models only after DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Initialize map first - IMPORTANT!
|
||||
initMap();
|
||||
loadCacheInfo();
|
||||
loadProvinces();
|
||||
|
||||
// Then load other data
|
||||
await loadPresets();
|
||||
await loadModels();
|
||||
await loadReports();
|
||||
await loadSystemInfo();
|
||||
await loadTrainingFiles(); // Load training shapefiles - map must be ready
|
||||
checkStatus();
|
||||
loadTrainingHistory();
|
||||
});
|
||||
|
||||
// Load training shapefile files
|
||||
async function loadTrainingFiles() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/training/files`);
|
||||
const data = await response.json();
|
||||
|
||||
const select = document.getElementById('trainingShapefile');
|
||||
select.innerHTML = '<option value="">-- Chọn training shapefile --</option>';
|
||||
|
||||
if (data.files && data.files.length > 0) {
|
||||
trainingFiles = data.files;
|
||||
|
||||
data.files.forEach(file => {
|
||||
const option = document.createElement('option');
|
||||
option.value = file.filename;
|
||||
|
||||
let displayText = file.filename;
|
||||
if (file.point_count) {
|
||||
displayText += ` (${file.point_count} points`;
|
||||
if (file.label_count) {
|
||||
displayText += `, ${file.label_count} classes`;
|
||||
}
|
||||
displayText += ')';
|
||||
} else if (file.error) {
|
||||
displayText += ' ⚠️ (Error)';
|
||||
}
|
||||
|
||||
option.textContent = displayText;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
// Select default shapefile
|
||||
const defaultFile = 'ST_training data_updated_1130points_new.shp';
|
||||
const defaultOption = Array.from(select.options).find(opt => opt.value === defaultFile);
|
||||
if (defaultOption) {
|
||||
select.value = defaultFile;
|
||||
await loadShapefileLabels(defaultFile);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading training files:', error);
|
||||
document.getElementById('trainingShapefile').innerHTML = '<option value="">Lỗi tải danh sách files</option>';
|
||||
}
|
||||
}
|
||||
|
||||
// Load labels from selected shapefile
|
||||
async function loadShapefileLabels(filename) {
|
||||
if (!filename) {
|
||||
document.getElementById('shapefileInfo').style.display = 'none';
|
||||
currentShapefileData = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/training/shapefile/${encodeURIComponent(filename)}/labels`);
|
||||
const data = await response.json();
|
||||
|
||||
currentShapefileData = data;
|
||||
|
||||
// Display shapefile info
|
||||
document.getElementById('shapefilePoints').textContent = data.point_count || '-';
|
||||
document.getElementById('shapefileLabelCol').textContent = data.label_column || '-';
|
||||
document.getElementById('shapefileLabelCount').textContent = data.label_count || '-';
|
||||
|
||||
if (data.bbox) {
|
||||
const bbox = data.bbox;
|
||||
document.getElementById('shapefileBbox').textContent =
|
||||
`[${bbox[0].toFixed(4)}, ${bbox[1].toFixed(4)}, ${bbox[2].toFixed(4)}, ${bbox[3].toFixed(4)}]`;
|
||||
|
||||
console.log('📦 Bbox from shapefile:', bbox);
|
||||
console.log('🗺️ Map status:', map ? 'initialized' : 'NOT initialized');
|
||||
console.log('📍 DrawnItems status:', drawnItems ? 'initialized' : 'NOT initialized');
|
||||
|
||||
// Auto-zoom map to shapefile bbox when selected
|
||||
if (map && drawnItems) {
|
||||
// Create bounds for Leaflet: [[south, west], [north, east]]
|
||||
// bbox is [minx, miny, maxx, maxy] = [west, south, east, north]
|
||||
const bounds = [[bbox[1], bbox[0]], [bbox[3], bbox[2]]];
|
||||
|
||||
console.log('🎯 Leaflet bounds to zoom:', bounds);
|
||||
|
||||
// Remove previous rectangle
|
||||
if (currentRectangle) {
|
||||
drawnItems.removeLayer(currentRectangle);
|
||||
}
|
||||
|
||||
// Draw preview rectangle with light styling
|
||||
currentRectangle = L.rectangle(bounds, {
|
||||
color: '#9C27B0', // Purple color for preview
|
||||
weight: 3,
|
||||
fillOpacity: 0.2,
|
||||
fillColor: '#9C27B0',
|
||||
dashArray: '10, 5' // Dashed line to show it's preview
|
||||
});
|
||||
drawnItems.addLayer(currentRectangle);
|
||||
console.log('✅ Rectangle drawn on map');
|
||||
|
||||
// Use setTimeout to ensure map is ready and give time for rendering
|
||||
setTimeout(() => {
|
||||
try {
|
||||
console.log('🚀 Attempting flyToBounds...');
|
||||
map.flyToBounds(bounds, {
|
||||
padding: [80, 80],
|
||||
duration: 1.5,
|
||||
maxZoom: 11
|
||||
});
|
||||
console.log('✅ flyToBounds called successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Error during flyToBounds:', error);
|
||||
}
|
||||
}, 200); // Small delay to ensure everything is ready
|
||||
} else {
|
||||
console.error('❌ Cannot zoom: map or drawnItems not initialized!');
|
||||
}
|
||||
}
|
||||
|
||||
// Display labels distribution
|
||||
const labelsList = document.getElementById('labelsList');
|
||||
labelsList.innerHTML = '';
|
||||
|
||||
if (data.labels && data.labels.length > 0) {
|
||||
data.labels.forEach(label => {
|
||||
const labelDiv = document.createElement('div');
|
||||
labelDiv.style.cssText = 'padding: 6px 10px; margin: 4px 0; background: white; border-radius: 4px; display: flex; justify-content: space-between; align-items: center;';
|
||||
|
||||
const mappedIcon = label.mapped ? '✅' : '⚠️';
|
||||
const mappedColor = label.mapped ? '#4caf50' : '#ff9800';
|
||||
|
||||
labelDiv.innerHTML = `
|
||||
<span style="font-weight: 600;">${mappedIcon} ${label.name}</span>
|
||||
<span style="color: ${mappedColor}; font-weight: 600;">Code: ${label.code} (${label.count} pts)</span>
|
||||
`;
|
||||
labelsList.appendChild(labelDiv);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('shapefileInfo').style.display = 'block';
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading shapefile labels:', error);
|
||||
document.getElementById('shapefileInfo').style.display = 'none';
|
||||
currentShapefileData = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply bbox from selected shapefile
|
||||
function applyShapefileBbox() {
|
||||
if (!currentShapefileData || !currentShapefileData.bbox) {
|
||||
alert('Không có bbox data từ shapefile');
|
||||
return;
|
||||
}
|
||||
|
||||
const bbox = currentShapefileData.bbox; // [minx, miny, maxx, maxy]
|
||||
|
||||
// Update bbox inputs
|
||||
document.getElementById('minLon').value = bbox[0].toFixed(6);
|
||||
document.getElementById('minLat').value = bbox[1].toFixed(6);
|
||||
document.getElementById('maxLon').value = bbox[2].toFixed(6);
|
||||
document.getElementById('maxLat').value = bbox[3].toFixed(6);
|
||||
|
||||
// Update bbox display
|
||||
document.getElementById('bboxDisplay').textContent =
|
||||
`Lon: ${bbox[0].toFixed(4)} → ${bbox[2].toFixed(4)}, Lat: ${bbox[1].toFixed(4)} → ${bbox[3].toFixed(4)}`;
|
||||
document.getElementById('provinceDisplay').textContent = `📍 Từ Shapefile: ${currentShapefileData.filename}`;
|
||||
|
||||
// Update map with new bbox
|
||||
if (map && drawnItems) {
|
||||
// Create bounds for Leaflet: [[south, west], [north, east]]
|
||||
const bounds = [[bbox[1], bbox[0]], [bbox[3], bbox[2]]];
|
||||
|
||||
// Remove previous rectangle if exists
|
||||
if (currentRectangle) {
|
||||
drawnItems.removeLayer(currentRectangle);
|
||||
}
|
||||
|
||||
// Create and add new rectangle with distinctive styling
|
||||
currentRectangle = L.rectangle(bounds, {
|
||||
color: '#FF5722', // Orange color to distinguish from manually drawn
|
||||
weight: 3,
|
||||
fillOpacity: 0.25,
|
||||
fillColor: '#FF9800'
|
||||
});
|
||||
drawnItems.addLayer(currentRectangle);
|
||||
|
||||
// Fit map to bounds with padding for better visibility
|
||||
map.fitBounds(bounds, {
|
||||
padding: [50, 50],
|
||||
maxZoom: 12 // Don't zoom in too much
|
||||
});
|
||||
|
||||
// Add animation effect
|
||||
setTimeout(() => {
|
||||
if (currentRectangle) {
|
||||
currentRectangle.setStyle({
|
||||
color: '#2196F3',
|
||||
fillColor: '#2196F3'
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Show notification
|
||||
showNotification('success', `✅ Đã áp dụng bbox từ shapefile: ${currentShapefileData.filename}\n📍 ${currentShapefileData.point_count} điểm training`);
|
||||
}
|
||||
|
||||
// Notification helper
|
||||
function showNotification(type, message) {
|
||||
const notification = document.createElement('div');
|
||||
const bgColor = type === 'success' ? '#28a745' : (type === 'error' ? '#dc3545' : '#ffc107');
|
||||
notification.style.cssText = `position:fixed;top:20px;right:20px;background:${bgColor};color:white;padding:15px 20px;border-radius:8px;box-shadow:0 4px 6px rgba(0,0,0,0.1);z-index:10000;animation:slideIn 0.3s ease-out;`;
|
||||
notification.textContent = message;
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.style.animation = 'slideOut 0.3s ease-out';
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Load preset configurations
|
||||
async function loadPresets() {
|
||||
try {
|
||||
@@ -648,6 +910,10 @@
|
||||
document.getElementById('trainingForm').onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Get selected training shapefile
|
||||
const selectedShapefile = document.getElementById('trainingShapefile').value;
|
||||
const trainingShapefile = selectedShapefile || 'train/ST_training data_updated_1130points_new.shp';
|
||||
|
||||
const config = {
|
||||
min_lon: parseFloat(document.getElementById('minLon').value),
|
||||
min_lat: parseFloat(document.getElementById('minLat').value),
|
||||
@@ -664,7 +930,8 @@
|
||||
learning_rate: parseFloat(document.getElementById('learningRate').value),
|
||||
test_size: parseFloat(document.getElementById('testSize').value),
|
||||
use_gpu: document.getElementById('useGpu').value === 'true',
|
||||
use_cache: document.getElementById('useCache').checked
|
||||
use_cache: document.getElementById('useCache').checked,
|
||||
training_shapefile: trainingShapefile
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -1516,14 +1783,15 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize map when page loads
|
||||
// Initialize map and setup event listeners when page loads
|
||||
// Note: Main initialization is in the earlier DOMContentLoaded handler
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initMap();
|
||||
loadCacheInfo(); // Load cache info
|
||||
loadProvinces(); // Load provinces list
|
||||
|
||||
// Add event listeners
|
||||
// Event listeners setup
|
||||
document.getElementById('provinceSelect').addEventListener('change', onProvinceSelect);
|
||||
document.getElementById('trainingShapefile').addEventListener('change', function(e) {
|
||||
console.log('🔄 Shapefile selection changed to:', e.target.value);
|
||||
loadShapefileLabels(e.target.value);
|
||||
});
|
||||
setupRegionFilters();
|
||||
|
||||
// Add model type change listener
|
||||
|
||||
Reference in New Issue
Block a user