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'; import moment from 'moment'; import {AppConfig} from '../shared/app.config'; import {WeatherCurrentObservation} from '../models/weatherCurrentObservation'; import {IItem} from '../models/item'; import {HourlyWeatherData, WeatherData} from '../models/WeatherData'; @Injectable({ providedIn: 'root', }) 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 { 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(observationUrl); } public getWeatherRangeForItem(item: IItem, daysBefore = 10, daysAfter = 10): Observable { const geometry = this.format.readGeometry(item.geometry); const centroid = getCenter(geometry.getExtent()); 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'); 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'); return this.getWeatherRange(centroid, startDateString, endDateString); } public getDailyHistoricalObservations(centroid: number[], startDate: string, endDate: string): Observable { 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); const edHistoricalDate = new Date(); edHistoricalDate.setHours(edHistoricalDate.getHours(), 0, 0, 0); const edHistorical = moment(edHistoricalDate).local().format('YYYY-MM-DD[T]HH:mm:ss'); const historical = `${endpoint}${this.apiObservation}/?c=${centroid[0]},${centroid[1]}&sd=${sd}&ed=${edHistorical}&t=cleanedobservation&interval=daily&key=${apiKey}`; return this.httpClient.get(historical); } public getWeatherRange(centroid: number[], startDate: string, endDate: string): Observable { 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); const edHistoricalDate = new Date(); edHistoricalDate.setHours(edHistoricalDate.getHours(), 0, 0, 0); const edHistorical = moment(edHistoricalDate).local().format('YYYY-MM-DD[T]HH:mm:ss'); 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(historical).pipe( map(h => h.map(d => ({...d, rain: d.rainPastHour}))), switchMap(h => { return this.httpClient.get(forecast) .pipe( map(f => [...h, ...f.filter(fd => fd.time <= endDate)] )); }) ); } 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, ], }); }, {}, ); } }