Files
test/outage.html
2025-12-09 07:59:46 +00:00

1101 lines
32 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>RLX Power Outage Map</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://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.4.2/chroma.min.js" integrity="sha512-zInFF17qBFVvvvFpIfeBzo7Tj7+rQxLeTJDmbxjBz5/zIr89YVbTNelNhdTT+/DCrxoVzBeUPVFJsczKbB7sew==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<script src="https://unpkg.com/file-saver@2.0.5/dist/FileSaver.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.js" integrity="sha512-wUa0ktp10dgVVhWdRVfcUO4vHS0ryT42WOEcXjVVF2+2rcYBKTY7Yx7JCEzjWgPV+rj2EDUr8TwsoWF6IoIOPg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript" src="/tabulator/dist/js/tabulator.min.js"></script>
<link href="/tabulator/dist/css/tabulator_midnight.css" rel="stylesheet">
</head>
<body>
<div class="box">
<div id="tabby"></div>
<div id="map">
</div>
<div id="toggle">
<button id="archive" onclick="togglea()">Toggle Archive Mode</button><br>
<div id='screenshots'>
Enter Start/End Times in UTC
<input type="datetime-local" id="start" name="start">
<input type="datetime-local" id="end" name="end">
<button id="playarchive" onclick="playarchive()">Load Archive Data</button><br>
Enable Screenshots: <input type="checkbox" id="llss" name="llss"><br>
Note: move the map slightly after loading data if you want to capture</div>
</div>
<div id = "slider-2"></div>
</div>
<div class="box2">
</div>
<style type="text/css">
.tabulator {
font-size: 26px; /* change font size */
padding:4px 4px;
}
/*#mapid { height: 800px; }*/
body {
padding: 0;
margin: 0;
}
html, body {
height: 100%;
width: 100%
}
#mapid {
height: 95%;
background: none !important;
}
.box{
position: absolute;
top: 225px;
z-index: 9999;
text-align: center;
width: 250px;
left: 10%;
margin-left: -75px; /* half of the width */
}
.box2{
position: absolute;
top: 500px;
z-index: 9999;
text-align: center;
width: 250px;
left: 10%;
margin-left: -75px; /* half of the width */
}
input[type=number] {
width: 50px;
}
input[type=text] {
width: 150px;
}
.legend {
line-height: 18px;
color: #555;
}
.legend i {
width: 15px;
height: 15px;
float: left;
margin-right: 8px;
opacity: 0.7;
}
.info {
padding: 6px 8px;
font: 14px/16px Arial, Helvetica, sans-serif;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
#slider-2 {
position: absolute;
left: 20px;
top: 200px;
width:300px;
z-index: 500;
}
#timeforsaveimages {
position: absolute;
left: 20px;
bottom: 20px;
color: black;
font-size: 30px;
}
.slider-container {
position: absolute;
bottom: 15px;
left: 500px;
background: rgba(255,255,255,0.7);
padding: 10px;
border-radius: 5px;
background-color: white;
z-index: 9999;
}
#tabby {
left: 0px;
top: 0px;
position: fixed;
}
</style>
<div id="mapid">
<div class="slider-container" id="slider-holder">
<label for="transparency">County Opacity:</label>
<input type="range" id="transparency" min="0" max="1" step="0.01" value="1">
<label for="floodToggle">Toggle Flood Layer</label>
<input type="checkbox" id="floodToggle" checked onchange="toggleFloodLayer()">
</div>
<div id="timeforsaveimages">
</div>
<div id="stats">
</div>
</div>
<div id="bottombar">
<a href="cams.html" class="w3-button w3-black">Cam List</a>
<a href="map.html" class="w3-button w3-black">Cam Map</a>
<a href="db.html" class="w3-button w3-black">WU obs</a>
<a href="5min.html" class="w3-button w3-black">5m ASOS obs</a>
<a href="today.html" class="w3-button w3-black">CoCoRaHS Remarks</a>
<a id = "buffer" href="outage.html?buffer=true" class="w3-button w3-black">Outage Map w/ Outside Counties</a>
<a href="https://docs.google.com/forms/d/1-2rTBkNyyBVe08G1vN1hcSOEOvvLUcS1Vs2SmmaudlU" class="w3-button w3-black" target="_blank">Questions? Comments?</a>
<a style="font-size:24px">Power Outage Tracker</a>
</div>
<script>
function toggleFloodLayer() {
var toggle = document.getElementById("floodToggle").checked;
if (toggle) {
geoJSONflood.addTo(mymap);
} else {
mymap.removeLayer(geoJSONflood);
}
}
var update_position_timeout;
let previousData = [];
var table = new Tabulator("#tabby", {
// height:100%, // set height of table (in CSS or here), this enables the Virtual DOM and improves render speed dramatically (can be any valid css height value)
// data:tabledata, //assign data to table
// layout:"fitDataTable",
columns:[ //Define Table Columns
{title:"County", field:"countystate", width: 220, headerSort:false, formatter: function(cell) {
let data = cell.getData();
let value = cell.getValue();
cell.getElement().style.color = getColor(data.perout)
return value
}},
{title:"# Out", field:"outage", width: 120, headerSort:false},
{title:"Served", field:"served", width: 120, headerSort:false},
{title:"% Out", field:"perout", headerSort:false, formatter: function(cell) {
let data = cell.getData();
let value = cell.getValue();
if (data.changeDirection) {
//cell.getElement().style.backgroundColor =
// data.changeDirection === "up" ? "red" : "green";
switch (data.changeDirection) {
case "up":
cell.getElement().style.backgroundColor = "red";
break;
case "down":
cell.getElement().style.backgroundColor = "green";
break;
case "upold":
cell.getElement().style.backgroundColor = "darkred";
break;
case "downold":
cell.getElement().style.backgroundColor = "darkgreen";
break;
default:
cell.getElement().style.backgroundColor = null; // or whatever default color you prefer
}
}
return Math.round(value*10,1)/10; // Display the value
}},
],
placeholder: "No Outages Over 0.25% Detected"
});
//update_position_timeout = setTimeout(update_position, 10000);
function updateTableData(data) {
for (i in data) {
data[i].countystate = data[i].county + ' ' + data[i].state
}
const newData = data
.filter(item => item.perout > 0.2)
newData.forEach(function(newRow) {
let oldRow = previousData.find(function(item) {
return item.countystate === newRow.countystate;
});
if (oldRow) {
let newValue = parseFloat(newRow.perout);
let oldValue = parseFloat(oldRow.perout);
if (!isNaN(newValue) && !isNaN(oldValue)) {
if (newValue !== oldValue) {
newRow.changeDirection = newValue > oldValue ? "up" : "down";
} else {
// When perout hasn't changed, modify existing direction
if (oldRow.changeDirection) {
if (oldRow.changeDirection === "up") {
newRow.changeDirection = "upold";
} else if (oldRow.changeDirection === "down") {
newRow.changeDirection = "downold";
} else {
// Preserve any other existing changeDirection values
newRow.changeDirection = oldRow.changeDirection;
}
} else {
if (previousData.length != 0) {
newRow.changeDirection = newValue > 0 ? "up" : "down";
} else {
newRow.changeDirection = "up";
}
}
}
}
}
});
// Update the table
table.setData(newData)
table.setSort("perout", "desc");
// Update previousData
previousData = JSON.parse(JSON.stringify(data));
}
//Need to update test county geojson to move county out of geometry
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const sad = urlParams.has('SAD')
const sad2 = urlParams.has('sad')
const displayBuffer = urlParams.has('buffer')
const urlstart = urlParams.get('start')
const urlend = urlParams.get('end')
if (displayBuffer == true) {
var countiesjson = 'test.geojson'
var powerapi = 'main.php?service=powerapitest'
var states = 'states.geojson'
var bufferlink = document.getElementById("buffer");
bufferlink.setAttribute('href', "outage.html");
bufferlink.innerHTML = "Outages Without Buffer";
} else {
var powerapi = 'main.php?service=powerapi'
var countiesjson = 'rlxtest.json'
var states = 'states.geojson'
}
var slider = document.getElementById('transparency');
function updateOpacity() {
geoJSONcounties.setStyle({ opacity: slider.value, fillOpacity: slider.value });
}
slider.addEventListener('mousedown', function() {
mymap.dragging.disable(); // Disable map dragging
});
slider.addEventListener('mouseup', function() {
mymap.dragging.enable(); // Re-enable map dragging
});
slider.addEventListener('input', updateOpacity);
function vtecget(vtectext){
vtectext = vtectext.slice(1,-1);
potato = vtectext.split('.');
vtecstring = "#20" + potato[6].substring(0,2) + "-";
for (let i = 0; i < 6; i++) {
vtecstring = vtecstring.concat(potato[i]);
if (i < 5) {
vtecstring = vtecstring.concat("-");
}}
return "https://mesonet.agron.iastate.edu/vtec/" + vtecstring
}
function googleMap(lat,lon){
return "http://maps.google.com/maps?t=k&q=loc:" + lat + "+" + lon + "&basemap=satellite";
}
function playarchive() {
pickerStart = document.getElementById('start').value
pickerEnd = document.getElementById('end').value
county_archive_play(pickerStart,pickerEnd)
clearTimeout(update_position_timeout);
}
function togglea() {
//var hours = document.getElementById("numberofhours").value;
pickerStart = document.getElementById('start').value
pickerEnd = document.getElementById('end').value
var el = document.getElementById("archive");
if (el.firstChild.data == "Toggle Archive Mode") {
mymap.removeLayer(geoJSONflood);
document.getElementById('start').style.display = 'block'; // or 'inline-block'
document.getElementById('end').style.display = 'block';
document.getElementById('screenshots').style.display = 'block';
document.getElementById("floodToggle").checked = false;
$("#slider-2" ).slider.display = 'block';
el.firstChild.data = "Return to Realtime";
clearTimeout(update_position_timeout);
//county_archive_play(pickerStart,pickerEnd)
//county_archive_play(new Date(new Date().getTime() - (hours * 60 * 60 * 1000)).toISOString(),new Date().toISOString());
////county_archive_play(new Date(new Date().getTime() - (hours * 60 * 60 * 1000)).toISOString().replace('T',' ').replace('Z',''),new Date().toISOString().replace('T',' ').replace('Z',''));
// .toISOString().replace('T',' ').replace('Z','')
}
else {
document.getElementById("floodToggle").checked = true;
geoJSONflood.addTo(mymap);
el.firstChild.data = "Toggle Archive Mode";
$("#slider-2" ).slider.display = 'none';
document.getElementById('start').style.display = 'none';
document.getElementById('end').style.display = 'none';
document.getElementById('screenshots').style.display = 'none';
update_position();
}
}
var mymap = L.map('mapid', {zoomDelta: 0.25, zoomSnap: 0}).setView([38.508, -81.652480], 8.0);
var Esri_WorldStreetMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri'
});
var Esri_WorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
});
var Esri_WorldTopoMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
});
var CartoDB_Positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
subdomains: 'abcd',
maxZoom: 20
});
var USGS_USImageryTopo = L.tileLayer('https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 20,
attribution: 'Tiles courtesy of the <a href="https://usgs.gov/">U.S. Geological Survey</a>'
});
var baselayers = {
"Black and White": CartoDB_Positron,
"Esri Street Map": Esri_WorldStreetMap,
"Esri Satellite": Esri_WorldImagery,
"Esri Topo": Esri_WorldTopoMap,
"USGS Sat/Topo": USGS_USImageryTopo,
}
var layerControl = L.control.layers(baselayers,null,{collapsed: false}).addTo(mymap);
//CartoDB_Positron.addTo(mymap);
var geoJSONcounties = L.geoJSON(false, {
style: function (feature) {
return {
weight: 3,
opacity: 1,
color: 'black',
fillColor: 'navy',
fillOpacity: 1
};
},
}).addTo(mymap);
var geoJSONstates = L.geoJSON(false, {
style: function (feature) {
return {
weight: 1,
opacity: 1,
color: 'blue',
fillColor: null,
fillOpacity: 0
};
},
}).addTo(mymap);
var geoJSONsvr = L.geoJSON(false, {
style: function (feature) {
return {
weight: 3,
opacity: 1,
color: getColorWarning(feature.properties.type),
fillColor: 'clear',
fillOpacity: 0
};
}
}).addTo(mymap);
var smallIcon = new L.Icon({
iconSize: [10, 10],
iconAnchor: [0, 0],
popupAnchor: [1, -24],
iconUrl: 'fire.png'
});
var medIcon = new L.Icon({
iconSize: [15, 15],
iconAnchor: [0, 0],
popupAnchor: [1, -24],
iconUrl: 'fire.png'
});
var bigIcon = new L.Icon({
iconSize: [20, 20],
iconAnchor: [0, 0],
popupAnchor: [1, -24],
iconUrl: 'fire.png'
});
var floodIcon = new L.Icon({
iconSize: [15, 15],
iconAnchor: [0, 0],
popupAnchor: [1, -24],
iconUrl: 'flood.png'
});
var geoJSONfire = L.geoJSON(false, {
pointToLayer: function(feature,latlng){
if (feature.properties.dailyacres < 50) {
return L.marker(latlng,{icon: smallIcon}).bindPopup('Fire Name: ' + feature.properties.incname + '<br> Acres: ' + feature.properties.dailyacres + '<br>Contained: ' + feature.properties.contained + '%<br> Personnel: ' + feature.properties.personnel + '<br>Last Update: ' + feature.properties.modified +'Z');
} else if (feature.properties.dailyacres < 200) {
return L.marker(latlng,{icon: medIcon}).bindPopup('Fire Name: ' + feature.properties.incname + '<br> Acres: ' + feature.properties.dailyacres + '<br>Contained: ' + feature.properties.contained + '%<br> Personnel: ' + feature.properties.personnel + '<br>Last Update: ' + feature.properties.modified + 'Z');
} else {
return L.marker(latlng,{icon: bigIcon}).bindPopup('Fire Name: ' + feature.properties.incname + '<br> Acres: ' + feature.properties.dailyacres + '<br>Contained: ' + feature.properties.contained + '%<br> Personnel: ' + feature.properties.personnel + '<br>Last Update: ' + feature.properties.modified + 'Z');
}
}
}).addTo(mymap);
var geoJSONflood = L.geoJSON(false, {
pointToLayer: function(feature,latlng){
return L.marker(latlng,{icon: floodIcon}).bindPopup('Location: ' + feature.properties.description + '<br> Start: ' + feature.properties.start + '<br>Road Status: ' + feature.properties.roadstatus + '<br> Lat: ' + feature.properties.lat + ' Lon: ' + feature.properties.lon);
}
}).addTo(mymap);
var geoJSONLayer = L.geoJSON(false, {
style: function (feature) {
return {
fillColor: getColor((feature.geometry.properties.outages / feature.geometry.properties.served) * 100),
color: 'navy',
weight: 3,
opacity: 1,
fillOpacity: 1
};
}
}).addTo(mymap);
var geoJSONPoint = L.geoJSON(false, {
pointToLayer: function(feature,latlng) {
if (/tree|weather/i.test(feature.properties.cause)) {
return new L.CircleMarker(latlng, {radius: 2, fillOpacity: 1, weight:12, interactive: true, color: getColorpoint(feature.properties.outage)})
.bindPopup((latlng.lat).toFixed(3) + ", " + (latlng.lng).toFixed(3) + "<br>Outage Start: " + feature.properties.time + "Z<br>Customers Affected: " + feature.properties.outage + "<br>Cause: " + feature.properties.cause + "<br><a href='" + googleMap((latlng.lat).toFixed(3),(latlng.lng).toFixed(3)) + "' target='_blank'>Google Map Link</a>");
}
else {
return new L.CircleMarker(latlng, {radius: 2, fillOpacity: 1, interactive: true, color: getColorpoint(feature.properties.outage)})
.bindPopup((latlng.lat).toFixed(3) + ", " + (latlng.lng).toFixed(3) + "<br>Outage Start: " + feature.properties.time + "Z<br>Customers Affected: " + feature.properties.outage + "<br>Cause: " + feature.properties.cause + "<br><a href='" + googleMap((latlng.lat).toFixed(3),(latlng.lng).toFixed(3)) + "' target='_blank'>Google Map Link</a>");
}
}
}).addTo(mymap);
// Testing Code here:
var geoJSONReports = L.geoJSON(false, {
pointToLayer: function(feature,latlng) {
if (/tree|weather/i.test(feature.properties.cause)) {
return new L.CircleMarker(latlng, {radius: 2, fillOpacity: 1, weight:12, interactive: true, color: getColorpoint(feature.properties.outage)})
.bindPopup((latlng.lat).toFixed(3) + ", " + (latlng.lng).toFixed(3) + "<br>Start Time: " + feature.properties.time + "<br>Issue: " + feature.properties.issue + "<br>Comments: " + feature.properties.comments + "<br><a href='" + googleMap((latlng.lat).toFixed(3),(latlng.lng).toFixed(3)) + "' target='_blank'>Google Map Link</a>");
}
else {
return new L.CircleMarker(latlng, {radius: 2, fillOpacity: 1, interactive: true, color: getColorpoint(feature.properties.outage)})
.bindPopup((latlng.lat).toFixed(3) + ", " + (latlng.lng).toFixed(3) + "<br>Start Time: " + feature.properties.time + "<br>Issue: " + feature.properties.issue + "<br>Comments: " + feature.properties.comments + "<br><a href='" + googleMap((latlng.lat).toFixed(3),(latlng.lng).toFixed(3)) + "' target='_blank'>Google Map Link</a>");
}
}
}).addTo(mymap);
//End test
function getColorWarning(d) {
if (d == 'SVRRLX'||d == 'SVR') {
return 'orange'
}
if (d == 'TORRLX'|| d == 'TOR') {
return 'red'
}
}
function getColorpoint(d) {
const scale = chroma.scale(['gray','#0cff0c','#ff9933','red','#fe019a']).domain([4,50,200,500,1000]);
return scale(d).hex();
}
function getColor(d) {
const scale = chroma.scale(['navy','steelblue','yellow','orange','darkorange','crimson','red','magenta']).domain([0,0.25,2,5,10,25,50,100]);
return scale(d).hex();
}
function initial_county_load() {
geoJSONcounties.clearLayers();
geoJSONstates.clearLayers();
// Load states (can be done in parallel)
$.getJSON(states, function(data) {
var geojsonFeature = data;
geoJSONstates.addData(geojsonFeature);
geoJSONstates.bringToBack();
});
// Load counties and THEN update their style
$.getJSON(countiesjson, function(data) {
var geojsonFeature = data;
geoJSONcounties.addData(geojsonFeature);
geoJSONcounties.bringToBack();
// **CRITICAL FIX:** Call update_position() here, AFTER the counties are loaded.
update_position();
});
}
function county_archive_play(start,end) {
geoJSONPoint.clearLayers();
geoJSONsvr.clearLayers();
//
$.getJSON(powerapi + `&archivepoint=t&start=${start}&end=${end}`, function(data3) {
var geojsonpoint = data3;
geoJSONPoint.clearLayers();
geoJSONPoint.addData(geojsonpoint);
geoJSONPoint.bringToFront();
geoJSONPoint.setStyle({color: 'rgba(0,0,0,0)'});
});
//
$.getJSON(powerapi + `&svr=archive&start=${start}&end=${end}`, function(data3) {
var geojsonsvr = data3;
geoJSONsvr.clearLayers();
geoJSONsvr.addData(geojsonsvr);
geoJSONsvr.bringToFront();
geoJSONsvr.removeEventListener('click');
});
$.getJSON(powerapi + `&countyarchive=t&start=${start}&end=${end}`, function(data) {
var uniqueNames = [];
for(i = 0; i< data.length; i++){
if(uniqueNames.indexOf(data[i].time) === -1){
uniqueNames.push(data[i].time);
}
}
//
$( "#slider-2" ).slider({
value: 0,
animate:"slow",
min: 0,
max: uniqueNames.length - 1,
range: false,
step: 1,
orientation: "horizontal",
slide: function(event,ui) {
$("#slider-2-value").text(uniqueNames[ui.value]);
archive_county(data,uniqueNames[ui.value]);
ss_map(uniqueNames[ui.value].replace(/[.]\d+/, 'Z'));
}
});
//
});
}
function archive_county(data,timeindex) {
$('#map').html(timeindex.replace(/[.]\d+/, 'Z'));
$('#timeforsaveimages').html(timeindex.replace(/[.]\d+/, 'Z'));
for (var i in data) {
geoJSONcounties.eachLayer(function(layer) {
if (data[i].time == timeindex) {
if(layer.feature.properties.county == data[i].county && layer.feature.properties.state == data[i].state) {
layer.setStyle({fillColor: getColor(data[i].outage / data[i].served*100)});
layer.bindPopup(data[i].county + " County Outages: " + data[i].outage + " of " + data[i].served + " Served (" + ((data[i].outage / data[i].served)*100).toFixed(2) + "%)");
}
}
});
}
//
geoJSONPoint.eachLayer(function(layer) {
if(Date.parse(layer.feature.properties.time) < Date.parse(timeindex) && Date.parse(layer.feature.properties.lastchange) > Date.parse(timeindex)) {
if (/tree|weather/i.test(layer.feature.properties.cause)) {
layer.setStyle({radius: 2, fillOpacity: 1, weight:12, interactive: true, color: getColorpoint(layer.feature.properties.outage)});
} else {
layer.setStyle({radius: 2, weight:3, fillOpacity: 1, interactive: true, color: getColorpoint(layer.feature.properties.outage)});
}
} else {
layer.setStyle({weight: 0, radius:0, color: 'rgba(0,0,0,0)'});
}
});
//
geoJSONsvr.eachLayer(function(layer) {
if(Date.parse(layer.feature.properties.issue) < Date.parse(timeindex) && Date.parse(layer.feature.properties.end) > Date.parse(timeindex)) {
layer.setStyle({color: getColorWarning(layer.feature.properties.type)});
vtecurl = vtecget(layer.feature.properties.vtec)
layer.bindPopup('<a href="' + vtecurl + '">' + layer.feature.properties.vtec + '</a>');
//console.log(layer.feature.properties.vtec);
} else {
layer.setStyle({color: 'rgba(0,0,0,0)'});
layer.unbindPopup();
}
});
}
function getlsr() {
$.getJSON(powerapi + '?lsr', function(data) {
geoJSONcounties.eachLayer(function(layer) {
console.log(data)
for(var i in data) {
// $('#map').html(data[i].time);
if(layer.feature.properties.county == data[i].county && layer.feature.properties.state == data[i].state) {
layer.bindPopup(data[i].county + " County Outages: " + data[i].outage + " of " + data[i].served + " Served (" + ((data[i].outage / data[i].served)*100).toFixed(2) + "%)");
layer.setStyle({fillColor: getColor(data[i].outage / data[i].served*100)});
}}});
// $('#map').html((data[0].time).replace(/[.]\d+/, 'Z'));
temptime = new Date( Date.now() );
$('#map').html(temptime.toISOString().replace(/[.]\d+/, '').replace(/T/,' '));
});
}
function countyTable(data) {
for (i in data) {
data[i].countystate = data[i].county + ' ' + data[i].state
}
const filtered = data.filter(item => item.perout > 0);
updateTableData(filtered)
//table.setData(filtered)
table.setSort("perout", "desc");
}
function update_position() {
$.getJSON(powerapi + '&county=r', function(data) {
console.log(data);
const dataMap = {};
for (const item of data) {
const key = `${item.state}-${item.county}`;
dataMap[key] = item;
}
geoJSONcounties.eachLayer(function(layer) {
const county = layer.feature.properties.county;
const state = layer.feature.properties.state;
const key = `${state}-${county}`;
if (dataMap[key]) {
const countyData = dataMap[key];
// --- ROBUSTNESS FIX: Manually calculate percentage like in the archive function ---
let percentageOut = 0;
if (countyData.served > 0) { // Prevent division by zero
percentageOut = (countyData.outage / countyData.served) * 100;
}
layer.bindPopup(
`${countyData.county} County Outages: ${countyData.outage} of ${countyData.served} Served (${percentageOut.toFixed(2)}%)`
);
// Use the newly calculated percentage for styling
layer.setStyle({ fillColor: getColor(percentageOut) });
}
});
temptime = new Date(Date.now());
$('#map').html(temptime.toISOString().replace(/[.]\d+/, '').replace(/T/, ' '));
console.log(data);
updateTableData(data);
});
$.getJSON(powerapi, function(data1) {
var geojsonPoint = data1;
geoJSONPoint.clearLayers();
geoJSONPoint.addData(geojsonPoint);
geoJSONPoint.bringToFront();
});
$.getJSON(powerapi + '&svr=current', function(data2) {
var geojsonsvr = data2;
geoJSONsvr.clearLayers();
geoJSONsvr.addData(geojsonsvr);
geoJSONsvr.bringToFront();
geoJSONsvr.removeEventListener('click');
});
$.getJSON('main.php?service=fire', function(data2) {
var fire = data2;
geoJSONfire.clearLayers();
geoJSONfire.addData(fire);
geoJSONfire.bringToFront();
geoJSONfire.removeEventListener('click');
});
$.getJSON('main.php?service=lsr&ohgo=p', function(data3) {
var flood = data3;
geoJSONflood.clearLayers();
geoJSONflood.addData(flood);
geoJSONflood.bringToFront();
geoJSONflood.removeEventListener('click');
});
//Updated code
//
/*
$.getJSON('/911/911.php', function(data3) {
var geojsonreport = data3;
geoJSONReports.clearLayers();
geoJSONReports.addData(geojsonreport);
geoJSONReports.bringToFront();
geoJSONReports.removeEventListener('click');
});
*/
//End updating code
if (update_position_timeout) {
clearTimeout(update_position_timeout);
}
update_position_timeout = setTimeout(update_position, 120000);
}
initial_county_load();
var legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [0,0.25,2,5,10,25,50,100],
points = [4,50,200,500,1000],
labels = [];
// loop through our density intervals and generate a label with a colored square for each interval
div.innerHTML += '% Customers Served Out' + '<br>' + '(County Shade)' + '<br><br>' + '<i style="background:' + getColor([0]) + '"></i> ' + 'No Outages<br>';
for (var i = 0; i < grades.length-1; i++) {
div.innerHTML +=
'<i style="background:' + getColor(grades[i+1]) + '"></i> ' +
'Up to ' + grades[i+1] + '%'+'<br>';
}
div.innerHTML += '<br> Point Outages' + '<br>' + '(dots)' + '<br>Big dots - explicit tree or wx cause<br><br><i style="background:' + getColorpoint(points[0]) + '"></i> ' + 'Up to 4<br>';
for (var i = 0; i < points.length-1; i++) {
div.innerHTML +=
'<i style="background:' + getColorpoint(points[i+1]) + '"></i> ' +
points[i+1]+ '<br>';
}
div.innerHTML += '<br><a href="24hrpower.txt" target=_blank>Click for 120hr tree/wx caused outages</a><br><br>'
div.innerHTML += 'Fire icons indicate wildfires less than 100%<br> contained and updated within the last 36 hours'
return div;
};
legend.addTo(mymap);
//L.simpleMapScreenshoter(pluginOptions).addTo(mymap)
function ss_map(filen) {
if (document.getElementById("llss").checked == true) {
domtoimage.toPng(document.getElementById('mapid'))
.then(function (dataUrl) {
var link = document.createElement('a');
link.href = dataUrl;
link.download = filen + '.png';
link.click();
});
}
}
function saddisplay(sad,sad2) {
if (sad == true || sad2 == true) {
document.getElementById('toggle').style.display = 'none';
document.getElementById('slider-2').style.display = 'none';
//document.getElementById('slider-container').style.display = 'none';
document.getElementById('timeforsaveimages').style.display = 'none';
document.getElementById('bottombar').remove();
document.getElementById('slider-holder').remove();
document.getElementById('map').style.display = 'none';
layerControl.remove();
document.getElementById('tabby').style.display = 'block';
document.getElementById('mapid').style.height = '100%';
mymap.zoomControl.remove();
mymap.setView([38.508, -82.152480],8.5)
//mymap.setZoom(8.5)
}
}
function populateStartEndTimes() {
if (urlstart) {
// Convert to format expected by datetime-local (YYYY-MM-DDTHH:MM:SS)
const startDate = new Date(urlstart);
document.getElementById("start").value = startDate.toISOString().slice(0, 19);
}
if (urlend) {
const endDate = new Date(urlend);
document.getElementById("end").value = endDate.toISOString().slice(0, 19);
}
// If we have both parameters, adjust times if needed and automatically fetch the data
if (urlstart && urlend) {
let startDate = new Date(urlstart);
let endDate = new Date(urlend);
// Calculate difference in minutes
const timeDifference = (endDate - startDate) / (1000 * 60); // Convert milliseconds to minutes
// If less than 60 minutes apart, adjust the dates
if (timeDifference < 60) {
startDate.setMinutes(startDate.getMinutes() - 30);
endDate.setMinutes(endDate.getMinutes() + 90);
// Update the input values with adjusted times
document.getElementById("start").value = startDate.toISOString().slice(0, 19);
document.getElementById("end").value = endDate.toISOString().slice(0, 19);
}
togglea();
playarchive();
}
if (!urlstart && !urlend) {
//prefill with current time and 24 hours
const now = new Date();
const startTime = new Date(now.getTime() - (24 * 60 * 60 * 1000)); // Subtract 24 hours in milliseconds
// Format dates to match datetime-local input format (YYYY-MM-DDThh:mm)
const formatDateTime = (date) => {
return date.toISOString().slice(0, 16); // Cuts off seconds and timezone
};
document.getElementById('start').value = formatDateTime(startTime);
document.getElementById('end').value = formatDateTime(now);
document.getElementById('start').style.display = 'none';
document.getElementById('end').style.display = 'none';
document.getElementById('screenshots').style.display = 'none';
}
}
document.getElementById('tabby').style.display = 'none';
saddisplay(sad,sad2);
populateStartEndTimes()
</script>
</body>
</html>