Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>UAS Pilot Logbook Visualization</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet"> | |
| <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script> | |
| <style> | |
| .chart-container { | |
| height: 400px; | |
| width: 100%; | |
| } | |
| .glow-card { | |
| box-shadow: 0 0 20px rgba(59, 130, 246, 0.5); | |
| transition: all 0.3s ease; | |
| } | |
| .glow-card:hover { | |
| box-shadow: 0 0 30px rgba(59, 130, 246, 0.8); | |
| transform: translateY(-5px); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white"> | |
| <div class="min-h-screen"> | |
| <!-- Header --> | |
| <header class="bg-gray-800 py-8"> | |
| <div class="container mx-auto px-4 text-center"> | |
| <h1 class="text-4xl font-bold mb-2">UAS Pilot Logbook Visualization</h1> | |
| <p class="text-xl text-blue-300">Flight Data Analysis - Year 2023</p> | |
| <div class="mt-6 flex justify-center space-x-4"> | |
| <div class="bg-blue-900 px-4 py-2 rounded-lg"> | |
| <span class="font-semibold">Total Hours:</span> 219:44 | |
| </div> | |
| <div class="bg-green-900 px-4 py-2 rounded-lg"> | |
| <span class="font-semibold">Day:</span> 118:10 | |
| </div> | |
| <div class="bg-purple-900 px-4 py-2 rounded-lg"> | |
| <span class="font-semibold">Night:</span> 101:34 | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="container mx-auto px-4 py-12"> | |
| <!-- Charts Section --> | |
| <section class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-12"> | |
| <!-- Flight Hours by Month --> | |
| <div class="bg-gray-800 p-6 rounded-xl glow-card" data-aos="fade-up"> | |
| <h2 class="text-2xl font-bold mb-4 flex items-center"> | |
| <i data-feather="calendar" class="mr-2"></i> Flight Hours by Month | |
| </h2> | |
| <div class="chart-container"> | |
| <canvas id="monthlyChart"></canvas> | |
| </div> | |
| </div> | |
| <!-- Day vs Night Flight Hours --> | |
| <div class="bg-gray-800 p-6 rounded-xl glow-card" data-aos="fade-up" data-aos-delay="200"> | |
| <h2 class="text-2xl font-bold mb-4 flex items-center"> | |
| <i data-feather="sun" class="mr-2"></i> Day vs Night Flight Hours | |
| </h2> | |
| <div class="chart-container"> | |
| <canvas id="dayNightChart"></canvas> | |
| </div> | |
| </div> | |
| <!-- Aircraft Usage --> | |
| <div class="bg-gray-800 p-6 rounded-xl glow-card" data-aos="fade-up"> | |
| <h2 class="text-2xl font-bold mb-4 flex items-center"> | |
| <i data-feather="package" class="mr-2"></i> Aircraft Usage | |
| </h2> | |
| <div class="chart-container"> | |
| <canvas id="aircraftChart"></canvas> | |
| </div> | |
| </div> | |
| <!-- Location Distribution --> | |
| <div class="bg-gray-800 p-6 rounded-xl glow-card" data-aos="fade-up" data-aos-delay="200"> | |
| <h2 class="text-2xl font-bold mb-4 flex items-center"> | |
| <i data-feather="map-pin" class="mr-2"></i> Location Distribution | |
| </h2> | |
| <div class="chart-container"> | |
| <canvas id="locationChart"></canvas> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Flight Summary --> | |
| <section class="mb-12" data-aos="fade-up"> | |
| <div class="bg-gray-800 p-6 rounded-xl"> | |
| <h2 class="text-2xl font-bold mb-6 flex items-center"> | |
| <i data-feather="bar-chart-2" class="mr-2"></i> Flight Summary | |
| </h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <div class="bg-blue-900 p-4 rounded-lg"> | |
| <h3 class="text-lg font-semibold mb-2">Most Active Day</h3> | |
| <p class="text-3xl font-bold">June 3</p> | |
| <p class="text-blue-200">271 mins (4h 31m)</p> | |
| </div> | |
| <div class="bg-green-900 p-4 rounded-lg"> | |
| <h3 class="text-lg font-semibold mb-2">Most Used Aircraft</h3> | |
| <p class="text-3xl font-bold">DJI M4T</p> | |
| <p class="text-green-200">5Y-1022A / 5Y-1023A</p> | |
| </div> | |
| <div class="bg-purple-900 p-4 rounded-lg"> | |
| <h3 class="text-lg font-semibold mb-2">Primary Location</h3> | |
| <p class="text-3xl font-bold">EPK WEST</p> | |
| <p class="text-purple-200">Most frequent operation site</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Recent Flights Table --> | |
| <section data-aos="fade-up"> | |
| <div class="bg-gray-800 p-6 rounded-xl overflow-x-auto"> | |
| <h2 class="text-2xl font-bold mb-6 flex items-center"> | |
| <i data-feather="list" class="mr-2"></i> Recent Flight Logs | |
| </h2> | |
| <table class="min-w-full divide-y divide-gray-700"> | |
| <thead class="bg-gray-700"> | |
| <tr> | |
| <th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Date</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Aircraft</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Location</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Day Time</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Night Time</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Auth. Number</th> | |
| </tr> | |
| </thead> | |
| <tbody class="bg-gray-800 divide-y divide-gray-700"> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap">30/6/2025</td> | |
| <td class="px-6 py-4 whitespace-nowrap">DJI M4T (5Y-1023A)</td> | |
| <td class="px-6 py-4 whitespace-nowrap">EPK EAST</td> | |
| <td class="px-6 py-4 whitespace-nowrap">236 mins</td> | |
| <td class="px-6 py-4 whitespace-nowrap">-</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-blue-400">KCAA/UAS/OPS/0856/2025</td> | |
| </tr> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap">29/6/2025</td> | |
| <td class="px-6 py-4 whitespace-nowrap">DJI M4T (5Y-1023A)</td> | |
| <td class="px-6 py-4 whitespace-nowrap">EPK EAST</td> | |
| <td class="px-6 py-4 whitespace-nowrap">198 mins</td> | |
| <td class="px-6 py-4 whitespace-nowrap">-</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-blue-400">KCAA/UAS/OPS/0856/2025</td> | |
| </tr> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap">26/6/2025</td> | |
| <td class="px-6 py-4 whitespace-nowrap">DJI M4T (5Y-1023A)</td> | |
| <td class="px-6 py-4 whitespace-nowrap">EPK EAST</td> | |
| <td class="px-6 py-4 whitespace-nowrap">264 mins</td> | |
| <td class="px-6 py-4 whitespace-nowrap">-</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-blue-400">KCAA/UAS/OPS/0790/2025</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="bg-gray-800 py-8"> | |
| <div class="container mx-auto px-4 text-center"> | |
| <div class="flex justify-center items-center mb-4"> | |
| <i data-feather="user" class="mr-2"></i> | |
| <p class="text-lg">Pilot: Tony Lagat</p> | |
| </div> | |
| <p class="text-gray-400">Certified that the entries in this log book are true</p> | |
| <p class="mt-2 text-sm text-gray-500">Data visualization created from UAS Pilot's Logbook 2023</p> | |
| </div> | |
| </footer> | |
| </div> | |
| <script> | |
| // Initialize AOS | |
| AOS.init({ | |
| duration: 800, | |
| easing: 'ease-in-out', | |
| once: true | |
| }); | |
| // Initialize Feather Icons | |
| feather.replace(); | |
| // Chart data and initialization | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Monthly Flight Hours Chart | |
| const monthlyCtx = document.getElementById('monthlyChart').getContext('2d'); | |
| new Chart(monthlyCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: ['April', 'May', 'June'], | |
| datasets: [{ | |
| label: 'Flight Hours (mins)', | |
| data: [7190, 6095, 8920], | |
| backgroundColor: [ | |
| 'rgba(59, 130, 246, 0.7)', | |
| 'rgba(16, 185, 129, 0.7)', | |
| 'rgba(139, 92, 246, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(59, 130, 246, 1)', | |
| 'rgba(16, 185, 129, 1)', | |
| 'rgba(139, 92, 246, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| title: { | |
| display: true, | |
| text: 'Minutes' | |
| } | |
| } | |
| }, | |
| plugins: { | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const mins = context.raw; | |
| const hours = Math.floor(mins / 60); | |
| const remainingMins = mins % 60; | |
| return `${hours}h ${remainingMins}m (${mins} mins)`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Day vs Night Flight Hours Chart | |
| const dayNightCtx = document.getElementById('dayNightChart').getContext('2d'); | |
| new Chart(dayNightCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Day Time', 'Night Time'], | |
| datasets: [{ | |
| data: [7090, 6094], | |
| backgroundColor: [ | |
| 'rgba(234, 179, 8, 0.7)', | |
| 'rgba(30, 41, 59, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(234, 179, 8, 1)', | |
| 'rgba(30, 41, 59, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'right', | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const mins = context.raw; | |
| const hours = Math.floor(mins / 60); | |
| const remainingMins = mins % 60; | |
| return `${context.label}: ${hours}h ${remainingMins}m (${mins} mins)`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Aircraft Usage Chart | |
| const aircraftCtx = document.getElementById('aircraftChart').getContext('2d'); | |
| new Chart(aircraftCtx, { | |
| type: 'polarArea', | |
| data: { | |
| labels: ['DJI M30T (5Y-0446A)', 'DJI M30T (5Y-0952A)', 'DJI M30T (5Y-0815A)', 'DJI M4T (5Y-0809A)', 'DJI M4T (5Y-1022A)', 'DJI M4T (5Y-1023A)'], | |
| datasets: [{ | |
| data: [223+250+224+228+284, 114+252+232+198+236+248, 261, 186, 245+232+256+173+216+286+114+212, 198+236+264+271+229+278+234+264+189+234+244+208+220+252+236+186+149+220+268+232+234+264+198+236], | |
| backgroundColor: [ | |
| 'rgba(59, 130, 246, 0.7)', | |
| 'rgba(16, 185, 129, 0.7)', | |
| 'rgba(139, 92, 246, 0.7)', | |
| 'rgba(220, 38, 38, 0.7)', | |
| 'rgba(234, 88, 12, 0.7)', | |
| 'rgba(6, 182, 212, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(59, 130, 246, 1)', | |
| 'rgba(16, 185, 129, 1)', | |
| 'rgba(139, 92, 246, 1)', | |
| 'rgba(220, 38, 38, 1)', | |
| 'rgba(234, 88, 12, 1)', | |
| 'rgba(6, 182, 212, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'right', | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const mins = context.raw; | |
| const hours = Math.floor(mins / 60); | |
| const remainingMins = mins % 60; | |
| return `${context.label}: ${hours}h ${remainingMins}m (${mins} mins)`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Location Distribution Chart | |
| const locationCtx = document.getElementById('locationChart').getContext('2d'); | |
| new Chart(locationCtx, { | |
| type: 'pie', | |
| data: { | |
| labels: ['DELMONTE', 'EPK EAST', 'EPK WEST'], | |
| datasets: [{ | |
| data: [223+250+224+228+284+114+252+232+198+236+248+261, 186+198+236+264+271+229+278+234+264+189+234+244+208+220+252+236+186+149+220+268+232+234+264+198+236, 245+232+256+173+216+286+114+212], | |
| backgroundColor: [ | |
| 'rgba(59, 130, 246, 0.7)', | |
| 'rgba(16, 185, 129, 0.7)', | |
| 'rgba(139, 92, 246, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(59, 130, 246, 1)', | |
| 'rgba(16, 185, 129, 1)', | |
| 'rgba(139, 92, 246, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'right', | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const mins = context.raw; | |
| const hours = Math.floor(mins / 60); | |
| const remainingMins = mins % 60; | |
| return `${context.label}: ${hours}h ${remainingMins}m (${mins} mins)`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |