Spaces:
No application file
No application file
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>WaterHub - Your Local Water Quality Report</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet"> | |
| <script src="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"></script> | |
| <style> | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%); | |
| } | |
| .water-wave { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .water-wave::after { | |
| content: ""; | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| height: 20px; | |
| background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 1200 120" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none"><path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z" fill="%23f8fafc" opacity=".25"/><path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z" fill="%23f8fafc" opacity=".5"/><path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z" fill="%23f8fafc"/></svg>'); | |
| background-size: cover; | |
| } | |
| .score-ring { | |
| stroke-dasharray: 440; | |
| transform: rotate(-90deg); | |
| transform-origin: 50% 50%; | |
| } | |
| .contaminant-bar { | |
| transition: width 0.5s ease-in-out; | |
| } | |
| .map-container { | |
| height: 300px; | |
| border-radius: 0.5rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .gradient-bar { | |
| background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%); | |
| } | |
| .location-search { | |
| position: relative; | |
| } | |
| .location-dropdown { | |
| position: absolute; | |
| top: 100%; | |
| left: 0; | |
| right: 0; | |
| background: white; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 0.375rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| z-index: 10; | |
| max-height: 200px; | |
| overflow-y: auto; | |
| } | |
| .location-dropdown-item { | |
| padding: 0.5rem 1rem; | |
| cursor: pointer; | |
| } | |
| .location-dropdown-item:hover { | |
| background-color: #f3f4f6; | |
| } | |
| #map { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .map-marker { | |
| background-color: #ef4444; | |
| border-radius: 50%; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid white; | |
| box-shadow: 0 0 0 10px rgba(239, 68, 68, 0.3); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); | |
| } | |
| 70% { | |
| box-shadow: 0 0 0 15px rgba(239, 68, 68, 0); | |
| } | |
| 100% { | |
| box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); | |
| } | |
| } | |
| .map-radius { | |
| border-radius: 50%; | |
| background-color: rgba(239, 68, 68, 0.2); | |
| border: 2px solid #ef4444; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 font-sans"> | |
| <!-- Header --> | |
| <header class="gradient-bg text-white water-wave"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <div class="flex justify-center"> | |
| <img src="https://waterhub.co/wp-content/uploads/2025/04/WaterHub-Updated-Logo.png" alt="WaterHub Logo" class="h-16"> | |
| </div> | |
| <div class="mt-12 mb-16 text-center"> | |
| <h2 class="text-4xl font-bold mb-4">Your Local Water Quality</h2> | |
| <p class="text-xl max-w-2xl mx-auto">See how clean and safe your drinking water is based on your location in Southern Ontario</p> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="container mx-auto px-4 py-12 -mt-10"> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden"> | |
| <!-- Location Info --> | |
| <div class="p-6 border-b border-gray-200"> | |
| <div class="flex flex-col md:flex-row justify-between items-start md:items-center"> | |
| <div class="w-full md:w-auto"> | |
| <h3 class="text-2xl font-semibold text-gray-800">Water Quality Report For:</h3> | |
| <div class="location-search mt-2 w-full md:w-96"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-map-marker-alt text-blue-500 mr-2"></i> | |
| <input type="text" id="location-input" class="flex-1 border-b border-gray-300 py-1 px-2 focus:outline-none focus:border-blue-500" placeholder="Enter your location..."> | |
| <button id="use-my-location" class="ml-2 text-blue-600 hover:text-blue-800"> | |
| <i class="fas fa-location-arrow"></i> | |
| </button> | |
| </div> | |
| <div id="location-dropdown" class="location-dropdown hidden"></div> | |
| </div> | |
| <p id="location-display" class="mt-2 text-lg text-gray-600 hidden"></p> | |
| </div> | |
| <button id="refresh-btn" class="mt-4 md:mt-0 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center"> | |
| <i class="fas fa-sync-alt mr-2"></i> Refresh | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Water Quality Dashboard --> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 p-6"> | |
| <!-- Score Card --> | |
| <div class="bg-gray-50 p-6 rounded-lg"> | |
| <h4 class="text-lg font-medium text-gray-700 mb-4">Overall Water Quality Score</h4> | |
| <div class="relative w-40 h-40 mx-auto mb-6"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#e5e7eb" stroke-width="8"/> | |
| <circle id="score-circle" cx="50" cy="50" r="45" fill="none" stroke="#3b82f6" stroke-width="8" stroke-linecap="round" class="score-ring" stroke-dashoffset="440"/> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center flex-col"> | |
| <span id="score-value" class="text-4xl font-bold text-gray-800">--</span> | |
| <span class="text-gray-500">out of 100</span> | |
| </div> | |
| </div> | |
| <div id="score-message" class="text-center text-gray-600"> | |
| Loading your water quality score... | |
| </div> | |
| </div> | |
| <!-- Contaminants Breakdown --> | |
| <div class="lg:col-span-2"> | |
| <h4 class="text-lg font-medium text-gray-700 mb-4">Contaminants Breakdown</h4> | |
| <div id="contaminants-container" class="space-y-4"> | |
| <div class="animate-pulse space-y-4"> | |
| <div class="h-4 bg-gray-200 rounded w-3/4"></div> | |
| <div class="h-4 bg-gray-200 rounded"></div> | |
| <div class="h-4 bg-gray-200 rounded w-5/6"></div> | |
| <div class="h-4 bg-gray-200 rounded w-2/3"></div> | |
| </div> | |
| </div> | |
| <div class="mt-8"> | |
| <h4 class="text-lg font-medium text-gray-700 mb-4">Safety Information</h4> | |
| <div id="safety-info" class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r"> | |
| <p class="text-blue-800">Your water meets Health Canada's safety standards. No immediate health risks detected.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Map and Additional Info --> | |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-8 p-6 border-t border-gray-200"> | |
| <div class="map-container"> | |
| <div id="map"></div> | |
| </div> | |
| <div> | |
| <h4 class="text-lg font-medium text-gray-700 mb-4">How to Improve Your Water Quality</h4> | |
| <div class="space-y-4"> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4"> | |
| <i class="fas fa-filter"></i> | |
| </div> | |
| <div> | |
| <h5 class="font-medium text-gray-800">Install a Water Filter</h5> | |
| <p class="text-gray-600 mt-1">Our advanced filtration systems can remove up to 99.9% of contaminants.</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4"> | |
| <i class="fas fa-tint"></i> | |
| </div> | |
| <div> | |
| <h5 class="font-medium text-gray-800">Test Your Water Regularly</h5> | |
| <p class="text-gray-600 mt-1">Water quality can change. Regular testing ensures ongoing safety.</p> | |
| </div> | |
| </div> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4"> | |
| <i class="fas fa-info-circle"></i> | |
| </div> | |
| <div> | |
| <h5 class="font-medium text-gray-800">Learn About Your Water</h5> | |
| <p class="text-gray-600 mt-1">Understanding contaminants helps you make informed decisions.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-8"> | |
| <a href="https://waterhub.co/shop" class="inline-flex items-center px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"> | |
| Shop Water Solutions | |
| <i class="fas fa-arrow-right ml-2"></i> | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="bg-gray-800 text-white py-12"> | |
| <div class="container mx-auto px-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-8"> | |
| <div> | |
| <img src="https://waterhub.co/wp-content/uploads/2025/04/WaterHub-Updated-Logo.png" alt="WaterHub Logo" class="h-10 mb-4"> | |
| <p class="text-gray-400">Providing clean, safe water solutions for Southern Ontario homes and businesses.</p> | |
| </div> | |
| <div> | |
| <h5 class="text-lg font-medium mb-4">Quick Links</h5> | |
| <ul class="space-y-2"> | |
| <li><a href="https://waterhub.co/" class="text-gray-400 hover:text-white transition">Home</a></li> | |
| <li><a href="https://waterhub.co/shop" class="text-gray-400 hover:text-white transition">Shop</a></li> | |
| <li><a href="https://waterhub.co/about" class="text-gray-400 hover:text-white transition">About Us</a></li> | |
| <li><a href="https://waterhub.co/contact" class="text-gray-400 hover:text-white transition">Contact</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h5 class="text-lg font-medium mb-4">Resources</h5> | |
| <ul class="space-y-2"> | |
| <li><a href="https://waterhub.co/blog" class="text-gray-400 hover:text-white transition">Blog</a></li> | |
| <li><a href="https://waterhub.co/water-quality" class="text-gray-400 hover:text-white transition">Water Quality Guide</a></li> | |
| <li><a href="https://waterhub.co/faq" class="text-gray-400 hover:text-white transition">FAQs</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h5 class="text-lg font-medium mb-4">Contact Us</h5> | |
| <ul class="space-y-2 text-gray-400"> | |
| <li class="flex items-center"><i class="fas fa-phone-alt mr-2"></i> +1 (647) 560-2795</li> | |
| <li class="flex items-center"><i class="fas fa-envelope mr-2"></i> info@waterhub.co</li> | |
| <li class="flex items-center"><i class="fas fa-map-marker-alt mr-2"></i> Toronto, ON</li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400"> | |
| <p>© 2023 WaterHub. All rights reserved.</p> | |
| </div> | |
| </div> | |
| </footer> | |
| <script> | |
| // Database of Southern Ontario water quality data with coordinates | |
| const waterQualityData = { | |
| "toronto": { | |
| score: 87, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.8, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 2.1, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Toronto's water meets all provincial and federal standards. Chlorine levels are slightly elevated but within safe limits.", | |
| coordinates: [-79.3832, 43.6532] | |
| }, | |
| "mississauga": { | |
| score: 92, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.5, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.001, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 1.8, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Mississauga has good water quality with all contaminants well below safety thresholds.", | |
| coordinates: [-79.6441, 43.5890] | |
| }, | |
| "brampton": { | |
| score: 85, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.5, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Brampton's water is safe to drink. Chlorine levels are higher than average but still within safe limits.", | |
| coordinates: [-79.7593, 43.7315] | |
| }, | |
| "hamilton": { | |
| score: 72, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.5, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 1.1, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 5.2, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Hamilton's water meets safety standards but has elevated levels of several contaminants. Consider filtration for improved taste.", | |
| coordinates: [-79.8711, 43.2557] | |
| }, | |
| "london": { | |
| score: 83, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 4.1, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "London's water quality is fair with all contaminants within safe levels. Nitrates are slightly elevated but not concerning.", | |
| coordinates: [-81.2453, 42.9849] | |
| }, | |
| "markham": { | |
| score: 89, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.7, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 2.8, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Markham's water quality is excellent with all contaminants well within safe limits.", | |
| coordinates: [-79.3297, 43.8561] | |
| }, | |
| "vaughan": { | |
| score: 88, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.0, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Vaughan's water quality is very good with all contaminants within safe levels.", | |
| coordinates: [-79.4985, 43.8361] | |
| }, | |
| "ajax": { | |
| score: 86, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.2, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Ajax's water quality is good with all contaminants within safe levels.", | |
| coordinates: [-79.0207, 43.8509] | |
| }, | |
| "peterborough": { | |
| score: 81, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.3, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 1.0, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 4.5, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Peterborough's water meets safety standards but has slightly elevated levels of some contaminants.", | |
| coordinates: [-78.3168, 44.3091] | |
| }, | |
| "newmarket": { | |
| score: 87, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.8, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.0, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Newmarket's water quality is very good with all contaminants within safe levels.", | |
| coordinates: [-79.4593, 44.0553] | |
| }, | |
| "barrie": { | |
| score: 84, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.1, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.8, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Barrie's water quality is good with all contaminants within safe levels.", | |
| coordinates: [-79.6900, 44.3894] | |
| }, | |
| "milton": { | |
| score: 90, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.6, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.001, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 2.5, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Milton's water quality is excellent with all contaminants well within safe limits.", | |
| coordinates: [-79.8770, 43.5183] | |
| }, | |
| "waterloo": { | |
| score: 85, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.5, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Waterloo's water quality is good with all contaminants within safe levels.", | |
| coordinates: [-80.5204, 43.4643] | |
| }, | |
| "kitchener": { | |
| score: 83, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 4.0, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Kitchener's water quality is fair with all contaminants within safe levels.", | |
| coordinates: [-80.4926, 43.4516] | |
| }, | |
| "st. catharines": { | |
| score: 80, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.4, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 1.1, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 4.8, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "St. Catharines' water meets safety standards but has elevated levels of some contaminants.", | |
| coordinates: [-79.2461, 43.1594] | |
| }, | |
| "collingwood": { | |
| score: 82, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 4.2, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Collingwood's water quality is fair with all contaminants within safe levels.", | |
| coordinates: [-80.2164, 44.5008] | |
| }, | |
| "bowmanville": { | |
| score: 86, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.1, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Bowmanville's water quality is good with all contaminants within safe levels.", | |
| coordinates: [-78.6833, 43.9167] | |
| }, | |
| "kingston": { | |
| score: 79, | |
| contaminants: [ | |
| { name: "Chlorine", level: 1.5, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 1.2, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 5.0, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Kingston's water meets safety standards but has elevated levels of several contaminants.", | |
| coordinates: [-76.4850, 44.2312] | |
| }, | |
| "ottawa": { | |
| score: 88, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.7, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 2.9, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Ottawa's water quality is excellent with all contaminants well within safe limits.", | |
| coordinates: [-75.6972, 45.4215] | |
| }, | |
| "default": { | |
| score: 82, | |
| contaminants: [ | |
| { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" }, | |
| { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" }, | |
| { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" }, | |
| { name: "Nitrates", level: 3.8, safeLevel: 10, unit: "mg/L" } | |
| ], | |
| message: "Your water meets all safety standards. No immediate health risks detected.", | |
| coordinates: [-79.3832, 43.6532] // Default to Toronto | |
| } | |
| }; | |
| // Initialize map | |
| mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw'; | |
| const map = new mapboxgl.Map({ | |
| container: 'map', | |
| style: 'mapbox://styles/mapbox/satellite-v9', | |
| center: [-79.3832, 43.6532], // Default to Toronto | |
| zoom: 8 | |
| }); | |
| // Add navigation controls | |
| map.addControl(new mapboxgl.NavigationControl()); | |
| let marker = null; | |
| let radiusCircle = null; | |
| // Function to update the map with marker and radius | |
| function updateMap(lngLat) { | |
| // Remove existing marker and radius if they exist | |
| if (marker) marker.remove(); | |
| if (radiusCircle) radiusCircle.remove(); | |
| // Add new marker | |
| const el = document.createElement('div'); | |
| el.className = 'map-marker'; | |
| marker = new mapboxgl.Marker(el) | |
| .setLngLat(lngLat) | |
| .addTo(map); | |
| // Add radius circle (50km) | |
| radiusCircle = new mapboxgl.Popup({ closeButton: false, closeOnClick: false }) | |
| .setLngLat(lngLat) | |
| .setHTML('<div class="map-radius" style="width: 100px; height: 100px;"></div>') | |
| .addTo(map); | |
| // Fly to the location | |
| map.flyTo({ | |
| center: lngLat, | |
| zoom: 9, | |
| essential: true | |
| }); | |
| } | |
| // Function to update the UI with water quality data | |
| function updateWaterQuality(city) { | |
| const cityLower = city.toLowerCase(); | |
| const data = waterQualityData[cityLower] || waterQualityData["default"]; | |
| // Update score | |
| document.getElementById('score-value').textContent = data.score; | |
| const circle = document.getElementById('score-circle'); | |
| const offset = 440 - (440 * data.score / 100); | |
| circle.style.strokeDashoffset = offset; | |
| // Update score message | |
| document.getElementById('score-message').textContent = getScoreMessage(data.score); | |
| // Update safety info with recommendation | |
| const safetyRating = data.score >= 95 ? "Excellent" : | |
| data.score >= 90 ? "Fair" : "Poor"; | |
| document.getElementById('safety-info').innerHTML = ` | |
| <p class="text-blue-800">${data.message}</p> | |
| <p class="mt-2 font-medium">Safety Rating: ${safetyRating}</p> | |
| <p class="mt-2">${getRecommendation(data.score)}</p> | |
| `; | |
| // Update contaminants | |
| const container = document.getElementById('contaminants-container'); | |
| container.innerHTML = ''; | |
| data.contaminants.forEach(contaminant => { | |
| const percentage = Math.min(100, (contaminant.level / contaminant.safeLevel) * 100); | |
| const isSafe = percentage <= 100; | |
| const contaminantEl = document.createElement('div'); | |
| contaminantEl.className = 'space-y-2'; | |
| contaminantEl.innerHTML = ` | |
| <div class="flex justify-between"> | |
| <span class="font-medium">${contaminant.name}</span> | |
| <span class="text-sm ${isSafe ? 'text-green-600' : 'text-red-600'}"> | |
| ${contaminant.level} ${contaminant.unit} / ${contaminant.safeLevel} ${contaminant.unit} | |
| </span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div class="contaminant-bar gradient-bar h-2.5 rounded-full" | |
| style="width: ${percentage}%"></div> | |
| </div> | |
| `; | |
| container.appendChild(contaminantEl); | |
| }); | |
| // Update map | |
| updateMap(data.coordinates); | |
| } | |
| // Get appropriate message based on score | |
| function getScoreMessage(score) { | |
| if (score >= 95) return "Excellent Water Quality"; | |
| if (score >= 90) return "Fair Water Quality"; | |
| if (score >= 80) return "Poor Water Quality"; | |
| return "Very Poor Water Quality"; | |
| } | |
| // Get recommendation based on score | |
| function getRecommendation(score) { | |
| if (score >= 95) return "Your water is excellent quality. Consider water softening solutions from <a href='https://waterhub.co/' class='text-blue-600 hover:underline'>WaterHub.co</a> for ideal conditions."; | |
| if (score >= 90) return "Your water is fair quality. A filter is recommended to ensure ideal conditions. Check <a href='https://waterhub.co/' class='text-blue-600 hover:underline'>WaterHub.co</a> for solutions."; | |
| if (score >= 80) return "Your water is poor quality. We recommend solutions from <a href='https://waterhub.co/' class='text-blue-600 hover:underline'>WaterHub.co</a>."; | |
| return "Your water quality is very poor. You need a water filter solution. Visit <a href='https://waterhub.co/' class='text-blue-600 hover:underline'>WaterHub.co</a> for options."; | |
| } | |
| // Function to get user's location via IP | |
| async function getLocationByIP() { | |
| try { | |
| const response = await fetch('https://ipapi.co/json/'); | |
| const data = await response.json(); | |
| // Check if the location is in Ontario | |
| if (data.region === "Ontario") { | |
| // Find the closest city in our database | |
| let closestCity = "toronto"; | |
| let minDistance = Infinity; | |
| for (const city in waterQualityData) { | |
| if (city === "default") continue; | |
| const cityData = waterQualityData[city]; | |
| const distance = Math.sqrt( | |
| Math.pow(data.longitude - cityData.coordinates[0], 2) + | |
| Math.pow(data.latitude - cityData.coordinates[1], 2) | |
| ); | |
| if (distance < minDistance) { | |
| minDistance = distance; | |
| closestCity = city; | |
| } | |
| } | |
| return closestCity; | |
| } else { | |
| return "toronto"; // Default to Toronto if not in Ontario | |
| } | |
| } catch (error) { | |
| console.error("Error getting location by IP:", error); | |
| return "toronto"; // Default to Toronto on error | |
| } | |
| } | |
| // Function to get user's precise location via geolocation API | |
| function getPreciseLocation() { | |
| return new Promise((resolve, reject) => { | |
| if (navigator.geolocation) { | |
| navigator.geolocation.getCurrentPosition( | |
| (position) => { | |
| // Find the closest city in our database | |
| let closestCity = "toronto"; | |
| let minDistance = Infinity; | |
| for (const city in waterQualityData) { | |
| if (city === "default") continue; | |
| const cityData = waterQualityData[city]; | |
| const distance = Math.sqrt( | |
| Math.pow(position.coords.longitude - cityData.coordinates[0], 2) + | |
| Math.pow(position.coords.latitude - cityData.coordinates[1], 2) | |
| ); | |
| if (distance < minDistance) { | |
| minDistance = distance; | |
| closestCity = city; | |
| } | |
| } | |
| resolve(closestCity); | |
| }, | |
| (error) => { | |
| console.error("Geolocation error:", error); | |
| resolve("toronto"); // Default to Toronto on error | |
| } | |
| ); | |
| } else { | |
| resolve("toronto"); // Default to Toronto if geolocation not supported | |
| } | |
| }); | |
| } | |
| // Function to search for locations as user types | |
| function searchLocations(query) { | |
| const results = []; | |
| const queryLower = query.toLowerCase(); | |
| for (const city in waterQualityData) { | |
| if (city === "default") continue; | |
| if (city.includes(queryLower)) { | |
| results.push({ | |
| name: city.charAt(0).toUpperCase() + city.slice(1), | |
| key: city | |
| }); | |
| } | |
| } | |
| return results; | |
| } | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', async () => { | |
| // Set default to Toronto | |
| let currentLocation = "toronto"; | |
| document.getElementById('location-input').value = "Toronto"; | |
| document.getElementById('location-display').textContent = "Toronto, ON"; | |
| document.getElementById('location-display').classList.remove('hidden'); | |
| updateWaterQuality(currentLocation); | |
| // Try to get more accurate location | |
| try { | |
| const preciseLocation = await getPreciseLocation(); | |
| if (preciseLocation !== "toronto") { | |
| currentLocation = preciseLocation; | |
| const displayName = preciseLocation.charAt(0).toUpperCase() + preciseLocation.slice(1); | |
| document.getElementById('location-input').value = displayName; | |
| document.getElementById('location-display').textContent = `${displayName}, ON`; | |
| updateWaterQuality(currentLocation); | |
| } else { | |
| // Fall back to IP-based location if precise location not available | |
| const ipLocation = await getLocationByIP(); | |
| if (ipLocation !== "toronto") { | |
| currentLocation = ipLocation; | |
| const displayName = ipLocation.charAt(0).toUpperCase() + ipLocation.slice(1); | |
| document.getElementById('location-input').value = displayName; | |
| document.getElementById('location-display').textContent = `${displayName}, ON`; | |
| updateWaterQuality(currentLocation); | |
| } | |
| } | |
| } catch (error) { | |
| console.error("Error getting location:", error); | |
| } | |
| // Location input event listeners | |
| const locationInput = document.getElementById('location-input'); | |
| const locationDropdown = document.getElementById('location-dropdown'); | |
| locationInput.addEventListener('input', () => { | |
| const query = locationInput.value.trim(); | |
| if (query.length > 1) { | |
| const results = searchLocations(query.toLowerCase()); | |
| if (results.length > 0) { | |
| locationDropdown.innerHTML = ''; | |
| results.forEach(result => { | |
| const item = document.createElement('div'); | |
| item.className = 'location-dropdown-item'; | |
| item.textContent = result.name; | |
| item.addEventListener('click', () => { | |
| locationInput.value = result.name; | |
| document.getElementById('location-display').textContent = `${result.name}, ON`; | |
| document.getElementById('location-display').classList.remove('hidden'); | |
| locationDropdown.classList.add('hidden'); | |
| updateWaterQuality(result.key); | |
| }); | |
| locationDropdown.appendChild(item); | |
| }); | |
| locationDropdown.classList.remove('hidden'); | |
| } else { | |
| locationDropdown.classList.add('hidden'); | |
| } | |
| } else { | |
| locationDropdown.classList.add('hidden'); | |
| } | |
| }); | |
| locationInput.addEventListener('focus', () => { | |
| if (locationInput.value.trim().length > 1 && locationDropdown.children.length > 0) { | |
| locationDropdown.classList.remove('hidden'); | |
| } | |
| }); | |
| document.addEventListener('click', (e) => { | |
| if (!locationInput.contains(e.target) && !locationDropdown.contains(e.target)) { | |
| locationDropdown.classList.add('hidden'); | |
| } | |
| }); | |
| // Use my location button | |
| document.getElementById('use-my-location').addEventListener('click', async () => { | |
| locationInput.value = "Detecting your location..."; | |
| document.getElementById('location-display').classList.add('hidden'); | |
| try { | |
| const location = await getPreciseLocation(); | |
| const displayName = location.charAt(0).toUpperCase() + location.slice(1); | |
| locationInput.value = displayName; | |
| document.getElementById('location-display').textContent = `${displayName}, ON`; | |
| document.getElementById('location-display').classList.remove('hidden'); | |
| updateWaterQuality(location); | |
| } catch (error) { | |
| console.error("Error getting location:", error); | |
| locationInput.value = "Toronto"; | |
| document.getElementById('location-display').textContent = "Toronto, ON"; | |
| document.getElementById('location-display').classList.remove('hidden'); | |
| updateWaterQuality("toronto"); | |
| } | |
| }); | |
| // Refresh button | |
| document.getElementById('refresh-btn').addEventListener('click', () => { | |
| const currentValue = locationInput.value.trim(); | |
| if (currentValue && waterQualityData[currentValue.toLowerCase()]) { | |
| updateWaterQuality(currentValue.toLowerCase()); | |
| } else { | |
| updateWaterQuality("toronto"); | |
| locationInput.value = "Toronto"; | |
| document.getElementById('location-display').textContent = "Toronto, ON"; | |
| } | |
| }); | |
| }); | |
| </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=Pandafacewin/beta3" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |