commit e99522350a9d53061e23e586af3a26a7df37bb40 Author: steckbrief <steckbrief@chefmail.de> Date: Sun Aug 15 17:32:18 2021 +0200 adjusted api example from https://github.com/rainviewer/rainviewer-api-example/blob/master/rainviewer-api-example.html diff --git a/rainviewer.html b/rainviewer.html new file mode 100644 index 0000000..54a3cd7 --- /dev/null +++ b/rainviewer.html @@ -0,0 +1,286 @@ +<!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; + } + </style> +</head> +<body> + +<ul style="text-align:center; position: absolute;top: 0; left: 0; right: 0; height: 50px;"> + <li> + <input type="radio" name="kind" onchange="setKind('radar')">Radar (Past + Future) + <input type="radio" name="kind" onchange="setKind('radarPast')">Radar (Past) + <input type="radio" name="kind" checked="checked" onchange="setKind('radarFuture')">Radar (Future) + <input type="radio" name="kind" onchange="setKind('satellite')">Infrared Satellite + </li> + + <li><input type="button" onclick="stop(); showFrame(animationPosition - 1); return;" value="<" /></li> + <li><input type="button" onclick="playStop();" value="Play / Stop" /></li> + <li><input type="button" onclick="stop(); showFrame(animationPosition + 1); return;" value=">" /></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" style="text-align:center; position: absolute;top: 50px; left: 0; right: 0; height: 80px;">FRAME TIME</div> + +<div id="mapid" style="position: absolute; top: 80px; 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); + + /** + * RainViewer radar animation part + * @type {number[]} + */ + var apiData = {}; + var mapFrames = []; + var lastPastFramePosition = -1; + var radarLayers = []; + + var optionKind = 'radar'; // can be 'radar' or 'satellite' + + 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) { + // 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)).toString(); + } + + /** + * 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(); + } + } + + /** + * Change map options + */ + function setKind(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>