From 5358f1b9f65f32c5ee93d5f383eee65696fa3774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Tue, 25 Mar 2025 09:33:25 +0100 Subject: [PATCH] WeatherDiagram: Temperature, Weekday-names, Legend --- src/main/angular/src/app/core/api.service.ts | 2 +- src/main/angular/src/app/value/Value.ts | 42 +++++++++--- .../weather/weather-diagram/WeatherHour.ts | 2 + .../weather-diagram.component.html | 15 ++++- .../weather-diagram.component.less | 66 +++++++++++++++++++ .../weather-diagram.component.ts | 37 ++++++++++- .../java/de/ph87/data/weather/WeatherDay.java | 21 ++++-- 7 files changed, 166 insertions(+), 19 deletions(-) diff --git a/src/main/angular/src/app/core/api.service.ts b/src/main/angular/src/app/core/api.service.ts index 49ff931..b6d864a 100644 --- a/src/main/angular/src/app/core/api.service.ts +++ b/src/main/angular/src/app/core/api.service.ts @@ -4,7 +4,7 @@ import {map, Subscription} from 'rxjs'; import {StompService} from '@stomp/ng2-stompjs'; import {FromJson, Next} from './types'; -const DEV_TO_PROD = true; +const DEV_TO_PROD = false; @Injectable({ providedIn: 'root' diff --git a/src/main/angular/src/app/value/Value.ts b/src/main/angular/src/app/value/Value.ts index c3f1bc6..7b54892 100644 --- a/src/main/angular/src/app/value/Value.ts +++ b/src/main/angular/src/app/value/Value.ts @@ -53,18 +53,44 @@ export class Value { return new Value(-this.value, this.unit, this.decimals, this.locale); } - plus(other: Value | undefined): Value | undefined { - if (!other) { - return undefined; - } - return new Value(this.value + other.value, this.unit, this.decimals, this.locale); + plus(other: Value | number | undefined | null): Value | undefined { + const v = this.extractValue(other); + return v === undefined ? undefined : new Value(this.value + v, this.unit, this.decimals, this.locale); } - minus(other: Value | undefined): Value | undefined { - if (!other) { + minus(other: Value | number | undefined | null): Value | undefined { + const v = this.extractValue(other); + return v === undefined ? undefined : new Value(this.value - v, this.unit, this.decimals, this.locale); + } + + gte(other: Value | number | undefined | null): boolean | undefined { + const v = this.extractValue(other); + return v === undefined ? undefined : this.value >= v; + } + + gt(other: Value | number | undefined | null): boolean | undefined { + const v = this.extractValue(other); + return v === undefined ? undefined : this.value > v; + } + + lte(other: Value | number | undefined | null): boolean | undefined { + const v = this.extractValue(other); + return v === undefined ? undefined : this.value <= v; + } + + lt(other: Value | number | undefined | null): boolean | undefined { + const v = this.extractValue(other); + return v === undefined ? undefined : this.value < v; + } + + extractValue(other: Value | number | undefined | null): number | undefined { + if (other === undefined || other === null) { return undefined; } - return new Value(this.value - other.value, this.unit, this.decimals, this.locale); + if (other instanceof Value && other.unit !== this.unit) { + throw new Error(`Unit mismatch: this=${JSON.stringify(this)}, other=${JSON.stringify(other)}`); + } + return typeof other === "number" ? other : other.value; } notNegative(): Value { diff --git a/src/main/angular/src/app/weather/weather-diagram/WeatherHour.ts b/src/main/angular/src/app/weather/weather-diagram/WeatherHour.ts index 874a12f..488f897 100644 --- a/src/main/angular/src/app/weather/weather-diagram/WeatherHour.ts +++ b/src/main/angular/src/app/weather/weather-diagram/WeatherHour.ts @@ -8,6 +8,7 @@ export class WeatherHour { readonly clouds: Value, readonly irradiation: Value, readonly precipitation: Value, + readonly temperature: Value, ) { // } @@ -18,6 +19,7 @@ export class WeatherHour { Value.fromJson2(json['clouds'], locale), Value.fromJson2(json['irradiation'], locale), Value.fromJson2(json['precipitation'], locale), + Value.fromJson2(json['temperature'], locale), ); } diff --git a/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.html b/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.html index 40ccb9a..832ab27 100644 --- a/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.html +++ b/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.html @@ -4,9 +4,22 @@
+
+ {{ dateFormat(hour.date | date:'E') }} +
-
+
+
+
+ ≥30°C + ≥20°C + ≥10°C + >0°C + ≤0°C +   + Niederschlag 100% = {{ PRECIPITATION_MAX_MM }}mm +
diff --git a/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.less b/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.less index a18ee55..e491a85 100644 --- a/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.less +++ b/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.less @@ -8,6 +8,7 @@ display: flex; align-items: flex-end; width: 100%; // any width will do (flex cares about real with) + overflow: visible; .bar { position: absolute; @@ -25,8 +26,73 @@ .precipitation { background-color: blue; + opacity: 0.75; + } + + .temperature { + border-top: 0.06em solid black; + opacity: 0.75; + } + + .temperatureGTE30 { + border-top-color: red; + } + + .temperatureGTE20 { + border-top-color: orange; + } + + .temperatureGTE10 { + border-top-color: yellow; + } + + .temperatureGT0 { + border-top-color: blue; + } + + .temperatureNegative { + border-top-color: white; + } + + .weekdayHolder { + overflow: visible; + opacity: 1; + height: 100%; } } } + +.legend { + display: flex; + width: 100%; + flex-direction: row; + justify-content: center; + + .line { + padding: 0 0.25em; + font-size: 50%; + } + + .temperatureGTE30 { + color: red; + } + + .temperatureGTE20 { + color: orange; + } + + .temperatureGTE10 { + color: yellow; + } + + .temperatureGT0 { + color: blue; + } + + .temperatureNegative { + color: white; + } + +} diff --git a/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.ts b/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.ts index fd742d0..e0dc786 100644 --- a/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.ts +++ b/src/main/angular/src/app/weather/weather-diagram/weather-diagram.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {NgForOf} from '@angular/common'; +import {DatePipe, NgClass, NgForOf, NgIf} from '@angular/common'; import {WeatherHour} from './WeatherHour'; import {WeatherService} from './weather.service'; import {WeatherDay} from './WeatherDay'; @@ -11,13 +11,18 @@ const DAY_COUNT = 7; @Component({ selector: 'app-weather-diagram', imports: [ - NgForOf + NgForOf, + NgIf, + DatePipe, + NgClass ], templateUrl: './weather-diagram.component.html', styleUrl: './weather-diagram.component.less' }) export class WeatherDiagramComponent implements OnInit { + protected readonly PRECIPITATION_MAX_MM = 15; + protected days: WeatherDay[] = []; protected hours: WeatherHour[] = []; @@ -44,7 +49,11 @@ export class WeatherDiagramComponent implements OnInit { } precipitation(hour: WeatherHour) { - return (hour.precipitation.percent(15)?.value || 0) + '%'; + return (hour.precipitation.percent(this.PRECIPITATION_MAX_MM)?.value || 0) + '%'; + } + + temperature(hour: WeatherHour) { + return (hour.temperature.plus(10)?.percent(50)?.value || 0) + '%'; } private updateHours() { @@ -74,4 +83,26 @@ export class WeatherDiagramComponent implements OnInit { } } + dateFormat(date: string | null) { + if (!date) { + return ""; + } + return date.substring(0, 2); + } + + temperatureClasses(hour: WeatherHour): {} { + const temperatureGTE30 = hour.temperature.gte(30); + const temperatureGTE20 = hour.temperature.gte(20); + const temperatureGTE10 = hour.temperature.gte(10); + const temperatureGT0 = hour.temperature.gt(0); + const temperatureNegative = hour.temperature.lte(0); + return { + "temperatureGTE30": temperatureGTE30, + "temperatureGTE20": temperatureGTE20 && !temperatureGTE30, + "temperatureGTE10": temperatureGTE10 && !temperatureGTE20, + "temperatureGT0": temperatureGT0 && !temperatureGTE10, + "temperatureNegative": temperatureNegative + }; + } + } diff --git a/src/main/java/de/ph87/data/weather/WeatherDay.java b/src/main/java/de/ph87/data/weather/WeatherDay.java index 74cf7bf..7b4653f 100644 --- a/src/main/java/de/ph87/data/weather/WeatherDay.java +++ b/src/main/java/de/ph87/data/weather/WeatherDay.java @@ -1,13 +1,18 @@ package de.ph87.data.weather; +import de.ph87.data.value.Unit; import de.ph87.data.value.Value; -import de.ph87.data.value.*; -import lombok.*; +import lombok.Data; +import lombok.NonNull; +import lombok.ToString; -import java.io.*; -import java.time.*; -import java.util.*; -import java.util.stream.*; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.stream.Collectors; @Data @ToString(includeFieldNames = false) @@ -67,11 +72,15 @@ public class WeatherDay { @NonNull public final Value precipitation; + @NonNull + public final Value temperature; + public Hour(@NonNull final BrightSkyDto.Weather dto) { date = dto.getTimestamp(); clouds = new Value(dto.getCloud_cover(), Unit.CLOUD_COVER_PERCENT); irradiation = new Value(dto.getSolar() * 1000, Unit.IRRADIATION_WH_M2); precipitation = new Value(dto.getPrecipitation(), Unit.PRECIPITATION_MM); + temperature = new Value((dto.getTemperature()), Unit.TEMPERATURE_C); } }