Files
test/test3.html
2025-11-27 22:25:36 +00:00

1008 lines
31 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>
/*
always EST
create (static?) map for each product in leaflet
instead of point in polygon just plot all lsrs that fall within the bounding box of map around polygon
find hydrographs in non river point warnings and plot
color lsrs by time, red before product, yellow 30 min to 15hrs depending, green inside product
could also color by criteria
filter lsr by product type
take arguments for time and distance buffer for LSR
create hydrograph as static image for flood Points
find hydrographs in areals and plot
Link for hydrographs
Link to severe polygon on power outage
https://wx.stoat.org/powerapi.php?svrpolys=potato
https://waterservices.usgs.gov/nwis/iv/?format=json&bBox=-83.254395,36.932330,-78.508301,40.145289&parameterCd=00065&siteType=ST
-83.254395,36.932330,-78.508301,40.145289
https://api.water.noaa.gov/nwps/v1/gauges?bbox.xmin=-83.254395&bbox.ymin=36.932330&bbox.xmax=-78.508301&bbox.ymax=40.145289&srid=EPSG_4326&catfim=false
time info flood and crest time
below flood
impacts
*/
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/ver.php'
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>