335 lines
11 KiB
HTML
335 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>RainViewer MAP</title>
|
|
|
|
<meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<link rel="stylesheet" href="https://gpslog.famzur.de/js/leaflet/leaflet.css"/>
|
|
<script src="https://gpslog.famzur.de/js/leaflet/leaflet.js"></script>
|
|
<style type="text/css">
|
|
li {
|
|
list-style: none;
|
|
display: inline-block;
|
|
}
|
|
.rainview-map-control {
|
|
background: rgba(255,255,255,0.8);
|
|
box-shadow: 0 0 15px rgb(0 0 0 / 20%);
|
|
border-radius: 5px;
|
|
padding: 6px 8px;
|
|
}
|
|
.rainview-button {
|
|
cursor: pointer;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<ul style="text-align:center; position: absolute;top: 0; left: 0; right: 0; height: 20px;">
|
|
<li>
|
|
<select name="kind" id="kind" onchange="changeKind(this)">
|
|
<option value="radarFuture">Radar (Future)</option>
|
|
<option value="radarPast">Radar (Past)</option>
|
|
<option value="radar">Radar (Past + Future)</option>
|
|
<option value="satellite">Infrared Satellite</option>
|
|
</select>
|
|
</li>
|
|
|
|
<li><select id="colors" onchange="setColors(); return;">
|
|
<option value="0">Black and White Values</option>
|
|
<option value="1">Original</option>
|
|
<option value="2" selected="selected">Universal Blue</option>
|
|
<option value="3">TITAN</option>
|
|
<option value="4">The Weather Channel</option>
|
|
<option value="5">Meteored</option>
|
|
<option value="6">NEXRAD Level-III</option>
|
|
<option value="7">RAINBOW @ SELEX-SI</option>
|
|
<option value="8">Dark Sky</option>
|
|
</select></li>
|
|
</ul>
|
|
|
|
<div id="timestamp" class="rainview-map-control">FRAME TIME</div>
|
|
|
|
<div id="mapid" style="position: absolute; top: 20px; left: 0; bottom: 0; right: 0;"></div>
|
|
|
|
<script>
|
|
var map = L.map('mapid').setView([48.678154534097324, 9.283021551577976], 12);
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attributions: 'Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors'
|
|
}).addTo(map);
|
|
|
|
L.marker([48.678154534097324, 9.283021551577976]).addTo(map);
|
|
|
|
var dateControl = L.control();
|
|
dateControl.onAdd = function(map) {
|
|
this.div = L.DomUtil.get('timestamp');
|
|
return this.div;
|
|
};
|
|
dateControl.addTo(map);
|
|
|
|
var playControl = L.control();
|
|
playControl.onAdd = function(map) {
|
|
this.div = L.DomUtil.create('div', 'rainview-map-control rainview-button');
|
|
this.div.innerHTML = 'Play';
|
|
L.DomEvent.addListener(this.div, 'click', function(evt) {
|
|
if (!stop()) {
|
|
play();
|
|
evt.target.innerHTML = 'Stop';
|
|
} else {
|
|
evt.target.innerHTML = 'Play';
|
|
}
|
|
});
|
|
|
|
return this.div;
|
|
};
|
|
playControl.addTo(map);
|
|
|
|
var nextControl = L.control();
|
|
nextControl.onAdd = function(map) {
|
|
this.div = L.DomUtil.create('div');
|
|
let previousButton = L.DomUtil.create('div', 'rainview-map-control rainview-button', this.div);
|
|
previousButton.innerHTML = '<';
|
|
previousButton.style = 'float: left;'
|
|
L.DomEvent.addListener(previousButton, 'click', function() {
|
|
stop();
|
|
showFrame(animationPosition - 1)
|
|
});
|
|
|
|
let nextButton = L.DomUtil.create('div', 'rainview-map-control rainview-button', this.div);
|
|
nextButton.innerHTML = '>';
|
|
nextButton.style = 'float: inherit; margin-left: 5px;'
|
|
L.DomEvent.addListener(nextButton, 'click', function() { stop(); showFrame(animationPosition + 1)});
|
|
|
|
return this.div;
|
|
};
|
|
nextControl.addTo(map);
|
|
|
|
/**
|
|
* RainViewer radar animation part
|
|
* @type {number[]}
|
|
*/
|
|
var apiData = {};
|
|
var mapFrames = [];
|
|
var lastPastFramePosition = -1;
|
|
var radarLayers = [];
|
|
|
|
var optionKind = document.getElementById('kind').value;
|
|
|
|
var optionTileSize = 256; // can be 256 or 512.
|
|
var optionColorScheme = 2; // from 0 to 8. Check the https://rainviewer.com/api/color-schemes.html for additional information
|
|
var optionSmoothData = 1; // 0 - not smooth, 1 - smooth
|
|
var optionSnowColors = 1; // 0 - do not show snow colors, 1 - show snow colors
|
|
|
|
var animationPosition = 0;
|
|
var animationTimer = false;
|
|
|
|
/**
|
|
* Load all the available maps frames from RainViewer API
|
|
*/
|
|
var apiRequest = new XMLHttpRequest();
|
|
apiRequest.open("GET", "https://api.rainviewer.com/public/weather-maps.json", true);
|
|
apiRequest.onload = function(e) {
|
|
// store the API response for re-use purposes in memory
|
|
apiData = JSON.parse(apiRequest.response);
|
|
initialize(apiData, optionKind);
|
|
};
|
|
apiRequest.send();
|
|
|
|
/**
|
|
* Initialize internal data from the API response and options
|
|
*/
|
|
function initialize(api, kind) {
|
|
console.debug("initializing data for: " + kind);
|
|
// remove all already added tiled layers
|
|
for (var i in radarLayers) {
|
|
map.removeLayer(radarLayers[i]);
|
|
}
|
|
mapFrames = [];
|
|
radarLayers = [];
|
|
animationPosition = 0;
|
|
|
|
if (!api) {
|
|
return;
|
|
}
|
|
switch (kind) {
|
|
case 'satellite':
|
|
if (api.satellite && api.satellite.infrared) {
|
|
mapFrames = api.satellite.infrared;
|
|
|
|
lastPastFramePosition = api.satellite.infrared.length - 1;
|
|
showFrame(lastPastFramePosition);
|
|
}
|
|
break;
|
|
case 'radar':
|
|
if (api.radar && api.radar.past) {
|
|
mapFrames = api.radar.past;
|
|
if (api.radar.nowcast) {
|
|
mapFrames = mapFrames.concat(api.radar.nowcast);
|
|
}
|
|
|
|
// show the last "past" frame
|
|
lastPastFramePosition = api.radar.past.length - 1;
|
|
showFrame(lastPastFramePosition);
|
|
}
|
|
break;
|
|
case 'radarPast':
|
|
if (api.radar && api.radar.past) {
|
|
mapFrames = api.radar.past;
|
|
|
|
// show the last "past" frame
|
|
lastPastFramePosition = api.radar.past.length - 1;
|
|
showFrame(lastPastFramePosition);
|
|
}
|
|
break;
|
|
case 'radarFuture':
|
|
if (api.radar && api.radar.past) {
|
|
mapFrames = mapFrames.concat(api.radar.past[api.radar.past.length - 1]);
|
|
if (api.radar.nowcast) {
|
|
mapFrames = mapFrames.concat(api.radar.nowcast);
|
|
}
|
|
|
|
// show the last "past" frame
|
|
lastPastFramePosition = 0;
|
|
showFrame(lastPastFramePosition);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Animation functions
|
|
* @param path - Path to the XYZ tile
|
|
*/
|
|
function addLayer(frame) {
|
|
if (!radarLayers[frame.path]) {
|
|
var colorScheme = optionKind == 'satellite' ? 0 : optionColorScheme;
|
|
var smooth = optionKind == 'satellite' ? 0 : optionSmoothData;
|
|
var snow = optionKind == 'satellite' ? 0 : optionSnowColors;
|
|
|
|
radarLayers[frame.path] = new L.TileLayer(apiData.host + frame.path + '/' + optionTileSize + '/{z}/{x}/{y}/' + colorScheme + '/' + smooth + '_' + snow + '.png', {
|
|
tileSize: 256,
|
|
opacity: 0.001,
|
|
zIndex: frame.time
|
|
});
|
|
}
|
|
if (!map.hasLayer(radarLayers[frame.path])) {
|
|
map.addLayer(radarLayers[frame.path]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display particular frame of animation for the @position
|
|
* If preloadOnly parameter is set to true, the frame layer only adds for the tiles preloading purpose
|
|
* @param position
|
|
* @param preloadOnly
|
|
*/
|
|
function changeRadarPosition(position, preloadOnly) {
|
|
while (position >= mapFrames.length) {
|
|
position -= mapFrames.length;
|
|
}
|
|
while (position < 0) {
|
|
position += mapFrames.length;
|
|
}
|
|
|
|
var currentFrame = mapFrames[animationPosition];
|
|
var nextFrame = mapFrames[position];
|
|
|
|
addLayer(nextFrame);
|
|
|
|
if (preloadOnly) {
|
|
return;
|
|
}
|
|
|
|
animationPosition = position;
|
|
|
|
if (radarLayers[currentFrame.path]) {
|
|
radarLayers[currentFrame.path].setOpacity(0);
|
|
}
|
|
radarLayers[nextFrame.path].setOpacity(100);
|
|
|
|
var pastOrForecast = nextFrame.time > Date.now() / 1000 ? 'FORECAST' : 'PAST';
|
|
|
|
document.getElementById("timestamp").innerHTML = pastOrForecast + ': ' + (new Date(nextFrame.time * 1000)).toLocaleString();
|
|
}
|
|
|
|
/**
|
|
* Check avialability and show particular frame position from the timestamps list
|
|
*/
|
|
function showFrame(nextPosition) {
|
|
var preloadingDirection = nextPosition - animationPosition > 0 ? 1 : -1;
|
|
|
|
changeRadarPosition(nextPosition);
|
|
|
|
// preload next next frame (typically, +1 frame)
|
|
// if don't do that, the animation will be blinking at the first loop
|
|
changeRadarPosition(nextPosition + preloadingDirection, true);
|
|
}
|
|
|
|
/**
|
|
* Stop the animation
|
|
* Check if the animation timeout is set and clear it.
|
|
*/
|
|
function stop() {
|
|
if (animationTimer) {
|
|
clearTimeout(animationTimer);
|
|
animationTimer = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function play() {
|
|
showFrame(animationPosition + 1);
|
|
|
|
// Main animation driver. Run this function every 500 ms
|
|
animationTimer = setTimeout(play, 500);
|
|
}
|
|
|
|
function playStop() {
|
|
if (!stop()) {
|
|
play();
|
|
}
|
|
}
|
|
|
|
function changeKind(element) {
|
|
var kind = element.value;
|
|
console.log("changing kind to: " + kind);
|
|
optionKind = kind;
|
|
initialize(apiData, optionKind);
|
|
}
|
|
|
|
function setColors() {
|
|
var e = document.getElementById('colors');
|
|
optionColorScheme = e.options[e.selectedIndex].value;
|
|
initialize(apiData, optionKind);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle arrow keys for navigation between next \ prev frames
|
|
*/
|
|
document.onkeydown = function (e) {
|
|
e = e || window.event;
|
|
switch (e.which || e.keyCode) {
|
|
case 37: // left
|
|
stop();
|
|
showFrame(animationPosition - 1);
|
|
break;
|
|
|
|
case 39: // right
|
|
stop();
|
|
showFrame(animationPosition + 1);
|
|
break;
|
|
|
|
default:
|
|
return; // exit this handler for other keys
|
|
}
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|