FarmMapsLib/projects/common/src/fm/services/weather.service.ts

123 lines
4.7 KiB
TypeScript
Raw Normal View History

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {GeoJSON} from 'ol/format';
import {map, switchMap} from 'rxjs/operators';
import {getCenter} from 'ol/extent';
2020-09-03 08:25:39 +00:00
import moment from 'moment';
2020-09-02 13:23:54 +00:00
import {AppConfig} from '../shared/app.config';
import {WeatherCurrentObservation} from '../models/weatherCurrentObservation';
2020-07-21 15:06:58 +00:00
import {IItem} from '../models/item';
2020-09-02 14:21:49 +00:00
import {HourlyWeatherData, WeatherData} from '../models/WeatherData';
@Injectable({
providedIn: 'root',
})
2020-09-02 13:28:12 +00:00
export class WeatherService {
private apiCurrentObservationUrl = 'currentobservation';
private apiObservation = 'observation';
private apiForecast = 'forecast';
private format: GeoJSON;
constructor(public httpClient: HttpClient, public appConfig: AppConfig) {
this.format = new GeoJSON();
}
public GetCurrentObservation(centroid: number[]): Observable<WeatherCurrentObservation> {
2020-02-04 16:38:04 +00:00
const endpoint = this.appConfig.getConfig('weatherApiEndPoint');
const apiKey = this.appConfig.getConfig('weatherApiKey');
const observationUrl = `${endpoint}${this.apiCurrentObservationUrl}/?c=${centroid[0]},${centroid[1]}&key=${apiKey}`;
return this.httpClient.get<WeatherCurrentObservation>(observationUrl);
}
2020-09-02 14:21:49 +00:00
public getWeatherRangeForItem(item: IItem, daysBefore = 10, daysAfter = 10): Observable<HourlyWeatherData[]> {
const geometry = this.format.readGeometry(item.geometry);
const centroid = getCenter(geometry.getExtent());
2020-09-02 13:23:54 +00:00
const startDate = new Date();
startDate.setDate(startDate.getDate() - daysBefore);
startDate.setHours(0, 0, 0, 0);
const startDateString = moment(startDate).local().format('YYYY-MM-DD[T]HH:mm:ss');
2020-09-02 13:23:54 +00:00
const endDate = new Date();
endDate.setDate(endDate.getDate() + daysAfter);
endDate.setHours(23, 0, 0, 0);
const endDateString = moment(endDate).local().format('YYYY-MM-DD[T]HH:mm:ss');
2020-09-02 13:23:54 +00:00
return this.getWeatherRange(centroid, startDateString, endDateString);
}
2020-09-02 14:21:49 +00:00
public getWeatherRange(centroid: number[], startDate: string, endDate: string): Observable<HourlyWeatherData[]> {
const endpoint = this.appConfig.getConfig('weatherApiEndPoint');
const apiKey = this.appConfig.getConfig('weatherApiKey');
// weather does not support UTC format, also remove Z
const sd = encodeURIComponent(startDate);
2020-09-02 13:23:54 +00:00
const edHistoricalDate = new Date();
edHistoricalDate.setHours(edHistoricalDate.getHours(), 0, 0, 0);
const edHistorical = moment(edHistoricalDate).local().format('YYYY-MM-DD[T]HH:mm:ss');
2020-09-02 13:23:54 +00:00
const historical = `${endpoint}${this.apiObservation}/?c=${centroid[0]},${centroid[1]}&sd=${sd}&ed=${edHistorical}&t=observation&interval=hourly&key=${apiKey}`;
const forecast = `${endpoint}${this.apiForecast}/?c=${centroid[0]},${centroid[1]}&interval=hourly&key=${apiKey}`;
return this.httpClient.get<any[]>(historical).pipe(
map(h => h.map(d => ({...d, rain: d.rainPastHour}))),
switchMap(h => {
return this.httpClient.get<any[]>(forecast)
.pipe(
2020-09-02 13:23:54 +00:00
map(f => [...h, ...f.filter(fd => fd.time <= endDate)] ));
})
);
}
2020-09-02 14:21:49 +00:00
public hourlyToDaily(hourlyWeatherData: HourlyWeatherData[]): WeatherData[] {
const days = this.groupBy(hourlyWeatherData, hi => hi.time.split('T')[0]);
return Object.entries(days)
.reduce ( (result, entry) => {
const wData = entry[1];
const sortData = wData.sort(d => new Date(d.time).getHours() - 12);
const closestToTwelveOClockData = sortData[0];
if (closestToTwelveOClockData.length === 0) {
return result;
}
const bestCardinal = closestToTwelveOClockData.windDirectionCardinal;
const bestWindDirection = closestToTwelveOClockData.windDirection;
const bestIconCode = closestToTwelveOClockData.iconCode;
return [
...result,
{
date: entry[0],
iconCode: bestIconCode,
minTemperature: Math.min(...wData.map(d => d.temperature)),
maxTemperature: Math.max(...wData.map(d => d.temperature)),
relHumidity: wData.reduce( (r, e) => r + e.relativeHumidity, 0) / wData.length,
precipitation: wData.reduce( (r, e) => r + e.rain, 0),
wSpeed: wData.reduce( (r, e) => r + e.windSpeed, 0) / wData.length,
wDir: bestWindDirection,
wCardinal: bestCardinal
}
];
}, []);
}
private groupBy(items: any[], selector: (item) => any): any[] {
return items.reduce(
(result, item) => {
const group = selector(item);
return ({
...result,
[group]: [
...(result[group] || []),
item,
],
2020-09-02 13:23:54 +00:00
}); },
{},
);
}
}