initial
This commit is contained in:
193
wx.html
Normal file
193
wx.html
Normal file
@@ -0,0 +1,193 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>NWS Weather Data Aggregator</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
.loader {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
.chart-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#output {
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200">
|
||||
|
||||
<div class="container mx-auto p-4 md:p-8">
|
||||
<header class="text-center mb-8">
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white">NWS Weather Data Aggregator</h1>
|
||||
<p class="text-md text-gray-600 dark:text-gray-400 mt-2">Click the button to fetch and combine weather data for analysis.</p>
|
||||
</header>
|
||||
|
||||
<!-- Data Fetching Section -->
|
||||
<section class="mb-12 bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||
<div class="flex flex-col items-center">
|
||||
<button id="fetchButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg transition-colors duration-300 text-lg">
|
||||
Fetch All Data
|
||||
</button>
|
||||
<div id="loader" class="loader mt-6 hidden"></div>
|
||||
<p id="status" class="mt-4 text-gray-600 dark:text-gray-400"></p>
|
||||
</div>
|
||||
|
||||
<div id="outputContainer" class="mt-6 hidden">
|
||||
<h2 class="text-2xl font-semibold mb-4 text-gray-900 dark:text-white">Combined Data Output</h2>
|
||||
<textarea id="output" class="w-full p-4 border border-gray-300 dark:border-gray-600 rounded-md bg-gray-50 dark:bg-gray-700 text-gray-800 dark:text-gray-200" readonly></textarea>
|
||||
<button id="copyButton" class="mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-lg transition-colors duration-300">
|
||||
Copy to Clipboard
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Graphical Forecasts Section -->
|
||||
<section>
|
||||
<h2 class="text-2xl font-semibold border-b-2 border-gray-500 pb-2 mb-6 text-gray-900 dark:text-white">Graphical Forecasts (for reference)</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-lg">
|
||||
<h3 class="font-bold text-xl mb-4 text-center">Surface Analysis</h3>
|
||||
<img class="chart-img w-full rounded-md" src="https://www.wpc.ncep.noaa.gov/sfc/lrgnamsfc.gif" alt="Surface Analysis Chart">
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-lg">
|
||||
<h3 class="font-bold text-xl mb-4 text-center">500mb Heights & Vorticity</h3>
|
||||
<img class="chart-img w-full rounded-md" src="https://www.wpc.ncep.noaa.gov/upperair/nam.gif" alt="500mb Analysis Chart">
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-lg">
|
||||
<h3 class="font-bold text-xl mb-4 text-center">850mb Temps, Heights & Winds</h3>
|
||||
<img class="chart-img w-full rounded-md" src="https://www.wpc.ncep.noaa.gov/upperair/nam85.gif" alt="850mb Analysis Chart">
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-lg">
|
||||
<h3 class="font-bold text-xl mb-4 text-center">250mb Jet Stream</h3>
|
||||
<img class="chart-img w-full rounded-md" src="https://www.wpc.ncep.noaa.gov/upperair/namhi.gif" alt="250mb Analysis Chart">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="text-center mt-12 py-4 border-t dark:border-gray-700">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Data provided by the National Weather Service.</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- DOM Element References ---
|
||||
const fetchButton = document.getElementById('fetchButton');
|
||||
const copyButton = document.getElementById('copyButton');
|
||||
const outputContainer = document.getElementById('outputContainer');
|
||||
const outputTextarea = document.getElementById('output');
|
||||
const loader = document.getElementById('loader');
|
||||
const statusText = document.getElementById('status');
|
||||
|
||||
// --- Data Source URLs ---
|
||||
const sources = {
|
||||
areaForecastDiscussion: `https://forecast.weather.gov/product.php?site=RLX&issuedby=RLX&product=AFD&format=txt&version=1&glossary=0`,
|
||||
spcOutlook: `https://www.spc.noaa.gov/products/outlook/day1otlk.html`,
|
||||
wpcShortRange: `https://www.wpc.ncep.noaa.gov/discussions/pmdspd.html`,
|
||||
wpcMediumRange: `https://www.wpc.ncep.noaa.gov/discussions/pmdepd.html`
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches content from a URL and extracts text from a <pre> tag.
|
||||
* @param {string} url - The URL to fetch data from.
|
||||
* @param {string} key - A key to identify the data source for error messages.
|
||||
* @returns {Promise<string>} A promise that resolves with the extracted text.
|
||||
*/
|
||||
async function fetchAndParse(url, key) {
|
||||
try {
|
||||
// NOTE: The CORS proxy has been removed as requested.
|
||||
// If you experience fetching errors when hosting this on a static site,
|
||||
// it's likely due to the browser's same-origin policy blocking the request.
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const html = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
const preElement = doc.querySelector('pre');
|
||||
return preElement ? preElement.innerText.trim() : `Could not find <pre> content for ${key}.`;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching ${key}:`, error);
|
||||
statusText.innerHTML += `<br><span class="text-red-500">Failed to fetch ${key}.</span>`;
|
||||
return `Error fetching data for ${key}: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to orchestrate fetching all data sources.
|
||||
*/
|
||||
async function fetchAllData() {
|
||||
// --- UI Updates: Start Loading ---
|
||||
fetchButton.disabled = true;
|
||||
fetchButton.classList.add('opacity-50', 'cursor-not-allowed');
|
||||
loader.classList.remove('hidden');
|
||||
statusText.textContent = 'Fetching data...';
|
||||
outputContainer.classList.add('hidden');
|
||||
|
||||
const data = {};
|
||||
|
||||
// --- Fetch all sources concurrently ---
|
||||
const promises = [
|
||||
fetchAndParse(sources.areaForecastDiscussion, 'Area Forecast Discussion').then(text => data.areaForecastDiscussion = text),
|
||||
fetchAndParse(sources.spcOutlook, 'SPC Outlook').then(text => data.spcOutlook = text),
|
||||
fetchAndParse(sources.wpcShortRange, 'WPC Short Range').then(text => data.wpcShortRange = text),
|
||||
fetchAndParse(sources.wpcMediumRange, 'WPC Medium Range').then(text => data.wpcMediumRange = text)
|
||||
];
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
// --- UI Updates: Display Results ---
|
||||
loader.classList.add('hidden');
|
||||
statusText.textContent = 'Data fetching complete!';
|
||||
|
||||
// Format the collected data as a JSON string and display it
|
||||
outputTextarea.value = JSON.stringify(data, null, 2);
|
||||
outputContainer.classList.remove('hidden');
|
||||
|
||||
// Re-enable the fetch button
|
||||
fetchButton.disabled = false;
|
||||
fetchButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the content of the textarea to the user's clipboard.
|
||||
*/
|
||||
function copyToClipboard() {
|
||||
outputTextarea.select();
|
||||
// Using document.execCommand as a fallback for broader compatibility
|
||||
// in environments where navigator.clipboard might be restricted.
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
copyButton.textContent = 'Copied!';
|
||||
setTimeout(() => { copyButton.textContent = 'Copy to Clipboard'; }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
copyButton.textContent = 'Copy Failed!';
|
||||
setTimeout(() => { copyButton.textContent = 'Copy to Clipboard'; }, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Event Listeners ---
|
||||
fetchButton.addEventListener('click', fetchAllData);
|
||||
copyButton.addEventListener('click', copyToClipboard);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user