added files
This commit is contained in:
parent
2b193b6a4a
commit
50509f80e1
58
index.html
Normal file
58
index.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Radar application</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css"/>
|
||||||
|
<script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet-src.js"></script>
|
||||||
|
<link rel="stylesheet" href="src/leaflet-radar.css"></script>
|
||||||
|
<script src="src/leaflet-radar.js"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-body-secondary">
|
||||||
|
<div class="p-3 container">
|
||||||
|
<h1>Radar application</h1>
|
||||||
|
<p>A simple radar application in HTML</p>
|
||||||
|
<div class="border rounded mb-3" style="height: 500px;" id="map"></div>
|
||||||
|
</div>
|
||||||
|
<footer class="py-3 my-4 border rounded container bg-body text-center">
|
||||||
|
<p><a href="https://git.ttnrtsite.me/SSP6904/radarmap">View source code</a></p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
// Set theme to the user's preferred color scheme
|
||||||
|
function updateTheme() {
|
||||||
|
const colorMode = window.matchMedia("(prefers-color-scheme: dark)").matches ?
|
||||||
|
"dark" :
|
||||||
|
"light";
|
||||||
|
document.querySelector("html").setAttribute("data-bs-theme", colorMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set theme on load
|
||||||
|
updateTheme()
|
||||||
|
|
||||||
|
// Update theme when the preferred scheme changes
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme)
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var map = L.map("map").setView([42.378836,-83.560764], 5);
|
||||||
|
|
||||||
|
var osmAttribution =
|
||||||
|
'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
|
||||||
|
var leafletRadarAttribution =
|
||||||
|
'<a href="https://github.com/rwev/leaflet-radar">Radar</a>';
|
||||||
|
|
||||||
|
L.tileLayer(
|
||||||
|
"https://tile-{s}.openstreetmap.fr/hot/{z}/{x}/{y}.png",
|
||||||
|
{
|
||||||
|
attribution: [
|
||||||
|
osmAttribution,
|
||||||
|
leafletRadarAttribution
|
||||||
|
].join(" | ")
|
||||||
|
}
|
||||||
|
).addTo(map);
|
||||||
|
L.control.radar({}).addTo(map);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
15
src/leaflet-radar.css
Normal file
15
src/leaflet-radar.css
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.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: black;
|
||||||
|
padding: 5px;
|
||||||
|
height: 67.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-radar .leaflet-radar-timestamp {
|
||||||
|
text-align: center;
|
||||||
|
}
|
229
src/leaflet-radar.js
Normal file
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: `►`,
|
||||||
|
pauseHTML: `▐`,
|
||||||
|
},
|
||||||
|
|
||||||
|
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 = `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);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user