Skip to content

Commit

Permalink
Feat/timeline (#14)
Browse files Browse the repository at this point in the history
* Fixed blank map error

* initial test

* improvements

* fixed readme

* reduced time size

* update readme

Co-authored-by: Jan Litzenburger <[email protected]>
  • Loading branch information
jalibu and Jan Litzenburger authored Aug 25, 2021
1 parent cb147f0 commit de213d1
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 35 deletions.
11 changes: 8 additions & 3 deletions @types/Module/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ declare module Module {

type Config = {
animationSpeedMs: number;
colorizeTime: boolean;
defaultZoomLevel: number;
displayTime: boolean;
displayClockSymbol: boolean;
displayTimeline: boolean;
displayOnlyOnRain: boolean;
extraDelayLastFrameMs: number;
extraDelayCurrentFrameMs: number;
markers: Marker[];
mapPositions: MapPosition[];
mapUrl: string;
Expand All @@ -54,14 +57,16 @@ type MapPosition = {
};

type RuntimeData = {
map: any;
timeframes: number[];
radarLayers: any[];
animationTimer: any;
animationPosition: number;
map: any;
mapPosition: number;
numHistoryFrames: number;
numForecastFrames: number;
radarLayers: any[];
loopNumber: number;
timeDiv: Element;
timeframes: number[];
};

declare const moment: Function;
Expand Down
41 changes: 37 additions & 4 deletions MMM-RAIN-MAP.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,46 @@
.rain-map-time-wrapper {
position: absolute;
display: block;
--color-history: #003AFF;
--color-now: #000;
--color-forecast: #ff0074;
position: absolute;
display: block;
background: #ccc;
padding: 0 10px 0 10px;
z-index: 999;
opacity: 0.75;
color: #000;
font-size: 1.5rem;
font-size: 1.2rem;
}
.rain-map-time-wrapper i {
margin-right: 6px;
}
}

.rain-map-timeslider {
width: 0;
height: 0;
margin-top: -8px;
margin-left: -2px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 10px solid #000;
position: relative;
display: block;
}

.rain-map-timeline {
display: block;
height: 4px;
margin-bottom: 5px;
}

.rain-map-time.rain-map-history {
color: var(--color-history);
}

.rain-map-time.rain-map-now {
color: var(--color-now);
}

.rain-map-time.rain-map-forecast {
color: var(--color-forecast);
}
2 changes: 1 addition & 1 deletion MMM-RAIN-MAP.js

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ If you like this module and want to thank, please buy me a beer.
## Features

- Displays Rainviewer.com radar layers on OpenStreetMap
* Every 10 minutes a new weather snapshot is published
* The snapshots of the last 2 hours are available, which show the weather events of the past
* Additionally 3 layers are displayed as forecast of the next 30 minutes
- Option to place multiple markers on map
- Option for multiple, alternating map positions
- Option to only show in current rainy weather conditions. Works only together with [weather](https://github.com/MichMich/MagicMirror/tree/master/modules/default/weather) or [MMM-OpenWeatherForecast](https://github.com/jclarke0000/MMM-OpenWeatherForecast) as dependency.
Expand All @@ -34,11 +37,14 @@ If you like this module and want to thank, please buy me a beer.
position: "top_left",
config: {
animationSpeedMs: 400,
colorizeTime: true,
defaultZoomLevel: 8,
displayTime: true,
displayTimeline: true,
displayClockSymbol: true,
displayOnlyOnRain: false,
extraDelayLastFrameMs: 2000,
extraDelayLastFrameMs: 1000,
extraDelayCurrentFrameMs: 3000,
markers: [
{ lat: 49.41, lng: 8.717, color: "red" },
{ lat: 48.856, lng: 2.35, color: "green" },
Expand All @@ -63,11 +69,14 @@ If you like this module and want to thank, please buy me a beer.
| Option | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `animationSpeedMs` | Determines how fast the frames are played. <br><br>**Type:** `int` <br> **Default value:** `400` (time per frame in milliseconds) |
| `colorizeTime` | Set true, to colorize history and forecast timestamps. <br><br>**Type:** `boolean` <br> **Default value:** `true` |
| `defaultZoomLevel` | Fallback/default zoom value that is used if it is not explicitly set in a MapPostion. <br><br>**Type:** `int`<br>**Range:** `0 (hole world) - 20 (small building)`<br> **Default value:** `8` |
| `displayTime` | Set true, to display the time for each frame. <br><br>**Type:** `boolean` <br> **Default value:** `true` |
| `displayClockSymbol` | Set true, to display a clock symbol as time prefix. <br><br>**Type:** `boolean` <br> **Default value:** `true` |
| `displayTimeline` | Set true, to display a timeline. <br><br>**Type:** `boolean` <br> **Default value:** `true` |
| `displayOnlyOnRain` | Set true, to only show the map if supported weather modules broadcast a current rainy weather condition.<br>Supported weather modules are: [weather](https://github.com/MichMich/MagicMirror/tree/master/modules/default/weather) and [MMM-OpenWeatherForecast](https://github.com/jclarke0000/MMM-OpenWeatherForecast). <br><br>**Type:** `boolean` <br> **Default value:** `false` |
| `extraDelayLastFrameMs` | Add an extra delay to pause the animation on the latest frame (current weather situation).<br><br>**Type:** `int` <br> **Default value:** `2000` (time in milliseconds) |
| `extraDelayLastFrameMs` | Add an extra delay to pause the animation on the last frame (last available forecast weather situation).<br><br>**Type:** `int` <br> **Default value:** `1000` (time in milliseconds) |
| `extraDelayCurrentFrameMs` | Add an extra delay to pause the animation on the frame for the current weather situation.<br><br>**Type:** `int` <br> **Default value:** `3000` (time in milliseconds) |
| `markers` | Optional list of markers on the map.<br> See examples and Markers-Object documentation below for details. <br><br>**Type:** `array[Marker]` <br> **Default value:** `Sample set` |
| `mapPositions` | **Required:** List of zoom/center positions for the map.<br> See examples and MapPosition-Object documentation below for details. <br><br>**Type:** `array[MapPosition]` <br> **Default value:** `Sample set` |
| `mapHeight` | Height of the map. <br><br>**Type:** `string` (pixels) <br> **Default value:** `'420px'` |
Expand Down
88 changes: 73 additions & 15 deletions src/client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import Utils from "./Utils";
Module.register("MMM-RAIN-MAP", {
defaults: {
animationSpeedMs: 400,
colorizeTime: true,
defaultZoomLevel: 8,
displayTime: true,
displayClockSymbol: true,
displayTime: true,
displayTimeline: true,
displayOnlyOnRain: false,
extraDelayLastFrameMs: 2000,
extraDelayCurrentFrameMs: 2000,
markers: [
{ lat: 49.41, lng: 8.717, color: "red" },
{ lat: 48.856, lng: 2.35, color: "green" },
Expand All @@ -28,18 +31,20 @@ Module.register("MMM-RAIN-MAP", {
},

runtimeData: {
map: null,
timeframes: [],
radarLayers: [],
animationTimer: null,
animationPosition: 0,
animationTimer: null,
map: null,
mapPosition: 0,
numHistoryFrames: 0,
numForecastFrames: 0,
loopNumber: 1,
radarLayers: [],
timeDiv: null,
timeframes: [],
},

getStyles() {
return ["font-awesome.css", "https://unpkg.com/[email protected]/dist/leaflet.css", "MMM-RAIN-MAP.css"];
return ["font-awesome.css", "leaflet.css", "MMM-RAIN-MAP.css"];
},

getScripts() {
Expand All @@ -66,6 +71,21 @@ Module.register("MMM-RAIN-MAP", {
this.runtimeData.timeDiv = document.createElement("span");
this.runtimeData.timeDiv.classList.add("rain-map-time");
timeWrapperDiv.appendChild(this.runtimeData.timeDiv);

if (this.config.displayTimeline) {
const timelineWrapper = document.createElement("span");
timelineWrapper.classList.add("rain-map-timeline-wrapper");

this.runtimeData.sliderDiv = document.createElement("span");
this.runtimeData.sliderDiv.classList.add("rain-map-timeslider");
timelineWrapper.appendChild(this.runtimeData.sliderDiv);
this.runtimeData.timelineDiv = document.createElement("span");
this.runtimeData.timelineDiv.classList.add("rain-map-timeline");
timelineWrapper.appendChild(this.runtimeData.timelineDiv);

timeWrapperDiv.appendChild(timelineWrapper);
}

app.appendChild(timeWrapperDiv);
}

Expand Down Expand Up @@ -121,10 +141,12 @@ Module.register("MMM-RAIN-MAP", {

play() {
const self = this;
const extraDelay =
self.runtimeData.animationPosition === self.runtimeData.timeframes.length - 1
? this.config.extraDelayLastFrameMs
: 0;
let extraDelay = 0;
if (self.runtimeData.animationPosition === self.runtimeData.timeframes.length - 1) {
extraDelay = this.config.extraDelayLastFrameMs;
} else if (self.runtimeData.animationPosition === this.runtimeData.numHistoryFrames - 1) {
extraDelay = this.config.extraDelayCurrentFrameMs;
}

this.runtimeData.animationTimer = setTimeout(() => {
self.tick();
Expand Down Expand Up @@ -178,8 +200,6 @@ Module.register("MMM-RAIN-MAP", {
currentRadarLayer.setOpacity(0.001);
}

this.runtimeData.animationPosition = nextAnimationPosition;

// Manage time
if (this.config.displayTime) {
const time = moment(nextTimeframe.time * 1000);
Expand All @@ -188,15 +208,38 @@ Module.register("MMM-RAIN-MAP", {
}
const hourSymbol = this.config.timeFormat === 24 ? "HH" : "h";
this.runtimeData.timeDiv.innerHTML = `${time.format(hourSymbol + ":mm")}`;

if (this.config.colorizeTime) {
if (nextAnimationPosition < this.runtimeData.numHistoryFrames - 1) {
this.runtimeData.timeDiv.classList.remove("rain-map-forecast");
this.runtimeData.timeDiv.classList.remove("rain-map-now");
this.runtimeData.timeDiv.classList.add("rain-map-history");
} else if (nextAnimationPosition === this.runtimeData.numHistoryFrames - 1) {
this.runtimeData.timeDiv.classList.remove("rain-map-forecast");
this.runtimeData.timeDiv.classList.add("rain-map-now");
this.runtimeData.timeDiv.classList.remove("rain-map-history");
} else {
this.runtimeData.timeDiv.classList.add("rain-map-forecast");
this.runtimeData.timeDiv.classList.remove("rain-map-now");
this.runtimeData.timeDiv.classList.remove("rain-map-history");
}
}

if (this.config.displayTimeline) {
this.runtimeData.sliderDiv.style.left = `${this.runtimeData.percentPerFrame * nextAnimationPosition}%`;
}
}
this.runtimeData.animationPosition = nextAnimationPosition;
},

loadData() {
const self = this;
fetch("https://api.rainviewer.com/public/weather-maps.json").then(async (response) => {
if (response.ok) {
const results = await response.json();
self.runtimeData.timeframes = [... results.radar.past, ... results.radar.nowcast];
self.runtimeData.numHistoryFrames = results.radar?.past?.length || 0;
self.runtimeData.numForecastFrames = results.radar?.nowcast?.length || 0;
self.runtimeData.timeframes = [...results.radar.past, ...results.radar.nowcast];

// Clear old radar layers
self.runtimeData.map.eachLayer((layer) => {
Expand Down Expand Up @@ -225,6 +268,21 @@ Module.register("MMM-RAIN-MAP", {

self.runtimeData.animationPosition = 0;

// Prepare timeline
if (this.config.displayTimeline) {
try {
this.runtimeData.percentPerFrame =
100 / (self.runtimeData.numHistoryFrames + self.runtimeData.numForecastFrames);
const historyPart = (self.runtimeData.numHistoryFrames - 1) * this.runtimeData.percentPerFrame;
const forecastPart = self.runtimeData.numForecastFrames * this.runtimeData.percentPerFrame;
this.runtimeData.timelineDiv.style.background = `linear-gradient(to right, var(--color-history) 0% ${historyPart}%, var(--color-now) ${historyPart}% ${
historyPart + this.runtimeData.percentPerFrame
}%, var(--color-forecast) ${forecastPart}%)`;
} catch (err) {
console.warn("Error rendering the map timeline");
}
}

console.debug("Done processing latest RainViewer API request.");
} else {
console.error("Error fetching RainViewer timeframes", response.statusText);
Expand Down Expand Up @@ -262,12 +320,12 @@ Module.register("MMM-RAIN-MAP", {
];
if (currentCondition && rainConditions.findIndex((condition) => currentCondition.includes(condition)) >= 0) {
if (!this.runtimeData.animationTimer) {
this.show(300, {lockString: this.identifier});
this.show(300, { lockString: this.identifier });
this.play();
}
} else {
if (this.runtimeData.animationTimer) {
this.hide(300, {lockString: this.identifier});
this.hide(300, { lockString: this.identifier });
clearTimeout(this.runtimeData.animationTimer);
this.runtimeData.animationTimer = null;
}
Expand Down
10 changes: 0 additions & 10 deletions templates/MMM-RAIN-MAP.njk

This file was deleted.

0 comments on commit de213d1

Please sign in to comment.