Files
test/lsrtool.html
2025-12-09 00:20:32 +00:00

343 lines
14 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>RLX Report Query</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet-timedimension/1.1.0/leaflet.timedimension.control.min.css" />
<link rel="stylesheet" href="/js/leaflet-radar.css">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="preload" href="https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}" as="image">
<style>
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#mapid {
height: 90%;
width: 100%;
position: absolute;
top: 0;
z-index: 1;
}
#slider-2 {
position: absolute;
left: 20px;
top: 200px;
width: 300px;
z-index: 1000;
}
#controls {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
width: 80%;
z-index: 1000;
background: rgba(255, 255, 255, 0.8);
padding: 10px;
}
#time-display, #local-time-display {
margin: 10px 0;
font-size: 14px;
}
.box, .box2 {
position: absolute;
z-index: 1000;
text-align: center;
width: 250px;
left: 10%;
margin-left: -125px;
}
.box { top: 225px; }
.box2 { top: 500px; }
.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: 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;
}
</style>
</head>
<body>
<div id="mapid"></div>
<div id="slider-2"></div>
<div class="box"></div>
<div class="box2"></div>
<div id="controls">
<input type="range" id="slider" min="0" max="12" step="1" value="0">
<div id="time-display">Time: </div>
<div id="local-time-display">Local Time (ET): </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js" defer></script>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin="" defer></script>
<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" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.43/moment-timezone-with-data.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-timedimension/1.1.0/leaflet.timedimension.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-timedimension/1.1.0/leaflet.timedimension.control.min.js" defer></script>
<script src="/js/leaflet-radar.js" defer></script>
<script src="https://unpkg.com/file-saver@2.0.5/dist/FileSaver.js" defer></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" defer></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof L === 'undefined') {
console.error('Leaflet is not loaded yet');
return;
}
if (typeof moment === 'undefined') {
console.error('Moment.js is not loaded yet');
return;
}
const mymap = L.map('mapid', {
zoomDelta: 0.25,
zoomSnap: 0,
fadeAnimation: false,
zoomAnimation: true,
zoomAnimationThreshold: 4,
preferCanvas: true
}).setView([38.508, -81.652480], 8.0);
const Esri_WorldStreetMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri',
maxZoom: 19,
tileSize: 256,
updateWhenIdle: true,
updateInterval: 200,
reuseTiles: true
}).addTo(mymap);
const baselayers = {
"Esri Street Map": Esri_WorldStreetMap,
"Esri Satellite": L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',
maxZoom: 19,
updateWhenIdle: true,
reuseTiles: true
}),
"Esri Topo": L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri — 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',
maxZoom: 19,
updateWhenIdle: true,
reuseTiles: true
}),
"USGS Sat/Topo": 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>',
updateWhenIdle: true,
reuseTiles: true
})
};
L.control.layers(baselayers, null, {collapsed: false}).addTo(mymap);
mymap.createPane('polygonPane');
mymap.getPane('polygonPane').style.zIndex = 300;
mymap.createPane('radarPane');
mymap.getPane('radarPane').style.zIndex = 350;
mymap.getPane('markerPane').style.zIndex = 400;
const geoJSONsvr = L.geoJSON(null, {
style: function(feature) {
return {
weight: 3,
opacity: 1,
color: getColorWarning(feature.properties.warntype),
fillOpacity: 0,
interactive: false
};
},
onEachFeature: function(feature, layer) {
const vtecurl = vtecget(feature.properties.vtec);
layer.bindPopup(`<a href="${vtecurl}">${feature.properties.vtec}</a>`);
},
pane: 'polygonPane'
}).addTo(mymap);
const geoJSONPoint = L.geoJSON(null, {
pointToLayer: function(feature, latlng) {
const isWeatherRelated = /tree|weather/i.test(feature.properties.cause);
return L.circleMarker(latlng, {
radius: isWeatherRelated ? 6 : 4,
fillOpacity: 1,
weight: isWeatherRelated ? 12 : 1,
color: getColorpoint(feature.properties.outage),
interactive: true,
pane: 'markerPane'
}).bindPopup(`
${latlng.lat.toFixed(3)}, ${latlng.lng.toFixed(3)}<br>
Outage Start: ${feature.properties.time}<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);
function googleMap(lat, lon) {
return `http://maps.google.com/maps?t=k&q=loc:${lat}+${lon}&basemap=satellite`;
}
function vtecget(vtectext) {
vtectext = vtectext.slice(1, -1);
const parts = vtectext.split('.');
let vtecstring = `#20${parts[6].substring(0, 2)}-`;
for (let i = 0; i < 6; i++) {
vtecstring += parts[i] + (i < 5 ? '-' : '');
}
return `https://mesonet.agron.iastate.edu/vtec/${vtecstring}`;
}
function getColorWarning(d) {
return d === 'SVRRLX' || d === 'SVR' ? 'orange' :
d === 'TORRLX' || d === 'TOR' ? 'red' : 'gray';
}
function getColorpoint(d) {
return chroma.scale(['gray', '#0cff0c', '#ff9933', 'red', '#fe019a'])
.domain([4, 50, 200, 500, 1000])(d).hex();
}
fetch('counties.json')
.then(res => res.json())
.then(data => L.geoJSON(data, {
style: {color: "#000000", weight: 1, fillOpacity: 0},
pane: 'polygonPane'
}).addTo(mymap))
.catch(err => console.error('Failed to load counties:', err));
async function saddisplay() {
const urlParams = new URLSearchParams(window.location.search);
const vtec = urlParams.get('vtec');
const ids = urlParams.get('id');
try {
const svrResponse = await fetch(`main.php?service=lsr&vtec=${vtec}`);
const geojsonsvr = await svrResponse.json();
geoJSONsvr.clearLayers().addData(geojsonsvr);
if (geojsonsvr.features.length > 0) {
const firstFeature = geojsonsvr.features[0].properties;
mymap.setView([firstFeature.lat, firstFeature.lon], 10);
createRadarMap(firstFeature.issue + "Z", firstFeature.endtime + "Z");
}
if (ids) {
const powerResponse = await fetch(`main.php?service=powerapi&poweridsgeojson=${ids}`);
const geojsonPoint = await powerResponse.json();
geoJSONPoint.clearLayers().addData(geojsonPoint);
}
geoJSONPoint.bringToFront();
} catch (error) {
console.error('Error in saddisplay:', error);
}
}
function createRadarMap(startTime, endTime) {
const coeff = 1000 * 60 * 5;
const start = new Date(Math.round(new Date(startTime).getTime() / coeff) * coeff);
const end = new Date(endTime);
const times = [];
let current = new Date(start);
while (current <= end) {
times.push(current.toISOString());
current.setMinutes(current.getMinutes() + 5);
}
const radarLayers = new Map();
const slider = document.getElementById("slider");
const timeDisplay = document.getElementById("time-display");
const localTimeDisplay = document.getElementById("local-time-display");
function createRadarLayer(time) {
return L.tileLayer.wms("https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0q-t.cgi", {
layers: 'nexrad-n0q-wmst',
format: 'image/png',
transparent: true,
attribution: "Weather data © 2025 IEM Nexrad",
time: time,
opacity: 0,
pane: 'radarPane',
updateWhenIdle: true,
reuseTiles: true
});
}
function updateLocalTimeDisplay(utcTime) {
const easternTime = moment.utc(utcTime).tz('America/New_York').format('YYYY-MM-DD HH:mm:ss');
localTimeDisplay.textContent = `Local Time (ET): ${easternTime}`;
}
slider.max = times.length - 1;
slider.value = 0;
slider.addEventListener("input", function() {
const index = parseInt(this.value);
const time = times[index];
if (!radarLayers.has(time)) {
const layer = createRadarLayer(time);
radarLayers.set(time, layer);
layer.addTo(mymap);
}
radarLayers.forEach((layer, layerTime) => {
layer.setOpacity(layerTime === time ? 0.8 : 0);
});
timeDisplay.textContent = `Time: ${time}`;
updateLocalTimeDisplay(time);
geoJSONPoint.bringToFront();
});
const firstLayer = createRadarLayer(times[0]);
radarLayers.set(times[0], firstLayer);
firstLayer.setOpacity(0.8).addTo(mymap);
timeDisplay.textContent = `Time: ${times[0]}`;
updateLocalTimeDisplay(times[0]);
geoJSONPoint.bringToFront();
setInterval(() => {
const currentIndex = parseInt(slider.value);
updateLocalTimeDisplay(times[currentIndex]);
}, 1000);
}
saddisplay();
mymap.on('overlayadd overlayremove zoomend', () => {
geoJSONPoint.bringToFront();
});
});
</script>
</body>
</html>