updated radar layer

This commit is contained in:
Shaun Hoffer 2024-08-18 14:01:50 -04:00
parent 3f4c4adc11
commit 6478d95a1a
3 changed files with 267 additions and 15 deletions

@ -1,17 +1,18 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<title>US Map | Shaun</title> <title>US Map</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script src="//files.shaunhoffer.cc/states.js"></script> <script src="//files.shaunhoffer.cc/states.js"></script>
<script src="//files.shaunhoffer.cc/counties.js"></script> <script src="//files.shaunhoffer.cc/counties.js"></script>
<meta content="#e8e8e8" name="theme-color"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link href="https://cdn.jsdelivr.net/npm/halfmoon@2.0.1/css/halfmoon.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/halfmoon@2.0.1/css/halfmoon.min.css" rel="stylesheet">
<link rel="stylesheet" href="src/leaflet-radar.css"></script>
<script src="src/leaflet-radar.js"></script>
<script> <script>
// Set theme to the user's preferred color scheme // Set theme to the user's preferred color scheme
function updateTheme() { function updateTheme() {
@ -29,35 +30,36 @@ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', upd
<body style="min-height: 100vh"> <body style="min-height: 100vh">
<div class="container-fluid p-2"> <div class="container-fluid p-2">
<div class="card"> <div class="card">
<div class="card-header">Simple map with leaflet</div> <div class="card-header">US map</div>
<div class="card-body p-0"> <div class="card-body p-0">
<div id="app" style="height: 600px;"></div> <div id="app" style="height: 600px;"></div>
</div> </div>
</div> </div>
<br>
<div class="card">
<div class="card-body">
<a href="https://git.ttnrtsite.me/ssp6904/usmap">View source code</a>
</div>
</div>
</div> </div>
<script> <script>
var leafletRadarAttribution = '<a href="https://github.com/rwev/leaflet-radar">Radar source code</a>';
var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19, maxZoom: 19,
attribution: '© OpenStreetMap' attribution: ['© OpenStreetMap | '+ leafletRadarAttribution]
}); });
var topo = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}g', { var topo = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}g', {
maxZoom: 19 maxZoom: 19,
attribution: ['© Arcgis maps | '+ leafletRadarAttribution]
}); });
var arcgissatellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { var arcgissatellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19
});
var radaroverlay = L.tileLayer('https://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/ridge::USCOMP-N0Q-0/{z}/{x}/{y}.png', {
maxZoom: 19
});
var google = L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', {
maxZoom: 19, maxZoom: 19,
subdomains: ['mt0', 'mt1', 'mt2', 'mt3'] attribution: ['© Arcgis maps | '+ leafletRadarAttribution]
}); });
var map = L.map('app', { var map = L.map('app', {
center: [37.8, -96], center: [37.8, -96],
zoom: 4, zoom: 4,
@ -74,10 +76,11 @@ var defaultStyle = {
var countiesL = L.geoJson(counties, {style: defaultStyle}); var countiesL = L.geoJson(counties, {style: defaultStyle});
var statesL = L.geoJson(statesData, {style: defaultStyle}); var statesL = L.geoJson(statesData, {style: defaultStyle});
var baseMaps = {"OpenStreetMap": osm,"Arcgis Satellite": arcgissatellite,"Topo": topo}; var baseMaps = {"OpenStreetMap": osm,"Arcgis Satellite": arcgissatellite,"Topo": topo};
var overlayMaps = {"Counties": countiesL,"States": statesL, "Radar overlay": radaroverlay}; var overlayMaps = {"Counties": countiesL,"States": statesL};
var layerControl = L.control.layers(baseMaps, overlayMaps, {collapsed: false}); var layerControl = L.control.layers(baseMaps, overlayMaps, {collapsed: false});
layerControl.addTo(map); layerControl.addTo(map);
L.control.radar({}).addTo(map);
// FeatureGroup is to store editable layers // FeatureGroup is to store editable layers
var drawnItems = new L.FeatureGroup(); var drawnItems = new L.FeatureGroup();

20
src/leaflet-radar.css Normal file

@ -0,0 +1,20 @@
.leaflet-radar {
/* from leaflet-control-layers */
border-radius: 5px;
background: #fff;
border: 2px solid rgba(0, 0, 0, 0.2);
background-clip: padding-box;
color: #333;
padding: 5px;
}
.leaflet-radar .leaflet-radar-timestamp {
text-align: center;
}
#leaflet-radar-toggle {
margin-top: 2px;
position: relative;
top: 1px;
}

229
src/leaflet-radar.js Normal file

@ -0,0 +1,229 @@
// TODO add refresh (reload time layers)
// TODO add buffer time to load layers where radar turned on
L.Control.Radar = L.Control.extend({
NEXRAD_URL: `https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0q.cgi`,
NEXRAD_LAYER: `nexrad-n0q-900913`,
isPaused: false,
timeLayerIndex: 0,
timeLayers: [],
options: {
position: `topright`,
opacity: 0.575,
zIndex: 200,
transitionMs: 750,
playHTML: `&#9658;`,
pauseHTML: `&#9616;`,
},
onRemove: function () {
L.DomUtil.remove(this.container);
},
onAdd: function (map) {
this.map = map;
// setup control container
this.container = L.DomUtil.create(`div`, "leaflet-radar");
L.DomEvent.disableClickPropagation(this.container);
L.DomEvent.on(this.container, `control_container`, function (e) {
L.DomEvent.stopPropagation(e);
});
L.DomEvent.disableScrollPropagation(this.container);
// add control elements within container
checkbox_div = L.DomUtil.create(
`div`,
`leaflet-radar-toggle`,
this.container
);
this.checkbox = document.createElement(`input`);
this.checkbox.id = `leaflet-radar-toggle`;
this.checkbox.type = `checkbox`;
this.checkbox.checked = false;
this.checkbox.onclick = () => this.toggle();
checkbox_div.appendChild(this.checkbox);
let checkbox_label = document.createElement(`span`);
checkbox_label.innerText = ` Toggle Radar`;
checkbox_div.appendChild(checkbox_label);
let slider_div = L.DomUtil.create(
`div`,
`leaflet-radar-slider`,
this.container
);
this.slider = document.createElement(`input`);
this.slider.id = `leaflet-radar-slider`;
this.slider.type = `range`;
this.slider.min = 0;
slider_div.appendChild(this.slider);
this.timestamp_div = L.DomUtil.create(
`div`,
`leaflet-radar-timestamp`,
this.container
);
this.setDisabled(true);
this.isPaused = true;
return this.container;
},
hideLayerByIndex: function (index) {
this.timeLayers[index].tileLayer.setOpacity(0);
this.timestamp_div.innerHTML = ``;
},
showLayerByIndex: function (index) {
this.timeLayers[index].tileLayer.setOpacity(
this.options.opacity
);
this.timestamp_div.innerHTML = this.timeLayers[index].timestamp;
},
setDisabled: function (disabled) {
this.slider.disabled = disabled;
this.timestamp_div.innerText = ``;
},
toggle: function () {
if (!this.checkbox.checked) {
this.setDisabled(true);
this.removeLayers();
return;
}
this.setDisabled(false);
this.timeLayers = this.generateLayers();
this.addLayers(this.timeLayers);
this.slider.max = `${this.timeLayers.length - 1}`;
this.timeLayerIndex = 0;
this.isPaused = false;
this.slider.oninput = () => {
this.hideLayerByIndex(this.timeLayerIndex);
this.timeLayerIndex = +this.slider.value;
this.showLayerByIndex(this.timeLayerIndex);
this.isPaused = true;
};
this.setTransitionTimer();
},
setTransitionTimer: function () {
setTimeout(() => {
if (this.isPaused) {
return;
}
this.timeLayers.forEach(timeLayer => {
timeLayer.tileLayer.setOpacity(0);
timeLayer.tileLayer.addTo(this.map);
});
if (this.checkbox.checked) {
this.hideLayerByIndex(this.timeLayerIndex);
this.incrementLayerIndex();
this.showLayerByIndex(this.timeLayerIndex);
this.slider.value = `${this.timeLayerIndex}`;
this.setTransitionTimer();
} else {
this.setDisabled(true);
this.removeLayers();
}
}, this.options.transitionMs);
},
incrementLayerIndex: function () {
this.timeLayerIndex++;
if (this.timeLayerIndex > this.timeLayers.length - 1) {
this.timeLayerIndex = 0;
}
},
addLayers: function () {
this.timeLayers.forEach(timeLayer => {
timeLayer.tileLayer.setOpacity(0);
timeLayer.tileLayer.addTo(this.map);
});
},
removeLayers: function () {
this.timeLayers.forEach(timeLayer =>
timeLayer.tileLayer.removeFrom(this.map)
);
this.timeLayers = [];
this.timeLayerIndex = 0;
},
generateLayers: function () {
let timeLayers = [];
const TOTAL_INTERVALS = 10;
const INTERVAL_LENGTH_HRS = 5;
const currentTime = new Date();
for (let i = 0; i <= TOTAL_INTERVALS; i++) {
const timeDiffMins =
TOTAL_INTERVALS * INTERVAL_LENGTH_HRS -
INTERVAL_LENGTH_HRS * i;
const suffix = (function(time) {
switch(time) {
case 0:
return '';
case 5:
return '-m05m';
default:
return '-m' + time + 'm';
}
})(timeDiffMins);
const layerRequest = this.NEXRAD_LAYER + suffix;
const layer = L.tileLayer.wms(this.NEXRAD_URL, {
layers: layerRequest,
format: `image/png`,
transparent: true,
opacity: this.options.opacity,
zIndex: this.options.zIndex
});
const timeString = new Date(
currentTime.valueOf() - timeDiffMins * 60 * 1000
).toLocaleTimeString();
timeLayers.push({
timestamp: `${timeString} (-${timeDiffMins} min)`,
tileLayer: layer
});
}
return timeLayers;
}
});
L.control.radar = function (options) {
return new L.Control.Radar(options);
};