diff --git a/index.html b/index.html index 5c26e67..8135c1d 100644 --- a/index.html +++ b/index.html @@ -16,158 +16,166 @@ rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css" /> - - -

Snow Water Equivalent Estimation Map

-

A Project by the SnowSource Team

+

SnowSource

- -
-
- - - - + +
+
+
+ + + + +
+ + +
- + diff --git a/swe.js b/swe.js index 39fa01a..692dd17 100644 --- a/swe.js +++ b/swe.js @@ -9,353 +9,354 @@ function parse_date_string(str) { var map, layercontrol; function loadMap() { - // Get selected dates from datepicker - var selectedDates = document.getElementById('datepicker').value.split(','); - - // Get the Leaflet map container - // removed default zoom control - map = L.map("map", { zoomControl: false }).setView([0, 0], 2); - - var basemaps = { - - Topography: L.tileLayer.wms('http://ows.mundialis.de/services/service?', { - layers: 'TOPO-WMS' - }), - - Places: L.tileLayer.wms('http://ows.mundialis.de/services/service?', { - layers: 'OSM-WMS' - }), - - 'SatelliteImagery': L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/'+ - '/tile/{z}/{y}/{x}', { - attribution: 'Esri', - maxZoom: 19 - }), - }; - - basemaps.SatelliteImagery.addTo(map); - - // Add layer control to the map - layercontrol = L.control.layers(basemaps).addTo(map); - - - // Add zoom control to the top right corner - L.control.zoom({ position: 'topright' }).addTo(map); - - var usaBounds = [ - // -125, 30, -78, 51 - [30, -125.000000], // Southwest - [51, -78] // Northeast - ]; - - // Fit the map to the bounding box - map.fitBounds(usaBounds); - - fetch('us-states.json') - .then(response => response.json()) - .then(data => { - fetch('state-abbreviation.json') - .then(response => response.json()) - .then(stateAbbreviations => { - var stateNameMarkers = L.layerGroup(); - var stateLayer = L.geoJSON(data, { - style: function (feature) { - return { - color: '#000000', - weight: 1 - }; - }, - onEachFeature: function (feature, layer) { - if (feature.properties && feature.properties.name) { - var stateName = feature.properties.name; - var abbreviation = stateAbbreviations[stateName] || stateName; - var center = layer.getBounds().getCenter(); - var marker = L.marker(center, { - icon: L.divIcon({ - className: 'state-label', - html: `${stateName}`, - iconSize: [100, 40] - }) - }).addTo(stateNameMarkers); - - function updateLabelContent() { - const zoomLevel = map.getZoom(); - const label = zoomLevel >= 4 ? stateName : abbreviation; - const isZoomedOut = zoomLevel < 3; - - marker.setIcon(L.divIcon({ - className: 'state-label', - html: `${label}`, - iconSize: [100, 40] - })); - - if (isZoomedOut) { - marker.remove(); - } else { - if (!map.hasLayer(marker)) { - marker.addTo(stateNameMarkers); - } - } - } - map.on('zoomend', updateLabelContent); - - updateLabelContent(); + // Get selected dates from datepicker + var selectedDates = document.getElementById("datepicker").value.split(","); + + // Get the Leaflet map container + map = L.map("map", { zoomControl: false }).setView([0, 0], 2); + + var basemaps = { + Topography: L.tileLayer.wms("http://ows.mundialis.de/services/service?", { + layers: "TOPO-WMS", + }), + + Places: L.tileLayer.wms("http://ows.mundialis.de/services/service?", { + layers: "OSM-WMS", + }), + + SatelliteImagery: L.tileLayer( + "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/" + + "/tile/{z}/{y}/{x}", + { + attribution: "Esri", + maxZoom: 19, + } + ), + }; + + basemaps.SatelliteImagery.addTo(map); + + // Add layer control to the map + layercontrol = L.control.layers(basemaps).addTo(map); + + + // Add zoom control to the top right corner + L.control.zoom({ position: "topright" }).addTo(map); + + var usaBounds = [ + [26, -114.0], // Southwest + [49, -78], // Northeast + ]; + + // Fit the map to the bounding box + map.fitBounds(usaBounds); + + fetch("us-states.json") + .then((response) => response.json()) + .then((data) => { + fetch("state-abbreviation.json") + .then((response) => response.json()) + .then((stateAbbreviations) => { + var stateNameMarkers = L.layerGroup(); + var stateLayer = L.geoJSON(data, { + style: function (feature) { + return { + color: "#000000", + weight: 1, + }; + }, + onEachFeature: function (feature, layer) { + if (feature.properties && feature.properties.name) { + var stateName = feature.properties.name; + var abbreviation = stateAbbreviations[stateName] || stateName; + var center = layer.getBounds().getCenter(); + + // Split the state name into words and capitalize + var stateNameParts = stateName + .split(" ") + .map(function (word) { + return ( + "" + + word.charAt(0).toUpperCase() + + word.slice(1).toLowerCase() + + "" + ); + }) + .join(""); // Join with line breaks + + var marker = L.marker(center, { + icon: L.divIcon({ + className: "state-label", + html: stateNameParts, // Use the split and capitalized state name + iconSize: [100, 40], + }), + }).addTo(stateNameMarkers); + + function updateLabelContent() { + const zoomLevel = map.getZoom(); + const label = zoomLevel >= 4 ? stateName : abbreviation; + const isZoomedOut = zoomLevel < 3; + + marker.setIcon( + L.divIcon({ + className: "state-label", + html: stateNameParts, // Updated with line breaks + iconSize: [100, 40], + }) + ); + + if (isZoomedOut) { + marker.remove(); + } else { + if (!map.hasLayer(marker)) { + marker.addTo(stateNameMarkers); } + } } - }).addTo(map); + map.on("zoomend", updateLabelContent); - stateLayer.bringToFront(); - stateNameMarkers.addTo(map); - layercontrol.addOverlay(stateLayer, "State Boundaries"); - layercontrol.addOverlay(stateNameMarkers, "State Names"); - layercontrol.addOverlay(wmslayer, "Predicted SWE " + date); - }) - .catch(error => { - console.error('Error loading abbreviations:', error); - }); + updateLabelContent(); + } + }, + }).addTo(map); + + stateLayer.bringToFront(); + stateNameMarkers.addTo(map); + layercontrol.addOverlay(stateLayer, "State Boundaries"); + layercontrol.addOverlay(stateNameMarkers, "State Names"); }) - .catch(error => { - console.error('Error loading GeoJSON data:', error); + .catch((error) => { + console.error("Error loading abbreviations:", error); }); - - // Event listener for map clicks - map.on('click', function(e) { - var lat = e.latlng.lat.toFixed(6); - var lon = e.latlng.lng.toFixed(6); - var content = `Coordinates:
Latitude: ${lat}
Longitude: ${lon}
`; - - L.popup() - .setLatLng(e.latlng) - .setContent(content) - .openOn(map); + }) + .catch((error) => { + console.error("Error loading GeoJSON data:", error); }); - + // Event listener for map clicks + map.on("click", function (e) { + var lat = e.latlng.lat.toFixed(6); + var lon = e.latlng.lng.toFixed(6); + var content = `Coordinates:
Latitude: ${lat}
Longitude: ${lon}
`; + + L.popup().setLatLng(e.latlng).setContent(content).openOn(map); + }); } // Function to copy coordinates to clipboard function copyCoordinates(lat, lon) { - const textToCopy = `${lat}, ${lon}`; - if (navigator.clipboard) { - navigator.clipboard.writeText(textToCopy) - .then(() => { - alert('Coordinates copied to clipboard!'); - }) - .catch(err => { - console.error('Failed to copy: ', err); - }); - } else { - // Fallback for older browsers - var tempInput = document.createElement('input'); - tempInput.value = textToCopy; - document.body.appendChild(tempInput); - tempInput.select(); - document.execCommand('copy'); - document.body.removeChild(tempInput); - alert('Coordinates copied to clipboard!'); - } + const textToCopy = `${lat}, ${lon}`; + if (navigator.clipboard) { + navigator.clipboard + .writeText(textToCopy) + .then(() => { + alert("Coordinates copied to clipboard!"); + }) + .catch((err) => { + console.error("Failed to copy: ", err); + }); + } else { + // Fallback for older browsers + var tempInput = document.createElement("input"); + tempInput.value = textToCopy; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); + alert("Coordinates copied to clipboard!"); + } } -function add_swe_predicted_geotiff(date){ - - // URL to your GeoTIFF file - var wmslayer = L.tileLayer.wms('http://geobrain.csiss.gmu.edu/cgi-bin/mapserv?'+ - 'map=/var/www/html/swe_forecasting/map/swe_predicted_'+date+'.tif.map&', { - layers: 'swemap', - format: 'image/png', - transparent: true - }); - wmslayer.addTo(map); - layercontrol.addOverlay(wmslayer, "Predicted SWE "+date); - +function add_swe_predicted_geotiff(date) { + // URL to your GeoTIFF file + var wmslayer = L.tileLayer.wms( + "http://geobrain.csiss.gmu.edu/cgi-bin/mapserv?" + + "map=/var/www/html/swe_forecasting/map/swe_predicted_" + + date + + ".tif.map&", + { + layers: "swemap", + format: "image/png", + transparent: true, + } + ); + wmslayer.addTo(map); + layercontrol.addOverlay(wmslayer, "Predicted SWE " + date); } -function setup_datepicker(dateArray){ - $('#datepicker').datepicker({ - format: 'yyyy-mm-dd', - todayHighlight: true, - timeZone: 'America/Los_Angeles', - autoclose: true, - beforeShowDay: function(date) { - // Convert date to yyyy-mm-dd format - var formattedDate = date.getFullYear() + '-' + - ('0' + (date.getMonth() + 1)).slice(-2) + '-' + - ('0' + date.getDate()).slice(-2); - - // Check if the date is in the dateArray - return dateArray.includes(formattedDate); - } - }).on('show', function(e) { - // Ensure the datepicker is properly positioned - var datepicker = $('.datepicker'); - var offset = $(this).offset(); - datepicker.css({ - top: offset.top + $(this).outerHeight(), - left: offset.left - }); +function setup_datepicker(dateArray) { + $("#datepicker") + .datepicker({ + format: "yyyy-mm-dd", + todayHighlight: true, + timeZone: "America/Los_Angeles", + autoclose: true, + beforeShowDay: function (date) { + // Convert date to yyyy-mm-dd format + var formattedDate = + date.getFullYear() + + "-" + + ("0" + (date.getMonth() + 1)).slice(-2) + + "-" + + ("0" + date.getDate()).slice(-2); + + // Check if the date is in the dateArray + return dateArray.includes(formattedDate); + }, + }) + .on("show", function (e) { + // Ensure the datepicker is properly positioned + var datepicker = $(".datepicker"); + var offset = $(this).offset(); + datepicker.css({ + top: offset.top + $(this).outerHeight(), + left: offset.left, + }); }); } // Function to find the latest date function findLatestDate(dates) { - if (dates.length === 0) { - return null; // Return null for an empty array - } + if (dates.length === 0) { + return null; // Return null for an empty array + } - // Use reduce to find the maximum date - var latestDate = dates.reduce(function (maxDate, currentDate) { - maxDateObject = new Date(maxDate) - currentDateObject = new Date(currentDate) - return currentDateObject > maxDateObject ? currentDate : maxDate; - }); + // Use reduce to find the maximum date + var latestDate = dates.reduce(function (maxDate, currentDate) { + maxDateObject = new Date(maxDate); + currentDateObject = new Date(currentDate); + return currentDateObject > maxDateObject ? currentDate : maxDate; + }); - return latestDate; + return latestDate; } -function refresh_calendar(){ +function refresh_calendar() { // Fetch the CSV file - fetch('../swe_forecasting/date_list.csv', { - method: 'GET', - cache: 'no-store', // 'no-store' disables caching + fetch("../swe_forecasting/date_list.csv", { + method: "GET", + cache: "no-store", // 'no-store' disables caching }) - .then(response => response.text()) - .then(data => { - console.log(data) - // Parse CSV data and convert the date column into an array - Papa.parse(data, { - header: true, - complete: function(results) { - // Assuming 'date' is the name of your date column - var dateArray = results.data.map(function(row) { - return row.date; - }); - console.log("dateArray = " + dateArray) - - // Initialize Bootstrap Datepicker with the dateArray - setup_datepicker(dateArray) - - // found the latest date and show on the map - var latestdate = findLatestDate(dateArray) - console.log("Found latest date is " + latestdate) - $('#datepicker').datepicker('setDate', new Date(latestdate)); - add_swe_predicted_geotiff(latestdate) - } - }); - + .then((response) => response.text()) + .then((data) => { + console.log(data); + // Parse CSV data and convert the date column into an array + Papa.parse(data, { + header: true, + complete: function (results) { + // Assuming 'date' is the name of your date column + var dateArray = results.data.map(function (row) { + return row.date; + }); + console.log("dateArray = " + dateArray); + + // Initialize Bootstrap Datepicker with the dateArray + setup_datepicker(dateArray); + + // found the latest date and show on the map + var latestdate = findLatestDate(dateArray); + console.log("Found latest date is " + latestdate); + $("#datepicker").datepicker("setDate", new Date(latestdate)); + add_swe_predicted_geotiff(latestdate); + }, + }); }) - .catch(error => console.error('Error fetching CSV file:', error)); - - - + .catch((error) => console.error("Error fetching CSV file:", error)); } -function add_listener_to_buttons(){ - - // Button click listener - $('#load_swe_to_map').on('click', function() { - // Get the selected date from the datepicker - var selectedDate = $('#datepicker').datepicker('getFormattedDate'); - console.log("loading layer for "+ selectedDate) - // Show overlay with selected date - add_swe_predicted_geotiff(selectedDate); - }); - - // Close overlay button click listener - $('#download_swe_geotiff').on('click', function() { - // Create a temporary anchor element - var selectedDate = $('#datepicker').datepicker('getFormattedDate'); - console.log("downloading geotiff for "+ selectedDate) - // Open a new window to initiate the download - window.open("../swe_forecasting/output/swe_predicted_"+selectedDate+".tif", '_blank'); - }); - +function add_listener_to_buttons() { + // Button click listener + $("#load_swe_to_map").on("click", function () { + // Get the selected date from the datepicker + var selectedDate = $("#datepicker").datepicker("getFormattedDate"); + console.log("loading layer for " + selectedDate); + // Show overlay with selected date + add_swe_predicted_geotiff(selectedDate); + }); + + // Close overlay button click listener + $("#download_swe_geotiff").on("click", function () { + // Create a temporary anchor element + var selectedDate = $("#datepicker").datepicker("getFormattedDate"); + console.log("downloading geotiff for " + selectedDate); + // Open a new window to initiate the download + window.open( + "../swe_forecasting/output/swe_predicted_" + selectedDate + ".tif", + "_blank" + ); + }); } function getColor(d) { - // Specify the number of classes (baskets) - // var numClasses = 10; - - // // Generate grades dynamically based on the number of classes - // var grades = Array.from({ length: numClasses + 1 }, function (_, index) { - // return (30 / numClasses) * index; - // }); - - // // Define color scale from gray to blue to purple - // var colorScale = chroma.scale(['#f0f0f0', '#4d4dff', '#9900cc']).mode('lab').colors(numClasses); - - // // - - // // Find the appropriate color class based on the input value - // for (var i = 0; i < grades.length - 1; i++) { - // if (d >= grades[i] && d < grades[i + 1]) { - // return colorScale[i]; - // } - // } - - // console.log(colorScale) - - // Define color classes - // var colors = ['#f0f0f0', '#d6caf5', '#b9a6f9', '#9782fc', '#6c5efe', - // '#5b48f9', '#713eee', '#8131e2', '#8e21d7', '#9900cc'] - - var colors = ['#003366', '#336699', '#6699CC', '#99CCFF', '#99FFFF', - '#CCFFFF', '#FFFFCC', '#FFFF99', '#FFFF66', '#FFFF33'] - - // Specify the number of classes (baskets) - var numClasses = 10; - - // Generate grades dynamically based on the number of classes - var grades = Array.from({ length: numClasses + 1 }, function (_, index) { - return (30 / numClasses) * index; - }); - - // Find the appropriate color class based on the input value - for (var i = 0; i < grades.length - 1; i++) { - if (d >= grades[i] && d < grades[i + 1]) { - return colors[i]; - } + // Define color scale based on the value + const colors = [ + "#003366", + "#336699", + "#6699CC", + "#99CCFF", + "#99FFFF", + "#CCFFFF", + "#FFFFCC", + "#FFFF99", + "#FFFF66", + "#FFFF33", + ]; + + const numClasses = 10; + + // Generate grades based on the number of classes and the range of values + const grades = Array.from( + { length: numClasses + 1 }, + (_, index) => (30 / numClasses) * index + ); + + // Assign the correct color based on the value of 'd' + for (let i = 0; i < grades.length - 1; i++) { + if (d >= grades[i] && d < grades[i + 1]) { + return colors[i]; // Return the appropriate color } - - // Handle the case where the input value is greater than the last grade - return colors[grades.length - 1]; } -function add_legend(){ - // Your MapServer configuration with 15 classes - var legend = L.control({position: 'bottomright'}); - - legend.onAdd = function (map) { - - var div = L.DomUtil.create('div', 'info legend'), - labels = []; - - div.style.backgroundColor = 'white'; - div.style.padding = '10px'; - // Specify the number of classes (baskets) - var numClasses = 10; - - // Generate grades dynamically based on the number of classes - var grades = Array.from({ length: numClasses + 1 }, function(_, index) { - return (30 / numClasses) * index; - }); + return colors[grades.length - 1]; // Default to the last color if no match +} - // loop through our density intervals and generate a label with a colored square for each interval - for (var i = 0; i < grades.length; i++) { - div.innerHTML += - ' ' + - grades[i] + (grades[i + 1] ? '–' + grades[i + 1] + '
' : '+'); - } +// Function to add a color legend to the map +function add_legend() { + var legend = L.control({ position: "bottomright" }); + + legend.onAdd = function (map) { + var div = L.DomUtil.create("div", "info legend"), + labels = []; + + div.style.backgroundColor = "white"; + div.style.padding = "10px"; + + // Define the range of values for the legend (these should correspond to the value range you're using) + const grades = Array.from( + { length: 10 + 1 }, + (_, index) => (30 / 10) * index + ); // Create 10 equally spaced grades + + // Generate the legend items + for (let i = 0; i < grades.length - 1; i++) { + div.innerHTML += + ' ' + + grades[i] + + (grades[i + 1] ? "–" + grades[i + 1] + "
" : "+"); + } - return div; - }; + return div; + }; - legend.addTo(map); + legend.addTo(map); // Add the legend to the map } // Automatically load the map when the document is ready -document.addEventListener('DOMContentLoaded', function() { - loadMap(); - refresh_calendar() - add_listener_to_buttons() - add_legend() +document.addEventListener("DOMContentLoaded", function () { + loadMap(); + refresh_calendar(); + add_listener_to_buttons(); + add_legend(); }); diff --git a/tempCodeRunnerFile.js b/tempCodeRunnerFile.js new file mode 100644 index 0000000..81319d5 --- /dev/null +++ b/tempCodeRunnerFile.js @@ -0,0 +1,364 @@ +function parse_date_string(str) { + var y = str.substr(0,4), + m = str.substr(4,2) - 1, + d = str.substr(6,2); + var D = new Date(y,m,d); + return (D.getFullYear() == y && D.getMonth() == m && D.getDate() == d) ? D : 'invalid date'; +} + +var map, layercontrol; + +function loadMap() { + // Get selected dates from datepicker + var selectedDates = document.getElementById("datepicker").value.split(","); + + // Get the Leaflet map container + map = L.map("map", { zoomControl: false }).setView([0, 0], 2); + + var basemaps = { + Topography: L.tileLayer.wms("http://ows.mundialis.de/services/service?", { + layers: "TOPO-WMS", + }), + + Places: L.tileLayer.wms("http://ows.mundialis.de/services/service?", { + layers: "OSM-WMS", + }), + + SatelliteImagery: L.tileLayer( + "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/" + + "/tile/{z}/{y}/{x}", + { + attribution: "Esri", + maxZoom: 19, + } + ), + }; + + basemaps.SatelliteImagery.addTo(map); + + // Add layer control to the map + layercontrol = L.control.layers(basemaps).addTo(map); + + // Customize the behavior to toggle layers on click (not hover) + const layerControlDiv = document.querySelector(".leaflet-control-layers"); + layerControlDiv.addEventListener("click", function (event) { + // We check if the click was on the checkbox (layer control) + if (event.target && event.target.tagName === "INPUT") { + event.target.click(); // Toggle the layer + } + }); + + // Add zoom control to the top right corner + L.control.zoom({ position: "topright" }).addTo(map); + + var usaBounds = [ + [22, -115.0], // Southwest + [51, -78], // Northeast + ]; + + // Fit the map to the bounding box + map.fitBounds(usaBounds); + + fetch("us-states.json") + .then((response) => response.json()) + .then((data) => { + fetch("state-abbreviation.json") + .then((response) => response.json()) + .then((stateAbbreviations) => { + var stateNameMarkers = L.layerGroup(); + var stateLayer = L.geoJSON(data, { + style: function (feature) { + return { + color: "#000000", + weight: 1, + }; + }, + onEachFeature: function (feature, layer) { + if (feature.properties && feature.properties.name) { + var stateName = feature.properties.name; + var abbreviation = stateAbbreviations[stateName] || stateName; + var center = layer.getBounds().getCenter(); + + // Split the state name into words and capitalize + var stateNameParts = stateName + .split(" ") + .map(function (word) { + return ( + "" + + word.charAt(0).toUpperCase() + + word.slice(1).toLowerCase() + + "" + ); + }) + .join(""); // Join with line breaks + + var marker = L.marker(center, { + icon: L.divIcon({ + className: "state-label", + html: stateNameParts, // Use the split and capitalized state name + iconSize: [100, 40], + }), + }).addTo(stateNameMarkers); + + function updateLabelContent() { + const zoomLevel = map.getZoom(); + const label = zoomLevel >= 4 ? stateName : abbreviation; + const isZoomedOut = zoomLevel < 3; + + marker.setIcon( + L.divIcon({ + className: "state-label", + html: stateNameParts, // Updated with line breaks + iconSize: [100, 40], + }) + ); + + if (isZoomedOut) { + marker.remove(); + } else { + if (!map.hasLayer(marker)) { + marker.addTo(stateNameMarkers); + } + } + } + map.on("zoomend", updateLabelContent); + + updateLabelContent(); + } + }, + }).addTo(map); + + stateLayer.bringToFront(); + stateNameMarkers.addTo(map); + layercontrol.addOverlay(stateLayer, "State Boundaries"); + layercontrol.addOverlay(stateNameMarkers, "State Names"); + }) + .catch((error) => { + console.error("Error loading abbreviations:", error); + }); + }) + .catch((error) => { + console.error("Error loading GeoJSON data:", error); + }); + + // Event listener for map clicks + map.on("click", function (e) { + var lat = e.latlng.lat.toFixed(6); + var lon = e.latlng.lng.toFixed(6); + var content = `Coordinates:
Latitude: ${lat}
Longitude: ${lon}
`; + + L.popup().setLatLng(e.latlng).setContent(content).openOn(map); + }); +} + +// Function to copy coordinates to clipboard +function copyCoordinates(lat, lon) { + const textToCopy = `${lat}, ${lon}`; + if (navigator.clipboard) { + navigator.clipboard + .writeText(textToCopy) + .then(() => { + alert("Coordinates copied to clipboard!"); + }) + .catch((err) => { + console.error("Failed to copy: ", err); + }); + } else { + // Fallback for older browsers + var tempInput = document.createElement("input"); + tempInput.value = textToCopy; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); + alert("Coordinates copied to clipboard!"); + } +} + +function add_swe_predicted_geotiff(date) { + // URL to your GeoTIFF file + var wmslayer = L.tileLayer.wms( + "http://geobrain.csiss.gmu.edu/cgi-bin/mapserv?" + + "map=/var/www/html/swe_forecasting/map/swe_predicted_" + + date + + ".tif.map&", + { + layers: "swemap", + format: "image/png", + transparent: true, + } + ); + wmslayer.addTo(map); + layercontrol.addOverlay(wmslayer, "Predicted SWE " + date); +} + +function setup_datepicker(dateArray) { + $("#datepicker") + .datepicker({ + format: "yyyy-mm-dd", + todayHighlight: true, + timeZone: "America/Los_Angeles", + autoclose: true, + beforeShowDay: function (date) { + // Convert date to yyyy-mm-dd format + var formattedDate = + date.getFullYear() + + "-" + + ("0" + (date.getMonth() + 1)).slice(-2) + + "-" + + ("0" + date.getDate()).slice(-2); + + // Check if the date is in the dateArray + return dateArray.includes(formattedDate); + }, + }) + .on("show", function (e) { + // Ensure the datepicker is properly positioned + var datepicker = $(".datepicker"); + var offset = $(this).offset(); + datepicker.css({ + top: offset.top + $(this).outerHeight(), + left: offset.left, + }); + }); +} + +// Function to find the latest date +function findLatestDate(dates) { + if (dates.length === 0) { + return null; // Return null for an empty array + } + + // Use reduce to find the maximum date + var latestDate = dates.reduce(function (maxDate, currentDate) { + maxDateObject = new Date(maxDate); + currentDateObject = new Date(currentDate); + return currentDateObject > maxDateObject ? currentDate : maxDate; + }); + + return latestDate; +} + +function refresh_calendar() { + // Fetch the CSV file + fetch("../swe_forecasting/date_list.csv", { + method: "GET", + cache: "no-store", // 'no-store' disables caching + }) + .then((response) => response.text()) + .then((data) => { + console.log(data); + // Parse CSV data and convert the date column into an array + Papa.parse(data, { + header: true, + complete: function (results) { + // Assuming 'date' is the name of your date column + var dateArray = results.data.map(function (row) { + return row.date; + }); + console.log("dateArray = " + dateArray); + + // Initialize Bootstrap Datepicker with the dateArray + setup_datepicker(dateArray); + + // found the latest date and show on the map + var latestdate = findLatestDate(dateArray); + console.log("Found latest date is " + latestdate); + $("#datepicker").datepicker("setDate", new Date(latestdate)); + add_swe_predicted_geotiff(latestdate); + }, + }); + }) + .catch((error) => console.error("Error fetching CSV file:", error)); +} + +function add_listener_to_buttons() { + // Button click listener + $("#load_swe_to_map").on("click", function () { + // Get the selected date from the datepicker + var selectedDate = $("#datepicker").datepicker("getFormattedDate"); + console.log("loading layer for " + selectedDate); + // Show overlay with selected date + add_swe_predicted_geotiff(selectedDate); + }); + + // Close overlay button click listener + $("#download_swe_geotiff").on("click", function () { + // Create a temporary anchor element + var selectedDate = $("#datepicker").datepicker("getFormattedDate"); + console.log("downloading geotiff for " + selectedDate); + // Open a new window to initiate the download + window.open( + "../swe_forecasting/output/swe_predicted_" + selectedDate + ".tif", + "_blank" + ); + }); +} + +function getColor(d) { + var colors = [ + "#003366", + "#336699", + "#6699CC", + "#99CCFF", + "#99FFFF", + "#CCFFFF", + "#FFFFCC", + "#FFFF99", + "#FFFF66", + "#FFFF33", + ]; + + var numClasses = 10; + + var grades = Array.from({ length: numClasses + 1 }, function (_, index) { + return (30 / numClasses) * index; + }); + + for (var i = 0; i < grades.length - 1; i++) { + if (d >= grades[i] && d < grades[i + 1]) { + return colors[i]; + } + } + + return colors[grades.length - 1]; +} + +function add_legend() { + var legend = L.control({ position: "bottomright" }); + + legend.onAdd = function (map) { + var div = L.DomUtil.create("div", "info legend"), + labels = []; + + div.style.backgroundColor = "white"; + div.style.padding = "10px"; + + var numClasses = 10; + + var grades = Array.from({ length: numClasses + 1 }, function (_, index) { + return (30 / numClasses) * index; + }); + + for (var i = 0; i < grades.length; i++) { + div.innerHTML += + ' ' + + grades[i] + + (grades[i + 1] ? "–" + grades[i + 1] + "
" : "+"); + } + + return div; + }; + + legend.addTo(map); +} + +// Automatically load the map when the document is ready +document.addEventListener("DOMContentLoaded", function () { + loadMap(); + refresh_calendar(); + add_listener_to_buttons(); + add_legend(); +});