Compare commits
3 Commits
85a749f199
...
5358f1b9f6
| Author | SHA1 | Date | |
|---|---|---|---|
| 5358f1b9f6 | |||
| d81034e6c4 | |||
| 49ce44eff9 |
@ -4,7 +4,7 @@ import {map, Subscription} from 'rxjs';
|
|||||||
import {StompService} from '@stomp/ng2-stompjs';
|
import {StompService} from '@stomp/ng2-stompjs';
|
||||||
import {FromJson, Next} from './types';
|
import {FromJson, Next} from './types';
|
||||||
|
|
||||||
const DEV_TO_PROD = true;
|
const DEV_TO_PROD = false;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|||||||
@ -28,6 +28,14 @@ export class Unit {
|
|||||||
|
|
||||||
static readonly ILLUMINANCE_LUX = new Unit('ILLUMINANCE_LUX', 'lux');
|
static readonly ILLUMINANCE_LUX = new Unit('ILLUMINANCE_LUX', 'lux');
|
||||||
|
|
||||||
|
static readonly BOOLEAN = new Unit('BOOLEAN', '');
|
||||||
|
|
||||||
|
static readonly DOOR_BOOLEAN = new Unit('DOOR_BOOLEAN', '');
|
||||||
|
|
||||||
|
static readonly WINDOW_BOOLEAN = new Unit('WINDOW_BOOLEAN', '');
|
||||||
|
|
||||||
|
static readonly LIGHT_BOOLEAN = new Unit('LIGHT_BOOLEAN', '');
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
readonly name: string,
|
readonly name: string,
|
||||||
readonly unit: string,
|
readonly unit: string,
|
||||||
|
|||||||
@ -53,18 +53,44 @@ export class Value {
|
|||||||
return new Value(-this.value, this.unit, this.decimals, this.locale);
|
return new Value(-this.value, this.unit, this.decimals, this.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
plus(other: Value | undefined): Value | undefined {
|
plus(other: Value | number | undefined | null): Value | undefined {
|
||||||
if (!other) {
|
const v = this.extractValue(other);
|
||||||
return undefined;
|
return v === undefined ? undefined : new Value(this.value + v, this.unit, this.decimals, this.locale);
|
||||||
}
|
|
||||||
return new Value(this.value + other.value, this.unit, this.decimals, this.locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
minus(other: Value | undefined): Value | undefined {
|
minus(other: Value | number | undefined | null): Value | undefined {
|
||||||
if (!other) {
|
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 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 {
|
notNegative(): Value {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export class WeatherHour {
|
|||||||
readonly clouds: Value,
|
readonly clouds: Value,
|
||||||
readonly irradiation: Value,
|
readonly irradiation: Value,
|
||||||
readonly precipitation: Value,
|
readonly precipitation: Value,
|
||||||
|
readonly temperature: Value,
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
@ -18,6 +19,7 @@ export class WeatherHour {
|
|||||||
Value.fromJson2(json['clouds'], locale),
|
Value.fromJson2(json['clouds'], locale),
|
||||||
Value.fromJson2(json['irradiation'], locale),
|
Value.fromJson2(json['irradiation'], locale),
|
||||||
Value.fromJson2(json['precipitation'], locale),
|
Value.fromJson2(json['precipitation'], locale),
|
||||||
|
Value.fromJson2(json['temperature'], locale),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="day">
|
<div class="day">
|
||||||
<div class="hour" *ngFor="let hour of hours">
|
<div class="hour" *ngFor="let hour of hours">
|
||||||
<div class="bar clouds" [style.height]="clouds(hour)"></div>
|
<div class="bar weekdayHolder" *ngIf="hour.date.getHours() === 8">
|
||||||
<div class="bar precipitation" [style.height]="precipitation(hour)"></div>
|
{{ dateFormat(hour.date | date:'E') }}
|
||||||
<div class="bar irradiation" [style.height]="irradiation(hour)"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bar clouds" [style.height]="clouds(hour)"></div>
|
||||||
|
<div class="bar irradiation" [style.height]="irradiation(hour)"></div>
|
||||||
|
<div class="bar precipitation" [style.height]="precipitation(hour)"></div>
|
||||||
|
<div class="bar temperature" [style.height]="temperature(hour)" [ngClass]="temperatureClasses(hour)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="legend">
|
||||||
|
<span class="line temperatureGTE30">≥30°C</span>
|
||||||
|
<span class="line temperatureGTE20">≥20°C</span>
|
||||||
|
<span class="line temperatureGTE10">≥10°C</span>
|
||||||
|
<span class="line temperatureGT0">>0°C</span>
|
||||||
|
<span class="line temperatureNegative">≤0°C</span>
|
||||||
|
<span class="line"> </span>
|
||||||
|
<span class="line">Niederschlag 100% = {{ PRECIPITATION_MAX_MM }}mm</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
width: 100%; // any width will do (flex cares about real with)
|
width: 100%; // any width will do (flex cares about real with)
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
.bar {
|
.bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -25,8 +26,73 @@
|
|||||||
|
|
||||||
.precipitation {
|
.precipitation {
|
||||||
background-color: blue;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {NgForOf} from '@angular/common';
|
import {DatePipe, NgClass, NgForOf, NgIf} from '@angular/common';
|
||||||
import {WeatherHour} from './WeatherHour';
|
import {WeatherHour} from './WeatherHour';
|
||||||
import {WeatherService} from './weather.service';
|
import {WeatherService} from './weather.service';
|
||||||
import {WeatherDay} from './WeatherDay';
|
import {WeatherDay} from './WeatherDay';
|
||||||
@ -11,13 +11,18 @@ const DAY_COUNT = 7;
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-weather-diagram',
|
selector: 'app-weather-diagram',
|
||||||
imports: [
|
imports: [
|
||||||
NgForOf
|
NgForOf,
|
||||||
|
NgIf,
|
||||||
|
DatePipe,
|
||||||
|
NgClass
|
||||||
],
|
],
|
||||||
templateUrl: './weather-diagram.component.html',
|
templateUrl: './weather-diagram.component.html',
|
||||||
styleUrl: './weather-diagram.component.less'
|
styleUrl: './weather-diagram.component.less'
|
||||||
})
|
})
|
||||||
export class WeatherDiagramComponent implements OnInit {
|
export class WeatherDiagramComponent implements OnInit {
|
||||||
|
|
||||||
|
protected readonly PRECIPITATION_MAX_MM = 15;
|
||||||
|
|
||||||
protected days: WeatherDay[] = [];
|
protected days: WeatherDay[] = [];
|
||||||
|
|
||||||
protected hours: WeatherHour[] = [];
|
protected hours: WeatherHour[] = [];
|
||||||
@ -44,7 +49,11 @@ export class WeatherDiagramComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
precipitation(hour: WeatherHour) {
|
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() {
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,11 @@ public enum Unit {
|
|||||||
IRRADIATION_KWH_M2("kWh/m²", 1000, IRRADIATION_WH_M2),
|
IRRADIATION_KWH_M2("kWh/m²", 1000, IRRADIATION_WH_M2),
|
||||||
|
|
||||||
PRECIPITATION_MM("mm"),
|
PRECIPITATION_MM("mm"),
|
||||||
|
|
||||||
|
BOOLEAN(""),
|
||||||
|
DOOR_BOOLEAN("", 1, BOOLEAN),
|
||||||
|
WINDOW_BOOLEAN("", 1, BOOLEAN),
|
||||||
|
LIGHT_BOOLEAN("", 1, BOOLEAN),
|
||||||
;
|
;
|
||||||
|
|
||||||
public final String unit;
|
public final String unit;
|
||||||
|
|||||||
@ -60,6 +60,18 @@ public class BrightSkyDto {
|
|||||||
this.timestamp = timestamp.withZoneSameInstant(TimeZone.getDefault().toZoneId());
|
this.timestamp = timestamp.withZoneSameInstant(TimeZone.getDefault().toZoneId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getSolar() {
|
||||||
|
return solar == null ? 0.0 : solar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCloud_cover() {
|
||||||
|
return cloud_cover == null ? 0.0 : cloud_cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPrecipitation() {
|
||||||
|
return precipitation == null ? 0.0 : precipitation;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
package de.ph87.data.weather;
|
package de.ph87.data.weather;
|
||||||
|
|
||||||
|
import de.ph87.data.value.Unit;
|
||||||
import de.ph87.data.value.Value;
|
import de.ph87.data.value.Value;
|
||||||
import de.ph87.data.value.*;
|
import lombok.Data;
|
||||||
import lombok.*;
|
import lombok.NonNull;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
import java.time.*;
|
import java.time.LocalDate;
|
||||||
import java.util.*;
|
import java.time.LocalTime;
|
||||||
import java.util.stream.*;
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ToString(includeFieldNames = false)
|
@ToString(includeFieldNames = false)
|
||||||
@ -67,11 +72,15 @@ public class WeatherDay {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public final Value precipitation;
|
public final Value precipitation;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public final Value temperature;
|
||||||
|
|
||||||
public Hour(@NonNull final BrightSkyDto.Weather dto) {
|
public Hour(@NonNull final BrightSkyDto.Weather dto) {
|
||||||
date = dto.getTimestamp();
|
date = dto.getTimestamp();
|
||||||
clouds = new Value(dto.getCloud_cover(), Unit.CLOUD_COVER_PERCENT);
|
clouds = new Value(dto.getCloud_cover(), Unit.CLOUD_COVER_PERCENT);
|
||||||
irradiation = new Value(dto.getSolar() * 1000, Unit.IRRADIATION_WH_M2);
|
irradiation = new Value(dto.getSolar() * 1000, Unit.IRRADIATION_WH_M2);
|
||||||
precipitation = new Value(dto.getPrecipitation(), Unit.PRECIPITATION_MM);
|
precipitation = new Value(dto.getPrecipitation(), Unit.PRECIPITATION_MM);
|
||||||
|
temperature = new Value((dto.getTemperature()), Unit.TEMPERATURE_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user