Files
test/test3.html
2025-12-11 02:36:27 +00:00

985 lines
30 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>RLX Ver Helper</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js" integrity="sha256-6XMVI0zB8cRzfZjqKcD01PBsAy3FlDASrlC8SxCpInY=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.1/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<script src="https://cdn.rawgit.com/hayeswise/Leaflet.PointInPolygon/v1.0.0/wise-leaflet-pip.js"></script>
<script src="https://unpkg.com/@mapbox/leaflet-pip@latest/leaflet-pip.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js"></script>
</head>
<body>
<style type="text/css">
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#container {
display: flex;
height: 100%;
width: 100%;
position: relative;
}
#pickwwa {
position: absolute;
top: 10px;
left: 10px;
width: 300px;
background: white;
padding: 10px;
z-index: 9998;
}
p {
margin-top: 0;
margin-bottom: 0;
line-height: 1.5; /* Optional: controls line spacing */
}
#richTextArea {
margin-left: 500px; /* Space for pickwwa + padding */
margin-right: 5px; /* Space for map + padding */
margin-top: 10px;
margin-bottom: 10px;
padding: 10px;
background: #f9f9f9;
border: 1px solid #ddd;
overflow-y: auto;
flex-grow: 1;
}
#selectable-list {
list-style-type: none;
padding: 0;
z-index: 9999;
}
#selectable-list li {
padding: 10px;
margin: 5px 0;
background-color: #f0f0f0;
cursor: pointer;
}
#selectable-list li.selected {
background-color: #007bff;
color: white;
}
.list-label {
font-weight: bold;
margin-bottom: 10px;
}
.my-label {
font-size: 6px;
border: 0px solid #ccc;
border-radius: 1px;
padding: 1px 2px;
}
#hydrograph {
display: none;
}
</style>
<div id="container">
<div id="pickwwa">
<label for="yeartowork">Choose a year:</label>
<select name="yeartowork" id="yeartowork" onchange="getwwas()">
<option value="2025">2025</option>
<option value="2024">2024</option>
<option value="2023">2023</option>
<option value="2022">2022</option>
</select>
<ul id="selectable-list"></ul>
<label for="lsrbuffer">Buffer LSR hrs on back end of product</label>
<input id="lsrbuffer" name="lsrbuffer" type="input" value="24">
<input id="LSR" type="button" value="Load LSRs" onclick="loadlsr();" />
</div>
<div id="mapid"></div>
<div id="richTextArea">
<canvas id="hydrograph" width="800" height="400"></canvas>
</div>
</div>
<script>
async function getNWSGaugesByBoundingBox(xmin, ymin, xmax, ymax) {
// Construct the API URL with provided bounding box values
const url = `https://api.water.noaa.gov/nwps/v1/gauges?bbox.xmin=${xmin}&bbox.ymin=${ymin}&bbox.xmax=${xmax}&bbox.ymax=${ymax}&srid=EPSG_4326&catfim=false`;
try {
// Fetch the data
const response = await fetch(url, {
headers: {
'Accept': 'application/json'
}
});
// Check if the response is OK
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Parse the JSON response
const data = await response.json();
// Return the gauge data (assuming the response contains a 'data' array)
return data || [];
} catch (error) {
console.error('Error fetching NWS gauge data:', error);
return [];
}
}
async function getRLXgauges() {
// Example bounding box values from your URL
const xmin = -83.254395; // West longitude
const ymin = 36.932330; // South latitude
const xmax = -78.508301; // East longitude
const ymax = 40.145289; // North latitude
const gauges = await getNWSGaugesByBoundingBox(xmin, ymin, xmax, ymax);
return gauges
}
var wwalsrtypes = []
function isNumber(value) {
// Check if the value is of type 'number' and is finite
return typeof value === 'number' && isFinite(value);
}
var mymap = L.map('mapid', {zoomDelta: 0.25, zoomSnap: 0}).setView([38.508, -82.652480], 8.0);
var countiesjson = 'https://wx.stoat.org/main.php?service=ver'
const sbwprods = ["TO","SV","EW","SQ","FA","FL","FF"]
const phenomenonCodes = {
"AF": "Ashfall (land)",
"AS": "Air Stagnation",
"BH": "Beach Hazard",
"BW": "Brisk Wind",
"BZ": "Blizzard",
"CF": "Coastal Flood",
"CW": "Cold Weather",
"DF": "Debris Flow",
"DS": "Dust Storm",
"DU": "Blowing Dust",
"EC": "Extreme Cold",
"EH": "Excessive Heat",
"XH": "Extreme Heat",
"EW": "Extreme Wind",
"FA": "Flood",
"FF": "Flash Flood",
"FG": "Dense Fog (land)",
"FL": "Flood (Forecast Points)",
"FR": "Frost",
"FW": "Fire Weather",
"FZ": "Freeze",
"GL": "Gale",
"HF": "Hurricane Force Wind",
"HT": "Heat",
"HU": "Hurricane",
"HW": "High Wind",
"HY": "Hydrologic",
"HZ": "Hard Freeze",
"IS": "Ice Storm",
"LE": "Lake Effect Snow",
"LO": "Low Water",
"LS": "Lakeshore Flood",
"LW": "Lake Wind",
"MA": "Marine",
"MF": "Dense Fog (marine)",
"MH": "Ashfall (marine)",
"MS": "Dense Smoke (marine)",
"RP": "Rip Current Risk",
"SC": "Small Craft",
"SE": "Hazardous Seas",
"SM": "Dense Smoke (land)",
"SR": "Storm",
"SS": "Storm Surge",
"SQ": "Snow Squall",
"SU": "High Surf",
"SV": "Severe Thunderstorm",
"TO": "Tornado",
"TR": "Tropical Storm",
"TS": "Tsunami",
"TY": "Typhoon",
"UP": "Heavy Freezing Spray",
"WC": "Wind Chill",
"WI": "Wind",
"WS": "Winter Storm",
"WW": "Winter Weather",
"ZF": "Freezing Fog",
"ZR": "Freezing Rain",
"ZY": "Freezing Spray"
};
function getPhenSig(code,sig) {
// Convert the code to uppercase to ensure case-insensitive lookup
code = code.toUpperCase();
// Check if the code exists in the phenomenonCodes object
if (code in phenomenonCodes) {
var phenomenon = phenomenonCodes[code];
}
if (sig in sigCodes) {
var significance = sigCodes[sig];
}
return phenomenon + " " + significance
}
sigCodes = {
"W": "Warning",
"A": "Watch",
"Y": "Advisory",
"S": "Statement",
}
function getUsgsIdFromNwsGauge(gaugeId, startDate, endDate) {
const nwsUrl = "https://api.water.noaa.gov/nwps/v1/gauges/" + gaugeId;
return fetch(nwsUrl, {
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" }
})
.then(nwsResponse => {
if (!nwsResponse.ok) {
throw new Error("NWS API error! Status: " + nwsResponse.status);
}
return nwsResponse.json();
})
.then(nwsData => {
let usgsId = nwsData.usgsId || null;
if (!usgsId && nwsData.dataAttribution && Array.isArray(nwsData.dataAttribution)) {
const usgsEntry = nwsData.dataAttribution.find(
entry => entry.abbrev === "USGS" && entry.text.match(/^\d{8}$/)
);
usgsId = usgsEntry ? usgsEntry.text : null;
}
console.log("USGS ID for " + gaugeId + ": " + (usgsId || "Not found"));
gaugeName = nwsData ? nwsData.name : null;
if (!usgsId) {
console.log("No USGS ID found, skipping requests.");
return null;
}
// Fetch flood stages
const floodStageUrl = "https://waterdata.usgs.gov/flood-stage/" + usgsId;
console.log("Fetching flood stages from: " + floodStageUrl);
return Promise.all([
fetch(floodStageUrl, {
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" }
}).then(response => {
if (!response.ok) {
throw new Error("Flood stage API error! Status: " + response.status);
}
return response.json();
}),
fetch("https://nwis.waterservices.usgs.gov/nwis/iv/?" +
"site=" + usgsId + "&" +
"startDT=" + startDate + "&" +
"endDT=" + endDate + "&" +
"parameterCd=00065&" +
"format=json", {
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" }
}).then(response => {
if (!response.ok) {
throw new Error("USGS API error! Status: " + response.status);
}
return response.json();
})
])
.then(([floodStageData, usgsData]) => {
// Extract flood stages
const floodStages = floodStageData.stages || {};
console.log("Flood stages:", floodStages);
// Extract gauge height data
if (usgsData.value && usgsData.value.timeSeries && usgsData.value.timeSeries.length > 0) {
const timeSeries = usgsData.value.timeSeries[0];
const values = timeSeries.values[0].value;
const results = values.map(value => ({
timestamp: value.dateTime,
gaugeHeight: parseFloat(value.value)
}));
console.log("Data retrieved:", results);
generateHydrograph(results, floodStages,gaugeId,gaugeName);
return results;
} else {
console.log("No data found for the specified range.");
return [];
}
});
})
.catch(error => {
console.error("Error processing " + gaugeId + ": ", error);
return null;
});
}
function generateHydrograph(data, floodStages,gaugeId,gaugeName) {
const ctx = document.getElementById("hydrograph").getContext("2d");
const labels = data.map(d => new Date(d.timestamp).toLocaleString());
const gaugeHeights = data.map(d => d.gaugeHeight);
// Build datasets for flood stages
const floodDatasets = [];
if (floodStages.action !== null) {
floodDatasets.push({
label: "Action Stage (" + floodStages.action + " ft)",
data: Array(labels.length).fill(floodStages.action),
borderColor: "yellow",
borderWidth: 2,
pointRadius: 0,
fill: false,
borderDash: [5, 5]
});
}
if (floodStages.minor !== null) {
floodDatasets.push({
label: "Minor Flood (" + floodStages.minor + " ft)",
data: Array(labels.length).fill(floodStages.minor),
borderColor: "orange",
borderWidth: 2,
pointRadius: 0,
fill: false,
borderDash: [5, 5]
});
}
if (floodStages.moderate !== null) {
floodDatasets.push({
label: "Moderate Flood (" + floodStages.moderate + " ft)",
data: Array(labels.length).fill(floodStages.moderate),
borderColor: "red",
borderWidth: 2,
pointRadius: 0,
fill: false,
borderDash: [5, 5]
});
}
if (floodStages.major !== null) {
floodDatasets.push({
label: "Major Flood (" + floodStages.major + " ft)",
data: Array(labels.length).fill(floodStages.major),
borderColor: "purple",
borderWidth: 2,
pointRadius: 0,
fill: false,
borderDash: [5, 5]
});
}
new Chart(ctx, {
type: "line",
data: {
labels: labels,
datasets: [
{
label: "Gauge Height (ft)",
data: gaugeHeights,
borderColor: "blue",
fill: false,
tension: 0.1
},
...floodDatasets // Spread flood stage datasets
]
},
options: {
responsive: false,
scales: {
x: {
title: { display: true, text: "Time" }
},
y: {
title: { display: true, text: "Gauge Height (ft)" },
suggestedMin: Math.min(...gaugeHeights, floodStages.action || Infinity, floodStages.minor || Infinity, floodStages.moderate || Infinity, floodStages.major || Infinity) - 2,
suggestedMax: Math.max(...gaugeHeights, floodStages.action || -Infinity, floodStages.minor || -Infinity, floodStages.moderate || -Infinity, floodStages.major || -Infinity) + 2
}
},
plugins: {
title: { display: true, text: "Hydrograph for " + gaugeId + " (" + gaugeName + ") with Flood Stages" },
legend: { position: "top" }
}
}
});
}
function displayHydrograph(gaugeName) {
const canvas = document.getElementById("hydrograph");
const ctx = canvas.getContext("2d");
// Hide the canvas (assuming it's already hidden via CSS, or add this line)
canvas.style.display = "none";
// Create/update the image
let img = document.getElementById("hydrograph-image");
if (!img) {
img = document.createElement("img");
img.id = "hydrograph-image";
const container = document.getElementById("hydrograph-container");
container.appendChild(img);
// Optional: Add a download button
const downloadBtn = document.createElement("a");
downloadBtn.textContent = "Download Hydrograph";
downloadBtn.id = "download-link";
container.appendChild(downloadBtn);
}
// Set the image source
const imageData = canvas.toDataURL("image/png");
img.src = imageData;
// Update download link with custom filename
const downloadLink = document.getElementById("download-link");
if (downloadLink) {
downloadLink.href = imageData;
downloadLink.download = `${gaugeName}.png`; // Sets filename to GAUGENAME.png
}
// Clear the canvas for reuse
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
var Esri_WorldStreetMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri'
});
function getwwas() {
var e = document.getElementById("yeartowork");
var value = e.options[e.selectedIndex].value;
year = value
url = `https://mesonet.agron.iastate.edu/json/vtec_events.py?wfo=RLX&year=${year}`
$.getJSON(url, function(data) {
events = []
for (i in data.events) {
if (data.events[i].significance != "A") {
events.push(data.events[i])
}
}
createEventSelector(events);
});
}
function createEventSelector(events) {
if ($("#eventSelector").length > 0) {
$("#eventSelector").remove();
}
const select = document.createElement('select');
select.id = 'eventSelector';
select.multiple = true; // This allows multiple selections
// Add size attribute to show more than one option at a time
select.size = Math.min(events.length, 25); // Display up to 10 options or all if fewer
// Populate the select with options from the events array
events.forEach(event => {
const option = document.createElement('option');
var whatitis = getPhenSig(event.phenomena,event.significance) + " " + event.eventid
//option.value = event.eventid;
option.value = whatitis;
option.text = `${whatitis} ${event.issue.slice(0, -4) + 'Z'}-${event.init_expire.slice(0, -4) + 'Z'}`;
select.add(option);
});
// Find the 'pickwwa' div to append the select to
const pickwwaDiv = document.getElementById('pickwwa');
if (pickwwaDiv) {
pickwwaDiv.appendChild(select);
} else {
console.error('Div with ID "pickwwa" not found.');
}
// Event listener for handling multiple selections
select.addEventListener('change', function() {
fullarray = []
var selectedOptions = Array.from(this.selectedOptions);
var selectedEvents = selectedOptions.map(option =>
events.find(e => getPhenSig(e.phenomena,e.significance) + " " + e.eventid == option.value)
);
const sortedEvents = selectedEvents.sort((a, b) => {
const significanceA = a.significance;
const significanceB = b.significance;
// Assign sorting weights: "W" has higher priority (lower value) than "Y"
if (significanceA === "Y" && significanceB !== "Y") return -1; // a comes first
if (significanceB === "Y" && significanceA !== "Y") return 1; // b comes first
// If both are the same or neither is "W", keep original order
return 0;
});
plotselectedproducts(sortedEvents)
});
return select;
}
function getEarliestAndLatest(fullarray) {
if (!Array.isArray(fullarray) || fullarray.length === 0) {
throw new Error("Input must be a non-empty array");
}
let earliestIssue = null;
let latestExpire = null;
for (const event of fullarray) {
const issueDate = new Date(event.issue);
const expireDate = new Date(event.init_expire);
if (isNaN(issueDate) || isNaN(expireDate)) {
console.warn(`Skipping event with invalid dates: ${JSON.stringify(event)}`);
continue;
}
if (!earliestIssue || issueDate < new Date(earliestIssue)) {
earliestIssue = event.issue;
}
if (!latestExpire || expireDate > new Date(latestExpire)) {
latestExpire = event.init_expire;
}
}
if (!earliestIssue || !latestExpire) {
throw new Error("No valid dates found in the array");
}
return { earliestIssue, latestExpire };
}
function plotselectedproducts(events) {
lsrs = grabLSRS(events)
let fullarray = [];
let promises = [];
// Convert events to array if it's an object and loop with for...of
const eventArray = Array.isArray(events) ? events : Object.values(events);
for (let event of eventArray) {
let year = yearfromissue(event.issue);
let url;
if (sbwprods.includes(event.phenomena)) {
url = `https://mesonet.agron.iastate.edu/geojson/vtec_event.py?wfo=RLX&phenomena=${event.phenomena}&significance=${event.significance}&etn=${event.eventid}&year=${year}&sbw=1`;
} else {
url = `https://mesonet.agron.iastate.edu/geojson/vtec_event.py?wfo=RLX&phenomena=${event.phenomena}&significance=${event.significance}&etn=${event.eventid}&year=${year}`;
}
// Create a promise for each request and push the result to fullarray
const promise = $.getJSON(url).then(function(data) {
const combined = {event,data}
fullarray.push(combined);
return data; // Optional: return data if needed later
});
promises.push(promise);
}
// Wait for all promises to resolve before calling generateText
Promise.all(promises)
.then(() => {
generateText(fullarray, events);
})
.catch(error => {
console.error('One or more requests failed:', error);
});
}
function grabLSRS(fullarray) {
console.log(fullarray)
lsrtimes = getEarliestAndLatest(fullarray)
lsrstart = lsrtimes.earliestIssue
lsrend = lsrtimes.latestExpire
//lsrend = convertTimestamp(lsrendobj.toISOString())
// lsrstart = convertTimestamp(lsrstartobj.toISOString())
var url = `https://mesonet.agron.iastate.edu/geojson/lsr.geojson?sts=${lsrstart}&ets=${lsrend}&wfos=RLX`
$.getJSON(url, function(data) {
data = deduplicateGeoJSON(data);
return data
});
}
function generateText(events,lsrs) {
let htmlContent = '';
events.forEach((event, index) => {
htmlContent += printEventSummary(event)
});
document.getElementById('richTextArea').innerHTML = htmlContent;
}
const markersLayer = L.layerGroup().addTo(mymap);
//Pull all lsrs, check appropriate types, add under product
function getRGBByWarningType(warningType) {
const warning = warnings.find(w => w.warningType === warningType);
return warning ? warning.colorName : null;
}
function getLSRbyWarnType(warningType) {
const warning = warnings.find(w => w.warningType === warningType);
return warning ? warning.code : null;
}
function wwacolor(feature) {
code = feature.properties.phenomena
sig = feature.properties.significance
product = getPhenSig(code,sig)
producthex = getRGBByWarningType(product)
lsrcodes = getLSRbyWarnType(product)
wwalsrtypes = lsrcodes
if (producthex != null) {
return producthex
}
else {
return 'purple'
}
}
function yearfromissue(issue) {
var date = new Date(issue)
return date.getFullYear()
}
function deduplicateGeoJSON(geojson) {
const toTimestamp = (dateStr) => new Date(dateStr).getTime();
let featuresArray = geojson.features;
featuresArray.sort((a, b) => {
return toTimestamp(b.properties.utc_valid) - toTimestamp(a.properties.utc_valid);
});
const seenCoords = new Set();
const uniqueFeatures = featuresArray.filter(feature => {
const coords = JSON.stringify(feature.geometry.coordinates);
if (!seenCoords.has(coords)) {
seenCoords.add(coords);
return true;
}
return false;
});
return {
type: "FeatureCollection",
features: uniqueFeatures
};
}
function isMarkerInsidePolygon(latlng, polygon) {
leafletPip.bassackwards = true
return leafletPip.pointInLayer(latlng, polygon, true); // If results has length, the point is inside
}
function convertZuluToLocalInput(zuluTimestamp) {
// Check if the input is valid
if (!zuluTimestamp || !zuluTimestamp.endsWith('Z')) {
return null; // Return null for invalid input
}
try {
// Create a Date object from the Zulu timestamp
const date = new Date(zuluTimestamp);
// Check if the date is valid
if (isNaN(date.getTime())) {
return null;
}
// Convert to ISO string and remove the 'Z' and milliseconds
// Format will be YYYY-MM-DDTHH:MM:SS
const localFormat = date.toISOString().slice(0, 19);
return localFormat;
} catch (error) {
console.error('Error converting timestamp:', error);
return null;
}
}
function convertTimestamp(timestamp) {
// Remove the 'Z' from the end and split the date and time
const dateTimeParts = timestamp.split('T'); // Split into date and time
const date = dateTimeParts[0]; // YYYY-MM-DD
const time = dateTimeParts[1].slice(0, 5); // HH:mm (from HH:mm:ss)
const [year, month, day] = date.split('-'); // [YYYY, MM, DD]
const formatted = `${year}${month}${day}${time.replace(":", "")}`; // YYYYMMDDHHmm
return formatted;
}
function addHoursToDate(date, hours) {
let newDate = new Date(date);
newDate.setHours(newDate.getHours() + hours);
return newDate;
}
function loadlsr() {
padding = parseInt(document.getElementById('lsrbuffer').value)
var lsrendbuffer = addHoursToDate(lsrendobj,padding)
lsrend = convertTimestamp(lsrendbuffer.toISOString())
lsrstart = convertTimestamp(lsrstartobj.toISOString())
lsrendbuffer = lsrendobj
markersLayer.clearLayers()
geoJSONwwa1.setStyle({fillColor: 'transparent'})
templsr = []
var newlsr = []
var templsrnotin = []
var verarray = []
var markers = []
var url = `https://mesonet.agron.iastate.edu/geojson/lsr.geojson?sts=${lsrstart}&ets=${lsrend}&wfos=RLX`
$.getJSON(url, function(data) {
data = deduplicateGeoJSON(data);
values = wwalsrtypes
//if we have lsr types we want, only show those
if (values) {
for(var i in data.features) {
if (values.includes(data.features[i].properties.type)) {
var returnval = isMarkerInsidePolygon(data.features[i].geometry.coordinates.reverse(),geoJSONwwa1);
if (returnval.length > 0) {
//data.features[i].properties.state_zone = (returnval[0].feature.id).replace(/Z/g, '')
data.features[i].properties.state_zone = returnval[0].feature.properties.state_zone
templsr.push(data.features[i])
}
}
}
}
//if we don't have lsr types we want, show everything
if (!values) {
for(var i in data.features) {
var returnval = isMarkerInsidePolygon(data.features[i].geometry.coordinates.reverse(),geoJSONwwa1);
if (returnval.length > 0) {
data.features[i].properties.state_zone = returnval[0].feature.properties.state_zone
templsr.push(data.features[i])
}
}
}
for (k in templsr) {
var marker = styleLSR(templsr[k].geometry.coordinates,templsr[k].properties.magnitude,templsr[k].properties.state_zone,templsr[k].properties)
markersLayer.addLayer(marker)
var flag = newflag(templsr[k].properties.type,templsr[k].properties.state_zone,templsr[k].properties.magf)
var sbwflag = 'test'
//var flag = checkagainstcriteria(templsr[k].properties.state_zone,templsr[k].properties.magf)
//create a second array or combine for polygon based products
if (flag != null) {
verarray.push([templsr[k].properties.state_zone,flag])
}
}
console.log(verarray)
zones = processZones(verarray)
console.log(verarray,zones)
geoJSONwwa1.eachLayer(function(layer) {
zone = layer.feature.properties.state + layer.feature.properties.zone
for (var l in zones) {
//console.log(zones[l],zone)
if (zone == zones[l][0]) {
if (zones[l][1].warning >= 2) {
layer.setStyle({fillColor: 'gold'});
}
else if (zones[l][1].advisory >= 2) {
layer.setStyle({fillColor: 'green'});
}
}
}
});
geoJSONwwas.eachLayer(function(layer) {
if (sbwprods.includes(layer.feature.properties.phenomena)) {
console.log(layer)
}
});
//add lsr autoupdate for products that are less than 24hrs from expiration
});
}
function printEventSummary(obj) {
const { event, data } = obj;
const hasFeatures = data.features && data.features.length > 0;
const featureIds = hasFeatures ? data.features.map(feature => feature.id || 'N/A').join(', ') : 'N/A';
const generateRadmapUrl = (uri) => {
const vtec = uri.split('/').pop(); // Extracts "2025-O-NEW-KRLX-CW-Y-0013" from "/vtec/event/2025-O-NEW-KRLX-CW-Y-0013"
return `https://mesonet.agron.iastate.edu/GIS/radmap.php?layers[]=nexrad&layers[]=sbw&layers[]=sbwh&layers[]=uscounties&vtec=${vtec}`;
};
const radmapUrl = generateRadmapUrl(event.uri);
const toEST = (utcDate) => {
const date = new Date(utcDate);
date.setUTCHours(date.getUTCHours() - 5);
return date.toLocaleString('en-US', {
timeZone: 'UTC',
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: '2-digit'
});
};
// Function to format UTC date in 24-hour format without seconds
const toUTC = (utcDate) => {
return new Date(utcDate).toLocaleString('en-US', {
timeZone: 'UTC',
hour12: false,
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
decoded = getPhenSig(event.phenomena, event.significance)
let html = `<div class="event-summary">
<p><strong>Event:</strong> ${event.phenomena}.${event.significance} - ${decoded}</p>
<p><strong>Issued:</strong> ${toUTC(event.issue)} (UTC) ${toEST(event.issue)} (EST)</p>
<p><strong>Initial Expiration:</strong> ${toUTC(event.init_expire)} (UTC) ${toEST(event.init_expire)} (EST)</p>
<p><strong>Expiration:</strong> ${toUTC(event.expire)} (UTC) ${toEST(event.expire)} (EST)</p>
<p><strong>Locations:</strong> ${event.locations ? event.locations : 'Not specified'}</p>
<p><strong>Radar Map:</strong> <img src="${radmapUrl}" alt="Radar map for ${event.phenomena}.${event.significance}"></p>
<p><strong>Timing Graph:</strong><a href="https://wx.stoat.org/metar.html?start=${convertZuluToLocalInput(event.issue)}&end=${convertZuluToLocalInput(event.init_expire)}" target="_blank">Link</a></p>
<p><strong>Power Outages For Duration:</strong><a href="https://wx.stoat.org/outage.html?start=${convertZuluToLocalInput(event.issue)}&end=${convertZuluToLocalInput(event.init_expire)}" target="_blank">Link</a></p>
`;
//https://wx.stoat.org/metar.html?start=2025-04-01T12:00:00&end=2025-04-02T12:00:00
if (event.hvtec_nwsli !== undefined && event.hvtec_nwsli !== null && event.hvtec_nwsli !== "00000") {
html += `
<p><strong>HVTEC NWSLI:</strong> ${event.hvtec_nwsli}</p>
`;
}
html += `
<p><strong>IAState VTEC Link:</strong> <a href="${event.url}" target="_blank">${event.url}</a></p>
<p><strong>Feature ID${hasFeatures && data.features.length > 1 ? 's' : ''}:</strong> ${featureIds}</p>
</div>
`;
return html;
}
getwwas()
</script>
</body>
</html>