change to NAS
This commit is contained in:
+548
-170
@@ -17,8 +17,9 @@
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background-attachment: fixed;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -26,27 +27,53 @@
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 25px 80px rgba(0,0,0,0.2), 0 0 0 1px rgba(255,255,255,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
padding: 40px 30px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
|
||||
animation: headerGlow 8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes headerGlow {
|
||||
0%, 100% { transform: translate(0, 0); }
|
||||
50% { transform: translate(-20%, -20%); }
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
font-size: 2.8em;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-shadow: 0 2px 20px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.header p {
|
||||
opacity: 0.9;
|
||||
font-size: 1.1em;
|
||||
opacity: 0.95;
|
||||
font-size: 1.15em;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -58,8 +85,9 @@
|
||||
|
||||
#predictMap {
|
||||
height: 500px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.map-container {
|
||||
@@ -67,59 +95,95 @@
|
||||
}
|
||||
|
||||
.map-instructions {
|
||||
background: #e3f2fd;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 15px;
|
||||
border-left: 4px solid #2196f3;
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #f0f7ff 100%);
|
||||
padding: 20px;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 5px solid #2196f3;
|
||||
box-shadow: 0 4px 15px rgba(33, 150, 243, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.map-instructions:hover {
|
||||
box-shadow: 0 6px 25px rgba(33, 150, 243, 0.15);
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
.map-instructions h3 {
|
||||
color: #1976d2;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.map-instructions p {
|
||||
color: #555;
|
||||
margin: 5px 0;
|
||||
margin: 6px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
padding: 28px;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(0,0,0,0.06);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.04);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.section:hover {
|
||||
box-shadow: 0 8px 30px rgba(102, 126, 234, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.section h2 {
|
||||
color: #667eea;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5em;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.6em;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
color: #374151;
|
||||
font-weight: 600;
|
||||
font-size: 0.95em;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
transition: border-color 0.3s;
|
||||
transition: all 0.3s ease;
|
||||
background: white;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.form-group input:hover, .form-group select:hover {
|
||||
border-color: #d1d5db;
|
||||
}
|
||||
|
||||
.form-group input:focus, .form-group select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
@@ -129,43 +193,78 @@
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 30px;
|
||||
padding: 14px 32px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 12px;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.3);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
}
|
||||
|
||||
.btn:hover::before {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #28a745;
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background: #218838;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.5);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #6c757d;
|
||||
background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(107, 114, 128, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(107, 114, 128, 0.5);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.status-box {
|
||||
@@ -192,42 +291,68 @@
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 30px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 15px;
|
||||
height: 32px;
|
||||
background: linear-gradient(to right, #e5e7eb, #f3f4f6);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
margin: 10px 0;
|
||||
margin: 12px 0;
|
||||
box-shadow: inset 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #667eea 100%);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 2s infinite;
|
||||
width: 0%;
|
||||
transition: width 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
font-size: 0.9em;
|
||||
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);
|
||||
padding: 24px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.06);
|
||||
text-align: center;
|
||||
border: 1px solid rgba(0,0,0,0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.metric-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 30px rgba(102, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
.metric-card h4 {
|
||||
color: #667eea;
|
||||
margin-bottom: 10px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 700;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.metric-card .value {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-size: 2.2em;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.alert {
|
||||
@@ -260,18 +385,22 @@
|
||||
}
|
||||
|
||||
.prediction-item {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 4px solid #667eea;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
|
||||
padding: 20px;
|
||||
border-radius: 14px;
|
||||
margin-bottom: 12px;
|
||||
border-left: 5px solid #667eea;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.prediction-item:hover {
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
|
||||
transform: translateX(5px);
|
||||
border-left-width: 7px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -292,22 +421,22 @@
|
||||
<p>Phân loại đất cho khu vực mới sử dụng model đã train</p>
|
||||
</div>
|
||||
|
||||
<div style="background: white; padding: 15px; display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; border-bottom: 2px solid #e0e0e0;">
|
||||
<a href="/" style="padding: 10px 20px; background: #667eea; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🏠 Trang Chủ</a>
|
||||
<a href="/training" style="padding: 10px 20px; background: #f093fb; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🎓 Training</a>
|
||||
<a href="/cloud-training" style="padding: 10px 20px; background: #00bcd4; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🌥️ Cloud Removal</a>
|
||||
<a href="/prediction" style="padding: 10px 20px; background: #4facfe; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🗺️ Prediction (Active)</a>
|
||||
<a href="/batch" style="padding: 10px 20px; background: #764ba2; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🚀 Batch Processing</a>
|
||||
<a href="/ndvi" style="padding: 10px 20px; background: #2ecc71; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🌿 NDVI Analysis</a>
|
||||
<a href="/reports" style="padding: 10px 20px; background: #ff6b6b; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">📝 Reports</a>
|
||||
<div style="background: rgba(255,255,255,0.8); backdrop-filter: blur(10px); padding: 18px; display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; border-bottom: 1px solid rgba(0,0,0,0.08); box-shadow: 0 2px 10px rgba(0,0,0,0.03);">
|
||||
<a href="/" style="padding: 12px 24px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(102, 126, 234, 0.3)'" onmouseout="this.style.transform=''; this.style.boxShadow='0 4px 12px rgba(102, 126, 234, 0.2)'">🏠 Trang Chủ</a>
|
||||
<a href="/training" style="padding: 12px 24px; background: linear-gradient(135deg, #f093fb, #f5576c); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 4px 12px rgba(240, 147, 251, 0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(240, 147, 251, 0.3)'" onmouseout="this.style.transform=''; this.style.boxShadow='0 4px 12px rgba(240, 147, 251, 0.2)'">🎓 Training</a>
|
||||
<a href="/cloud-training" style="padding: 12px 24px; background: linear-gradient(135deg, #00bcd4, #0097a7); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 4px 12px rgba(0, 188, 212, 0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(0, 188, 212, 0.3)'" onmouseout="this.style.transform=''; this.style.boxShadow='0 4px 12px rgba(0, 188, 212, 0.2)'">🌥️ Cloud Removal</a>
|
||||
<a href="/prediction" style="padding: 12px 24px; background: linear-gradient(135deg, #4facfe, #00f2fe); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 6px 20px rgba(79, 172, 254, 0.4); transform: translateY(-2px);">🗺️ Prediction (Active)</a>
|
||||
<a href="/batch" style="padding: 12px 24px; background: linear-gradient(135deg, #764ba2, #667eea); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 4px 12px rgba(118, 75, 162, 0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(118, 75, 162, 0.3)'" onmouseout="this.style.transform=''; this.style.boxShadow='0 4px 12px rgba(118, 75, 162, 0.2)'">🚀 Batch Processing</a>
|
||||
<a href="/ndvi" style="padding: 12px 24px; background: linear-gradient(135deg, #2ecc71, #27ae60); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 4px 12px rgba(46, 204, 113, 0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(46, 204, 113, 0.3)'" onmouseout="this.style.transform=''; this.style.boxShadow='0 4px 12px rgba(46, 204, 113, 0.2)'">🌿 NDVI Analysis</a>
|
||||
<a href="/reports" style="padding: 12px 24px; background: linear-gradient(135deg, #ff6b6b, #ee5a6f); color: white; border-radius: 12px; text-decoration: none; font-weight: 600; transition: all 0.3s; box-shadow: 0 4px 12px rgba(255, 107, 107, 0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(255, 107, 107, 0.3)'" onmouseout="this.style.transform=''; this.style.boxShadow='0 4px 12px rgba(255, 107, 107, 0.2)'">📝 Reports</a>
|
||||
</div>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div style="background: white; padding: 15px; border-bottom: 2px solid #e0e0e0; display: flex; gap: 10px; justify-content: center;">
|
||||
<button onclick="switchPredictTab('prediction')" id="tabPrediction" style="padding: 10px 20px; background: #4facfe; color: white; border: none; border-radius: 8px; font-weight: 600; cursor: pointer;">
|
||||
<div style="background: linear-gradient(to bottom, rgba(255,255,255,0.9), rgba(248,250,252,0.9)); backdrop-filter: blur(10px); padding: 20px; border-bottom: 1px solid rgba(0,0,0,0.08); display: flex; gap: 12px; justify-content: center;">
|
||||
<button onclick="switchPredictTab('prediction')" id="tabPrediction" style="padding: 14px 32px; background: linear-gradient(135deg, #4facfe, #00f2fe); color: white; border: none; border-radius: 12px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3); font-size: 1.05em;">
|
||||
🗺️ Prediction
|
||||
</button>
|
||||
<button onclick="switchPredictTab('ndvi')" id="tabNDVI" style="padding: 10px 20px; background: #ccc; color: #666; border: none; border-radius: 8px; font-weight: 600; cursor: pointer;">
|
||||
<button onclick="switchPredictTab('ndvi')" id="tabNDVI" style="padding: 14px 32px; background: linear-gradient(135deg, #e5e7eb, #d1d5db); color: #6b7280; border: none; border-radius: 12px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(0,0,0,0.08); font-size: 1.05em;">
|
||||
🌿 NDVI Analysis
|
||||
</button>
|
||||
</div>
|
||||
@@ -375,9 +504,82 @@
|
||||
<h2>🤖 Chọn Model</h2>
|
||||
<div class="form-group">
|
||||
<label for="modelSelect">Model đã train:</label>
|
||||
<select id="modelSelect">
|
||||
<option value="">Đang tải...</option>
|
||||
</select>
|
||||
<div style="display: flex; gap: 10px; align-items: center; margin-bottom: 10px;">
|
||||
<select id="modelSelect" style="flex: 1;">
|
||||
<option value="">Đang tải...</option>
|
||||
</select>
|
||||
<button onclick="loadModels(); return false;" style="padding: 10px 15px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; white-space: nowrap;">
|
||||
🔄 Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Upload Land Classification Model -->
|
||||
<div style="margin-top: 15px; padding: 10px; background: #f5f5f5; border-radius: 6px;">
|
||||
<input type="file" id="landModelUpload" accept=".pth,.pkl,.joblib,.h5,.keras" style="display: none;" onchange="showLandMetadataForm()">
|
||||
<button onclick="document.getElementById('landModelUpload').click()" style="padding: 8px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px;">
|
||||
📤 Upload Land Classification Model
|
||||
</button>
|
||||
<span id="uploadLandStatus" style="margin-left: 10px; font-size: 12px; color: #666;"></span>
|
||||
|
||||
<!-- Land Model Metadata Form -->
|
||||
<div id="landMetadataForm" style="display: none; margin-top: 15px; padding: 15px; background: white; border: 2px solid #4CAF50; border-radius: 6px;">
|
||||
<h4 style="margin: 0 0 10px 0; color: #2e7d32;">📝 Model Metadata</h4>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px;">
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Model Type:</label>
|
||||
<select id="uploadModelType" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="mobilenet">MobileNet</option>
|
||||
<option value="cnn">CNN</option>
|
||||
<option value="swin">Swin Transformer</option>
|
||||
<option value="xgboost">XGBoost</option>
|
||||
<option value="random_forest">Random Forest</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Epoch:</label>
|
||||
<input type="number" id="uploadLandEpoch" min="0" placeholder="e.g., 100" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Train Accuracy (%):</label>
|
||||
<input type="number" id="uploadTrainAcc" step="0.01" min="0" max="100" placeholder="e.g., 95.5" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Val Accuracy (%):</label>
|
||||
<input type="number" id="uploadValAcc" step="0.01" min="0" max="100" placeholder="e.g., 93.2" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Train Loss:</label>
|
||||
<input type="number" id="uploadLandTrainLoss" step="0.0001" min="0" placeholder="e.g., 0.12" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Val Loss:</label>
|
||||
<input type="number" id="uploadLandValLoss" step="0.0001" min="0" placeholder="e.g., 0.18" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Number of Classes:</label>
|
||||
<input type="number" id="uploadNumClasses" min="2" placeholder="e.g., 10" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Input Size:</label>
|
||||
<input type="number" id="uploadInputSize" min="16" placeholder="e.g., 64" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Description (optional):</label>
|
||||
<input type="text" id="uploadLandDescription" placeholder="e.g., Trained on Mekong Delta dataset" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<button onclick="uploadLandModelWithMetadata()" style="flex: 1; padding: 8px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: 600;">
|
||||
✅ Upload with Metadata
|
||||
</button>
|
||||
<button onclick="cancelLandUpload()" style="padding: 8px 15px; background: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
||||
❌ Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Cache selection dropdown -->
|
||||
<div class="form-group" style="margin-top:15px;">
|
||||
@@ -398,149 +600,184 @@
|
||||
<div class="section">
|
||||
<h2>⏰ Thời gian & Dữ liệu</h2>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="predStartDate">Từ ngày:</label>
|
||||
<input type="date" id="predStartDate" value="2023-03-01">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="predEndDate">Đến ngày:</label>
|
||||
<input type="date" id="predEndDate" value="2023-05-31">
|
||||
<!-- Date Range -->
|
||||
<div style="background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); padding: 20px; border-radius: 12px; margin-bottom: 20px; border: 1px solid #bae6fd;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #0369a1; font-size: 1.1em; font-weight: 600; display: flex; align-items: center; gap: 8px;">
|
||||
📅 Khoảng thời gian
|
||||
</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<label for="predStartDate">Từ ngày:</label>
|
||||
<input type="date" id="predStartDate" value="2023-03-01">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<label for="predEndDate">Đến ngày:</label>
|
||||
<input type="date" id="predEndDate" value="2023-05-31">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="predMaxScenes">Max Scenes:</label>
|
||||
<input type="number" id="predMaxScenes" value="12" min="1" max="100">
|
||||
<!-- Data Parameters -->
|
||||
<div style="background: linear-gradient(135deg, #fef3f2 0%, #fee2e2 100%); padding: 20px; border-radius: 12px; margin-bottom: 20px; border: 1px solid #fecaca;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #b91c1c; font-size: 1.1em; font-weight: 600; display: flex; align-items: center; gap: 8px;">
|
||||
🛰️ Tham số dữ liệu vệ tinh
|
||||
</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<label for="predMaxScenes">
|
||||
Số lượng ảnh tối đa:
|
||||
<span style="font-size: 0.85em; color: #666; font-weight: 400;">(1-100)</span>
|
||||
</label>
|
||||
<input type="number" id="predMaxScenes" value="12" min="1" max="100">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<label for="predCloudCover">
|
||||
Độ phủ mây tối đa:
|
||||
<span style="font-size: 0.85em; color: #666; font-weight: 400;">(0-100%)</span>
|
||||
</label>
|
||||
<input type="number" id="predCloudCover" value="30" min="0" max="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="predCloudCover">Cloud Cover (%):</label>
|
||||
<input type="number" id="predCloudCover" value="30" min="0" max="100">
|
||||
<div class="form-group" style="margin: 15px 0 0 0;">
|
||||
<label for="predResolution">Độ phân giải không gian:</label>
|
||||
<select id="predResolution">
|
||||
<option value="10">10m (Chi tiết cao - Chậm hơn)</option>
|
||||
<option value="20" selected>20m (Cân bằng - Khuyến nghị)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="predResolution">Resolution:</label>
|
||||
<select id="predResolution">
|
||||
<option value="10">10m (Chi tiết cao - Chậm)</option>
|
||||
<option value="20" selected>20m (Cân bằng)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Cloud Removal Configuration -->
|
||||
<div class="form-group" style="margin-top: 15px; padding: 15px; background: #e3f2fd; border-radius: 8px; border-left: 4px solid #2196F3;">
|
||||
<label for="cloudRemovalMethod" style="font-weight: 600; color: #1976d2; margin-bottom: 10px; display: block;">
|
||||
🌥️ Cloud Removal Method
|
||||
</label>
|
||||
<select id="cloudRemovalMethod" onchange="handleCloudMethodChange()" style="padding: 10px; width: 100%; border: 2px solid #2196F3; border-radius: 6px; font-size: 14px; cursor: pointer; margin-bottom: 10px;">
|
||||
<option value="none">🚫 No Cloud Removal - Keep Original Data</option>
|
||||
<option value="classic">Classic (3-step: temporal + median + spatial)</option>
|
||||
<option value="hybrid" selected>Hybrid (Classical + ML KNN) - Recommended</option>
|
||||
<option value="temporal_only">Temporal Only (Fast)</option>
|
||||
<option value="median_composite">Median Composite</option>
|
||||
<option value="ml_knn">ML KNN (K-Nearest Neighbors)</option>
|
||||
<option value="ml_rf">ML Random Forest</option>
|
||||
<option value="deep">Deep Learning (U-Net)</option>
|
||||
</select>
|
||||
<div style="background: linear-gradient(135deg, #f0fdfa 0%, #ccfbf1 100%); padding: 20px; border-radius: 12px; margin-bottom: 20px; border: 1px solid #99f6e4;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #0f766e; font-size: 1.1em; font-weight: 600; display: flex; align-items: center; gap: 8px;">
|
||||
🌥️ Loại bỏ mây
|
||||
</h3>
|
||||
<div class="form-group" style="margin-bottom: 12px;">
|
||||
<label for="cloudRemovalMethod">Phương pháp:</label>
|
||||
<select id="cloudRemovalMethod" onchange="handleCloudMethodChange()">
|
||||
<option value="none">🚫 Không xử lý - Giữ nguyên dữ liệu gốc</option>
|
||||
<option value="classic">Classic - 3 bước (Temporal + Median + Spatial)</option>
|
||||
<option value="hybrid" selected>Hybrid (Classical + ML KNN) - Khuyến nghị</option>
|
||||
<option value="temporal_only">Temporal Only - Nhanh nhất</option>
|
||||
<option value="median_composite">Median Composite</option>
|
||||
<option value="ml_knn">ML K-Nearest Neighbors</option>
|
||||
<option value="ml_rf">ML Random Forest</option>
|
||||
<option value="deep">Deep Learning U-Net - Tốt nhất</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Deep Learning Model Selection (only shown when method is 'deep') -->
|
||||
<div id="cloudModelSelection" style="display: none; margin-top: 10px;">
|
||||
<label for="cloudModelSelect" style="font-weight: 500; color: #1565c0; margin-bottom: 5px; display: block;">
|
||||
📦 Select Trained Model:
|
||||
<!-- Deep Learning Model Selection -->
|
||||
<div id="cloudModelSelection" style="display: none; margin-top: 15px; padding: 15px; background: rgba(255,255,255,0.7); border-radius: 8px;">
|
||||
<label for="cloudModelSelect" style="font-weight: 600; color: #0f766e; margin-bottom: 8px; display: block;">
|
||||
📦 Chọn model đã train:
|
||||
</label>
|
||||
<div style="display: flex; gap: 10px; align-items: center; margin-bottom: 10px;">
|
||||
<select id="cloudModelSelect" style="flex: 1; padding: 10px; border: 2px solid #64b5f6; border-radius: 6px; font-size: 14px; cursor: pointer;">
|
||||
<option value="">Loading models...</option>
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 12px;">
|
||||
<select id="cloudModelSelect" style="flex: 1;">
|
||||
<option value="">Đang tải models...</option>
|
||||
</select>
|
||||
<button onclick="loadCloudRemovalModels(); return false;" style="padding: 10px 15px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; white-space: nowrap;">
|
||||
🔄 Refresh
|
||||
<button onclick="loadCloudRemovalModels(); return false;" class="btn btn-secondary" style="padding: 10px 16px; margin: 0; font-size: 0.9em;">
|
||||
🔄
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Upload Model Button -->
|
||||
<div style="margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 6px;">
|
||||
<!-- Upload Model -->
|
||||
<div style="padding: 12px; background: rgba(76, 175, 80, 0.1); border-radius: 6px; border: 1px dashed #4CAF50;">
|
||||
<input type="file" id="cloudModelUpload" accept=".pth" style="display: none;" onchange="showMetadataForm()">
|
||||
<button onclick="document.getElementById('cloudModelUpload').click()" style="padding: 8px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px;">
|
||||
📤 Upload Cloud Removal Model (.pth)
|
||||
<button onclick="document.getElementById('cloudModelUpload').click()" class="btn btn-success" style="padding: 10px 16px; margin: 0; font-size: 0.9em;">
|
||||
📤 Upload Model (.pth)
|
||||
</button>
|
||||
<span id="uploadCloudStatus" style="margin-left: 10px; font-size: 12px; color: #666;"></span>
|
||||
<span id="uploadCloudStatus" style="margin-left: 10px; font-size: 0.85em;"></span>
|
||||
|
||||
<!-- Metadata Form (shown after file selection) -->
|
||||
<div id="cloudMetadataForm" style="display: none; margin-top: 15px; padding: 15px; background: white; border: 2px solid #4CAF50; border-radius: 6px;">
|
||||
<h4 style="margin: 0 0 10px 0; color: #2e7d32;">📝 Model Metadata</h4>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px;">
|
||||
<!-- Metadata Form -->
|
||||
<div id="cloudMetadataForm" style="display: none; margin-top: 12px; padding: 12px; background: white; border-radius: 6px; border: 1px solid #4CAF50;">
|
||||
<h4 style="margin: 0 0 10px 0; color: #2e7d32; font-size: 0.95em;">📝 Thông tin Model</h4>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px;">
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Epoch:</label>
|
||||
<input type="number" id="uploadEpoch" min="0" placeholder="e.g., 50" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Epoch:</label>
|
||||
<input type="number" id="uploadEpoch" min="0" placeholder="50" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Validation Loss:</label>
|
||||
<input type="number" id="uploadValLoss" step="0.0001" min="0" placeholder="e.g., 0.0134" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Val Loss:</label>
|
||||
<input type="number" id="uploadValLoss" step="0.0001" min="0" placeholder="0.0134" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Train Loss:</label>
|
||||
<input type="number" id="uploadTrainLoss" step="0.0001" min="0" placeholder="e.g., 0.0142" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Train Loss:</label>
|
||||
<input type="number" id="uploadTrainLoss" step="0.0001" min="0" placeholder="0.0142" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Input Channels:</label>
|
||||
<input type="number" id="uploadInChannels" min="1" placeholder="e.g., 6" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Channels In:</label>
|
||||
<input type="number" id="uploadInChannels" min="1" placeholder="6" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Output Channels:</label>
|
||||
<input type="number" id="uploadOutChannels" min="1" placeholder="e.g., 4" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Channels Out:</label>
|
||||
<input type="number" id="uploadOutChannels" min="1" placeholder="4" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Use Sentinel-1:</label>
|
||||
<select id="uploadUseS1" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Use S1:</label>
|
||||
<select id="uploadUseS1" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<label style="font-size: 12px; color: #666; display: block; margin-bottom: 3px;">Description (optional):</label>
|
||||
<input type="text" id="uploadDescription" placeholder="e.g., Trained on winter dataset, 50 epochs" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<div style="margin-bottom: 8px;">
|
||||
<label style="font-size: 0.8em; color: #666; display: block; margin-bottom: 3px;">Description:</label>
|
||||
<input type="text" id="uploadDescription" placeholder="Optional notes" style="width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.9em;">
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<button onclick="uploadCloudModelWithMetadata()" style="flex: 1; padding: 8px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: 600;">
|
||||
✅ Upload with Metadata
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button onclick="uploadCloudModelWithMetadata()" class="btn btn-success" style="flex: 1; padding: 8px; margin: 0; font-size: 0.85em;">
|
||||
✅ Upload
|
||||
</button>
|
||||
<button onclick="cancelUpload()" style="padding: 8px 15px; background: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
||||
❌ Cancel
|
||||
<button onclick="cancelUpload()" class="btn btn-secondary" style="padding: 8px 12px; margin: 0; font-size: 0.85em;">
|
||||
❌
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 12px; color: #1976d2; margin-top: 8px;">
|
||||
💡 Hybrid method balances speed and quality. Deep learning provides best results but requires trained model.
|
||||
<div style="font-size: 0.85em; color: #0f766e; margin-top: 10px; padding: 8px; background: rgba(255,255,255,0.5); border-radius: 6px;">
|
||||
💡 <strong>Hybrid</strong> cân bằng tốc độ và chất lượng. <strong>Deep Learning</strong> cho kết quả tốt nhất nhưng cần model đã train.
|
||||
</div>
|
||||
</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)
|
||||
<!-- Processing Options -->
|
||||
<div style="background: linear-gradient(135deg, #fef9f3 0%, #fed7aa 100%); padding: 20px; border-radius: 12px; margin-bottom: 20px; border: 1px solid #fdba74;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #c2410c; font-size: 1.1em; font-weight: 600; display: flex; align-items: center; gap: 8px;">
|
||||
⚙️ Tùy chọn xử lý
|
||||
</h3>
|
||||
|
||||
<div class="form-group" style="margin-bottom: 15px;">
|
||||
<label style="display: flex; align-items: flex-start; cursor: pointer; margin: 0;">
|
||||
<input type="checkbox" id="useGpuPred" checked style="width: 20px; height: 20px; margin-right: 12px; margin-top: 2px; cursor: pointer;">
|
||||
<div>
|
||||
<div style="font-weight: 600; color: #c2410c; margin-bottom: 4px;">
|
||||
🚀 Sử dụng GPU cho Deep Learning Models
|
||||
</div>
|
||||
<div style="font-size: 0.85em; color: #9a3412;">
|
||||
Tăng tốc CNN/Swin-UNet (cần GPU có CUDA)
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<label style="display: flex; align-items: flex-start; cursor: pointer; margin: 0;">
|
||||
<input type="checkbox" id="exportNDVI" checked style="width: 20px; height: 20px; margin-right: 12px; margin-top: 2px; cursor: pointer;">
|
||||
<div>
|
||||
<div style="font-weight: 600; color: #c2410c; margin-bottom: 4px;">
|
||||
🌿 Xuất NDVI Raster
|
||||
</div>
|
||||
<div style="font-size: 0.85em; color: #9a3412;">
|
||||
Tạo file GeoTIFF chứa chỉ số NDVI cho toàn bộ khu vực
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</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;">
|
||||
<span style="font-weight: 600; color: #1976d2;">🌿 Export NDVI Raster</span>
|
||||
</label>
|
||||
<div style="font-size: 12px; color: #1976d2; margin-top: 8px; margin-left: 28px;">
|
||||
✅ Xuất ra file GeoTIFF chứa giá trị NDVI cho toàn bộ khu vực
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="startPrediction()" id="predictBtn" style="margin-top: 15px;">
|
||||
🚀 Start Prediction (với NDVI)
|
||||
<button class="btn btn-primary" onclick="startPrediction()" id="predictBtn" style="margin-top: 5px; width: 100%; font-size: 1.1em; padding: 16px;">
|
||||
🚀 Bắt đầu phân loại (với NDVI)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1203,6 +1440,147 @@
|
||||
statusSpan.textContent = '';
|
||||
}
|
||||
|
||||
// ===== LAND CLASSIFICATION MODEL UPLOAD =====
|
||||
|
||||
// Show land model metadata form
|
||||
function showLandMetadataForm() {
|
||||
const fileInput = document.getElementById('landModelUpload');
|
||||
const file = fileInput.files[0];
|
||||
const statusSpan = document.getElementById('uploadLandStatus');
|
||||
const metadataForm = document.getElementById('landMetadataForm');
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show form
|
||||
metadataForm.style.display = 'block';
|
||||
statusSpan.textContent = `📁 Selected: ${file.name} (${(file.size / (1024 * 1024)).toFixed(2)} MB)`;
|
||||
statusSpan.style.color = '#2196F3';
|
||||
}
|
||||
|
||||
// Upload land model with metadata
|
||||
async function uploadLandModelWithMetadata() {
|
||||
const fileInput = document.getElementById('landModelUpload');
|
||||
const file = fileInput.files[0];
|
||||
const statusSpan = document.getElementById('uploadLandStatus');
|
||||
const metadataForm = document.getElementById('landMetadataForm');
|
||||
|
||||
if (!file) {
|
||||
alert('No file selected');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get values from form with defaults
|
||||
const modelType = document.getElementById('uploadModelType').value || 'mobilenet';
|
||||
const epoch = document.getElementById('uploadLandEpoch').value || '0';
|
||||
const trainAcc = document.getElementById('uploadTrainAcc').value || '0';
|
||||
const valAcc = document.getElementById('uploadValAcc').value || '0';
|
||||
const trainLoss = document.getElementById('uploadLandTrainLoss').value || '0';
|
||||
const valLoss = document.getElementById('uploadLandValLoss').value || '0';
|
||||
const numClasses = document.getElementById('uploadNumClasses').value || '10';
|
||||
const inputSize = document.getElementById('uploadInputSize').value || '64';
|
||||
const description = document.getElementById('uploadLandDescription').value || '';
|
||||
|
||||
// Debug log
|
||||
console.log('[Land Upload] Form values:', {
|
||||
modelType, epoch, trainAcc, valAcc, trainLoss, valLoss, numClasses, inputSize, description
|
||||
});
|
||||
|
||||
// Confirm upload
|
||||
if (!confirm(`Upload ${file.name}?\nType: ${modelType}\nEpoch: ${epoch}\nVal Acc: ${valAcc}%`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
statusSpan.textContent = '⏳ Uploading with metadata...';
|
||||
statusSpan.style.color = '#2196F3';
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('model_type', modelType);
|
||||
formData.append('epoch', epoch);
|
||||
formData.append('train_accuracy', trainAcc);
|
||||
formData.append('val_accuracy', valAcc);
|
||||
formData.append('train_loss', trainLoss);
|
||||
formData.append('val_loss', valLoss);
|
||||
formData.append('num_classes', numClasses);
|
||||
formData.append('input_size', inputSize);
|
||||
formData.append('description', description);
|
||||
|
||||
console.log('[Land Upload] Sending FormData...');
|
||||
|
||||
const response = await fetch('/api/land-classification/upload', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
console.log('[Land Upload] Response:', result);
|
||||
|
||||
if (response.ok) {
|
||||
statusSpan.textContent = `✅ Uploaded: ${result.filename} (${result.size_mb} MB)`;
|
||||
statusSpan.style.color = '#4CAF50';
|
||||
|
||||
// Hide form
|
||||
metadataForm.style.display = 'none';
|
||||
|
||||
// Clear form
|
||||
document.getElementById('uploadModelType').value = 'mobilenet';
|
||||
document.getElementById('uploadLandEpoch').value = '';
|
||||
document.getElementById('uploadTrainAcc').value = '';
|
||||
document.getElementById('uploadValAcc').value = '';
|
||||
document.getElementById('uploadLandTrainLoss').value = '';
|
||||
document.getElementById('uploadLandValLoss').value = '';
|
||||
document.getElementById('uploadNumClasses').value = '';
|
||||
document.getElementById('uploadInputSize').value = '';
|
||||
document.getElementById('uploadLandDescription').value = '';
|
||||
|
||||
// Reload models list
|
||||
await loadModels();
|
||||
|
||||
// Try to auto-select the newly uploaded model
|
||||
setTimeout(() => {
|
||||
const modelSelect = document.getElementById('modelSelect');
|
||||
for (let i = 0; i < modelSelect.options.length; i++) {
|
||||
if (modelSelect.options[i].text.includes(result.filename)) {
|
||||
modelSelect.selectedIndex = i;
|
||||
updateModelInfo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
|
||||
console.log('[Land Upload] Success:', result);
|
||||
} else {
|
||||
statusSpan.textContent = `❌ ${result.detail || 'Upload failed'}`;
|
||||
statusSpan.style.color = 'red';
|
||||
}
|
||||
} catch (error) {
|
||||
statusSpan.textContent = `❌ Error: ${error.message}`;
|
||||
statusSpan.style.color = 'red';
|
||||
console.error('[Land Upload] Error:', error);
|
||||
}
|
||||
|
||||
// Clear file input
|
||||
fileInput.value = '';
|
||||
}
|
||||
|
||||
// Cancel land model upload
|
||||
function cancelLandUpload() {
|
||||
const fileInput = document.getElementById('landModelUpload');
|
||||
const statusSpan = document.getElementById('uploadLandStatus');
|
||||
const metadataForm = document.getElementById('landMetadataForm');
|
||||
|
||||
// Clear and hide
|
||||
fileInput.value = '';
|
||||
metadataForm.style.display = 'none';
|
||||
statusSpan.textContent = '';
|
||||
}
|
||||
|
||||
// ===== END LAND CLASSIFICATION UPLOAD =====
|
||||
|
||||
// Update model info display
|
||||
function updateModelInfo() {
|
||||
const select = document.getElementById('modelSelect');
|
||||
|
||||
Reference in New Issue
Block a user