Spaces:
Running
Running
Update templates/index.html
Browse files- templates/index.html +175 -22
templates/index.html
CHANGED
|
@@ -992,6 +992,12 @@
|
|
| 992 |
animation: height-pulse 0.8s ease;
|
| 993 |
}
|
| 994 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 995 |
/* Responsive design */
|
| 996 |
@media (max-width: 1024px) {
|
| 997 |
.main-container {
|
|
@@ -1051,9 +1057,6 @@
|
|
| 1051 |
<div class="page-wrapper">
|
| 1052 |
<header class="hero-header" style="position: relative; padding-top: 40px; text-align: center;">
|
| 1053 |
|
| 1054 |
-
<!-- <div style="position: absolute; top: 7%; left: 1%; font-size: 3.2vw; line-height: 0.8;">
|
| 1055 |
-
💦💧🌊
|
| 1056 |
-
</div> -->
|
| 1057 |
|
| 1058 |
<div style="display: inline-block; text-align: center; max-width: 90%; margin: 0 auto;">
|
| 1059 |
<h1 style="margin: 0; font-size: 3rem; line-height: 1.2;">
|
|
@@ -1092,14 +1095,15 @@
|
|
| 1092 |
<div class="input-card">
|
| 1093 |
<label for="latitude">Latitude</label>
|
| 1094 |
<input type="text" id="latitude" name="latitude" inputmode="text"
|
| 1095 |
-
pattern="-?[0-9]*[.,]?[0-9]*" required placeholder="40.7128">
|
| 1096 |
<p class="helper-text">Range: -90 to 90</p>
|
| 1097 |
</div>
|
| 1098 |
|
| 1099 |
<div class="input-card">
|
| 1100 |
<label for="longitude">Longitude</label>
|
| 1101 |
<input type="text" id="longitude" name="longitude" inputmode="text"
|
| 1102 |
-
pattern="-?[0-9]*[.,]?[0-9]*" autocomplete="off" required placeholder="-74.0060"
|
|
|
|
| 1103 |
<p class="helper-text">Range: -180 to 180</p>
|
| 1104 |
</div>
|
| 1105 |
|
|
@@ -1158,13 +1162,13 @@
|
|
| 1158 |
<div class="input-card">
|
| 1159 |
<label for="latitude2">Latitude</label>
|
| 1160 |
<input type="text" id="latitude2" name="latitude2" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1161 |
-
autocomplete="off" required placeholder="40.7128">
|
| 1162 |
</div>
|
| 1163 |
|
| 1164 |
<div class="input-card">
|
| 1165 |
<label for="longitude2">Longitude</label>
|
| 1166 |
<input type="text" id="longitude2" name="longitude2" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1167 |
-
autocomplete="off" required placeholder="-74.0060">
|
| 1168 |
</div>
|
| 1169 |
|
| 1170 |
<div class="input-card">
|
|
@@ -1221,13 +1225,13 @@
|
|
| 1221 |
<div class="input-card">
|
| 1222 |
<label for="latitude3">Latitude</label>
|
| 1223 |
<input type="text" id="latitude3" name="latitude3" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1224 |
-
autocomplete="off" required placeholder="40.7128">
|
| 1225 |
</div>
|
| 1226 |
|
| 1227 |
<div class="input-card">
|
| 1228 |
<label for="longitude3">Longitude</label>
|
| 1229 |
<input type="text" id="longitude3" name="longitude3" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1230 |
-
autocomplete="off" required placeholder="-74.0060">
|
| 1231 |
</div>
|
| 1232 |
|
| 1233 |
<div class="input-card">
|
|
@@ -1329,23 +1333,162 @@
|
|
| 1329 |
</footer>
|
| 1330 |
</div>
|
| 1331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1332 |
<script>
|
| 1333 |
-
|
| 1334 |
-
|
| 1335 |
-
|
| 1336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1337 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1338 |
|
| 1339 |
-
|
| 1340 |
-
const latId = button.dataset.latId;
|
| 1341 |
-
const lonId = button.dataset.lonId;
|
| 1342 |
-
const heightId = button.dataset.heightId;
|
| 1343 |
-
const errorId = button.dataset.errorId;
|
| 1344 |
|
| 1345 |
-
|
| 1346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1347 |
});
|
| 1348 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1349 |
});
|
| 1350 |
|
| 1351 |
async function predictHeight(latId, lonId, heightId, errorId, button) {
|
|
@@ -1407,6 +1550,14 @@
|
|
| 1407 |
}
|
| 1408 |
}
|
| 1409 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1410 |
function switchTab(tabName) {
|
| 1411 |
document.querySelectorAll('.assessment-card').forEach(card => {
|
| 1412 |
card.classList.remove('active');
|
|
@@ -1443,6 +1594,8 @@
|
|
| 1443 |
const data = await response.json();
|
| 1444 |
|
| 1445 |
if (data.status === 'success') {
|
|
|
|
|
|
|
| 1446 |
displayResults(data, resultsId, endpoint);
|
| 1447 |
} else {
|
| 1448 |
throw new Error(data.detail || 'Assessment failed');
|
|
@@ -1631,8 +1784,8 @@
|
|
| 1631 |
}
|
| 1632 |
|
| 1633 |
async function getHeightFromGBA(latId, lonId, heightId, errorId, button) {
|
| 1634 |
-
const lat =
|
| 1635 |
-
const lon =
|
| 1636 |
|
| 1637 |
const errorBox = document.getElementById(errorId);
|
| 1638 |
if (errorBox) errorBox.textContent = '';
|
|
|
|
| 992 |
animation: height-pulse 0.8s ease;
|
| 993 |
}
|
| 994 |
|
| 995 |
+
input[type="text"]::-webkit-calendar-picker-indicator,
|
| 996 |
+
input[type="number"]::-webkit-calendar-picker-indicator {
|
| 997 |
+
filter: invert(1);
|
| 998 |
+
cursor: pointer;
|
| 999 |
+
}
|
| 1000 |
+
|
| 1001 |
/* Responsive design */
|
| 1002 |
@media (max-width: 1024px) {
|
| 1003 |
.main-container {
|
|
|
|
| 1057 |
<div class="page-wrapper">
|
| 1058 |
<header class="hero-header" style="position: relative; padding-top: 40px; text-align: center;">
|
| 1059 |
|
|
|
|
|
|
|
|
|
|
| 1060 |
|
| 1061 |
<div style="display: inline-block; text-align: center; max-width: 90%; margin: 0 auto;">
|
| 1062 |
<h1 style="margin: 0; font-size: 3rem; line-height: 1.2;">
|
|
|
|
| 1095 |
<div class="input-card">
|
| 1096 |
<label for="latitude">Latitude</label>
|
| 1097 |
<input type="text" id="latitude" name="latitude" inputmode="text"
|
| 1098 |
+
pattern="-?[0-9]*[.,]?[0-9]*" required placeholder="40.7128" list="lat-suggestions">
|
| 1099 |
<p class="helper-text">Range: -90 to 90</p>
|
| 1100 |
</div>
|
| 1101 |
|
| 1102 |
<div class="input-card">
|
| 1103 |
<label for="longitude">Longitude</label>
|
| 1104 |
<input type="text" id="longitude" name="longitude" inputmode="text"
|
| 1105 |
+
pattern="-?[0-9]*[.,]?[0-9]*" autocomplete="off" required placeholder="-74.0060"
|
| 1106 |
+
list="lon-suggestions">
|
| 1107 |
<p class="helper-text">Range: -180 to 180</p>
|
| 1108 |
</div>
|
| 1109 |
|
|
|
|
| 1162 |
<div class="input-card">
|
| 1163 |
<label for="latitude2">Latitude</label>
|
| 1164 |
<input type="text" id="latitude2" name="latitude2" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1165 |
+
autocomplete="off" required placeholder="40.7128" list="lat-suggestions">
|
| 1166 |
</div>
|
| 1167 |
|
| 1168 |
<div class="input-card">
|
| 1169 |
<label for="longitude2">Longitude</label>
|
| 1170 |
<input type="text" id="longitude2" name="longitude2" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1171 |
+
autocomplete="off" required placeholder="-74.0060" list="lon-suggestions">
|
| 1172 |
</div>
|
| 1173 |
|
| 1174 |
<div class="input-card">
|
|
|
|
| 1225 |
<div class="input-card">
|
| 1226 |
<label for="latitude3">Latitude</label>
|
| 1227 |
<input type="text" id="latitude3" name="latitude3" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1228 |
+
autocomplete="off" required placeholder="40.7128" list="lat-suggestions">
|
| 1229 |
</div>
|
| 1230 |
|
| 1231 |
<div class="input-card">
|
| 1232 |
<label for="longitude3">Longitude</label>
|
| 1233 |
<input type="text" id="longitude3" name="longitude3" pattern="-?[0-9]*[.,]?[0-9]*"
|
| 1234 |
+
autocomplete="off" required placeholder="-74.0060" list="lon-suggestions">
|
| 1235 |
</div>
|
| 1236 |
|
| 1237 |
<div class="input-card">
|
|
|
|
| 1333 |
</footer>
|
| 1334 |
</div>
|
| 1335 |
|
| 1336 |
+
<datalist id="lat-suggestions"></datalist>
|
| 1337 |
+
<datalist id="lon-suggestions"></datalist>
|
| 1338 |
+
<datalist id="height-suggestions"></datalist>
|
| 1339 |
+
<datalist id="basement-suggestions"></datalist>
|
| 1340 |
+
|
| 1341 |
<script>
|
| 1342 |
+
const CACHE_KEY = 'flood_assessment_cache';
|
| 1343 |
+
const MAX_CACHE_SIZE = 5;
|
| 1344 |
+
|
| 1345 |
+
// Get cached entries
|
| 1346 |
+
function getCachedEntries() {
|
| 1347 |
+
try {
|
| 1348 |
+
const cached = localStorage.getItem(CACHE_KEY);
|
| 1349 |
+
return cached ? JSON.parse(cached) : [];
|
| 1350 |
+
} catch (e) {
|
| 1351 |
+
console.error('Error reading cache:', e);
|
| 1352 |
+
return [];
|
| 1353 |
}
|
| 1354 |
+
}
|
| 1355 |
+
|
| 1356 |
+
function saveToCacheFunction(latitude, longitude, height, basement) {
|
| 1357 |
+
try {
|
| 1358 |
+
let entries = getCachedEntries();
|
| 1359 |
+
|
| 1360 |
+
const newEntry = {
|
| 1361 |
+
latitude: parseFloat(latitude).toFixed(6),
|
| 1362 |
+
longitude: parseFloat(longitude).toFixed(6),
|
| 1363 |
+
height: parseFloat(height).toFixed(2),
|
| 1364 |
+
basement: parseFloat(basement).toFixed(2),
|
| 1365 |
+
timestamp: Date.now()
|
| 1366 |
+
};
|
| 1367 |
+
|
| 1368 |
+
// checknig if entry already exists
|
| 1369 |
+
const exists = entries.some(entry =>
|
| 1370 |
+
entry.latitude === newEntry.latitude &&
|
| 1371 |
+
entry.longitude === newEntry.longitude &&
|
| 1372 |
+
entry.height === newEntry.height &&
|
| 1373 |
+
entry.basement === newEntry.basement
|
| 1374 |
+
);
|
| 1375 |
+
|
| 1376 |
+
if (!exists) {
|
| 1377 |
+
entries.unshift(newEntry);
|
| 1378 |
+
|
| 1379 |
+
if (entries.length > MAX_CACHE_SIZE) {
|
| 1380 |
+
entries = entries.slice(0, MAX_CACHE_SIZE);
|
| 1381 |
+
}
|
| 1382 |
+
|
| 1383 |
+
localStorage.setItem(CACHE_KEY, JSON.stringify(entries));
|
| 1384 |
+
updateDatalistSuggestions();
|
| 1385 |
+
}
|
| 1386 |
+
} catch (e) {
|
| 1387 |
+
console.error('Error saving to cache:', e);
|
| 1388 |
+
}
|
| 1389 |
+
}
|
| 1390 |
+
|
| 1391 |
+
function updateDatalistSuggestions() {
|
| 1392 |
+
const entries = getCachedEntries();
|
| 1393 |
|
| 1394 |
+
updateDatalist('lat-suggestions', entries, 'latitude');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1395 |
|
| 1396 |
+
updateDatalist('lon-suggestions', entries, 'longitude');
|
| 1397 |
+
|
| 1398 |
+
updateDatalist('height-suggestions', entries, 'height');
|
| 1399 |
+
|
| 1400 |
+
updateDatalist('basement-suggestions', entries, 'basement');
|
| 1401 |
+
}
|
| 1402 |
+
|
| 1403 |
+
function updateDatalist(datalistId, entries, field) {
|
| 1404 |
+
const datalist = document.getElementById(datalistId);
|
| 1405 |
+
if (!datalist) return;
|
| 1406 |
+
|
| 1407 |
+
datalist.innerHTML = '';
|
| 1408 |
+
|
| 1409 |
+
entries.forEach((entry) => {
|
| 1410 |
+
const option = document.createElement('option');
|
| 1411 |
+
option.value = entry[field];
|
| 1412 |
+
|
| 1413 |
+
if (field === 'latitude') {
|
| 1414 |
+
option.label = `${entry.latitude} | Lon: ${entry.longitude}, H: ${entry.height}m, B: ${entry.basement}m`;
|
| 1415 |
+
} else if (field === 'longitude') {
|
| 1416 |
+
option.label = `${entry.longitude} | Lat: ${entry.latitude}, H: ${entry.height}m, B: ${entry.basement}m`;
|
| 1417 |
+
} else if (field === 'height') {
|
| 1418 |
+
option.label = `${entry.height}m | At: ${entry.latitude}, ${entry.longitude}`;
|
| 1419 |
+
} else if (field === 'basement') {
|
| 1420 |
+
option.label = `${entry.basement}m | At: ${entry.latitude}, ${entry.longitude}`;
|
| 1421 |
+
}
|
| 1422 |
+
|
| 1423 |
+
datalist.appendChild(option);
|
| 1424 |
+
});
|
| 1425 |
+
}
|
| 1426 |
+
|
| 1427 |
+
function setupAutoFill() {
|
| 1428 |
+
const allInputs = [
|
| 1429 |
+
{ id: 'latitude', suffix: '' },
|
| 1430 |
+
{ id: 'longitude', suffix: '' },
|
| 1431 |
+
{ id: 'latitude2', suffix: '2' },
|
| 1432 |
+
{ id: 'longitude2', suffix: '2' },
|
| 1433 |
+
{ id: 'latitude3', suffix: '3' },
|
| 1434 |
+
{ id: 'longitude3', suffix: '3' }
|
| 1435 |
+
];
|
| 1436 |
+
|
| 1437 |
+
allInputs.forEach(({ id, suffix }) => {
|
| 1438 |
+
const input = document.getElementById(id);
|
| 1439 |
+
if (!input) return;
|
| 1440 |
+
|
| 1441 |
+
input.addEventListener('change', function () {
|
| 1442 |
+
const value = parseNumber(this.value);
|
| 1443 |
+
if (isNaN(value)) return;
|
| 1444 |
+
|
| 1445 |
+
const entries = getCachedEntries();
|
| 1446 |
+
const field = id.includes('latitude') ? 'latitude' : 'longitude';
|
| 1447 |
+
const normalizedValue = value.toFixed(6);
|
| 1448 |
+
|
| 1449 |
+
// find matching entry
|
| 1450 |
+
const match = entries.find(entry => entry[field] === normalizedValue);
|
| 1451 |
+
|
| 1452 |
+
if (match) {
|
| 1453 |
+
// fill fields
|
| 1454 |
+
const latInput = document.getElementById('latitude' + suffix);
|
| 1455 |
+
const lonInput = document.getElementById('longitude' + suffix);
|
| 1456 |
+
const heightInput = document.getElementById('height' + suffix);
|
| 1457 |
+
const basementInput = document.getElementById('basement' + suffix);
|
| 1458 |
+
|
| 1459 |
+
if (latInput && lonInput && heightInput && basementInput) {
|
| 1460 |
+
latInput.value = match.latitude;
|
| 1461 |
+
lonInput.value = match.longitude;
|
| 1462 |
+
heightInput.value = match.height;
|
| 1463 |
+
basementInput.value = match.basement;
|
| 1464 |
+
|
| 1465 |
+
[latInput, lonInput, heightInput, basementInput].forEach(input => {
|
| 1466 |
+
input.classList.add('height-pulse');
|
| 1467 |
+
setTimeout(() => input.classList.remove('height-pulse'), 800);
|
| 1468 |
+
});
|
| 1469 |
+
}
|
| 1470 |
+
}
|
| 1471 |
});
|
| 1472 |
});
|
| 1473 |
+
}
|
| 1474 |
+
|
| 1475 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 1476 |
+
updateDatalistSuggestions();
|
| 1477 |
+
setupAutoFill();
|
| 1478 |
+
|
| 1479 |
+
const buttons = document.querySelectorAll('.predict-height-btn');
|
| 1480 |
+
if (buttons.length > 0) {
|
| 1481 |
+
buttons.forEach(button => {
|
| 1482 |
+
const latId = button.dataset.latId;
|
| 1483 |
+
const lonId = button.dataset.lonId;
|
| 1484 |
+
const heightId = button.dataset.heightId;
|
| 1485 |
+
const errorId = button.dataset.errorId;
|
| 1486 |
+
|
| 1487 |
+
button.addEventListener('click', () => {
|
| 1488 |
+
predictHeight(latId, lonId, heightId, errorId, button);
|
| 1489 |
+
});
|
| 1490 |
+
});
|
| 1491 |
+
}
|
| 1492 |
});
|
| 1493 |
|
| 1494 |
async function predictHeight(latId, lonId, heightId, errorId, button) {
|
|
|
|
| 1550 |
}
|
| 1551 |
}
|
| 1552 |
|
| 1553 |
+
// function to parse numbers with comma or dot as decimal separator
|
| 1554 |
+
function parseNumber(value) {
|
| 1555 |
+
if (typeof value === 'string') {
|
| 1556 |
+
value = value.replace(',', '.');
|
| 1557 |
+
}
|
| 1558 |
+
return parseFloat(value);
|
| 1559 |
+
}
|
| 1560 |
+
|
| 1561 |
function switchTab(tabName) {
|
| 1562 |
document.querySelectorAll('.assessment-card').forEach(card => {
|
| 1563 |
card.classList.remove('active');
|
|
|
|
| 1594 |
const data = await response.json();
|
| 1595 |
|
| 1596 |
if (data.status === 'success') {
|
| 1597 |
+
// Save to cache on successful assessment
|
| 1598 |
+
saveToCacheFunction(latitude, longitude, height, basement);
|
| 1599 |
displayResults(data, resultsId, endpoint);
|
| 1600 |
} else {
|
| 1601 |
throw new Error(data.detail || 'Assessment failed');
|
|
|
|
| 1784 |
}
|
| 1785 |
|
| 1786 |
async function getHeightFromGBA(latId, lonId, heightId, errorId, button) {
|
| 1787 |
+
const lat = parseNumber(document.getElementById(latId).value);
|
| 1788 |
+
const lon = parseNumber(document.getElementById(lonId).value);
|
| 1789 |
|
| 1790 |
const errorBox = document.getElementById(errorId);
|
| 1791 |
if (errorBox) errorBox.textContent = '';
|