diff --git a/radar.html b/radar.html
new file mode 100644
index 0000000..e74fe77
--- /dev/null
+++ b/radar.html
@@ -0,0 +1,136 @@
+
+
+
+
+ ' + alertInfo.properties.event + '
';
+ construct = construct + '
Expires: ' + formatTimestamp(alertInfo.properties.expires) + '
';
+ construct = construct + '
Issued: ' + formatTimestamp(alertInfo.properties.sent) + '
';
+ construct = construct + '
Areas: ' + alertInfo.properties.areaDesc + '
'
+
+ try {
+ var hazards = alertInfo.properties.description.split("HAZARD...")[1].split("\n\n")[0].replace(/\n/g, " ");
+ } catch {
+ var hazards = "No hazards identified."
+ }
+
+ construct = construct + '
Hazards: ' + hazards + '
'
+
+ try {
+ var impacts = alertInfo.properties.description.split("IMPACTS...")[1].split("\n\n")[0].replace(/\n/g, " ");
+ } catch {
+ try {
+ var impacts = alertInfo.properties.description.split("IMPACT...")[1].split("\n\n")[0].replace(/\n/g, " ");
+ } catch {
+ var impacts = "No impacts identified."
+ }
+ }
+ construct = construct + '
Impacts: ' + impacts + '
'
+
+ construct = construct + '
' + alertInfo.properties.description.replace("\n", "
") + '
'
+
+ console.log(construct)
+ return construct;
+}
+
+function loadAlerts() {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'https://api.weather.gov/alerts/active', true);
+ xhr.setRequestHeader('Accept', 'Application/geo+json');
+
+ map.eachLayer(function(layer) {
+ if (layer instanceof L.Polygon) {
+ map.removeLayer(layer);
+ }
+ });
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ var alerts = JSON.parse(xhr.responseText).features;
+ alerts.forEach(function(alert) {
+ try {
+ var thisItem = alert.geometry.coordinates[0];
+ console.log(thisItem);
+ if (alert.properties.event.includes("Severe Thunderstorm")){
+ if (displaySvrWarnings) {
+ var polygon = L.polygon(reverseSubarrays(thisItem), {color: 'yellow'}).addTo(map);
+ polygon.setStyle({fillOpacity: alertOpacity});
+ var thisAlert = [];
+ thisAlert.push(polygon.getLatLngs().join())
+ thisAlert.push(alert.properties.id)
+ allalerts.push(thisAlert);
+ polygon.bindPopup(getAlert(alert), {"autoPan": true, 'maxheight': '500' , 'maxWidth': '400', 'className': 'alertpopup'});
+ polygon.on('mouseover', function (e) {
+ polygon.setStyle({ color: 'orange', fillOpacity: 0.7 });
+ }); polygon.on('mouseout', function (e) {
+ polygon.setStyle({ color: 'yellow', fillOpacity: alertOpacity });
+ });
+ }
+ } else if (alert.properties.event.includes("Tornado")){
+ if (displayTorWarnings) {
+ var polygon = L.polygon(reverseSubarrays(thisItem), {color: 'red'}).addTo(map);
+ polygon.setStyle({fillOpacity: alertOpacity});
+ var thisAlert = [];
+ thisAlert.push(polygon.getLatLngs().join())
+ thisAlert.push(alert.properties.id)
+ allalerts.push(thisAlert);
+ polygon.bindPopup(getAlert(alert), {"autoPan": true, 'maxheight': '500' , 'maxWidth': '400', 'className': 'alertpopup'});
+ polygon.on('mouseover', function (e) {
+ polygon.setStyle({ color: 'orange', fillOpacity: 0.7 });
+ }); polygon.on('mouseout', function (e) {
+ polygon.setStyle({ color: 'red', fillOpacity: alertOpacity});
+ });
+ }
+ } else if (alert.properties.event.includes("Special Weather")){
+ if (displaySpecWarnings) {
+ var polygon = L.polygon(reverseSubarrays(thisItem), {color: 'blue'}).addTo(map);
+ polygon.setStyle({fillOpacity: alertOpacity});
+ var thisAlert = [];
+ thisAlert.push(polygon.getLatLngs().join())
+ thisAlert.push(alert.properties.id)
+ allalerts.push(thisAlert);
+ polygon.bindPopup(getAlert(alert), {"autoPan": true, 'maxheight': '500' , 'maxWidth': '400', 'className': 'alertpopup'});
+ polygon.on('mouseover', function (e) {
+ polygon.setStyle({ color: 'orange', fillOpacity: 0.7 });
+ }); polygon.on('mouseout', function (e) {
+ polygon.setStyle({ color: 'blue', fillOpacity: alertOpacity});
+ });
+ }
+ } else if (alert.properties.event.includes("Flash Flood")){
+ if (displayFFloodWarnings) {
+ var polygon = L.polygon(reverseSubarrays(thisItem), {color: 'green'}).addTo(map);
+ polygon.setStyle({fillOpacity: alertOpacity});
+ var thisAlert = [];
+ thisAlert.push(polygon.getLatLngs().join())
+ thisAlert.push(alert.properties.id)
+ allalerts.push(thisAlert);
+ polygon.bindPopup(getAlert(alert), {"autoPan": true, 'maxheight': '500' , 'maxWidth': '400', 'className': 'alertpopup'});
+ polygon.on('mouseover', function (e) {
+ polygon.setStyle({ color: 'orange', fillOpacity: 0.7 });
+ }); polygon.on('mouseout', function (e) {
+ polygon.setStyle({ color: 'green', fillOpacity: alertOpacity});
+ });
+ }
+ } else if (alert.properties.event.includes("Flood Warning")){
+ if (displayFloodWarnings) {
+ var polygon = L.polygon(reverseSubarrays(thisItem), {color: 'magenta'}).addTo(map);
+ polygon.setStyle({fillOpacity: alertOpacity});
+ var thisAlert = [];
+ thisAlert.push(polygon.getLatLngs().join())
+ thisAlert.push(alert.properties.id)
+ allalerts.push(thisAlert);
+ polygon.bindPopup(getAlert(alert), {"autoPan": true, 'maxheight': '500' , 'maxWidth': '400', 'className': 'alertpopup'});
+ polygon.on('mouseover', function (e) {
+ polygon.setStyle({ color: 'orange', fillOpacity: 0.7 });
+ }); polygon.on('mouseout', function (e) {
+ polygon.setStyle({ color: 'magenta', fillOpacity: alertOpacity });
+ });
+ }
+ } else {
+ if (displayOtherWarnings) {
+ var polygon = L.polygon(reverseSubarrays(thisItem), {color: '#FF8E02'}).addTo(map);
+ polygon.setStyle({fillOpacity: alertOpacity});
+ var thisAlert = [];
+ thisAlert.push(polygon.getLatLngs().join())
+ thisAlert.push(alert.properties.id)
+ allalerts.push(thisAlert);
+ polygon.bindPopup(getAlert(alert), {"autoPan": true, 'maxheight': '500' , 'maxWidth': '400', 'className': 'alertpopup'});
+ polygon.on('mouseover', function (e) {
+ polygon.setStyle({ color: 'orange', fillOpacity: 0.7 });
+ }); polygon.on('mouseout', function (e) {
+ polygon.setStyle({ color: '#FF8E02', fillOpacity: alertOpacity });
+ });
+ }
+ }
+ console.log("Added alert")
+ } catch {
+ console.log("No coords for obj.")
+ }
+ });
+ }
+ };
+ xhr.send();
+}
+
+function formatDate(inputDateString) {
+ // Parse the input date string
+ const inputDate = new Date(inputDateString);
+
+ // Get the time portion as a string
+ const timeString = inputDate.toTimeString();
+
+ // Extract hours and minutes
+ const hours = inputDate.getHours();
+ const minutes = inputDate.getMinutes();
+
+ // Convert hours to 12-hour format
+ const formattedHours = (hours % 12) || 12;
+
+ // Determine AM or PM
+ const amOrPm = hours >= 12 ? 'PM' : 'AM';
+
+ // Construct the final formatted string
+ const formattedTimeString = `${formattedHours}:${minutes.toString().padStart(2, '0')} ${amOrPm} (Eastern Daylight Time)`;
+
+ return formattedTimeString;
+}
+
+function startLoadingTile() {
+ loadingTilesCount++;
+}
+function finishLoadingTile() {
+ // Delayed increase loaded count to prevent changing the layer before
+ // it will be replaced by next
+ setTimeout(function() { loadedTilesCount++; }, 250);
+}
+function isTilesLoading() {
+ return loadingTilesCount > loadedTilesCount;
+}
+
+/**
+ * Load all the available maps frames from RainViewer API
+ */
+ var apiRequest = new XMLHttpRequest();
+apiRequest.open("GET", "https://api.rainviewer.com/public/weather-maps.json", true);
+apiRequest.onload = function(e) {
+ // store the API response for re-use purposes in memory
+ apiData = JSON.parse(apiRequest.response);
+ initialize(apiData, optionKind);
+};
+apiRequest.send();
+
+/**
+ * Initialize internal data from the API response and options
+ */
+function initialize(api, kind) {
+ // remove all already added tiled layers
+ for (var i in radarLayers) {
+ map.removeLayer(radarLayers[i]);
+ }
+ mapFrames = [];
+ radarLayers = [];
+ animationPosition = 0;
+
+ if (!api) {
+ return;
+ }
+ if (kind == 'satellite' && api.satellite && api.satellite.infrared) {
+ mapFrames = api.satellite.infrared;
+
+ lastPastFramePosition = api.satellite.infrared.length - 1;
+ showFrame(lastPastFramePosition, true);
+ }
+ else if (api.radar && api.radar.past) {
+ mapFrames = api.radar.past;
+ if (api.radar.nowcast) {
+ if (doFuture){
+ mapFrames = mapFrames.concat(api.radar.nowcast);
+ }
+ }
+
+ // show the last "past" frame
+ lastPastFramePosition = api.radar.past.length - 1;
+ showFrame(lastPastFramePosition, true);
+ }
+}
+
+/**
+ * Animation functions
+ * @param path - Path to the XYZ tile
+ */
+function addLayer(frame) {
+ if (!radarLayers[frame.path]) {
+ var colorScheme = optionKind == 'satellite' ? 0 : optionColorScheme;
+ var smooth = optionKind == 'satellite' ? 0 : optionSmoothData;
+ var snow = optionKind == 'satellite' ? 0 : optionSnowColors;
+
+ var source = new L.TileLayer(apiData.host + frame.path + '/' + optionTileSize + '/{z}/{x}/{y}/' + colorScheme + '/' + smooth + '_' + snow + '.png', {
+ tileSize: 256,
+ opacity: 0.01,
+ zIndex: frame.time
+ });
+
+ // Track layer loading state to not display the overlay
+ // before it will completelly loads
+ source.on('loading', startLoadingTile);
+ source.on('load', finishLoadingTile);
+ source.on('remove', finishLoadingTile);
+
+ radarLayers[frame.path] = source;
+ }
+ if (!map.hasLayer(radarLayers[frame.path])) {
+ map.addLayer(radarLayers[frame.path]);
+ }
+}
+
+/**
+ * Display particular frame of animation for the @position
+ * If preloadOnly parameter is set to true, the frame layer only adds for the tiles preloading purpose
+ * @param position
+ * @param preloadOnly
+ * @param force - display layer immediatelly
+ */
+function changeRadarPosition(position, preloadOnly, force) {
+ while (position >= mapFrames.length) {
+ position -= mapFrames.length;
+ }
+ while (position < 0) {
+ position += mapFrames.length;
+ }
+
+ var currentFrame = mapFrames[animationPosition];
+ var nextFrame = mapFrames[position];
+
+ addLayer(nextFrame);
+
+ // Quit if this call is for preloading only by design
+ // or some times still loading in background
+ if (preloadOnly || (isTilesLoading() && !force)) {
+ return;
+ }
+
+ animationPosition = position;
+
+ if (radarLayers[currentFrame.path]) {
+ radarLayers[currentFrame.path].setOpacity(0);
+ }
+ radarLayers[nextFrame.path].setOpacity(radarOpacity);
+
+
+ var pastOrForecast = nextFrame.time > Date.now() / 1000 ? 'FORECAST' : 'PAST';
+
+ document.getElementById("timestamp").innerHTML = pastOrForecast + ' | ' + formatDate((new Date(nextFrame.time * 1000)).toString());
+}
+
+/**
+ * Check avialability and show particular frame position from the timestamps list
+ */
+function showFrame(nextPosition, force) {
+ var preloadingDirection = nextPosition - animationPosition > 0 ? 1 : -1;
+
+ changeRadarPosition(nextPosition, false, force);
+
+ // preload next next frame (typically, +1 frame)
+ // if don't do that, the animation will be blinking at the first loop
+ changeRadarPosition(nextPosition + preloadingDirection, true);
+
+}
+
+/**
+ * Stop the animation
+ * Check if the animation timeout is set and clear it.
+ */
+function stop() {
+ if (animationTimer) {
+ clearTimeout(animationTimer);
+ animationTimer = false;
+ return true;
+ }
+ return false;
+}
+
+
+function play() {
+ showFrame(animationPosition + 1);
+
+ // Main animation driver. Run this function every 400 ms,
+ // unless this is the last frame, then wait 1500ms
+ if (animationPosition == 12){
+ animationTimer = setTimeout(play, 1500);
+ } else {
+ animationTimer = setTimeout(play, 400);
+ }
+}
+
+function playStop() {
+ if (!stop()) {
+ document.getElementById("stbtn").innerHTML = 'pause'
+ play();
+ } else {
+ document.getElementById("stbtn").innerHTML = 'play_arrow'
+ }
+}
+
+/**
+ * Change map options
+ */
+function setKind(kind) {
+ if (kind == 'satellite' || kind == 'radar'){
+ optionKind = kind;
+ initialize(apiData, optionKind);
+ } else if (kind == 'future') {
+ doFuture = true;
+ initialize(apiData, optionKind);
+ } else if (kind == 'past') {
+ doFuture = false;
+ initialize(apiData, optionKind);
+ }
+}
+
+
+function setColors() {
+ var e = document.getElementById('colors');
+ optionColorScheme = e.options[e.selectedIndex].value;
+ initialize(apiData, optionKind);
+}
+
+
+/**
+ * Handle arrow keys for navigation between next \ prev frames
+ */
+document.onkeydown = function (e) {
+ e = e || window.event;
+ switch (e.which || e.keyCode) {
+ case 37: // left
+ stop();
+ showFrame(animationPosition - 1, true);
+ break;
+
+ case 39: // right
+ stop();
+ showFrame(animationPosition + 1, true);
+ break;
+
+ default:
+ return; // exit this handler for other keys
+ }
+ e.preventDefault();
+ return false;
+}
+
+function refresh(){
+ document.getElementById("alertDeets").style.visibility = "hidden";
+ var apiRequest = new XMLHttpRequest();
+ apiRequest.open("GET", "https://api.rainviewer.com/public/weather-maps.json", true);
+ apiRequest.onload = function(e) {
+ // store the API response for re-use purposes in memory
+ apiData = JSON.parse(apiRequest.response);
+ initialize(apiData, optionKind);
+ };
+ apiRequest.send();
+ loadAlerts();
+ console.log("Refreshed!")
+ }
+
+
+function loop() {
+ var apiRequest = new XMLHttpRequest();
+ apiRequest.open("GET", "https://api.rainviewer.com/public/weather-maps.json", true);
+ apiRequest.onload = function(e) {
+ // store the API response for re-use purposes in memory
+ apiData = JSON.parse(apiRequest.response);
+ initialize(apiData, optionKind);
+ };
+ apiRequest.send();
+ loadAlerts();
+ console.log("Refreshed!")
+ setTimeout(loop, 60000);
+}
+
+loop()
\ No newline at end of file
diff --git a/radarstyle.css b/radarstyle.css
new file mode 100644
index 0000000..d86749c
--- /dev/null
+++ b/radarstyle.css
@@ -0,0 +1,91 @@
+a {
+ color: lightblue;
+}
+a:hover {
+ color: white;
+}
+.sli {
+ list-style: none;
+ display: inline-block;
+}
+body{
+ background-color: rgb(26, 26, 26);
+ color: white;
+ font-family: Bahnschrift, sans-serif;
+ overflow-y: hidden;
+ font-size: 16px;
+}
+.material-symbols-outlined {
+ font-variation-settings:
+ 'FILL' 0,
+ 'wght' 600,
+ 'GRAD' 0,
+ 'opsz' 20
+}
+button {
+ background-color: white;
+ border: grey 2px solid;
+ padding: 5px;
+ padding-left: 10px;
+ padding-right: 10px;
+ font-size: 15px;
+ font-weight: bold;
+ cursor: pointer;
+ border-radius: 10px;
+ transition-duration: 0.1s;
+}
+button:hover {
+ background-color: rgb(165, 165, 165);
+}
+
+.close {
+ border-radius: 100%;
+ border: none;
+ background: red;
+ color: white;
+ text-align: center;
+ padding: 0;
+ padding-left: 2px;
+ padding-right: 2px;
+ padding-top: 1px;
+ position: relative;
+ left: calc(100% - 20px);
+}
+
+.close:hover {
+ background-color: darkred;
+}
+
+select {
+ width: 100%;
+ border: none;
+ border-radius: 100px;
+ background: white;
+ padding: 5px;
+}
+
+.overlay-object {
+ border-left: 0px;
+ border-radius: 20px;
+ padding: 10px;
+ position: absolute;
+ z-index: 999;
+ background: black;
+}
+
+.alertpopup .leaflet-popup-content-wrapper {
+ background: #000000;
+ color: #eee;
+ font-size: 16px;
+ line-height: 24px;
+ border-radius: 10px;
+ max-width: 350px;
+ max-height: 400px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ scrollbar-width: none;
+}
+
+.alertpopup .leaflet-popup-tip {
+ background-color: #000000;
+}
\ No newline at end of file