alsmoels / index.html
wasmdashai's picture
Update index.html
f47b0f9 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Maritime AI Route Generation & Optimization Platform</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #1e3a8a;
--secondary: #3b82f6;
--accent: #60a5fa;
--success: #059669;
--warning: #d97706;
--danger: #dc2626;
--dark: #0f172a;
--light: #f8fafc;
--background: #ffffff;
--text: #1e293b;
--border: #e2e8f0;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: var(--background);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
}
.container {
max-width: 2000px;
margin: 0 auto;
padding: 20px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
background: var(--background);
border-radius: 16px;
margin-bottom: 25px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid var(--border);
}
.logo {
display: flex;
align-items: center;
gap: 15px;
}
.logo i {
font-size: 2.5rem;
color: var(--primary);
}
.logo-text h1 {
font-size: 2.2rem;
margin-bottom: 5px;
color: var(--primary);
}
.logo-text p {
font-size: 1rem;
color: var(--dark);
opacity: 0.8;
}
.app-container {
display: grid;
grid-template-columns: 1fr 3fr;
gap: 25px;
height: calc(100vh - 180px);
}
.control-panel {
background: var(--background);
border-radius: 16px;
padding: 25px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid var(--border);
overflow-y: auto;
display: flex;
flex-direction: column;
}
.map-container {
background: var(--background);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid var(--border);
position: relative;
display: flex;
flex-direction: column;
}
#map {
height: 100%;
width: 100%;
background: #e8f4f8;
}
.panel-section {
margin-bottom: 30px;
}
.section-title {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid var(--primary);
font-size: 1.3rem;
color: var(--primary);
}
.section-title i {
color: var(--primary);
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: var(--dark);
}
input, select, button, textarea {
width: 100%;
padding: 14px;
border: 1px solid var(--border);
border-radius: 10px;
font-size: 16px;
background: var(--background);
color: var(--text);
}
input::placeholder {
color: var(--dark);
opacity: 0.6;
}
button {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
border: none;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(37, 99, 235, 0.3);
}
.btn-secondary {
background: linear-gradient(135deg, var(--warning) 0%, #e58e0c 100%);
}
.btn-danger {
background: linear-gradient(135deg, var(--danger) 0%, #dc2626 100%);
}
.btn-success {
background: linear-gradient(135deg, var(--success) 0%, #047857 100%);
}
.model-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 15px;
}
.model-card {
background: var(--light);
border-radius: 12px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid var(--border);
text-align: center;
}
.model-card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
.model-card.active {
border-color: var(--primary);
background: linear-gradient(135deg, var(--light) 0%, #e0f2fe 100%);
}
.model-name {
font-weight: bold;
margin-bottom: 5px;
color: var(--primary);
}
.model-desc {
font-size: 0.8rem;
color: var(--dark);
opacity: 0.8;
}
.coordinates-input {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.input-group {
display: flex;
flex-direction: column;
}
.input-group label {
font-size: 0.9rem;
margin-bottom: 5px;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 20px;
}
.stat-card {
background: var(--light);
border-radius: 12px;
padding: 15px;
text-align: center;
border: 1px solid var(--border);
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
.stat-value {
font-size: 1.8rem;
font-weight: bold;
margin: 10px 0;
color: var(--primary);
}
.stat-label {
font-size: 0.9rem;
color: var(--dark);
opacity: 0.8;
}
.route-item {
background: var(--light);
border-radius: 12px;
padding: 15px;
margin-bottom: 15px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 4px solid var(--primary);
border: 1px solid var(--border);
}
.route-item:hover {
background: var(--background);
transform: translateX(-5px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.route-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.route-name {
font-weight: bold;
font-size: 1.1rem;
color: var(--dark);
}
.route-type {
background: var(--primary);
color: white;
padding: 4px 10px;
border-radius: 20px;
font-size: 0.8rem;
}
.route-details {
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: var(--dark);
opacity: 0.8;
}
.map-overlay {
position: absolute;
top: 20px;
left: 20px;
right: 20px;
z-index: 1000;
display: flex;
gap: 15px;
}
.search-box {
flex: 1;
background: var(--background);
border-radius: 12px;
padding: 12px 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 10px;
border: 1px solid var(--border);
}
.search-box input {
background: transparent;
border: none;
flex: 1;
padding: 0;
}
.map-controls {
display: flex;
gap: 10px;
}
.control-btn {
width: 50px;
height: 50px;
border-radius: 12px;
background: var(--background);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
border: 1px solid var(--border);
color: var(--primary);
}
.control-btn:hover {
background: var(--primary);
color: white;
transform: translateY(-3px);
}
.legend {
position: absolute;
bottom: 20px;
left: 20px;
background: var(--background);
border-radius: 12px;
padding: 15px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
z-index: 1000;
border: 1px solid var(--border);
}
.legend-title {
font-weight: bold;
margin-bottom: 10px;
text-align: center;
color: var(--primary);
}
.legend-items {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9rem;
}
.legend-color {
width: 15px;
height: 15px;
border-radius: 50%;
}
.ship-marker {
background: var(--primary);
border: 3px solid white;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.route-popup {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
min-width: 250px;
}
.popup-title {
font-weight: bold;
margin-bottom: 10px;
color: var(--primary);
border-bottom: 1px solid var(--border);
padding-bottom: 5px;
}
.popup-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 10px;
}
.popup-detail {
font-size: 0.9rem;
}
.popup-label {
font-weight: bold;
color: var(--primary);
}
/* Scrollbar improvements */
.control-panel::-webkit-scrollbar {
width: 8px;
}
.control-panel::-webkit-scrollbar-track {
background: var(--light);
border-radius: 10px;
}
.control-panel::-webkit-scrollbar-thumb {
background: var(--primary);
border-radius: 10px;
}
/* Responsive design */
@media (max-width: 1200px) {
.app-container {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.control-panel {
max-height: 400px;
}
}
.anomaly-indicator {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 10px;
border-radius: 8px;
margin-top: 10px;
font-weight: bold;
}
.anomaly-low {
background: #fef3c7;
color: #d97706;
border: 1px solid #f59e0b;
}
.anomaly-medium {
background: #fed7aa;
color: #ea580c;
border: 1px solid #f97316;
}
.anomaly-high {
background: #fecaca;
color: #dc2626;
border: 1px solid #ef4444;
}
.context-input {
height: 100px;
resize: vertical;
}
.generation-results {
margin-top: 20px;
padding: 15px;
background: var(--light);
border-radius: 12px;
border: 1px solid var(--border);
}
.performance-metrics {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 15px;
}
.metric {
text-align: center;
padding: 10px;
background: var(--background);
border-radius: 8px;
border: 1px solid var(--border);
}
.metric-value {
font-weight: bold;
font-size: 1.2rem;
color: var(--primary);
}
.metric-label {
font-size: 0.8rem;
color: var(--dark);
opacity: 0.8;
}
.file-upload {
border: 2px dashed var(--border);
border-radius: 12px;
padding: 25px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 20px;
}
.file-upload:hover {
border-color: var(--primary);
background: var(--light);
}
.file-upload i {
font-size: 3rem;
margin-bottom: 15px;
color: var(--primary);
}
.tab-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.tab {
flex: 1;
padding: 12px;
text-align: center;
background: var(--light);
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
}
.tab.active {
background: var(--primary);
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.optimization-panel {
background: var(--light);
border-radius: 12px;
padding: 20px;
margin-top: 20px;
border: 1px solid var(--border);
}
.model-comparison {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 15px;
margin-top: 15px;
}
.comparison-card {
background: var(--background);
border-radius: 12px;
padding: 15px;
text-align: center;
border: 1px solid var(--border);
transition: all 0.3s ease;
cursor: pointer;
}
.comparison-card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
.comparison-card.active {
border-color: var(--success);
background: linear-gradient(135deg, var(--light) 0%, #d1fae5 100%);
}
.comparison-value {
font-size: 1.5rem;
font-weight: bold;
margin: 10px 0;
color: var(--success);
}
.comparison-label {
font-size: 0.9rem;
color: var(--dark);
opacity: 0.8;
}
.route-controls {
display: flex;
gap: 10px;
margin-top: 15px;
}
.route-control {
flex: 1;
padding: 10px;
background: var(--light);
border: 1px solid var(--border);
border-radius: 8px;
cursor: pointer;
text-align: center;
transition: all 0.3s ease;
}
.route-control:hover {
background: var(--primary);
color: white;
}
.route-control.active {
background: var(--primary);
color: white;
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">
<i class="fas fa-ship"></i>
<div class="logo-text">
<h1>Maritime AI Route Generation & Optimization</h1>
<p>Professional platform for route prediction and optimization</p>
</div>
</div>
<div class="header-controls">
<button class="btn-secondary" id="exportBtn"><i class="fas fa-download"></i> Export Report</button>
</div>
</header>
<div class="app-container">
<div class="control-panel">
<div class="tab-container">
<div class="tab active" data-tab="input">Input Data</div>
<div class="tab" data-tab="generation">Route Generation</div>
<div class="tab" data-tab="optimization">Route Optimization</div>
</div>
<div class="tab-content active" id="input-tab">
<div class="panel-section">
<div class="section-title">
<i class="fas fa-database"></i>
<h3>Data Input Methods</h3>
</div>
<div class="file-upload" id="fileUpload">
<i class="fas fa-file-excel"></i>
<p>Click or drag Excel file here</p>
<p class="file-format">Supports CSV and Excel files</p>
</div>
<input type="file" id="fileInput" accept=".csv,.xlsx,.xls" style="display: none;">
<div class="form-group">
<label><i class="fas fa-map-marker-alt"></i> Manual Point Selection</label>
<div class="coordinates-input">
<div class="input-group">
<label>Start Latitude</label>
<input type="number" id="startLat" step="0.0001" value="12.6" placeholder="12.6">
</div>
<div class="input-group">
<label>Start Longitude</label>
<input type="number" id="startLon" step="0.0001" value="43.0" placeholder="43.0">
</div>
</div>
</div>
<div class="form-group">
<label><i class="fas fa-flag-checkered"></i> End Point</label>
<div class="coordinates-input">
<div class="input-group">
<label>End Latitude</label>
<input type="number" id="endLat" step="0.0001" value="30.5" placeholder="30.5">
</div>
<div class="input-group">
<label>End Longitude</label>
<input type="number" id="endLon" step="0.0001" value="32.3" placeholder="32.3">
</div>
</div>
</div>
<div class="form-group">
<label for="contextInput"><i class="fas fa-code"></i> Context Data</label>
<textarea id="contextInput" class="context-input" placeholder="Enter route context data here...">Historical route from Bab el Mandeb to Suez Canal with AIS data</textarea>
</div>
<button id="saveContextBtn"><i class="fas fa-save"></i> Save Context</button>
</div>
</div>
<div class="tab-content" id="generation-tab">
<div class="panel-section">
<div class="section-title">
<i class="fas fa-brain"></i>
<h3>AI Model Selection</h3>
</div>
<div class="model-cards">
<div class="model-card active" data-model="TrAISformer">
<div class="model-name">TrAISformer</div>
<div class="model-desc">Transformer-based model</div>
</div>
<div class="model-card" data-model="EnhcTrAISformer">
<div class="model-name">EnhcTrAISformer</div>
<div class="model-desc">Enhanced Transformer</div>
</div>
<div class="model-card" data-model="eDQTI-GRU">
<div class="model-name">eDQTI-GRU</div>
<div class="model-desc">GRU with DQTI</div>
</div>
<div class="model-card" data-model="eDQTI-LSTM">
<div class="model-name">eDQTI-LSTM</div>
<div class="model-desc">LSTM with DQTI</div>
</div>
<div class="model-card" data-model="eDQTI-HYPER">
<div class="model-name">eDQTI-HYPER</div>
<div class="model-desc">Advanced hybrid model</div>
</div>
</div>
<div class="form-group">
<label><i class="fas fa-layer-group"></i> Display Mode</label>
<div class="route-controls">
<div class="route-control active" data-display="single">Single Model</div>
<div class="route-control" data-display="compare">Compare All</div>
</div>
</div>
<div class="form-group">
<label for="anomalyModel"><i class="fas fa-exclamation-triangle"></i> Anomaly Detection</label>
<select id="anomalyModel">
<option value="auto">Auto Detection</option>
<option value="enabled">Anomaly Detection Enabled</option>
<option value="disabled">No Anomaly Detection</option>
</select>
</div>
</div>
<div class="panel-section">
<div class="section-title">
<i class="fas fa-route"></i>
<h3>Generation Parameters</h3>
</div>
<div class="form-group">
<label for="hours"><i class="fas fa-clock"></i> Hours to Generate</label>
<input type="number" id="hours" min="1" max="168" value="24" placeholder="24">
</div>
<div class="form-group">
<label for="pointsPerHour"><i class="fas fa-map-pin"></i> Points per Hour</label>
<input type="number" id="pointsPerHour" min="1" max="10" value="2" placeholder="2">
</div>
<button id="generateBtn" class="btn-success"><i class="fas fa-play"></i> Generate Routes</button>
</div>
</div>
<div class="tab-content" id="optimization-tab">
<div class="panel-section">
<div class="section-title">
<i class="fas fa-magic"></i>
<h3>Route Optimization</h3>
</div>
<div class="optimization-panel">
<p>Optimize generated routes using advanced algorithms</p>
<div class="model-comparison">
<div class="comparison-card" data-model="eDQTI-GRU">
<div class="model-name">eDQTI-GRU</div>
<div class="comparison-value">92%</div>
<div class="comparison-label">Optimization Score</div>
</div>
<div class="comparison-card" data-model="eDQTI-LSTM">
<div class="model-name">eDQTI-LSTM</div>
<div class="comparison-value">88%</div>
<div class="comparison-label">Optimization Score</div>
</div>
<div class="comparison-card" data-model="eDQTI-HYPER">
<div class="model-name">eDQTI-HYPER</div>
<div class="comparison-value">95%</div>
<div class="comparison-label">Optimization Score</div>
</div>
</div>
<button id="optimizeBtn" class="btn-success"><i class="fas fa-bolt"></i> Optimize Selected Routes</button>
</div>
</div>
<div class="panel-section">
<div class="section-title">
<i class="fas fa-chart-bar"></i>
<h3>Optimization Results</h3>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Distance Saved</div>
<div class="stat-value" id="distanceSaved">42</div>
<div class="stat-label">km</div>
</div>
<div class="stat-card">
<div class="stat-label">Time Saved</div>
<div class="stat-value" id="timeSaved">3.5</div>
<div class="stat-label">hours</div>
</div>
<div class="stat-card">
<div class="stat-label">Fuel Efficiency</div>
<div class="stat-value" id="fuelEfficiency">+12%</div>
<div class="stat-label">improvement</div>
</div>
<div class="stat-card">
<div class="stat-label">Safety Score</div>
<div class="stat-value" id="safetyScore">94%</div>
<div class="stat-label">rating</div>
</div>
</div>
</div>
</div>
<div class="panel-section">
<div class="section-title">
<i class="fas fa-history"></i>
<h3>Generation History</h3>
</div>
<div id="generationHistory">
<!-- Will be populated dynamically -->
</div>
</div>
</div>
<div class="map-container">
<div id="map"></div>
<div class="map-overlay">
<div class="search-box">
<i class="fas fa-search"></i>
<input type="text" placeholder="Search for location or route...">
</div>
<div class="map-controls">
<div class="control-btn" id="zoomIn">
<i class="fas fa-plus"></i>
</div>
<div class="control-btn" id="zoomOut">
<i class="fas fa-minus"></i>
</div>
<div class="control-btn" id="locateRedSea">
<i class="fas fa-crosshairs"></i>
</div>
<div class="control-btn" id="clearRoutes">
<i class="fas fa-trash"></i>
</div>
</div>
</div>
<div class="legend">
<div class="legend-title">Map Legend</div>
<div class="legend-items">
<div class="legend-item">
<div class="legend-color" style="background: #1e3a8a;"></div>
<span>Context Route</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #dc2626;"></div>
<span>Generated Route</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #3b82f6;"></div>
<span>Optimized Route</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #10b981;"></div>
<span>Anomaly Point</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// Initialize map with clean sea layer
const map = L.map('map', {
zoomControl: false,
attributionControl: false
}).setView([20.0, 38.0], 6);
// Add clean maritime map layer (no country names)
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', {
maxZoom: 10
}).addTo(map);
// Add Red Sea specific layer
const redSeaBounds = L.rectangle([[12, 32], [31, 44]], {
color: "#1e3a8a",
fillColor: "#1e3a8a",
fillOpacity: 0.05,
weight: 2,
dashArray: '5, 5'
}).addTo(map);
// Key points
const babMandeb = [12.6, 43.0]; // Bab el Mandeb
const suezCanal = [30.5, 32.3]; // Suez Canal
// Add start and end points
const startMarker = L.marker(babMandeb, {
icon: L.divIcon({
className: 'ship-marker',
html: '🚢',
iconSize: [30, 30]
})
}).addTo(map).bindPopup('<div class="route-popup"><div class="popup-title">Bab el Mandeb</div><div>Starting point for Red Sea navigation</div></div>').openPopup();
const endMarker = L.marker(suezCanal, {
icon: L.divIcon({
className: 'ship-marker',
html: '⚓',
iconSize: [30, 30]
})
}).addTo(map).bindPopup('<div class="route-popup"><div class="popup-title">Suez Canal</div><div>End point for Red Sea navigation</div></div>');
// Variables to store routes and state
let contextRoute = [];
let generatedRoutes = {};
let optimizedRoutes = {};
let routeLayers = [];
let currentModel = 'TrAISformer';
let displayMode = 'single';
let savedContext = null;
// UI Elements
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
const modelCards = document.querySelectorAll('.model-card');
const routeControls = document.querySelectorAll('.route-control');
const comparisonCards = document.querySelectorAll('.comparison-card');
const generateBtn = document.getElementById('generateBtn');
const optimizeBtn = document.getElementById('optimizeBtn');
const saveContextBtn = document.getElementById('saveContextBtn');
const fileUpload = document.getElementById('fileUpload');
const fileInput = document.getElementById('fileInput');
const contextInput = document.getElementById('contextInput');
const startLat = document.getElementById('startLat');
const startLon = document.getElementById('startLon');
const endLat = document.getElementById('endLat');
const endLon = document.getElementById('endLon');
const hours = document.getElementById('hours');
const pointsPerHour = document.getElementById('pointsPerHour');
const generationHistory = document.getElementById('generationHistory');
const zoomInBtn = document.getElementById('zoomIn');
const zoomOutBtn = document.getElementById('zoomOut');
const locateRedSeaBtn = document.getElementById('locateRedSea');
const clearRoutesBtn = document.getElementById('clearRoutes');
const distanceSaved = document.getElementById('distanceSaved');
const timeSaved = document.getElementById('timeSaved');
const fuelEfficiency = document.getElementById('fuelEfficiency');
const safetyScore = document.getElementById('safetyScore');
// Tab switching
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const tabId = tab.getAttribute('data-tab');
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(tc => tc.classList.remove('active'));
tab.classList.add('active');
document.getElementById(`${tabId}-tab`).classList.add('active');
});
});
// Model selection handling
modelCards.forEach(card => {
card.addEventListener('click', () => {
if (displayMode === 'single') {
modelCards.forEach(c => c.classList.remove('active'));
card.classList.add('active');
currentModel = card.getAttribute('data-model');
} else {
card.classList.toggle('active');
}
});
});
// Display mode handling
routeControls.forEach(control => {
control.addEventListener('click', () => {
routeControls.forEach(c => c.classList.remove('active'));
control.classList.add('active');
displayMode = control.getAttribute('data-display');
if (displayMode === 'single') {
// Reset to single model selection
modelCards.forEach(c => c.classList.remove('active'));
modelCards[0].classList.add('active');
currentModel = modelCards[0].getAttribute('data-model');
}
});
});
// File upload handling
fileUpload.addEventListener('click', () => {
fileInput.click();
});
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
// Simulate file processing
alert(`File "${file.name}" loaded successfully!`);
// In a real application, you would process the Excel/CSV file here
}
});
// Save context
saveContextBtn.addEventListener('click', () => {
const start = [parseFloat(startLat.value), parseFloat(startLon.value)];
const end = [parseFloat(endLat.value), parseFloat(endLon.value)];
const context = contextInput.value;
savedContext = {
start,
end,
context,
timestamp: new Date().toLocaleString()
};
// Update markers on map
startMarker.setLatLng(start);
endMarker.setLatLng(end);
alert('Context saved successfully!');
});
// Generate context route from coordinates
function generateContextRoute() {
if (!savedContext) {
alert('Please save context first!');
return null;
}
const start = savedContext.start;
const end = savedContext.end;
const route = [];
const numPoints = 8;
// Add starting point
route.push([start[0], start[1]]);
// Generate intermediate points
for (let i = 1; i < numPoints - 1; i++) {
const progress = i / (numPoints - 1);
const lat = start[0] + (end[0] - start[0]) * progress + (Math.random() - 0.5) * 0.2;
const lon = start[1] + (end[1] - start[1]) * progress + (Math.random() - 0.5) * 0.3;
route.push([lat, lon]);
}
// Add end point
route.push([end[0], end[1]]);
return route;
}
// Generate route based on selected model
function generateRouteWithModel(contextRoute, model, totalHours, pointsPerHour) {
const totalPoints = totalHours * pointsPerHour;
const route = [];
// Add starting point (same as context start)
route.push([contextRoute[0][0], contextRoute[0][1]]);
// Generate route based on selected model
for (let i = 1; i < totalPoints - 1; i++) {
const progress = i / (totalPoints - 1);
const contextIndex = Math.floor(progress * (contextRoute.length - 1));
const contextPoint = contextRoute[contextIndex];
let lat, lon;
// Simulate different model behaviors
switch(model) {
case 'TrAISformer':
lat = contextPoint[0] + (Math.random() - 0.5) * 0.3;
lon = contextPoint[1] + (Math.random() - 0.5) * 0.4;
break;
case 'EnhcTrAISformer':
lat = contextPoint[0] + (Math.random() - 0.5) * 0.2;
lon = contextPoint[1] + (Math.random() - 0.5) * 0.3;
break;
case 'eDQTI-GRU':
lat = contextPoint[0] + (Math.random() - 0.5) * 0.4;
lon = contextPoint[1] + (Math.random() - 0.5) * 0.5;
break;
case 'eDQTI-LSTM':
lat = contextPoint[0] + (Math.random() - 0.5) * 0.5;
lon = contextPoint[1] + (Math.random() - 0.5) * 0.6;
break;
case 'eDQTI-HYPER':
const pattern = Math.sin(progress * Math.PI * 3) * 0.3;
lat = contextPoint[0] + (Math.random() - 0.5) * 0.3 + pattern;
lon = contextPoint[1] + (Math.random() - 0.5) * 0.4 + pattern;
break;
}
route.push([lat, lon]);
}
// Add end point (same as context end)
route.push([contextRoute[contextRoute.length - 1][0], contextRoute[contextRoute.length - 1][1]]);
return route;
}
// Optimize route (simulated)
function optimizeRoute(route, model) {
const optimized = [...route];
// Simulate optimization by smoothing the route
for (let i = 1; i < optimized.length - 1; i++) {
// Simple smoothing algorithm
optimized[i][0] = (optimized[i-1][0] + optimized[i][0] + optimized[i+1][0]) / 3;
optimized[i][1] = (optimized[i-1][1] + optimized[i][1] + optimized[i+1][1]) / 3;
}
return optimized;
}
// Draw routes on map
function drawRoutes() {
// Clear previous routes
clearRoutes();
if (!contextRoute) return;
// Draw context route
const contextPolyline = L.polyline(contextRoute, {
color: '#1e3a8a',
weight: 3,
opacity: 0.7,
lineJoin: 'round',
dashArray: '5, 5'
}).addTo(map);
routeLayers.push(contextPolyline);
// Draw generated routes
const modelColors = {
'TrAISformer': '#dc2626',
'EnhcTrAISformer': '#ea580c',
'eDQTI-GRU': '#d97706',
'eDQTI-LSTM': '#059669',
'eDQTI-HYPER': '#7c3aed'
};
Object.keys(generatedRoutes).forEach(model => {
if (displayMode === 'single' && model !== currentModel) return;
const route = generatedRoutes[model];
const color = modelColors[model] || '#dc2626';
const polyline = L.polyline(route, {
color: color,
weight: 4,
opacity: 0.8,
lineJoin: 'round'
}).addTo(map);
polyline.bindPopup(`<div class="route-popup">
<div class="popup-title">${model} Generated Route</div>
<div class="popup-details">
<div class="popup-detail"><span class="popup-label">Points:</span> ${route.length}</div>
<div class="popup-detail"><span class="popup-label">Model:</span> ${model}</div>
<div class="popup-detail"><span class="popup-label">Status:</span> Generated</div>
</div>
</div>`);
routeLayers.push(polyline);
});
// Draw optimized routes
Object.keys(optimizedRoutes).forEach(model => {
const route = optimizedRoutes[model];
const color = '#3b82f6';
const polyline = L.polyline(route, {
color: color,
weight: 5,
opacity: 0.9,
lineJoin: 'round'
}).addTo(map);
polyline.bindPopup(`<div class="route-popup">
<div class="popup-title">${model} Optimized Route</div>
<div class="popup-details">
<div class="popup-detail"><span class="popup-label">Points:</span> ${route.length}</div>
<div class="popup-detail"><span class="popup-label">Model:</span> ${model}</div>
<div class="popup-detail"><span class="popup-label">Status:</span> Optimized</div>
</div>
</div>`);
routeLayers.push(polyline);
});
// Adjust map bounds to fit routes
const group = L.featureGroup(routeLayers);
map.fitBounds(group.getBounds().pad(0.1));
}
// Clear all routes
function clearRoutes() {
routeLayers.forEach(layer => map.removeLayer(layer));
routeLayers = [];
}
// Add to generation history
function addToHistory(model, points, anomalyLevel) {
const historyItem = document.createElement('div');
historyItem.className = 'route-item';
historyItem.innerHTML = `
<div class="route-header">
<div class="route-name">${model} - ${new Date().toLocaleString()}</div>
<div class="route-type">${model}</div>
</div>
<div class="route-details">
<span>Points: ${points}</span>
<span>Anomaly: ${anomalyLevel}%</span>
</div>
`;
historyItem.addEventListener('click', () => {
// Switch to single display and show this route
routeControls.forEach(c => c.classList.remove('active'));
document.querySelector('[data-display="single"]').classList.add('active');
displayMode = 'single';
modelCards.forEach(c => c.classList.remove('active'));
document.querySelector(`[data-model="${model}"]`).classList.add('active');
currentModel = model;
drawRoutes();
});
generationHistory.insertBefore(historyItem, generationHistory.firstChild);
// Keep limited items in history
if (generationHistory.children.length > 5) {
generationHistory.removeChild(generationHistory.lastChild);
}
}
// Generate button handler
generateBtn.addEventListener('click', () => {
// Generate context route
contextRoute = generateContextRoute();
if (!contextRoute) return;
// Clear previous generated routes
generatedRoutes = {};
// Get selected models
const selectedModels = displayMode === 'single'
? [currentModel]
: Array.from(document.querySelectorAll('.model-card.active'))
.map(card => card.getAttribute('data-model'));
if (selectedModels.length === 0) {
alert('Please select at least one model!');
return;
}
// Generate routes for selected models
selectedModels.forEach(model => {
const route = generateRouteWithModel(
contextRoute,
model,
parseInt(hours.value),
parseInt(pointsPerHour.value)
);
generatedRoutes[model] = route;
// Simulate anomaly detection
const anomalyLevel = (Math.random() * 10).toFixed(1);
// Add to history
addToHistory(model, route.length, anomalyLevel);
});
// Draw routes
drawRoutes();
alert(`Successfully generated routes for ${selectedModels.length} model(s)!`);
});
// Optimize button handler
optimizeBtn.addEventListener('click', () => {
if (Object.keys(generatedRoutes).length === 0) {
alert('Please generate routes first!');
return;
}
// Get selected models for optimization
const selectedModels = Array.from(document.querySelectorAll('.comparison-card.active'))
.map(card => card.getAttribute('data-model'));
if (selectedModels.length === 0) {
alert('Please select at least one model to optimize!');
return;
}
// Optimize selected routes
selectedModels.forEach(model => {
if (generatedRoutes[model]) {
optimizedRoutes[model] = optimizeRoute(generatedRoutes[model], model);
}
});
// Update optimization metrics
distanceSaved.textContent = Math.floor(Math.random() * 100);
timeSaved.textContent = (Math.random() * 5).toFixed(1);
fuelEfficiency.textContent = `+${Math.floor(Math.random() * 20)}%`;
safetyScore.textContent = `${80 + Math.floor(Math.random() * 20)}%`;
// Draw routes
drawRoutes();
alert(`Successfully optimized ${selectedModels.length} route(s)!`);
});
// Comparison card selection
comparisonCards.forEach(card => {
card.addEventListener('click', () => {
card.classList.toggle('active');
});
});
// Map control elements
zoomInBtn.addEventListener('click', () => {
map.zoomIn();
});
zoomOutBtn.addEventListener('click', () => {
map.zoomOut();
});
locateRedSeaBtn.addEventListener('click', () => {
map.setView([20.0, 38.0], 6);
});
clearRoutesBtn.addEventListener('click', () => {
clearRoutes();
generatedRoutes = {};
optimizedRoutes = {};
});
// Initialize with example data
window.addEventListener('load', () => {
// Auto-save initial context
setTimeout(() => {
saveContextBtn.click();
}, 500);
});
</script>
</body>
</html>