| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>TideSlots | Fraser River Salmon & Tide Tracking</title> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <script> |
| | tailwind.config = { |
| | theme: { |
| | extend: { |
| | colors: { |
| | primary: { |
| | light: '#f8fafc', |
| | DEFAULT: '#f1f5f9', |
| | dark: '#e2e8f0' |
| | }, |
| | accent: { |
| | light: '#3b82f6', |
| | DEFAULT: '#2563eb', |
| | dark: '#1d4ed8' |
| | }, |
| | salmon: { |
| | chinook: '#3b82f6', |
| | sockeye: '#ef4444', |
| | coho: '#10b981', |
| | pink: '#8b5cf6' |
| | } |
| | }, |
| | fontFamily: { |
| | sans: ['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'sans-serif'], |
| | }, |
| | } |
| | } |
| | } |
| | </script> |
| | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| | <style> |
| | @keyframes fadeIn { |
| | from { opacity: 0; transform: translateY(10px); } |
| | to { opacity: 1; transform: translateY(0); } |
| | } |
| | |
| | .animate-fade-in { |
| | animation: fadeIn 0.5s ease-out forwards; |
| | } |
| | |
| | .card { |
| | backdrop-filter: blur(10px); |
| | background: rgba(255, 255, 255, 0.8); |
| | border: 1px solid rgba(255, 255, 255, 0.2); |
| | box-shadow: 0 4px 30px rgba(0, 0, 0, 0.05); |
| | } |
| | |
| | .card-hover { |
| | transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); |
| | } |
| | |
| | .card-hover:hover { |
| | transform: translateY(-4px); |
| | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08); |
| | } |
| | |
| | .salmon-icon { |
| | filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); |
| | } |
| | |
| | .loading-spinner { |
| | border: 3px solid rgba(59, 130, 246, 0.1); |
| | border-radius: 50%; |
| | border-top: 3px solid #3b82f6; |
| | width: 24px; |
| | height: 24px; |
| | animation: spin 1s linear infinite; |
| | margin: 0 auto; |
| | } |
| | |
| | @keyframes spin { |
| | 0% { transform: rotate(0deg); } |
| | 100% { transform: rotate(360deg); } |
| | } |
| | |
| | .glass-nav { |
| | backdrop-filter: blur(10px); |
| | background: rgba(255, 255, 255, 0.8); |
| | border-bottom: 1px solid rgba(255, 255, 255, 0.2); |
| | } |
| | |
| | .hero-gradient { |
| | background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%); |
| | } |
| | |
| | .source-link { |
| | color: #3b82f6; |
| | text-decoration: underline; |
| | font-size: 0.75rem; |
| | } |
| | |
| | .source-link:hover { |
| | color: #2563eb; |
| | } |
| | </style> |
| | </head> |
| | <body class="font-sans bg-primary-light text-gray-900"> |
| | |
| | <header class="glass-nav fixed w-full z-50"> |
| | <div class="container mx-auto px-6 py-4"> |
| | <div class="flex items-center justify-between"> |
| | <div class="flex items-center space-x-3"> |
| | <svg class="salmon-icon w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| | <path d="M22 12L18 8L15 11L12 8L9 11L6 8L2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12Z" fill="#2563eb"/> |
| | <path d="M6 8L2 12L6 16L9 13L12 16L15 13L18 16L22 12L18 8" fill="#FECACA"/> |
| | </svg> |
| | <h1 class="text-xl font-semibold">TideSlots</h1> |
| | </div> |
| | <div class="text-sm bg-white px-3 py-1 rounded-full border border-gray-200"> |
| | <span id="last-updated">Loading data...</span> |
| | </div> |
| | </div> |
| | </div> |
| | </header> |
| |
|
| | |
| | <section class="hero-gradient pt-24 pb-16"> |
| | <div class="container mx-auto px-6"> |
| | <div class="max-w-3xl mx-auto text-center animate-fade-in" style="animation-delay: 0.1s"> |
| | <h2 class="text-4xl md:text-5xl font-semibold text-white mb-4">Fraser River Salmon Tracker</h2> |
| | <p class="text-xl text-blue-100 mb-8">Real-time tracking of salmon migration & tide patterns</p> |
| | |
| | <div class="grid grid-cols-3 gap-4 max-w-md mx-auto"> |
| | <div class="card rounded-xl p-4 text-center relative"> |
| | <div class="text-2xl font-medium text-accent-DEFAULT" id="salmon-counter">0</div> |
| | <div class="text-gray-600 text-sm">Salmon counted</div> |
| | <a href="https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html" class="source-link absolute bottom-1 right-2">DFO Canada</a> |
| | </div> |
| | <div class="card rounded-xl p-4 text-center relative"> |
| | <div class="text-2xl font-medium text-accent-DEFAULT" id="water-temp">0°C</div> |
| | <div class="text-gray-600 text-sm">Water temp</div> |
| | <a href="https://wateroffice.ec.gc.ca/" class="source-link absolute bottom-1 right-2">Water Office</a> |
| | </div> |
| | <div class="card rounded-xl p-4 text-center relative"> |
| | <div class="text-2xl font-medium text-accent-DEFAULT" id="river-flow">0 m³/s</div> |
| | <div class="text-gray-600 text-sm">River flow</div> |
| | <a href="https://wateroffice.ec.gc.ca/" class="source-link absolute bottom-1 right-2">Water Office</a> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </section> |
| |
|
| | |
| | <main class="container mx-auto px-6 py-12 -mt-10"> |
| | |
| | <section class="mb-16 animate-fade-in" style="animation-delay: 0.2s"> |
| | <div class="flex items-center justify-between mb-8"> |
| | <h2 class="text-2xl font-semibold text-gray-900">Current Salmon Runs</h2> |
| | <div class="text-sm text-gray-500">Data source: |
| | <a href="https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html" class="source-link">DFO Canada</a> |
| | </div> |
| | </div> |
| |
|
| | <div id="salmon-data-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> |
| | |
| | <div class="col-span-4 py-12"> |
| | <div class="loading-spinner"></div> |
| | <p class="text-center mt-3 text-gray-500">Loading salmon data...</p> |
| | </div> |
| | </div> |
| | </section> |
| |
|
| | |
| | <section class="mb-16 animate-fade-in" style="animation-delay: 0.3s"> |
| | <div class="card rounded-2xl p-6"> |
| | <div class="flex items-center justify-between mb-6"> |
| | <h2 class="text-2xl font-semibold text-gray-900">River & Tide Conditions</h2> |
| | <div class="text-sm text-gray-500">Data source: |
| | <a href="https://wateroffice.ec.gc.ca/" class="source-link">Water Office of Canada</a> |
| | </div> |
| | </div> |
| |
|
| | <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"> |
| | <div class="card rounded-xl p-6 card-hover relative"> |
| | <div class="flex items-center mb-4"> |
| | <div class="bg-blue-100 p-3 rounded-xl mr-4"> |
| | <i class="fas fa-temperature-high text-blue-600 text-xl"></i> |
| | </div> |
| | <div> |
| | <h3 class="font-medium text-gray-700">Water Temperature</h3> |
| | <div class="text-2xl font-semibold text-gray-900" id="current-temp">0°C</div> |
| | </div> |
| | </div> |
| | <div class="h-40" id="temp-chart"></div> |
| | <a href="https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MH024" class="source-link absolute bottom-2 right-3">Station 08MH024</a> |
| | </div> |
| |
|
| | <div class="card rounded-xl p-6 card-hover relative"> |
| | <div class="flex items-center mb-4"> |
| | <div class="bg-blue-100 p-3 rounded-xl mr-4"> |
| | <i class="fas fa-water text-blue-600 text-xl"></i> |
| | </div> |
| | <div> |
| | <h3 class="font-medium text-gray-700">Tide Level</h3> |
| | <div class="text-2xl font-semibold text-gray-900" id="current-level">0m</div> |
| | </div> |
| | </div> |
| | <div class="h-40" id="level-chart"></div> |
| | <a href="https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MF005" class="source-link absolute bottom-2 right-3">Station 08MF005</a> |
| | </div> |
| |
|
| | <div class="card rounded-xl p-6 card-hover relative"> |
| | <div class="flex items-center mb-4"> |
| | <div class="bg-blue-100 p-3 rounded-xl mr-4"> |
| | <i class="fas fa-tachometer-alt text-blue-600 text-xl"></i> |
| | </div> |
| | <div> |
| | <h3 class="font-medium text-gray-700">Flow Rate</h3> |
| | <div class="text-2xl font-semibold text-gray-900" id="current-flow">0 m³/s</div> |
| | </div> |
| | </div> |
| | <div class="h-40" id="flow-chart"></div> |
| | <a href="https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MG005" class="source-link absolute bottom-2 right-3">Station 08MG005</a> |
| | </div> |
| | </div> |
| |
|
| | <div class="bg-primary-DEFAULT rounded-xl p-4"> |
| | <h3 class="font-medium text-gray-700 mb-3">Monitoring Stations</h3> |
| | <div class="overflow-x-auto"> |
| | <table class="min-w-full divide-y divide-gray-200"> |
| | <thead> |
| | <tr> |
| | <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Station</th> |
| | <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Level</th> |
| | <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Temp</th> |
| | <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Flow</th> |
| | <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Updated</th> |
| | </tr> |
| | </thead> |
| | <tbody id="station-data" class="divide-y divide-gray-200"> |
| | |
| | <tr> |
| | <td colspan="5" class="px-4 py-8 text-center"> |
| | <div class="loading-spinner"></div> |
| | <p class="mt-2 text-sm text-gray-500">Loading station data...</p> |
| | </td> |
| | </tr> |
| | </tbody> |
| | </table> |
| | </div> |
| | </div> |
| | </div> |
| | </section> |
| |
|
| | |
| | <section class="animate-fade-in" style="animation-delay: 0.4s"> |
| | <div class="card rounded-2xl p-6"> |
| | <div class="flex items-center justify-between mb-6"> |
| | <h2 class="text-2xl font-semibold text-gray-900">Migration & Tide Forecast</h2> |
| | <div class="text-sm text-gray-500">Data sources: |
| | <a href="https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html" class="source-link">DFO Canada</a> & |
| | <a href="https://www.dfo-mpo.gc.ca/science/publications/psarc-ssars/index-eng.html" class="source-link">PSARC</a> |
| | </div> |
| | </div> |
| |
|
| | <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> |
| | <div class="card rounded-xl p-6 card-hover relative"> |
| | <h3 class="font-medium text-gray-700 mb-4">Weekly Projection</h3> |
| | <div class="h-64" id="weekly-chart"></div> |
| | <a href="https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html" class="source-link absolute bottom-2 right-3">DFO Forecast</a> |
| | </div> |
| |
|
| | <div class="card rounded-xl p-6 card-hover relative"> |
| | <h3 class="font-medium text-gray-700 mb-4">Historical Comparison</h3> |
| | <div class="h-64" id="historical-chart"></div> |
| | <a href="https://www.dfo-mpo.gc.ca/science/publications/psarc-ssars/index-eng.html" class="source-link absolute bottom-2 right-3">PSARC Reports</a> |
| | </div> |
| | </div> |
| | </div> |
| | </section> |
| | </main> |
| |
|
| | |
| | <footer class="bg-gray-900 text-white py-12"> |
| | <div class="container mx-auto px-6"> |
| | <div class="flex flex-col md:flex-row justify-between items-center"> |
| | <div class="mb-6 md:mb-0"> |
| | <div class="flex items-center space-x-3"> |
| | <svg class="salmon-icon w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| | <path d="M22 12L18 8L15 11L12 8L9 11L6 8L2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12Z" fill="white"/> |
| | <path d="M6 8L2 12L6 16L9 13L12 16L15 13L18 16L22 12L18 8" fill="#FECACA"/> |
| | </svg> |
| | <h3 class="text-xl font-semibold">TideSlots</h3> |
| | </div> |
| | <p class="mt-2 text-gray-400">Tracking the pulse of the Fraser River ecosystem</p> |
| | </div> |
| | <div class="text-gray-400 text-sm text-center md:text-right"> |
| | <p>Data sources: |
| | <a href="https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html" class="text-blue-300 hover:text-blue-200">DFO Canada</a>, |
| | <a href="https://wateroffice.ec.gc.ca/" class="text-blue-300 hover:text-blue-200">Water Office of Canada</a>, |
| | <a href="https://www.dfo-mpo.gc.ca/science/publications/psarc-ssars/index-eng.html" class="text-blue-300 hover:text-blue-200">PSARC</a> |
| | </p> |
| | <p class="mt-1">Images from <a href="https://unsplash.com/" class="text-blue-300 hover:text-blue-200">Unsplash</a></p> |
| | <p class="mt-1">© 2024 TideSlots Tracker</p> |
| | </div> |
| | </div> |
| | </div> |
| | </footer> |
| |
|
| | |
| | <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script> |
| | <script> |
| | |
| | const DATA_SOURCES = { |
| | salmon: { |
| | url: 'https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html', |
| | name: 'Fisheries and Oceans Canada' |
| | }, |
| | hydrometric: { |
| | url: 'https://wateroffice.ec.gc.ca/', |
| | name: 'Water Office of Canada' |
| | }, |
| | forecast: { |
| | url: 'https://www.pac.dfo-mpo.gc.ca/fm-gp/salmon-saumon/index-eng.html', |
| | name: 'DFO Forecast' |
| | }, |
| | historical: { |
| | url: 'https://www.dfo-mpo.gc.ca/science/publications/psarc-ssars/index-eng.html', |
| | name: 'PSARC Reports' |
| | } |
| | }; |
| | |
| | |
| | const STATIONS = { |
| | '08MH024': { |
| | name: 'Mission Bridge', |
| | url: 'https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MH024' |
| | }, |
| | '08MF005': { |
| | name: 'Hope', |
| | url: 'https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MF005' |
| | }, |
| | '08MG005': { |
| | name: 'Lillooet', |
| | url: 'https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MG005' |
| | } |
| | }; |
| | |
| | |
| | const SALMON_IMAGES = { |
| | chinook: 'https://images.unsplash.com/photo-1591208200026-7f2f6a1a2e3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80', |
| | sockeye: 'https://images.unsplash.com/photo-1591208200026-7f2f6a1a2e3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80', |
| | coho: 'https://images.unsplash.com/photo-1591208200026-7f2f6a1a2e3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80', |
| | pink: 'https://images.unsplash.com/photo-1591208200026-7f2f6a1a2e3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=80' |
| | }; |
| | |
| | |
| | const DEMO_DATA = { |
| | salmon: [ |
| | { |
| | name: 'Chinook', |
| | key: 'chinook', |
| | currentRun: 18500, |
| | lastWeek: 15200, |
| | seasonTotal: 42000, |
| | trend: 'up', |
| | percentage: 85, |
| | image: SALMON_IMAGES.chinook, |
| | source: DATA_SOURCES.salmon |
| | }, |
| | { |
| | name: 'Sockeye', |
| | key: 'sockeye', |
| | currentRun: 520000, |
| | lastWeek: 480000, |
| | seasonTotal: 1500000, |
| | trend: 'up', |
| | percentage: 75, |
| | image: SALMON_IMAGES.sockeye, |
| | source: DATA_SOURCES.salmon |
| | }, |
| | { |
| | name: 'Coho', |
| | key: 'coho', |
| | currentRun: 32000, |
| | lastWeek: 35000, |
| | seasonTotal: 75000, |
| | trend: 'down', |
| | percentage: 60, |
| | image: SALMON_IMAGES.coho, |
| | source: DATA_SOURCES.salmon |
| | }, |
| | { |
| | name: 'Pink', |
| | key: 'pink', |
| | currentRun: 1100000, |
| | lastWeek: 900000, |
| | seasonTotal: 3000000, |
| | trend: 'up', |
| | percentage: 90, |
| | image: SALMON_IMAGES.pink, |
| | source: DATA_SOURCES.salmon |
| | } |
| | ], |
| | river: { |
| | stations: [ |
| | { |
| | id: '08MH024', |
| | name: 'Mission Bridge', |
| | url: 'https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MH024', |
| | temp: '16.5', |
| | level: '4.25', |
| | flow: 7500, |
| | updated: 15 |
| | }, |
| | { |
| | id: '08MF005', |
| | name: 'Hope', |
| | url: 'https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MF005', |
| | temp: '15.8', |
| | level: '3.75', |
| | flow: 6800, |
| | updated: 22 |
| | }, |
| | { |
| | id: '08MG005', |
| | name: 'Lillooet', |
| | url: 'https://wateroffice.ec.gc.ca/report/real_time_e.html?stn=08MG005', |
| | temp: '14.2', |
| | level: '3.45', |
| | flow: 2500, |
| | updated: 30 |
| | } |
| | ], |
| | currentTemp: '16.2', |
| | currentLevel: '4.15', |
| | currentFlow: 7200, |
| | tempHistory: [15.8, 16.0, 16.2, 16.1, 16.3, 16.5, 16.4], |
| | levelHistory: [4.05, 4.10, 4.15, 4.20, 4.18, 4.25, 4.22], |
| | flowHistory: [6500, 6800, 7000, 7200, 7100, 7500, 7400], |
| | source: DATA_SOURCES.hydrometric |
| | } |
| | }; |
| | |
| | document.addEventListener('DOMContentLoaded', function() { |
| | |
| | const now = new Date(); |
| | document.getElementById('last-updated').textContent = `Updated: ${now.toLocaleString('en-US', { |
| | month: 'short', |
| | day: 'numeric', |
| | hour: '2-digit', |
| | minute: '2-digit' |
| | })}`; |
| | |
| | |
| | displaySalmonData(DEMO_DATA.salmon); |
| | displayRiverData(DEMO_DATA.river); |
| | updateHeroMetrics(DEMO_DATA.river, DEMO_DATA.salmon); |
| | createCharts(DEMO_DATA.river, DEMO_DATA.salmon); |
| | |
| | |
| | setInterval(() => { |
| | |
| | const now = new Date(); |
| | document.getElementById('last-updated').textContent = `Updated: ${now.toLocaleString('en-US', { |
| | month: 'short', |
| | day: 'numeric', |
| | hour: '2-digit', |
| | minute: '2-digit' |
| | })}`; |
| | }, 300000); |
| | }); |
| | |
| | function displaySalmonData(data) { |
| | const container = document.getElementById('salmon-data-container'); |
| | container.innerHTML = ''; |
| | |
| | data.forEach(species => { |
| | const trendIcon = species.trend === 'up' ? |
| | `<i class="fas fa-arrow-up text-green-500"></i>` : |
| | `<i class="fas fa-arrow-down text-red-500"></i>`; |
| | |
| | const colorClass = `salmon-${species.key}`; |
| | |
| | container.innerHTML += ` |
| | <div class="card rounded-xl overflow-hidden card-hover relative"> |
| | <div class="h-40 overflow-hidden"> |
| | <img src="${species.image}" alt="${species.name} Salmon" class="w-full h-full object-cover"> |
| | <div class="absolute bottom-2 left-2 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded"> |
| | Photo: <a href="https://unsplash.com/" class="text-blue-300">Unsplash</a> |
| | </div> |
| | </div> |
| | <div class="p-5"> |
| | <div class="flex justify-between items-start mb-3"> |
| | <h3 class="text-lg font-semibold text-gray-900">${species.name}</h3> |
| | <span class="text-sm font-medium ${species.trend === 'up' ? 'text-green-600' : 'text-red-600'}"> |
| | ${trendIcon} ${species.trend === 'up' ? 'Increasing' : 'Decreasing'} |
| | </span> |
| | </div> |
| | |
| | <div class="space-y-3"> |
| | <div> |
| | <div class="flex justify-between text-sm mb-1"> |
| | <span class="text-gray-500">This week</span> |
| | <span class="font-medium text-gray-900">${species.currentRun.toLocaleString()}</span> |
| | </div> |
| | <div class="w-full bg-gray-200 rounded-full h-1.5"> |
| | <div class="bg-${colorClass} h-1.5 rounded-full" style="width: ${species.percentage}%"></div> |
| | </div> |
| | </div> |
| | |
| | <div class="grid grid-cols-2 gap-4"> |
| | <div> |
| | <div class="text-xs text-gray-500">Last week</div> |
| | <div class="font-medium">${species.lastWeek.toLocaleString()}</div> |
| | </div> |
| | <div> |
| | <div class="text-xs text-gray-500">Season total</div> |
| | <div class="font-medium">${species.se seasonTotal.toLocaleString()}</div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | <a href="${species.source.url}" class="source-link absolute bottom-2 right-3">${species.source.name}</a> |
| | </div> |
| | `; |
| | }); |
| | } |
| | |
| | function displayRiverData(data) { |
| | |
| | document.getElementById('current-temp').textContent = `${data.currentTemp}°C`; |
| | document.getElementById('current-level').textContent = `${data.currentLevel}m`; |
| | document.getElementById('current-flow').textContent = `${data.currentFlow.toLocaleString()} m³/s`; |
| | |
| | |
| | const stationContainer = document.getElementById('station-data'); |
| | stationContainer.innerHTML = ''; |
| | |
| | data.stations.forEach(station => { |
| | const tempChange = (Math.random() * 1.5 - 0.75).toFixed(1); |
| | const levelChange = (Math.random() * 0.3 - 0.15).toFixed(2); |
| | const flowChange = Math.floor(Math.random() * 500 - 250); |
| | |
| | stationContainer.innerHTML += ` |
| | <tr> |
| | <td class="px-4 py-3 whitespace-nowrap"> |
| | <div class="font-medium">${station.name}</div> |
| | <div class="text-xs text-gray-500"> |
| | <a href="${station.url}" class="source-link">${station.id}</a> |
| | </div> |
| | </td> |
| | <td class="px-4 py-3 whitespace-nowrap"> |
| | <div class="font-medium">${station.level}m</div> |
| | <div class="text-xs ${levelChange > 0 ? 'text-green-600' : 'text-red-600'}"> |
| | ${levelChange > 0 ? '↑' : '↓'} ${Math.abs(levelChange)}m |
| | </div> |
| | </td> |
| | <td class="px-4 py-3 whitespace-nowrap"> |
| | <div class="font-medium">${station.temp}°C</div> |
| | <div class="text-xs ${tempChange > 0 ? 'text-red-600' : 'text-blue-600'}"> |
| | ${tempChange > 0 ? '↑' : '↓'} ${Math.abs(tempChange)}° |
| | </div> |
| | </td> |
| | <td class="px-4 py-3 whitespace-nowrap"> |
| | <div class="font-medium">${station.flow.toLocaleString()} m³/s</div> |
| | <div class="text-xs ${flowChange > 0 ? 'text-green-600' : 'text-red-600'}"> |
| | ${flowChange > 0 ? '↑' : '↓'} ${Math.abs(flowChange).toLocaleString()} |
| | </div> |
| | </td> |
| | <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500"> |
| | ${station.updated} min ago |
| | </td> |
| | </tr> |
| | `; |
| | }); |
| | } |
| | |
| | function updateHeroMetrics(riverData, salmonData) { |
| | |
| | const totalSalmon = salmonData.reduce((sum, species) => sum + species.seasonTotal, 0); |
| | animateCounter('salmon-counter', totalSalmon); |
| | |
| | |
| | document.getElementById('water-temp').textContent = `${riverData.currentTemp}°C`; |
| | |
| | |
| | document.getElementById('river-flow').textContent = `${riverData.currentFlow.toLocaleString()} m³/s`; |
| | } |
| | |
| | function animateCounter(elementId, target) { |
| | const element = document.getElementById(elementId); |
| | const duration = 2000; |
| | const start = 0; |
| | const increment = target / (duration / 16); |
| | |
| | let current = start; |
| | const timer = setInterval(() => { |
| | current += increment; |
| | if (current >= target) { |
| | clearInterval(timer); |
| | current = target; |
| | } |
| | element.textContent = Math.floor(current).toLocaleString(); |
| | }, 16); |
| | } |
| | |
| | function createCharts(riverData, salmonData) { |
| | |
| | const tempChart = new ApexCharts(document.querySelector("#temp-chart"), { |
| | series: [{ |
| | name: "Temperature (°C)", |
| | data: riverData.tempHistory |
| | }], |
| | chart: { |
| | type: 'area', |
| | height: '100%', |
| | sparkline: { |
| | enabled: true |
| | }, |
| | animations: { |
| | enabled: false |
| | } |
| | }, |
| | stroke: { |
| | curve: 'smooth', |
| | width: 2 |
| | }, |
| | fill: { |
| | type: 'gradient', |
| | gradient: { |
| | shadeIntensity: 1, |
| | opacityFrom: 0.7, |
| | opacityTo: 0.3, |
| | } |
| | }, |
| | colors: ['#3b82f6'], |
| | tooltip: { |
| | fixed: { |
| | enabled: false |
| | }, |
| | x: { |
| | show: false |
| | }, |
| | marker: { |
| | show: false |
| | } |
| | } |
| | }); |
| | tempChart.render(); |
| | |
| | |
| | const levelChart = new ApexCharts(document.querySelector("#level-chart"), { |
| | series: [{ |
| | name: "Level (m)", |
| | data: riverData.levelHistory |
| | }], |
| | chart: { |
| | type: 'area', |
| | height: '100%', |
| | sparkline: { |
| | enabled: true |
| | }, |
| | animations: { |
| | enabled: false |
| | } |
| | }, |
| | stroke: { |
| | curve: 'smooth', |
| | width: 2 |
| | }, |
| | fill: { |
| | type: 'gradient', |
| | gradient: { |
| | shadeIntensity: 1, |
| | opacityFrom: 0.7, |
| | opacityTo: 0.3, |
| | } |
| | }, |
| | colors: ['#10b981'], |
| | tooltip: { |
| | fixed: { |
| | enabled: false |
| | }, |
| | x: { |
| | show: false |
| | }, |
| | marker: { |
| | show: false |
| | } |
| | } |
| | }); |
| | levelChart.render(); |
| | |
| | |
| | const flowChart = new ApexCharts(document.querySelector("#flow-chart"), { |
| | series: [{ |
| | name: "Flow (m³/s)", |
| | data: riverData.flowHistory |
| | }], |
| | chart: { |
| | type: 'area', |
| | height: '100%', |
| | sparkline: { |
| | enabled: true |
| | }, |
| | animations: { |
| | enabled: false |
| | } |
| | }, |
| | stroke: { |
| | curve: 'smooth', |
| | width: 2 |
| | }, |
| | fill: { |
| | type: 'gradient', |
| | gradient: { |
| | shadeIntensity: 1, |
| | opacityFrom: 0.7, |
| | opacityTo: 0.3, |
| | } |
| | }, |
| | colors: ['#8b5cf6'], |
| | tooltip: { |
| | fixed: { |
| | enabled: false |
| | }, |
| | x: { |
| | show: false |
| | }, |
| | marker: { |
| | show: false |
| | } |
| | } |
| | }); |
| | flowChart.render(); |
| | |
| | |
| | const weeklyChart = new ApexCharts(document.querySelector("#weekly-chart"), { |
| | series: [{ |
| | name: "Expected Salmon Count", |
| | data: [5200, 5800, 6200, 6500, 7000, 7500, 8000] |
| | }], |
| | chart: { |
| | type: 'line', |
| | height: '100%', |
| | toolbar: { |
| | show: false |
| | }, |
| | zoom: { |
| | enabled: false |
| | } |
| | }, |
| | colors: ['#3b82f6'], |
| | stroke: { |
| | width: 3, |
| | curve: 'smooth' |
| | }, |
| | fill: { |
| | type: 'gradient', |
| | gradient: { |
| | shadeIntensity: 1, |
| | opacityFrom: 0.7, |
| | opacityTo: 0.3, |
| | } |
| | }, |
| | xaxis: { |
| | categories: ['Today', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
| | labels: { |
| | style: { |
| | colors: '#6b7280' |
| | } |
| | } |
| | }, |
| | yaxis: { |
| | labels: { |
| | formatter: function(val) { |
| | return val.toLocaleString(); |
| | }, |
| | style: { |
| | colors: '#6b7280' |
| | } |
| | } |
| | }, |
| | tooltip: { |
| | y: { |
| | formatter: function(val) { |
| | return val.toLocaleString(); |
| | } |
| | } |
| | } |
| | }); |
| | weeklyChart.render(); |
| | |
| | |
| | const historicalChart = new ApexCharts(document.querySelector("#historical-chart"), { |
| | series: [{ |
| | name: "Total Salmon Count", |
| | data: [ |
| | 1200000, |
| | 1500000, |
| | 1100000, |
| | 1800000, |
| | 1400000 |
| | ] |
| | }], |
| | chart: { |
| | type: 'bar', |
| | height: '100%', |
| | toolbar: { |
| | show: false |
| | } |
| | }, |
| | colors: ['#10b981'], |
| | plotOptions: { |
| | bar: { |
| | borderRadius: 4, |
| | columnWidth: '60%' |
| | } |
| | }, |
| | xaxis: { |
| | categories: ['2020', '2021', '2022', '2023', '2024'], |
| | labels: { |
| | style: { |
| | colors: '#6b7280' |
| | } |
| | } |
| | }, |
| | yaxis: { |
| | labels: { |
| | formatter: function(val) { |
| | return (val/1000).toFixed(0) + 'K'; |
| | }, |
| | style: { |
| | colors: '#6b7280' |
| | } |
| | } |
| | }, |
| | tooltip: { |
| | y: { |
| | formatter: function(val) { |
| | return val.toLocaleString(); |
| | } |
| | } |
| | } |
| | }); |
| | historicalChart.render(); |
| | } |
| | </script> |
| | <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=adogs1/test" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| | </html> |