WaterHubApp commited on
Commit
3cd4e40
·
verified ·
1 Parent(s): 0ae49e6

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +819 -819
index.html CHANGED
@@ -1,820 +1,820 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>WaterHub - Your Local Water Quality Report</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
- <link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet">
10
- <script src="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"></script>
11
- <style>
12
- .gradient-bg {
13
- background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
14
- }
15
- .water-wave {
16
- position: relative;
17
- overflow: hidden;
18
- }
19
- .water-wave::after {
20
- content: "";
21
- position: absolute;
22
- bottom: 0;
23
- left: 0;
24
- right: 0;
25
- height: 20px;
26
- 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>');
27
- background-size: cover;
28
- }
29
- .score-ring {
30
- stroke-dasharray: 440;
31
- transform: rotate(-90deg);
32
- transform-origin: 50% 50%;
33
- }
34
- .contaminant-bar {
35
- transition: width 0.5s ease-in-out;
36
- }
37
- .map-container {
38
- height: 300px;
39
- border-radius: 0.5rem;
40
- position: relative;
41
- overflow: hidden;
42
- }
43
- .gradient-bar {
44
- background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);
45
- }
46
- .location-search {
47
- position: relative;
48
- }
49
- .location-dropdown {
50
- position: absolute;
51
- top: 100%;
52
- left: 0;
53
- right: 0;
54
- background: white;
55
- border: 1px solid #e5e7eb;
56
- border-radius: 0.375rem;
57
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
58
- z-index: 10;
59
- max-height: 200px;
60
- overflow-y: auto;
61
- }
62
- .location-dropdown-item {
63
- padding: 0.5rem 1rem;
64
- cursor: pointer;
65
- }
66
- .location-dropdown-item:hover {
67
- background-color: #f3f4f6;
68
- }
69
- #map {
70
- width: 100%;
71
- height: 100%;
72
- }
73
- .map-marker {
74
- background-color: #ef4444;
75
- border-radius: 50%;
76
- width: 20px;
77
- height: 20px;
78
- border: 2px solid white;
79
- box-shadow: 0 0 0 10px rgba(239, 68, 68, 0.3);
80
- animation: pulse 2s infinite;
81
- }
82
- @keyframes pulse {
83
- 0% {
84
- box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
85
- }
86
- 70% {
87
- box-shadow: 0 0 0 15px rgba(239, 68, 68, 0);
88
- }
89
- 100% {
90
- box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
91
- }
92
- }
93
- .map-radius {
94
- border-radius: 50%;
95
- background-color: rgba(239, 68, 68, 0.2);
96
- border: 2px solid #ef4444;
97
- }
98
- </style>
99
- </head>
100
- <body class="bg-gray-50 font-sans">
101
- <!-- Header -->
102
- <header class="gradient-bg text-white water-wave">
103
- <div class="container mx-auto px-4 py-8">
104
- <div class="flex justify-center">
105
- <img src="https://waterhub.co/wp-content/uploads/2025/04/WaterHub-Updated-Logo.png" alt="WaterHub Logo" class="h-16">
106
- </div>
107
-
108
- <div class="mt-12 mb-16 text-center">
109
- <h2 class="text-4xl font-bold mb-4">Your Local Water Quality</h2>
110
- <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>
111
- </div>
112
- </div>
113
- </header>
114
-
115
- <!-- Main Content -->
116
- <main class="container mx-auto px-4 py-12 -mt-10">
117
- <div class="bg-white rounded-xl shadow-lg overflow-hidden">
118
- <!-- Location Info -->
119
- <div class="p-6 border-b border-gray-200">
120
- <div class="flex flex-col md:flex-row justify-between items-start md:items-center">
121
- <div class="w-full md:w-auto">
122
- <h3 class="text-2xl font-semibold text-gray-800">Water Quality Report For:</h3>
123
- <div class="location-search mt-2 w-full md:w-96">
124
- <div class="flex items-center">
125
- <i class="fas fa-map-marker-alt text-blue-500 mr-2"></i>
126
- <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...">
127
- <button id="use-my-location" class="ml-2 text-blue-600 hover:text-blue-800">
128
- <i class="fas fa-location-arrow"></i>
129
- </button>
130
- </div>
131
- <div id="location-dropdown" class="location-dropdown hidden"></div>
132
- </div>
133
- <p id="location-display" class="mt-2 text-lg text-gray-600 hidden"></p>
134
- </div>
135
- <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">
136
- <i class="fas fa-sync-alt mr-2"></i> Refresh
137
- </button>
138
- </div>
139
- </div>
140
-
141
- <!-- Water Quality Dashboard -->
142
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 p-6">
143
- <!-- Score Card -->
144
- <div class="bg-gray-50 p-6 rounded-lg">
145
- <h4 class="text-lg font-medium text-gray-700 mb-4">Overall Water Quality Score</h4>
146
- <div class="relative w-40 h-40 mx-auto mb-6">
147
- <svg class="w-full h-full" viewBox="0 0 100 100">
148
- <circle cx="50" cy="50" r="45" fill="none" stroke="#e5e7eb" stroke-width="8"/>
149
- <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"/>
150
- </svg>
151
- <div class="absolute inset-0 flex items-center justify-center flex-col">
152
- <span id="score-value" class="text-4xl font-bold text-gray-800">--</span>
153
- <span class="text-gray-500">out of 100</span>
154
- </div>
155
- </div>
156
- <div id="score-message" class="text-center text-gray-600">
157
- Loading your water quality score...
158
- </div>
159
- </div>
160
-
161
- <!-- Contaminants Breakdown -->
162
- <div class="lg:col-span-2">
163
- <h4 class="text-lg font-medium text-gray-700 mb-4">Contaminants Breakdown</h4>
164
- <div id="contaminants-container" class="space-y-4">
165
- <div class="animate-pulse space-y-4">
166
- <div class="h-4 bg-gray-200 rounded w-3/4"></div>
167
- <div class="h-4 bg-gray-200 rounded"></div>
168
- <div class="h-4 bg-gray-200 rounded w-5/6"></div>
169
- <div class="h-4 bg-gray-200 rounded w-2/3"></div>
170
- </div>
171
- </div>
172
-
173
- <div class="mt-8">
174
- <h4 class="text-lg font-medium text-gray-700 mb-4">Safety Information</h4>
175
- <div id="safety-info" class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r">
176
- <p class="text-blue-800">Your water meets Health Canada's safety standards. No immediate health risks detected.</p>
177
- </div>
178
- </div>
179
- </div>
180
- </div>
181
-
182
- <!-- Map and Additional Info -->
183
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-8 p-6 border-t border-gray-200">
184
- <div class="map-container">
185
- <div id="map"></div>
186
- </div>
187
-
188
- <div>
189
- <h4 class="text-lg font-medium text-gray-700 mb-4">How to Improve Your Water Quality</h4>
190
- <div class="space-y-4">
191
- <div class="flex items-start">
192
- <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4">
193
- <i class="fas fa-filter"></i>
194
- </div>
195
- <div>
196
- <h5 class="font-medium text-gray-800">Install a Water Filter</h5>
197
- <p class="text-gray-600 mt-1">Our advanced filtration systems can remove up to 99.9% of contaminants.</p>
198
- </div>
199
- </div>
200
-
201
- <div class="flex items-start">
202
- <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4">
203
- <i class="fas fa-tint"></i>
204
- </div>
205
- <div>
206
- <h5 class="font-medium text-gray-800">Test Your Water Regularly</h5>
207
- <p class="text-gray-600 mt-1">Water quality can change. Regular testing ensures ongoing safety.</p>
208
- </div>
209
- </div>
210
-
211
- <div class="flex items-start">
212
- <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4">
213
- <i class="fas fa-info-circle"></i>
214
- </div>
215
- <div>
216
- <h5 class="font-medium text-gray-800">Learn About Your Water</h5>
217
- <p class="text-gray-600 mt-1">Understanding contaminants helps you make informed decisions.</p>
218
- </div>
219
- </div>
220
- </div>
221
-
222
- <div class="mt-8">
223
- <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">
224
- Shop Water Solutions
225
- <i class="fas fa-arrow-right ml-2"></i>
226
- </a>
227
- </div>
228
- </div>
229
- </div>
230
- </div>
231
- </main>
232
-
233
- <!-- Footer -->
234
- <footer class="bg-gray-800 text-white py-12">
235
- <div class="container mx-auto px-4">
236
- <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
237
- <div>
238
- <img src="https://waterhub.co/wp-content/uploads/2025/04/WaterHub-Updated-Logo.png" alt="WaterHub Logo" class="h-10 mb-4">
239
- <p class="text-gray-400">Providing clean, safe water solutions for Southern Ontario homes and businesses.</p>
240
- </div>
241
- <div>
242
- <h5 class="text-lg font-medium mb-4">Quick Links</h5>
243
- <ul class="space-y-2">
244
- <li><a href="https://waterhub.co/" class="text-gray-400 hover:text-white transition">Home</a></li>
245
- <li><a href="https://waterhub.co/shop" class="text-gray-400 hover:text-white transition">Shop</a></li>
246
- <li><a href="https://waterhub.co/about" class="text-gray-400 hover:text-white transition">About Us</a></li>
247
- <li><a href="https://waterhub.co/contact" class="text-gray-400 hover:text-white transition">Contact</a></li>
248
- </ul>
249
- </div>
250
- <div>
251
- <h5 class="text-lg font-medium mb-4">Resources</h5>
252
- <ul class="space-y-2">
253
- <li><a href="https://waterhub.co/blog" class="text-gray-400 hover:text-white transition">Blog</a></li>
254
- <li><a href="https://waterhub.co/water-quality" class="text-gray-400 hover:text-white transition">Water Quality Guide</a></li>
255
- <li><a href="https://waterhub.co/faq" class="text-gray-400 hover:text-white transition">FAQs</a></li>
256
- </ul>
257
- </div>
258
- <div>
259
- <h5 class="text-lg font-medium mb-4">Contact Us</h5>
260
- <ul class="space-y-2 text-gray-400">
261
- <li class="flex items-center"><i class="fas fa-phone-alt mr-2"></i> +1 (647) 560-2795</li>
262
- <li class="flex items-center"><i class="fas fa-envelope mr-2"></i> info@waterhub.co</li>
263
- <li class="flex items-center"><i class="fas fa-map-marker-alt mr-2"></i> Toronto, ON</li>
264
- </ul>
265
- </div>
266
- </div>
267
- <div class="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400">
268
- <p>&copy; 2023 WaterHub. All rights reserved.</p>
269
- </div>
270
- </div>
271
- </footer>
272
-
273
- <script>
274
- // Database of Southern Ontario water quality data with coordinates
275
- const waterQualityData = {
276
- "toronto": {
277
- score: 87,
278
- contaminants: [
279
- { name: "Chlorine", level: 0.8, safeLevel: 4, unit: "mg/L" },
280
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
281
- { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
282
- { name: "Nitrates", level: 2.1, safeLevel: 10, unit: "mg/L" }
283
- ],
284
- message: "Toronto's water meets all provincial and federal standards. Chlorine levels are slightly elevated but within safe limits.",
285
- coordinates: [-79.3832, 43.6532]
286
- },
287
- "mississauga": {
288
- score: 92,
289
- contaminants: [
290
- { name: "Chlorine", level: 0.5, safeLevel: 4, unit: "mg/L" },
291
- { name: "Lead", level: 0.001, safeLevel: 0.005, unit: "mg/L" },
292
- { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
293
- { name: "Nitrates", level: 1.8, safeLevel: 10, unit: "mg/L" }
294
- ],
295
- message: "Mississauga has good water quality with all contaminants well below safety thresholds.",
296
- coordinates: [-79.6441, 43.5890]
297
- },
298
- "brampton": {
299
- score: 85,
300
- contaminants: [
301
- { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" },
302
- { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
303
- { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" },
304
- { name: "Nitrates", level: 3.5, safeLevel: 10, unit: "mg/L" }
305
- ],
306
- message: "Brampton's water is safe to drink. Chlorine levels are higher than average but still within safe limits.",
307
- coordinates: [-79.7593, 43.7315]
308
- },
309
- "hamilton": {
310
- score: 72,
311
- contaminants: [
312
- { name: "Chlorine", level: 1.5, safeLevel: 4, unit: "mg/L" },
313
- { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
314
- { name: "Fluoride", level: 1.1, safeLevel: 1.5, unit: "mg/L" },
315
- { name: "Nitrates", level: 5.2, safeLevel: 10, unit: "mg/L" }
316
- ],
317
- message: "Hamilton's water meets safety standards but has elevated levels of several contaminants. Consider filtration for improved taste.",
318
- coordinates: [-79.8711, 43.2557]
319
- },
320
- "london": {
321
- score: 83,
322
- contaminants: [
323
- { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" },
324
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
325
- { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
326
- { name: "Nitrates", level: 4.1, safeLevel: 10, unit: "mg/L" }
327
- ],
328
- message: "London's water quality is fair with all contaminants within safe levels. Nitrates are slightly elevated but not concerning.",
329
- coordinates: [-81.2453, 42.9849]
330
- },
331
- "markham": {
332
- score: 89,
333
- contaminants: [
334
- { name: "Chlorine", level: 0.7, safeLevel: 4, unit: "mg/L" },
335
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
336
- { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
337
- { name: "Nitrates", level: 2.8, safeLevel: 10, unit: "mg/L" }
338
- ],
339
- message: "Markham's water quality is excellent with all contaminants well within safe limits.",
340
- coordinates: [-79.3297, 43.8561]
341
- },
342
- "vaughan": {
343
- score: 88,
344
- contaminants: [
345
- { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" },
346
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
347
- { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
348
- { name: "Nitrates", level: 3.0, safeLevel: 10, unit: "mg/L" }
349
- ],
350
- message: "Vaughan's water quality is very good with all contaminants within safe levels.",
351
- coordinates: [-79.4985, 43.8361]
352
- },
353
- "ajax": {
354
- score: 86,
355
- contaminants: [
356
- { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" },
357
- { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
358
- { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" },
359
- { name: "Nitrates", level: 3.2, safeLevel: 10, unit: "mg/L" }
360
- ],
361
- message: "Ajax's water quality is good with all contaminants within safe levels.",
362
- coordinates: [-79.0207, 43.8509]
363
- },
364
- "peterborough": {
365
- score: 81,
366
- contaminants: [
367
- { name: "Chlorine", level: 1.3, safeLevel: 4, unit: "mg/L" },
368
- { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
369
- { name: "Fluoride", level: 1.0, safeLevel: 1.5, unit: "mg/L" },
370
- { name: "Nitrates", level: 4.5, safeLevel: 10, unit: "mg/L" }
371
- ],
372
- message: "Peterborough's water meets safety standards but has slightly elevated levels of some contaminants.",
373
- coordinates: [-78.3168, 44.3091]
374
- },
375
- "newmarket": {
376
- score: 87,
377
- contaminants: [
378
- { name: "Chlorine", level: 0.8, safeLevel: 4, unit: "mg/L" },
379
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
380
- { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
381
- { name: "Nitrates", level: 3.0, safeLevel: 10, unit: "mg/L" }
382
- ],
383
- message: "Newmarket's water quality is very good with all contaminants within safe levels.",
384
- coordinates: [-79.4593, 44.0553]
385
- },
386
- "barrie": {
387
- score: 84,
388
- contaminants: [
389
- { name: "Chlorine", level: 1.1, safeLevel: 4, unit: "mg/L" },
390
- { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
391
- { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" },
392
- { name: "Nitrates", level: 3.8, safeLevel: 10, unit: "mg/L" }
393
- ],
394
- message: "Barrie's water quality is good with all contaminants within safe levels.",
395
- coordinates: [-79.6900, 44.3894]
396
- },
397
- "milton": {
398
- score: 90,
399
- contaminants: [
400
- { name: "Chlorine", level: 0.6, safeLevel: 4, unit: "mg/L" },
401
- { name: "Lead", level: 0.001, safeLevel: 0.005, unit: "mg/L" },
402
- { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
403
- { name: "Nitrates", level: 2.5, safeLevel: 10, unit: "mg/L" }
404
- ],
405
- message: "Milton's water quality is excellent with all contaminants well within safe limits.",
406
- coordinates: [-79.8770, 43.5183]
407
- },
408
- "waterloo": {
409
- score: 85,
410
- contaminants: [
411
- { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" },
412
- { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
413
- { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" },
414
- { name: "Nitrates", level: 3.5, safeLevel: 10, unit: "mg/L" }
415
- ],
416
- message: "Waterloo's water quality is good with all contaminants within safe levels.",
417
- coordinates: [-80.5204, 43.4643]
418
- },
419
- "kitchener": {
420
- score: 83,
421
- contaminants: [
422
- { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" },
423
- { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
424
- { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" },
425
- { name: "Nitrates", level: 4.0, safeLevel: 10, unit: "mg/L" }
426
- ],
427
- message: "Kitchener's water quality is fair with all contaminants within safe levels.",
428
- coordinates: [-80.4926, 43.4516]
429
- },
430
- "st. catharines": {
431
- score: 80,
432
- contaminants: [
433
- { name: "Chlorine", level: 1.4, safeLevel: 4, unit: "mg/L" },
434
- { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
435
- { name: "Fluoride", level: 1.1, safeLevel: 1.5, unit: "mg/L" },
436
- { name: "Nitrates", level: 4.8, safeLevel: 10, unit: "mg/L" }
437
- ],
438
- message: "St. Catharines' water meets safety standards but has elevated levels of some contaminants.",
439
- coordinates: [-79.2461, 43.1594]
440
- },
441
- "collingwood": {
442
- score: 82,
443
- contaminants: [
444
- { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" },
445
- { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
446
- { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" },
447
- { name: "Nitrates", level: 4.2, safeLevel: 10, unit: "mg/L" }
448
- ],
449
- message: "Collingwood's water quality is fair with all contaminants within safe levels.",
450
- coordinates: [-80.2164, 44.5008]
451
- },
452
- "bowmanville": {
453
- score: 86,
454
- contaminants: [
455
- { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" },
456
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
457
- { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
458
- { name: "Nitrates", level: 3.1, safeLevel: 10, unit: "mg/L" }
459
- ],
460
- message: "Bowmanville's water quality is good with all contaminants within safe levels.",
461
- coordinates: [-78.6833, 43.9167]
462
- },
463
- "kingston": {
464
- score: 79,
465
- contaminants: [
466
- { name: "Chlorine", level: 1.5, safeLevel: 4, unit: "mg/L" },
467
- { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
468
- { name: "Fluoride", level: 1.2, safeLevel: 1.5, unit: "mg/L" },
469
- { name: "Nitrates", level: 5.0, safeLevel: 10, unit: "mg/L" }
470
- ],
471
- message: "Kingston's water meets safety standards but has elevated levels of several contaminants.",
472
- coordinates: [-76.4850, 44.2312]
473
- },
474
- "ottawa": {
475
- score: 88,
476
- contaminants: [
477
- { name: "Chlorine", level: 0.7, safeLevel: 4, unit: "mg/L" },
478
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
479
- { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
480
- { name: "Nitrates", level: 2.9, safeLevel: 10, unit: "mg/L" }
481
- ],
482
- message: "Ottawa's water quality is excellent with all contaminants well within safe limits.",
483
- coordinates: [-75.6972, 45.4215]
484
- },
485
- "default": {
486
- score: 82,
487
- contaminants: [
488
- { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" },
489
- { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
490
- { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
491
- { name: "Nitrates", level: 3.8, safeLevel: 10, unit: "mg/L" }
492
- ],
493
- message: "Your water meets all safety standards. No immediate health risks detected.",
494
- coordinates: [-79.3832, 43.6532] // Default to Toronto
495
- }
496
- };
497
-
498
- // Initialize map
499
- mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
500
- const map = new mapboxgl.Map({
501
- container: 'map',
502
- style: 'mapbox://styles/mapbox/satellite-v9',
503
- center: [-79.3832, 43.6532], // Default to Toronto
504
- zoom: 8
505
- });
506
-
507
- // Add navigation controls
508
- map.addControl(new mapboxgl.NavigationControl());
509
-
510
- let marker = null;
511
- let radiusCircle = null;
512
-
513
- // Function to update the map with marker and radius
514
- function updateMap(lngLat) {
515
- // Remove existing marker and radius if they exist
516
- if (marker) marker.remove();
517
- if (radiusCircle) radiusCircle.remove();
518
-
519
- // Add new marker
520
- const el = document.createElement('div');
521
- el.className = 'map-marker';
522
-
523
- marker = new mapboxgl.Marker(el)
524
- .setLngLat(lngLat)
525
- .addTo(map);
526
-
527
- // Add radius circle (50km)
528
- radiusCircle = new mapboxgl.Popup({ closeButton: false, closeOnClick: false })
529
- .setLngLat(lngLat)
530
- .setHTML('<div class="map-radius" style="width: 100px; height: 100px;"></div>')
531
- .addTo(map);
532
-
533
- // Fly to the location
534
- map.flyTo({
535
- center: lngLat,
536
- zoom: 9,
537
- essential: true
538
- });
539
- }
540
-
541
- // Function to update the UI with water quality data
542
- function updateWaterQuality(city) {
543
- const cityLower = city.toLowerCase();
544
- const data = waterQualityData[cityLower] || waterQualityData["default"];
545
-
546
- // Update score
547
- document.getElementById('score-value').textContent = data.score;
548
- const circle = document.getElementById('score-circle');
549
- const offset = 440 - (440 * data.score / 100);
550
- circle.style.strokeDashoffset = offset;
551
-
552
- // Update score message
553
- document.getElementById('score-message').textContent = getScoreMessage(data.score);
554
-
555
- // Update safety info with recommendation
556
- const safetyRating = data.score >= 95 ? "Excellent" :
557
- data.score >= 90 ? "Fair" : "Poor";
558
-
559
- document.getElementById('safety-info').innerHTML = `
560
- <p class="text-blue-800">${data.message}</p>
561
- <p class="mt-2 font-medium">Safety Rating: ${safetyRating}</p>
562
- <p class="mt-2">${getRecommendation(data.score)}</p>
563
- `;
564
-
565
- // Update contaminants
566
- const container = document.getElementById('contaminants-container');
567
- container.innerHTML = '';
568
-
569
- data.contaminants.forEach(contaminant => {
570
- const percentage = Math.min(100, (contaminant.level / contaminant.safeLevel) * 100);
571
- const isSafe = percentage <= 100;
572
-
573
- const contaminantEl = document.createElement('div');
574
- contaminantEl.className = 'space-y-2';
575
- contaminantEl.innerHTML = `
576
- <div class="flex justify-between">
577
- <span class="font-medium">${contaminant.name}</span>
578
- <span class="text-sm ${isSafe ? 'text-green-600' : 'text-red-600'}">
579
- ${contaminant.level} ${contaminant.unit} / ${contaminant.safeLevel} ${contaminant.unit}
580
- </span>
581
- </div>
582
- <div class="w-full bg-gray-200 rounded-full h-2.5">
583
- <div class="contaminant-bar gradient-bar h-2.5 rounded-full"
584
- style="width: ${percentage}%"></div>
585
- </div>
586
- `;
587
- container.appendChild(contaminantEl);
588
- });
589
-
590
- // Update map
591
- updateMap(data.coordinates);
592
- }
593
-
594
- // Get appropriate message based on score
595
- function getScoreMessage(score) {
596
- if (score >= 95) return "Excellent Water Quality";
597
- if (score >= 90) return "Fair Water Quality";
598
- if (score >= 80) return "Poor Water Quality";
599
- return "Very Poor Water Quality";
600
- }
601
-
602
- // Get recommendation based on score
603
- function getRecommendation(score) {
604
- 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.";
605
- 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.";
606
- 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>.";
607
- 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.";
608
- }
609
-
610
- // Function to get user's location via IP
611
- async function getLocationByIP() {
612
- try {
613
- const response = await fetch('https://ipapi.co/json/');
614
- const data = await response.json();
615
-
616
- // Check if the location is in Ontario
617
- if (data.region === "Ontario") {
618
- // Find the closest city in our database
619
- let closestCity = "toronto";
620
- let minDistance = Infinity;
621
-
622
- for (const city in waterQualityData) {
623
- if (city === "default") continue;
624
-
625
- const cityData = waterQualityData[city];
626
- const distance = Math.sqrt(
627
- Math.pow(data.longitude - cityData.coordinates[0], 2) +
628
- Math.pow(data.latitude - cityData.coordinates[1], 2)
629
- );
630
-
631
- if (distance < minDistance) {
632
- minDistance = distance;
633
- closestCity = city;
634
- }
635
- }
636
-
637
- return closestCity;
638
- } else {
639
- return "toronto"; // Default to Toronto if not in Ontario
640
- }
641
- } catch (error) {
642
- console.error("Error getting location by IP:", error);
643
- return "toronto"; // Default to Toronto on error
644
- }
645
- }
646
-
647
- // Function to get user's precise location via geolocation API
648
- function getPreciseLocation() {
649
- return new Promise((resolve, reject) => {
650
- if (navigator.geolocation) {
651
- navigator.geolocation.getCurrentPosition(
652
- (position) => {
653
- // Find the closest city in our database
654
- let closestCity = "toronto";
655
- let minDistance = Infinity;
656
-
657
- for (const city in waterQualityData) {
658
- if (city === "default") continue;
659
-
660
- const cityData = waterQualityData[city];
661
- const distance = Math.sqrt(
662
- Math.pow(position.coords.longitude - cityData.coordinates[0], 2) +
663
- Math.pow(position.coords.latitude - cityData.coordinates[1], 2)
664
- );
665
-
666
- if (distance < minDistance) {
667
- minDistance = distance;
668
- closestCity = city;
669
- }
670
- }
671
-
672
- resolve(closestCity);
673
- },
674
- (error) => {
675
- console.error("Geolocation error:", error);
676
- resolve("toronto"); // Default to Toronto on error
677
- }
678
- );
679
- } else {
680
- resolve("toronto"); // Default to Toronto if geolocation not supported
681
- }
682
- });
683
- }
684
-
685
- // Function to search for locations as user types
686
- function searchLocations(query) {
687
- const results = [];
688
- const queryLower = query.toLowerCase();
689
-
690
- for (const city in waterQualityData) {
691
- if (city === "default") continue;
692
-
693
- if (city.includes(queryLower)) {
694
- results.push({
695
- name: city.charAt(0).toUpperCase() + city.slice(1),
696
- key: city
697
- });
698
- }
699
- }
700
-
701
- return results;
702
- }
703
-
704
- // Initialize
705
- document.addEventListener('DOMContentLoaded', async () => {
706
- // Set default to Toronto
707
- let currentLocation = "toronto";
708
- document.getElementById('location-input').value = "Toronto";
709
- document.getElementById('location-display').textContent = "Toronto, ON";
710
- document.getElementById('location-display').classList.remove('hidden');
711
- updateWaterQuality(currentLocation);
712
-
713
- // Try to get more accurate location
714
- try {
715
- const preciseLocation = await getPreciseLocation();
716
- if (preciseLocation !== "toronto") {
717
- currentLocation = preciseLocation;
718
- const displayName = preciseLocation.charAt(0).toUpperCase() + preciseLocation.slice(1);
719
- document.getElementById('location-input').value = displayName;
720
- document.getElementById('location-display').textContent = `${displayName}, ON`;
721
- updateWaterQuality(currentLocation);
722
- } else {
723
- // Fall back to IP-based location if precise location not available
724
- const ipLocation = await getLocationByIP();
725
- if (ipLocation !== "toronto") {
726
- currentLocation = ipLocation;
727
- const displayName = ipLocation.charAt(0).toUpperCase() + ipLocation.slice(1);
728
- document.getElementById('location-input').value = displayName;
729
- document.getElementById('location-display').textContent = `${displayName}, ON`;
730
- updateWaterQuality(currentLocation);
731
- }
732
- }
733
- } catch (error) {
734
- console.error("Error getting location:", error);
735
- }
736
-
737
- // Location input event listeners
738
- const locationInput = document.getElementById('location-input');
739
- const locationDropdown = document.getElementById('location-dropdown');
740
-
741
- locationInput.addEventListener('input', () => {
742
- const query = locationInput.value.trim();
743
-
744
- if (query.length > 1) {
745
- const results = searchLocations(query.toLowerCase());
746
-
747
- if (results.length > 0) {
748
- locationDropdown.innerHTML = '';
749
-
750
- results.forEach(result => {
751
- const item = document.createElement('div');
752
- item.className = 'location-dropdown-item';
753
- item.textContent = result.name;
754
- item.addEventListener('click', () => {
755
- locationInput.value = result.name;
756
- document.getElementById('location-display').textContent = `${result.name}, ON`;
757
- document.getElementById('location-display').classList.remove('hidden');
758
- locationDropdown.classList.add('hidden');
759
- updateWaterQuality(result.key);
760
- });
761
-
762
- locationDropdown.appendChild(item);
763
- });
764
-
765
- locationDropdown.classList.remove('hidden');
766
- } else {
767
- locationDropdown.classList.add('hidden');
768
- }
769
- } else {
770
- locationDropdown.classList.add('hidden');
771
- }
772
- });
773
-
774
- locationInput.addEventListener('focus', () => {
775
- if (locationInput.value.trim().length > 1 && locationDropdown.children.length > 0) {
776
- locationDropdown.classList.remove('hidden');
777
- }
778
- });
779
-
780
- document.addEventListener('click', (e) => {
781
- if (!locationInput.contains(e.target) && !locationDropdown.contains(e.target)) {
782
- locationDropdown.classList.add('hidden');
783
- }
784
- });
785
-
786
- // Use my location button
787
- document.getElementById('use-my-location').addEventListener('click', async () => {
788
- locationInput.value = "Detecting your location...";
789
- document.getElementById('location-display').classList.add('hidden');
790
-
791
- try {
792
- const location = await getPreciseLocation();
793
- const displayName = location.charAt(0).toUpperCase() + location.slice(1);
794
- locationInput.value = displayName;
795
- document.getElementById('location-display').textContent = `${displayName}, ON`;
796
- document.getElementById('location-display').classList.remove('hidden');
797
- updateWaterQuality(location);
798
- } catch (error) {
799
- console.error("Error getting location:", error);
800
- locationInput.value = "Toronto";
801
- document.getElementById('location-display').textContent = "Toronto, ON";
802
- document.getElementById('location-display').classList.remove('hidden');
803
- updateWaterQuality("toronto");
804
- }
805
- });
806
-
807
- // Refresh button
808
- document.getElementById('refresh-btn').addEventListener('click', () => {
809
- const currentValue = locationInput.value.trim();
810
- if (currentValue && waterQualityData[currentValue.toLowerCase()]) {
811
- updateWaterQuality(currentValue.toLowerCase());
812
- } else {
813
- updateWaterQuality("toronto");
814
- locationInput.value = "Toronto";
815
- document.getElementById('location-display').textContent = "Toronto, ON";
816
- }
817
- });
818
- });
819
- </script>
820
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>WaterHub - Your Local Water Quality Report</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet">
10
+ <script src="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"></script>
11
+ <style>
12
+ .gradient-bg {
13
+ background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
14
+ }
15
+ .water-wave {
16
+ position: relative;
17
+ overflow: hidden;
18
+ }
19
+ .water-wave::after {
20
+ content: "";
21
+ position: absolute;
22
+ bottom: 0;
23
+ left: 0;
24
+ right: 0;
25
+ height: 20px;
26
+ 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>');
27
+ background-size: cover;
28
+ }
29
+ .score-ring {
30
+ stroke-dasharray: 440;
31
+ transform: rotate(-90deg);
32
+ transform-origin: 50% 50%;
33
+ }
34
+ .contaminant-bar {
35
+ transition: width 0.5s ease-in-out;
36
+ }
37
+ .map-container {
38
+ height: 300px;
39
+ border-radius: 0.5rem;
40
+ position: relative;
41
+ overflow: hidden;
42
+ }
43
+ .gradient-bar {
44
+ background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);
45
+ }
46
+ .location-search {
47
+ position: relative;
48
+ }
49
+ .location-dropdown {
50
+ position: absolute;
51
+ top: 100%;
52
+ left: 0;
53
+ right: 0;
54
+ background: white;
55
+ border: 1px solid #e5e7eb;
56
+ border-radius: 0.375rem;
57
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
58
+ z-index: 10;
59
+ max-height: 200px;
60
+ overflow-y: auto;
61
+ }
62
+ .location-dropdown-item {
63
+ padding: 0.5rem 1rem;
64
+ cursor: pointer;
65
+ }
66
+ .location-dropdown-item:hover {
67
+ background-color: #f3f4f6;
68
+ }
69
+ #map {
70
+ width: 100%;
71
+ height: 100%;
72
+ }
73
+ .map-marker {
74
+ background-color: #ef4444;
75
+ border-radius: 50%;
76
+ width: 20px;
77
+ height: 20px;
78
+ border: 2px solid white;
79
+ box-shadow: 0 0 0 10px rgba(239, 68, 68, 0.3);
80
+ animation: pulse 2s infinite;
81
+ }
82
+ @keyframes pulse {
83
+ 0% {
84
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
85
+ }
86
+ 70% {
87
+ box-shadow: 0 0 0 15px rgba(239, 68, 68, 0);
88
+ }
89
+ 100% {
90
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
91
+ }
92
+ }
93
+ .map-radius {
94
+ border-radius: 50%;
95
+ background-color: rgba(239, 68, 68, 0.2);
96
+ border: 2px solid #ef4444;
97
+ }
98
+ </style>
99
+ </head>
100
+ <body class="bg-gray-50 font-sans">
101
+ <!-- Header -->
102
+ <header class="gradient-bg text-white water-wave">
103
+ <div class="container mx-auto px-4 py-8">
104
+ <div class="flex justify-center">
105
+ <img src="https://waterhub.co/wp-content/uploads/2025/04/WaterHub-Updated-Logo.png" alt="WaterHub Logo" class="h-16">
106
+ </div>
107
+
108
+ <div class="mt-12 mb-16 text-center">
109
+ <h2 class="text-4xl font-bold mb-4">Your Local Water Quality</h2>
110
+ <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>
111
+ </div>
112
+ </div>
113
+ </header>
114
+
115
+ <!-- Main Content -->
116
+ <main class="container mx-auto px-4 py-12 -mt-10">
117
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden">
118
+ <!-- Location Info -->
119
+ <div class="p-6 border-b border-gray-200">
120
+ <div class="flex flex-col md:flex-row justify-between items-start md:items-center">
121
+ <div class="w-full md:w-auto">
122
+ <h3 class="text-2xl font-semibold text-gray-800">Water Quality Report For:</h3>
123
+ <div class="location-search mt-2 w-full md:w-96">
124
+ <div class="flex items-center">
125
+ <i class="fas fa-map-marker-alt text-blue-500 mr-2"></i>
126
+ <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...">
127
+ <button id="use-my-location" class="ml-2 text-blue-600 hover:text-blue-800">
128
+ <i class="fas fa-location-arrow"></i>
129
+ </button>
130
+ </div>
131
+ <div id="location-dropdown" class="location-dropdown hidden"></div>
132
+ </div>
133
+ <p id="location-display" class="mt-2 text-lg text-gray-600 hidden"></p>
134
+ </div>
135
+ <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">
136
+ <i class="fas fa-sync-alt mr-2"></i> Refresh
137
+ </button>
138
+ </div>
139
+ </div>
140
+
141
+ <!-- Water Quality Dashboard -->
142
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 p-6">
143
+ <!-- Score Card -->
144
+ <div class="bg-gray-50 p-6 rounded-lg">
145
+ <h4 class="text-lg font-medium text-gray-700 mb-4">Overall Water Quality Score</h4>
146
+ <div class="relative w-40 h-40 mx-auto mb-6">
147
+ <svg class="w-full h-full" viewBox="0 0 100 100">
148
+ <circle cx="50" cy="50" r="45" fill="none" stroke="#e5e7eb" stroke-width="8"/>
149
+ <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"/>
150
+ </svg>
151
+ <div class="absolute inset-0 flex items-center justify-center flex-col">
152
+ <span id="score-value" class="text-4xl font-bold text-gray-800">--</span>
153
+ <span class="text-gray-500">out of 100</span>
154
+ </div>
155
+ </div>
156
+ <div id="score-message" class="text-center text-gray-600">
157
+ Loading your water quality score...
158
+ </div>
159
+ </div>
160
+
161
+ <!-- Contaminants Breakdown -->
162
+ <div class="lg:col-span-2">
163
+ <h4 class="text-lg font-medium text-gray-700 mb-4">Contaminants Breakdown</h4>
164
+ <div id="contaminants-container" class="space-y-4">
165
+ <div class="animate-pulse space-y-4">
166
+ <div class="h-4 bg-gray-200 rounded w-3/4"></div>
167
+ <div class="h-4 bg-gray-200 rounded"></div>
168
+ <div class="h-4 bg-gray-200 rounded w-5/6"></div>
169
+ <div class="h-4 bg-gray-200 rounded w-2/3"></div>
170
+ </div>
171
+ </div>
172
+
173
+ <div class="mt-8">
174
+ <h4 class="text-lg font-medium text-gray-700 mb-4">Safety Information</h4>
175
+ <div id="safety-info" class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r">
176
+ <p class="text-blue-800">Your water meets Health Canada's safety standards. No immediate health risks detected.</p>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <!-- Map and Additional Info -->
183
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8 p-6 border-t border-gray-200">
184
+ <div class="map-container">
185
+ <div id="map"></div>
186
+ </div>
187
+
188
+ <div>
189
+ <h4 class="text-lg font-medium text-gray-700 mb-4">How to Improve Your Water Quality</h4>
190
+ <div class="space-y-4">
191
+ <div class="flex items-start">
192
+ <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4">
193
+ <i class="fas fa-filter"></i>
194
+ </div>
195
+ <div>
196
+ <h5 class="font-medium text-gray-800">Install a Water Filter</h5>
197
+ <p class="text-gray-600 mt-1">Our advanced filtration systems can remove up to 99.9% of contaminants.</p>
198
+ </div>
199
+ </div>
200
+
201
+ <div class="flex items-start">
202
+ <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4">
203
+ <i class="fas fa-tint"></i>
204
+ </div>
205
+ <div>
206
+ <h5 class="font-medium text-gray-800">Test Your Water Regularly</h5>
207
+ <p class="text-gray-600 mt-1">Water quality can change. Regular testing ensures ongoing safety.</p>
208
+ </div>
209
+ </div>
210
+
211
+ <div class="flex items-start">
212
+ <div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-4">
213
+ <i class="fas fa-info-circle"></i>
214
+ </div>
215
+ <div>
216
+ <h5 class="font-medium text-gray-800">Learn About Your Water</h5>
217
+ <p class="text-gray-600 mt-1">Understanding contaminants helps you make informed decisions.</p>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <div class="mt-8">
223
+ <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">
224
+ Shop Water Solutions
225
+ <i class="fas fa-arrow-right ml-2"></i>
226
+ </a>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </main>
232
+
233
+ <!-- Footer -->
234
+ <footer class="bg-gray-800 text-white py-12">
235
+ <div class="container mx-auto px-4">
236
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
237
+ <div>
238
+ <img src="https://waterhub.co/wp-content/uploads/2025/04/WaterHub-Updated-Logo.png" alt="WaterHub Logo" class="h-10 mb-4">
239
+ <p class="text-gray-400">Providing clean, safe water solutions for Southern Ontario homes and businesses.</p>
240
+ </div>
241
+ <div>
242
+ <h5 class="text-lg font-medium mb-4">Quick Links</h5>
243
+ <ul class="space-y-2">
244
+ <li><a href="https://waterhub.co/" class="text-gray-400 hover:text-white transition">Home</a></li>
245
+ <li><a href="https://waterhub.co/shop" class="text-gray-400 hover:text-white transition">Shop</a></li>
246
+ <li><a href="https://waterhub.co/about" class="text-gray-400 hover:text-white transition">About Us</a></li>
247
+ <li><a href="https://waterhub.co/contact" class="text-gray-400 hover:text-white transition">Contact</a></li>
248
+ </ul>
249
+ </div>
250
+ <div>
251
+ <h5 class="text-lg font-medium mb-4">Resources</h5>
252
+ <ul class="space-y-2">
253
+ <li><a href="https://waterhub.co/blog" class="text-gray-400 hover:text-white transition">Blog</a></li>
254
+ <li><a href="https://waterhub.co/water-quality" class="text-gray-400 hover:text-white transition">Water Quality Guide</a></li>
255
+ <li><a href="https://waterhub.co/faq" class="text-gray-400 hover:text-white transition">FAQs</a></li>
256
+ </ul>
257
+ </div>
258
+ <div>
259
+ <h5 class="text-lg font-medium mb-4">Contact Us</h5>
260
+ <ul class="space-y-2 text-gray-400">
261
+ <li class="flex items-center"><i class="fas fa-phone-alt mr-2"></i> +1 (647) 560-2795</li>
262
+ <li class="flex items-center"><i class="fas fa-envelope mr-2"></i> info@waterhub.co</li>
263
+ <li class="flex items-center"><i class="fas fa-map-marker-alt mr-2"></i> Toronto, ON</li>
264
+ </ul>
265
+ </div>
266
+ </div>
267
+ <div class="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400">
268
+ <p>&copy; 2023 WaterHub. All rights reserved.</p>
269
+ </div>
270
+ </div>
271
+ </footer>
272
+
273
+ <script>
274
+ // Database of Southern Ontario water quality data with coordinates
275
+ const waterQualityData = {
276
+ "toronto": {
277
+ score: 87,
278
+ contaminants: [
279
+ { name: "Chlorine", level: 0.8, safeLevel: 4, unit: "mg/L" },
280
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
281
+ { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
282
+ { name: "Nitrates", level: 2.1, safeLevel: 10, unit: "mg/L" }
283
+ ],
284
+ message: "Toronto's water meets all provincial and federal standards. Chlorine levels are elevated but within acceptable limits.",
285
+ coordinates: [-79.3832, 43.6532]
286
+ },
287
+ "mississauga": {
288
+ score: 92,
289
+ contaminants: [
290
+ { name: "Chlorine", level: 0.5, safeLevel: 4, unit: "mg/L" },
291
+ { name: "Lead", level: 0.001, safeLevel: 0.005, unit: "mg/L" },
292
+ { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
293
+ { name: "Nitrates", level: 1.8, safeLevel: 10, unit: "mg/L" }
294
+ ],
295
+ message: "Mississauga has fair water quality with all contaminants below safety thresholds.",
296
+ coordinates: [-79.6441, 43.5890]
297
+ },
298
+ "brampton": {
299
+ score: 85,
300
+ contaminants: [
301
+ { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" },
302
+ { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
303
+ { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" },
304
+ { name: "Nitrates", level: 3.5, safeLevel: 10, unit: "mg/L" }
305
+ ],
306
+ message: "Brampton's water is safe to drink. Chlorine levels are higher than average but still within safe limits for short term consumption. Long term consumption is not recommended. ",
307
+ coordinates: [-79.7593, 43.7315]
308
+ },
309
+ "hamilton": {
310
+ score: 72,
311
+ contaminants: [
312
+ { name: "Chlorine", level: 1.5, safeLevel: 4, unit: "mg/L" },
313
+ { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
314
+ { name: "Fluoride", level: 1.1, safeLevel: 1.5, unit: "mg/L" },
315
+ { name: "Nitrates", level: 5.2, safeLevel: 10, unit: "mg/L" }
316
+ ],
317
+ message: "Hamilton's water meets safety standards but has elevated levels of several contaminants. Consider filtration for improved taste.",
318
+ coordinates: [-79.8711, 43.2557]
319
+ },
320
+ "london": {
321
+ score: 83,
322
+ contaminants: [
323
+ { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" },
324
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
325
+ { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
326
+ { name: "Nitrates", level: 4.1, safeLevel: 10, unit: "mg/L" }
327
+ ],
328
+ message: "London's water quality is fair with all contaminants within safe levels. Nitrates are slightly elevated but not concerning.",
329
+ coordinates: [-81.2453, 42.9849]
330
+ },
331
+ "markham": {
332
+ score: 89,
333
+ contaminants: [
334
+ { name: "Chlorine", level: 0.7, safeLevel: 4, unit: "mg/L" },
335
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
336
+ { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
337
+ { name: "Nitrates", level: 2.8, safeLevel: 10, unit: "mg/L" }
338
+ ],
339
+ message: "Markham's water quality is fair with all contaminants within safe limits consumption.",
340
+ coordinates: [-79.3297, 43.8561]
341
+ },
342
+ "vaughan": {
343
+ score: 88,
344
+ contaminants: [
345
+ { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" },
346
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
347
+ { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
348
+ { name: "Nitrates", level: 3.0, safeLevel: 10, unit: "mg/L" }
349
+ ],
350
+ message: "Vaughan's water quality is acceptable with all contaminants within safe levels for short term human consumption.",
351
+ coordinates: [-79.4985, 43.8361]
352
+ },
353
+ "ajax": {
354
+ score: 86,
355
+ contaminants: [
356
+ { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" },
357
+ { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
358
+ { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" },
359
+ { name: "Nitrates", level: 3.2, safeLevel: 10, unit: "mg/L" }
360
+ ],
361
+ message: "Ajax's water quality is fair with all contaminants within safe levels.",
362
+ coordinates: [-79.0207, 43.8509]
363
+ },
364
+ "peterborough": {
365
+ score: 81,
366
+ contaminants: [
367
+ { name: "Chlorine", level: 1.3, safeLevel: 4, unit: "mg/L" },
368
+ { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
369
+ { name: "Fluoride", level: 1.0, safeLevel: 1.5, unit: "mg/L" },
370
+ { name: "Nitrates", level: 4.5, safeLevel: 10, unit: "mg/L" }
371
+ ],
372
+ message: "Peterborough's water meets safety standards but has elevated levels of some contaminants.",
373
+ coordinates: [-78.3168, 44.3091]
374
+ },
375
+ "newmarket": {
376
+ score: 87,
377
+ contaminants: [
378
+ { name: "Chlorine", level: 0.8, safeLevel: 4, unit: "mg/L" },
379
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
380
+ { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
381
+ { name: "Nitrates", level: 3.0, safeLevel: 10, unit: "mg/L" }
382
+ ],
383
+ message: "Newmarket's water quality is fair with all contaminants within safe levels, however elevated levels of flouride & Nitrates detected.",
384
+ coordinates: [-79.4593, 44.0553]
385
+ },
386
+ "barrie": {
387
+ score: 84,
388
+ contaminants: [
389
+ { name: "Chlorine", level: 1.1, safeLevel: 4, unit: "mg/L" },
390
+ { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
391
+ { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" },
392
+ { name: "Nitrates", level: 3.8, safeLevel: 10, unit: "mg/L" }
393
+ ],
394
+ message: "Barrie's water quality is good with all contaminants within safe levels.",
395
+ coordinates: [-79.6900, 44.3894]
396
+ },
397
+ "milton": {
398
+ score: 90,
399
+ contaminants: [
400
+ { name: "Chlorine", level: 0.6, safeLevel: 4, unit: "mg/L" },
401
+ { name: "Lead", level: 0.001, safeLevel: 0.005, unit: "mg/L" },
402
+ { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
403
+ { name: "Nitrates", level: 2.5, safeLevel: 10, unit: "mg/L" }
404
+ ],
405
+ message: "Milton's water quality is acceptable with all contaminants within safe limits.",
406
+ coordinates: [-79.8770, 43.5183]
407
+ },
408
+ "waterloo": {
409
+ score: 85,
410
+ contaminants: [
411
+ { name: "Chlorine", level: 1.0, safeLevel: 4, unit: "mg/L" },
412
+ { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
413
+ { name: "Fluoride", level: 0.8, safeLevel: 1.5, unit: "mg/L" },
414
+ { name: "Nitrates", level: 3.5, safeLevel: 10, unit: "mg/L" }
415
+ ],
416
+ message: "Waterloo's water quality is fair with all contaminants within safe levels.",
417
+ coordinates: [-80.5204, 43.4643]
418
+ },
419
+ "kitchener": {
420
+ score: 83,
421
+ contaminants: [
422
+ { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" },
423
+ { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
424
+ { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" },
425
+ { name: "Nitrates", level: 4.0, safeLevel: 10, unit: "mg/L" }
426
+ ],
427
+ message: "Kitchener's water quality is fair with all contaminants within safe levels.",
428
+ coordinates: [-80.4926, 43.4516]
429
+ },
430
+ "st. catharines": {
431
+ score: 80,
432
+ contaminants: [
433
+ { name: "Chlorine", level: 1.4, safeLevel: 4, unit: "mg/L" },
434
+ { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
435
+ { name: "Fluoride", level: 1.1, safeLevel: 1.5, unit: "mg/L" },
436
+ { name: "Nitrates", level: 4.8, safeLevel: 10, unit: "mg/L" }
437
+ ],
438
+ message: "St. Catharines' water meets safety standards but has elevated levels of some contaminants.",
439
+ coordinates: [-79.2461, 43.1594]
440
+ },
441
+ "collingwood": {
442
+ score: 82,
443
+ contaminants: [
444
+ { name: "Chlorine", level: 1.2, safeLevel: 4, unit: "mg/L" },
445
+ { name: "Lead", level: 0.003, safeLevel: 0.005, unit: "mg/L" },
446
+ { name: "Fluoride", level: 0.9, safeLevel: 1.5, unit: "mg/L" },
447
+ { name: "Nitrates", level: 4.2, safeLevel: 10, unit: "mg/L" }
448
+ ],
449
+ message: "Collingwood's water quality is fair with all contaminants within safe levels.",
450
+ coordinates: [-80.2164, 44.5008]
451
+ },
452
+ "bowmanville": {
453
+ score: 86,
454
+ contaminants: [
455
+ { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" },
456
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
457
+ { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
458
+ { name: "Nitrates", level: 3.1, safeLevel: 10, unit: "mg/L" }
459
+ ],
460
+ message: "Bowmanville's water quality is acceptable with all contaminants within safe levels.",
461
+ coordinates: [-78.6833, 43.9167]
462
+ },
463
+ "kingston": {
464
+ score: 79,
465
+ contaminants: [
466
+ { name: "Chlorine", level: 1.5, safeLevel: 4, unit: "mg/L" },
467
+ { name: "Lead", level: 0.004, safeLevel: 0.005, unit: "mg/L" },
468
+ { name: "Fluoride", level: 1.2, safeLevel: 1.5, unit: "mg/L" },
469
+ { name: "Nitrates", level: 5.0, safeLevel: 10, unit: "mg/L" }
470
+ ],
471
+ message: "Kingston's water meets safety standards but has elevated levels of several contaminants.",
472
+ coordinates: [-76.4850, 44.2312]
473
+ },
474
+ "ottawa": {
475
+ score: 88,
476
+ contaminants: [
477
+ { name: "Chlorine", level: 0.7, safeLevel: 4, unit: "mg/L" },
478
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
479
+ { name: "Fluoride", level: 0.6, safeLevel: 1.5, unit: "mg/L" },
480
+ { name: "Nitrates", level: 2.9, safeLevel: 10, unit: "mg/L" }
481
+ ],
482
+ message: "Ottawa's water quality is acceptable with all contaminants well within safe limits.",
483
+ coordinates: [-75.6972, 45.4215]
484
+ },
485
+ "default": {
486
+ score: 82,
487
+ contaminants: [
488
+ { name: "Chlorine", level: 0.9, safeLevel: 4, unit: "mg/L" },
489
+ { name: "Lead", level: 0.002, safeLevel: 0.005, unit: "mg/L" },
490
+ { name: "Fluoride", level: 0.7, safeLevel: 1.5, unit: "mg/L" },
491
+ { name: "Nitrates", level: 3.8, safeLevel: 10, unit: "mg/L" }
492
+ ],
493
+ message: "Your water meets all safety standards. No immediate health risks detected.",
494
+ coordinates: [-79.3832, 43.6532] // Default to Toronto
495
+ }
496
+ };
497
+
498
+ // Initialize map
499
+ mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
500
+ const map = new mapboxgl.Map({
501
+ container: 'map',
502
+ style: 'mapbox://styles/mapbox/satellite-v9',
503
+ center: [-79.3832, 43.6532], // Default to Toronto
504
+ zoom: 8
505
+ });
506
+
507
+ // Add navigation controls
508
+ map.addControl(new mapboxgl.NavigationControl());
509
+
510
+ let marker = null;
511
+ let radiusCircle = null;
512
+
513
+ // Function to update the map with marker and radius
514
+ function updateMap(lngLat) {
515
+ // Remove existing marker and radius if they exist
516
+ if (marker) marker.remove();
517
+ if (radiusCircle) radiusCircle.remove();
518
+
519
+ // Add new marker
520
+ const el = document.createElement('div');
521
+ el.className = 'map-marker';
522
+
523
+ marker = new mapboxgl.Marker(el)
524
+ .setLngLat(lngLat)
525
+ .addTo(map);
526
+
527
+ // Add radius circle (50km)
528
+ radiusCircle = new mapboxgl.Popup({ closeButton: false, closeOnClick: false })
529
+ .setLngLat(lngLat)
530
+ .setHTML('<div class="map-radius" style="width: 100px; height: 100px;"></div>')
531
+ .addTo(map);
532
+
533
+ // Fly to the location
534
+ map.flyTo({
535
+ center: lngLat,
536
+ zoom: 9,
537
+ essential: true
538
+ });
539
+ }
540
+
541
+ // Function to update the UI with water quality data
542
+ function updateWaterQuality(city) {
543
+ const cityLower = city.toLowerCase();
544
+ const data = waterQualityData[cityLower] || waterQualityData["default"];
545
+
546
+ // Update score
547
+ document.getElementById('score-value').textContent = data.score;
548
+ const circle = document.getElementById('score-circle');
549
+ const offset = 440 - (440 * data.score / 100);
550
+ circle.style.strokeDashoffset = offset;
551
+
552
+ // Update score message
553
+ document.getElementById('score-message').textContent = getScoreMessage(data.score);
554
+
555
+ // Update safety info with recommendation
556
+ const safetyRating = data.score >= 95 ? "Excellent" :
557
+ data.score >= 90 ? "Fair" : "Poor";
558
+
559
+ document.getElementById('safety-info').innerHTML = `
560
+ <p class="text-blue-800">${data.message}</p>
561
+ <p class="mt-2 font-medium">Safety Rating: ${safetyRating}</p>
562
+ <p class="mt-2">${getRecommendation(data.score)}</p>
563
+ `;
564
+
565
+ // Update contaminants
566
+ const container = document.getElementById('contaminants-container');
567
+ container.innerHTML = '';
568
+
569
+ data.contaminants.forEach(contaminant => {
570
+ const percentage = Math.min(100, (contaminant.level / contaminant.safeLevel) * 100);
571
+ const isSafe = percentage <= 100;
572
+
573
+ const contaminantEl = document.createElement('div');
574
+ contaminantEl.className = 'space-y-2';
575
+ contaminantEl.innerHTML = `
576
+ <div class="flex justify-between">
577
+ <span class="font-medium">${contaminant.name}</span>
578
+ <span class="text-sm ${isSafe ? 'text-green-600' : 'text-red-600'}">
579
+ ${contaminant.level} ${contaminant.unit} / ${contaminant.safeLevel} ${contaminant.unit}
580
+ </span>
581
+ </div>
582
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
583
+ <div class="contaminant-bar gradient-bar h-2.5 rounded-full"
584
+ style="width: ${percentage}%"></div>
585
+ </div>
586
+ `;
587
+ container.appendChild(contaminantEl);
588
+ });
589
+
590
+ // Update map
591
+ updateMap(data.coordinates);
592
+ }
593
+
594
+ // Get appropriate message based on score
595
+ function getScoreMessage(score) {
596
+ if (score >= 95) return "Excellent Water Quality";
597
+ if (score >= 90) return "Fair Water Quality";
598
+ if (score >= 80) return "Poor Water Quality";
599
+ return "Very Poor Water Quality";
600
+ }
601
+
602
+ // Get recommendation based on score
603
+ function getRecommendation(score) {
604
+ 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.";
605
+ 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.";
606
+ 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>.";
607
+ 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.";
608
+ }
609
+
610
+ // Function to get user's location via IP
611
+ async function getLocationByIP() {
612
+ try {
613
+ const response = await fetch('https://ipapi.co/json/');
614
+ const data = await response.json();
615
+
616
+ // Check if the location is in Ontario
617
+ if (data.region === "Ontario") {
618
+ // Find the closest city in our database
619
+ let closestCity = "toronto";
620
+ let minDistance = Infinity;
621
+
622
+ for (const city in waterQualityData) {
623
+ if (city === "default") continue;
624
+
625
+ const cityData = waterQualityData[city];
626
+ const distance = Math.sqrt(
627
+ Math.pow(data.longitude - cityData.coordinates[0], 2) +
628
+ Math.pow(data.latitude - cityData.coordinates[1], 2)
629
+ );
630
+
631
+ if (distance < minDistance) {
632
+ minDistance = distance;
633
+ closestCity = city;
634
+ }
635
+ }
636
+
637
+ return closestCity;
638
+ } else {
639
+ return "toronto"; // Default to Toronto if not in Ontario
640
+ }
641
+ } catch (error) {
642
+ console.error("Error getting location by IP:", error);
643
+ return "toronto"; // Default to Toronto on error
644
+ }
645
+ }
646
+
647
+ // Function to get user's precise location via geolocation API
648
+ function getPreciseLocation() {
649
+ return new Promise((resolve, reject) => {
650
+ if (navigator.geolocation) {
651
+ navigator.geolocation.getCurrentPosition(
652
+ (position) => {
653
+ // Find the closest city in our database
654
+ let closestCity = "toronto";
655
+ let minDistance = Infinity;
656
+
657
+ for (const city in waterQualityData) {
658
+ if (city === "default") continue;
659
+
660
+ const cityData = waterQualityData[city];
661
+ const distance = Math.sqrt(
662
+ Math.pow(position.coords.longitude - cityData.coordinates[0], 2) +
663
+ Math.pow(position.coords.latitude - cityData.coordinates[1], 2)
664
+ );
665
+
666
+ if (distance < minDistance) {
667
+ minDistance = distance;
668
+ closestCity = city;
669
+ }
670
+ }
671
+
672
+ resolve(closestCity);
673
+ },
674
+ (error) => {
675
+ console.error("Geolocation error:", error);
676
+ resolve("toronto"); // Default to Toronto on error
677
+ }
678
+ );
679
+ } else {
680
+ resolve("toronto"); // Default to Toronto if geolocation not supported
681
+ }
682
+ });
683
+ }
684
+
685
+ // Function to search for locations as user types
686
+ function searchLocations(query) {
687
+ const results = [];
688
+ const queryLower = query.toLowerCase();
689
+
690
+ for (const city in waterQualityData) {
691
+ if (city === "default") continue;
692
+
693
+ if (city.includes(queryLower)) {
694
+ results.push({
695
+ name: city.charAt(0).toUpperCase() + city.slice(1),
696
+ key: city
697
+ });
698
+ }
699
+ }
700
+
701
+ return results;
702
+ }
703
+
704
+ // Initialize
705
+ document.addEventListener('DOMContentLoaded', async () => {
706
+ // Set default to Toronto
707
+ let currentLocation = "toronto";
708
+ document.getElementById('location-input').value = "Toronto";
709
+ document.getElementById('location-display').textContent = "Toronto, ON";
710
+ document.getElementById('location-display').classList.remove('hidden');
711
+ updateWaterQuality(currentLocation);
712
+
713
+ // Try to get more accurate location
714
+ try {
715
+ const preciseLocation = await getPreciseLocation();
716
+ if (preciseLocation !== "toronto") {
717
+ currentLocation = preciseLocation;
718
+ const displayName = preciseLocation.charAt(0).toUpperCase() + preciseLocation.slice(1);
719
+ document.getElementById('location-input').value = displayName;
720
+ document.getElementById('location-display').textContent = `${displayName}, ON`;
721
+ updateWaterQuality(currentLocation);
722
+ } else {
723
+ // Fall back to IP-based location if precise location not available
724
+ const ipLocation = await getLocationByIP();
725
+ if (ipLocation !== "toronto") {
726
+ currentLocation = ipLocation;
727
+ const displayName = ipLocation.charAt(0).toUpperCase() + ipLocation.slice(1);
728
+ document.getElementById('location-input').value = displayName;
729
+ document.getElementById('location-display').textContent = `${displayName}, ON`;
730
+ updateWaterQuality(currentLocation);
731
+ }
732
+ }
733
+ } catch (error) {
734
+ console.error("Error getting location:", error);
735
+ }
736
+
737
+ // Location input event listeners
738
+ const locationInput = document.getElementById('location-input');
739
+ const locationDropdown = document.getElementById('location-dropdown');
740
+
741
+ locationInput.addEventListener('input', () => {
742
+ const query = locationInput.value.trim();
743
+
744
+ if (query.length > 1) {
745
+ const results = searchLocations(query.toLowerCase());
746
+
747
+ if (results.length > 0) {
748
+ locationDropdown.innerHTML = '';
749
+
750
+ results.forEach(result => {
751
+ const item = document.createElement('div');
752
+ item.className = 'location-dropdown-item';
753
+ item.textContent = result.name;
754
+ item.addEventListener('click', () => {
755
+ locationInput.value = result.name;
756
+ document.getElementById('location-display').textContent = `${result.name}, ON`;
757
+ document.getElementById('location-display').classList.remove('hidden');
758
+ locationDropdown.classList.add('hidden');
759
+ updateWaterQuality(result.key);
760
+ });
761
+
762
+ locationDropdown.appendChild(item);
763
+ });
764
+
765
+ locationDropdown.classList.remove('hidden');
766
+ } else {
767
+ locationDropdown.classList.add('hidden');
768
+ }
769
+ } else {
770
+ locationDropdown.classList.add('hidden');
771
+ }
772
+ });
773
+
774
+ locationInput.addEventListener('focus', () => {
775
+ if (locationInput.value.trim().length > 1 && locationDropdown.children.length > 0) {
776
+ locationDropdown.classList.remove('hidden');
777
+ }
778
+ });
779
+
780
+ document.addEventListener('click', (e) => {
781
+ if (!locationInput.contains(e.target) && !locationDropdown.contains(e.target)) {
782
+ locationDropdown.classList.add('hidden');
783
+ }
784
+ });
785
+
786
+ // Use my location button
787
+ document.getElementById('use-my-location').addEventListener('click', async () => {
788
+ locationInput.value = "Detecting your location...";
789
+ document.getElementById('location-display').classList.add('hidden');
790
+
791
+ try {
792
+ const location = await getPreciseLocation();
793
+ const displayName = location.charAt(0).toUpperCase() + location.slice(1);
794
+ locationInput.value = displayName;
795
+ document.getElementById('location-display').textContent = `${displayName}, ON`;
796
+ document.getElementById('location-display').classList.remove('hidden');
797
+ updateWaterQuality(location);
798
+ } catch (error) {
799
+ console.error("Error getting location:", error);
800
+ locationInput.value = "Toronto";
801
+ document.getElementById('location-display').textContent = "Toronto, ON";
802
+ document.getElementById('location-display').classList.remove('hidden');
803
+ updateWaterQuality("toronto");
804
+ }
805
+ });
806
+
807
+ // Refresh button
808
+ document.getElementById('refresh-btn').addEventListener('click', () => {
809
+ const currentValue = locationInput.value.trim();
810
+ if (currentValue && waterQualityData[currentValue.toLowerCase()]) {
811
+ updateWaterQuality(currentValue.toLowerCase());
812
+ } else {
813
+ updateWaterQuality("toronto");
814
+ locationInput.value = "Toronto";
815
+ document.getElementById('location-display').textContent = "Toronto, ON";
816
+ }
817
+ });
818
+ });
819
+ </script>
820
  </html>