raain-app 1.6.21 → 1.6.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/cumulative-selector/cumulative-selector.component.d.ts +45 -0
- package/esm2020/cumulative-selector/cumulative-selector.component.mjs +199 -0
- package/esm2020/index.mjs +2 -1
- package/esm2020/profile.service.mjs +55 -1
- package/esm2020/raain-compare-stack/raain-compare-stack.component.mjs +3 -3
- package/esm2020/raain-details/raain-details.component.mjs +73 -7
- package/esm2020/shared.module.mjs +8 -3
- package/esm2020/tools/CompareManager.mjs +31 -8
- package/esm2020/tools/RefreshManager.mjs +2 -2
- package/fesm2015/raain-app.mjs +1622 -1267
- package/fesm2015/raain-app.mjs.map +1 -1
- package/fesm2020/raain-app.mjs +1515 -1175
- package/fesm2020/raain-app.mjs.map +1 -1
- package/index.d.ts +1 -0
- package/package.json +2 -2
- package/profile.service.d.ts +14 -1
- package/raain-details/raain-details.component.d.ts +7 -1
- package/shared.module.d.ts +6 -5
- package/tools/CompareManager.d.ts +8 -2
package/fesm2015/raain-app.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, EventEmitter, Component, Input, Output, ViewChild, HostListener, ChangeDetectionStrategy, Pipe, Directive, NgModule, Optional, SkipSelf } from '@angular/core';
|
|
3
|
-
import { CartesianTools, GaugeNode, SpeedMatrixContainer, RadarNode, CartesianMeasureValue,
|
|
3
|
+
import { CartesianTools, GaugeNode, SpeedMatrixContainer, RadarNode, CartesianMeasureValue, Link, EventNode, BuildQueryString, TeamNode, RainNode, RainComputationMap, RainComputationQuality, GaugeMeasure } from 'raain-model';
|
|
4
4
|
import { RaainDivIcon, ElementsFactory, MapLatLng, ChartScaleColors, ScaleElementInput, CompareElementInput, ConfigurationElementInput, Tools, DateRange, DateStatusElementInput, DynamicDateStatusElementInput, SpeedMatrixElementInput, EarthMapElementInput, TimeframeContainers, CartesianMapValue, PolarMapValue } from 'raain-ui';
|
|
5
5
|
import * as i2$1 from 'fidj-angular';
|
|
6
6
|
import { LocalStorage, LoggerLevelEnum } from 'fidj-angular';
|
|
@@ -1347,10 +1347,10 @@ class RaainCompareStackComponent {
|
|
|
1347
1347
|
}
|
|
1348
1348
|
}
|
|
1349
1349
|
RaainCompareStackComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RaainCompareStackComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
|
|
1350
|
-
RaainCompareStackComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: RaainCompareStackComponent, selector: "raain-compare-stack", inputs: { compareManager: "compareManager", currentHeight: "currentHeight", cumulative: "cumulative" }, outputs: { selectedPoint: "selectedPoint" }, ngImport: i0, template: "<div *ngIf=\"cumulative\">\n <ion-col size-md=\"6\" size-xs=\"12\">\n <ion-card class=\"card-compare\">\n <ion-card-header>Cumulative\n [{{ compareManager.compareDates[0] | date:'yyyy-MM-dd HH:mm' }}\n - {{ compareManager.compareDates[compareManager.compareDates.length - 1] | date:'yyyy-MM-dd HH:mm' }}
|
|
1350
|
+
RaainCompareStackComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: RaainCompareStackComponent, selector: "raain-compare-stack", inputs: { compareManager: "compareManager", currentHeight: "currentHeight", cumulative: "cumulative" }, outputs: { selectedPoint: "selectedPoint" }, ngImport: i0, template: "<div *ngIf=\"cumulative\">\n <ion-col size-md=\"6\" size-xs=\"12\">\n <ion-card class=\"card-compare\">\n <ion-card-header>Cumulative\n [{{ compareManager.compareDates[0] | date:'yyyy-MM-dd HH:mm' }}\n - {{ compareManager.compareDates[compareManager.compareDates.length - 1] | date:'yyyy-MM-dd HH:mm' }}[\n </ion-card-header>\n <ion-card-content *ngIf=\"compareManager.globalComparePoints?.length\">\n <raain-compare\n (selectedPoint)=\"onClick($event)\"\n [compareIndex]=\"0\"\n [currentHeight]=\"500\"\n [pointMax]=\"compareManager.globalComparePointsMax\"\n [points]=\"compareManager.globalComparePoints\"></raain-compare>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-button (click)=\"exportCumulativeToCsv()\" *ngIf=\"compareManager.globalComparePoints?.length\" fill=\"outline\"\n size=\"small\">\n <ion-icon name=\"download-outline\" slot=\"start\"></ion-icon>\n Export CSV\n </ion-button>\n</div>\n\n<div *ngIf=\"!cumulative\">\n <ion-col *ngFor=\"let compare of compareManager?.uiCompares; index as compareIndex\" size-md=\"6\" size-xs=\"12\">\n <ion-card class=\"card-compare\">\n <ion-card-header>Gauges filled {{ compare.name }} {{ compare.remarks }}\n </ion-card-header>\n <ion-card-content *ngIf=\"compare.comparePoints?.length\">\n <raain-compare\n (selectedPoint)=\"onClick($event)\"\n [compareIndex]=\"compareIndex\"\n [currentHeight]=\"500\"\n [pointMax]=\"compare.comparePointsMax\"\n [points]=\"compare.comparePoints\"></raain-compare>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-button (click)=\"exportGranularToCsv()\" *ngIf=\"compareManager.uiCompares?.length\" fill=\"outline\" size=\"small\">\n <ion-icon name=\"download-outline\" slot=\"start\"></ion-icon>\n Export CSV\n </ion-button>\n</div>\n", styles: ["#card-matrix{width:170px}.card-compare{min-width:300px}ion-card-header{padding:0 0 0 10px}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i4.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i4.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i4.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i4.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i4.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: RaainCompareComponent, selector: "raain-compare", inputs: ["points", "pointMax", "remarks", "compareIndex", "currentHeight", "currentWidth"], outputs: ["selectedPoint"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }] });
|
|
1351
1351
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RaainCompareStackComponent, decorators: [{
|
|
1352
1352
|
type: Component,
|
|
1353
|
-
args: [{ selector: 'raain-compare-stack', template: "<div *ngIf=\"cumulative\">\n <ion-col size-md=\"6\" size-xs=\"12\">\n <ion-card class=\"card-compare\">\n <ion-card-header>Cumulative\n [{{ compareManager.compareDates[0] | date:'yyyy-MM-dd HH:mm' }}\n - {{ compareManager.compareDates[compareManager.compareDates.length - 1] | date:'yyyy-MM-dd HH:mm' }}
|
|
1353
|
+
args: [{ selector: 'raain-compare-stack', template: "<div *ngIf=\"cumulative\">\n <ion-col size-md=\"6\" size-xs=\"12\">\n <ion-card class=\"card-compare\">\n <ion-card-header>Cumulative\n [{{ compareManager.compareDates[0] | date:'yyyy-MM-dd HH:mm' }}\n - {{ compareManager.compareDates[compareManager.compareDates.length - 1] | date:'yyyy-MM-dd HH:mm' }}[\n </ion-card-header>\n <ion-card-content *ngIf=\"compareManager.globalComparePoints?.length\">\n <raain-compare\n (selectedPoint)=\"onClick($event)\"\n [compareIndex]=\"0\"\n [currentHeight]=\"500\"\n [pointMax]=\"compareManager.globalComparePointsMax\"\n [points]=\"compareManager.globalComparePoints\"></raain-compare>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-button (click)=\"exportCumulativeToCsv()\" *ngIf=\"compareManager.globalComparePoints?.length\" fill=\"outline\"\n size=\"small\">\n <ion-icon name=\"download-outline\" slot=\"start\"></ion-icon>\n Export CSV\n </ion-button>\n</div>\n\n<div *ngIf=\"!cumulative\">\n <ion-col *ngFor=\"let compare of compareManager?.uiCompares; index as compareIndex\" size-md=\"6\" size-xs=\"12\">\n <ion-card class=\"card-compare\">\n <ion-card-header>Gauges filled {{ compare.name }} {{ compare.remarks }}\n </ion-card-header>\n <ion-card-content *ngIf=\"compare.comparePoints?.length\">\n <raain-compare\n (selectedPoint)=\"onClick($event)\"\n [compareIndex]=\"compareIndex\"\n [currentHeight]=\"500\"\n [pointMax]=\"compare.comparePointsMax\"\n [points]=\"compare.comparePoints\"></raain-compare>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-button (click)=\"exportGranularToCsv()\" *ngIf=\"compareManager.uiCompares?.length\" fill=\"outline\" size=\"small\">\n <ion-icon name=\"download-outline\" slot=\"start\"></ion-icon>\n Export CSV\n </ion-button>\n</div>\n", styles: ["#card-matrix{width:170px}.card-compare{min-width:300px}ion-card-header{padding:0 0 0 10px}\n"] }]
|
|
1354
1354
|
}], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { compareManager: [{
|
|
1355
1355
|
type: Input
|
|
1356
1356
|
}], currentHeight: [{
|
|
@@ -1512,11 +1512,11 @@ class CompareManager {
|
|
|
1512
1512
|
this.onChanges();
|
|
1513
1513
|
});
|
|
1514
1514
|
}
|
|
1515
|
-
refreshGlobalCompareQuality(targetsOrdered, withCompareDuplicate, cumulativeHours = 0) {
|
|
1515
|
+
refreshGlobalCompareQuality(targetsOrdered, period, withCompareDuplicate, cumulativeHours = 0) {
|
|
1516
1516
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1517
1517
|
try {
|
|
1518
1518
|
yield this.fetchRainComputationQualities(targetsOrdered, cumulativeHours);
|
|
1519
|
-
yield this.buildComparesTimeline(targetsOrdered, withCompareDuplicate);
|
|
1519
|
+
yield this.buildComparesTimeline(targetsOrdered, period, withCompareDuplicate);
|
|
1520
1520
|
if (!this.buildCompares.compareCumulative) {
|
|
1521
1521
|
// throw Error('needs cumulative compare');
|
|
1522
1522
|
return;
|
|
@@ -1534,6 +1534,9 @@ class CompareManager {
|
|
|
1534
1534
|
setGaugesInMap() {
|
|
1535
1535
|
var _a;
|
|
1536
1536
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1537
|
+
// Get all gauge IDs linked to this rainNode
|
|
1538
|
+
const rainNodeGaugeIds = this.rainNode.getLinks(GaugeNode.TYPE).map((l) => l.getId());
|
|
1539
|
+
// Fetch gauges from API (may be filtered/limited)
|
|
1537
1540
|
let gaugesToFilter = yield this.profileService.getGauges((_a = this.rainNode) === null || _a === void 0 ? void 0 : _a.id, this.rainNode.getCenter());
|
|
1538
1541
|
gaugesToFilter = gaugesToFilter
|
|
1539
1542
|
.sort((a, b) => {
|
|
@@ -1541,9 +1544,29 @@ class CompareManager {
|
|
|
1541
1544
|
b.approxDistanceFrom(this.rainNode.getCenter()));
|
|
1542
1545
|
})
|
|
1543
1546
|
.filter((v, index) => index < 200);
|
|
1544
|
-
|
|
1545
|
-
const
|
|
1546
|
-
|
|
1547
|
+
// Build a map of gauges from API response
|
|
1548
|
+
const gaugesFromApi = new Map();
|
|
1549
|
+
for (const gauge of gaugesToFilter) {
|
|
1550
|
+
gaugesFromApi.set(gauge.id, gauge);
|
|
1551
|
+
}
|
|
1552
|
+
// Fetch missing gauges individually (those linked but not in API response)
|
|
1553
|
+
const missingGaugeIds = rainNodeGaugeIds.filter((id) => !gaugesFromApi.has(id));
|
|
1554
|
+
for (const gaugeId of missingGaugeIds) {
|
|
1555
|
+
try {
|
|
1556
|
+
const gauge = yield this.profileService.getGauge(gaugeId);
|
|
1557
|
+
if (gauge) {
|
|
1558
|
+
gaugesFromApi.set(gauge.id, gauge);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
catch (e) {
|
|
1562
|
+
console.warn(`Failed to fetch gauge ${gaugeId}:`, e);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
// Filter to only gauges linked to this rainNode
|
|
1566
|
+
const visibleGauges = rainNodeGaugeIds
|
|
1567
|
+
.map((id) => gaugesFromApi.get(id))
|
|
1568
|
+
.filter((g) => !!g);
|
|
1569
|
+
console.log('visibleGauges:', visibleGauges.length, '/', rainNodeGaugeIds.length);
|
|
1547
1570
|
const gaugesLatLng = [];
|
|
1548
1571
|
for (const gauge of visibleGauges) {
|
|
1549
1572
|
gaugesLatLng.push(new MapLatLng(gauge.latitude, gauge.longitude, undefined, gauge.id, gauge.name));
|
|
@@ -1657,14 +1680,14 @@ class CompareManager {
|
|
|
1657
1680
|
}
|
|
1658
1681
|
return result.sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
1659
1682
|
}
|
|
1660
|
-
buildComparesTimeline(targetsOrdered, withCompareDuplicate) {
|
|
1683
|
+
buildComparesTimeline(targetsOrdered, period, withCompareDuplicate) {
|
|
1661
1684
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1662
1685
|
const compareDates = targetsOrdered.map((t) => t.date);
|
|
1663
1686
|
const qualities = compareDates
|
|
1664
1687
|
.filter((d) => !!d)
|
|
1665
1688
|
.map((d) => this.getRainComputationQuality(d))
|
|
1666
1689
|
.filter((rcq) => !!rcq);
|
|
1667
|
-
this.compareDates = compareDates; // compareDates.slice(1, -1);
|
|
1690
|
+
this.compareDates = (compareDates === null || compareDates === void 0 ? void 0 : compareDates.length) > 1 ? compareDates : [period.begin, period.end]; // compareDates.slice(1, -1);
|
|
1668
1691
|
this.buildCompares = SpeedMatrixContainer.BuildCompares(qualities, !withCompareDuplicate);
|
|
1669
1692
|
return this.buildCompares;
|
|
1670
1693
|
});
|
|
@@ -2044,7 +2067,7 @@ class RefreshManager {
|
|
|
2044
2067
|
const cumulativeHours = this.cumulative
|
|
2045
2068
|
? (this.period.end.getTime() - this.period.begin.getTime()) / (60 * 60 * 1000)
|
|
2046
2069
|
: 0;
|
|
2047
|
-
yield this.compareManager.refreshGlobalCompareQuality(targets, !this.removeDuplicate, cumulativeHours);
|
|
2070
|
+
yield this.compareManager.refreshGlobalCompareQuality(targets, this.period, !this.removeDuplicate, cumulativeHours);
|
|
2048
2071
|
}
|
|
2049
2072
|
catch (e) {
|
|
2050
2073
|
console.error('refreshGlobalCompareReport', e);
|
|
@@ -2149,1514 +2172,1842 @@ function mapDateRangeToString(range) {
|
|
|
2149
2172
|
}
|
|
2150
2173
|
}
|
|
2151
2174
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
this.
|
|
2156
|
-
this.
|
|
2157
|
-
this.
|
|
2158
|
-
this.
|
|
2159
|
-
this.showPixelMarkers = false;
|
|
2160
|
-
this.qualityIndicators = [];
|
|
2161
|
-
this.qualityIndicatorsLoading = false;
|
|
2162
|
-
// Cached computed values (to avoid method calls in templates)
|
|
2163
|
-
this.percentOfComputations = 0;
|
|
2164
|
-
this.percentOfImages = 0;
|
|
2165
|
-
this.truncatedError = '';
|
|
2166
|
-
this.cumulativeDurationInMinutes = 10;
|
|
2167
|
-
this.DateRange = DateRange;
|
|
2168
|
-
// Wrapper function that preserves the async nature of fetchData
|
|
2169
|
-
this.fetchDataWrapper = (focusDate, focusRange) => __awaiter(this, void 0, void 0, function* () {
|
|
2170
|
-
return yield this.fetchData(focusDate, focusRange);
|
|
2171
|
-
});
|
|
2175
|
+
class FidjStorageNode {
|
|
2176
|
+
constructor() {
|
|
2177
|
+
this.radars = [];
|
|
2178
|
+
this.rains = [];
|
|
2179
|
+
this.gauges = [];
|
|
2180
|
+
this.events = [];
|
|
2181
|
+
this.infos = {};
|
|
2172
2182
|
}
|
|
2173
|
-
static
|
|
2174
|
-
|
|
2175
|
-
if (date) {
|
|
2176
|
-
d = new Date(date);
|
|
2177
|
-
const userTimezoneOffset = d.getTimezoneOffset() * 60000;
|
|
2178
|
-
d = new Date(d.getTime() - userTimezoneOffset);
|
|
2179
|
-
}
|
|
2180
|
-
const exampleFormattedDate = '2017-06-01T08:30';
|
|
2181
|
-
return d.toISOString().substring(0, exampleFormattedDate.length);
|
|
2183
|
+
static getEmptyNode() {
|
|
2184
|
+
return new FidjStorageNode();
|
|
2182
2185
|
}
|
|
2183
|
-
static
|
|
2184
|
-
const
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2186
|
+
static getDemoNode() {
|
|
2187
|
+
const demoNode = new FidjStorageNode();
|
|
2188
|
+
const link = new Link('rain', 'https://demo/api/rains/2');
|
|
2189
|
+
/*
|
|
2190
|
+
demoNode.radars = [
|
|
2191
|
+
new RadarNode({
|
|
2192
|
+
id: '5efd04569cb1f4161bd69dc7',
|
|
2193
|
+
name: 'demo radar A',
|
|
2194
|
+
links: [link],
|
|
2195
|
+
latitude: 48.774569,
|
|
2196
|
+
longitude: 2.008407
|
|
2197
|
+
}),
|
|
2198
|
+
new RadarNode({
|
|
2199
|
+
id: 'dr2',
|
|
2200
|
+
name: 'demo radar B',
|
|
2201
|
+
links: [link],
|
|
2202
|
+
latitude: 0.11,
|
|
2203
|
+
longitude: -0.753
|
|
2204
|
+
}),
|
|
2205
|
+
new RadarNode({
|
|
2206
|
+
id: 'dr3',
|
|
2207
|
+
name: 'demo radar C',
|
|
2208
|
+
latitude: 0.13,
|
|
2209
|
+
longitude: -0.753,
|
|
2210
|
+
links: []
|
|
2211
|
+
}),
|
|
2212
|
+
new RadarNode({
|
|
2213
|
+
id: 'dr4',
|
|
2214
|
+
name: 'demo radar D',
|
|
2215
|
+
latitude: 0.14,
|
|
2216
|
+
longitude: -0.74,
|
|
2217
|
+
links: []
|
|
2218
|
+
})];
|
|
2219
|
+
demoNode.rains = [
|
|
2220
|
+
new RainNode({
|
|
2221
|
+
id: '5efd04569cb1f4161bd69dc8',
|
|
2222
|
+
name: 'Demo rain zone A',
|
|
2223
|
+
links: [new Link('radar', 'https://demo/api/radars/5efcfe619cb1f4161bd69dc3')],
|
|
2224
|
+
status: 0,
|
|
2225
|
+
quality: 75,
|
|
2226
|
+
latitude: 48.774569,
|
|
2227
|
+
longitude: 2.008407
|
|
2228
|
+
}),
|
|
2229
|
+
new RainNode({
|
|
2230
|
+
id: 'dz2',
|
|
2231
|
+
name: 'Demo rain zone B',
|
|
2232
|
+
radars: [demoNode.radars[0], demoNode.radars[1]],
|
|
2233
|
+
status: 1,
|
|
2234
|
+
quality: 50,
|
|
2235
|
+
latitude: 48.774569,
|
|
2236
|
+
longitude: 2.008407
|
|
2237
|
+
}),
|
|
2238
|
+
new RainNode({
|
|
2239
|
+
id: 'dz3',
|
|
2240
|
+
name: 'Demo rain zone C',
|
|
2241
|
+
radars: [demoNode.radars[0], demoNode.radars[1]],
|
|
2242
|
+
status: 2,
|
|
2243
|
+
quality: 75,
|
|
2244
|
+
latitude: 48.774569,
|
|
2245
|
+
longitude: 2.008407
|
|
2246
|
+
}),
|
|
2247
|
+
new RainNode({
|
|
2248
|
+
id: 'dz4',
|
|
2249
|
+
name: 'Demo rain zone D',
|
|
2250
|
+
radars: [demoNode.radars[0], demoNode.radars[1]],
|
|
2251
|
+
status: 3,
|
|
2252
|
+
quality: 95,
|
|
2253
|
+
latitude: 48.774569,
|
|
2254
|
+
longitude: 2.008407
|
|
2255
|
+
})];
|
|
2256
|
+
|
|
2257
|
+
demoNode.gauges = [
|
|
2258
|
+
new GaugeNode({
|
|
2259
|
+
id: 'g1',
|
|
2260
|
+
name: 'Gauge A',
|
|
2261
|
+
latitude: 48.7748,
|
|
2262
|
+
longitude: 2.28407,
|
|
2263
|
+
}),
|
|
2264
|
+
new GaugeNode({
|
|
2265
|
+
id: 'g2',
|
|
2266
|
+
name: 'Gauge B',
|
|
2267
|
+
latitude: 48.874569,
|
|
2268
|
+
longitude: 2.108407,
|
|
2269
|
+
})];
|
|
2270
|
+
demoNode.events = [{
|
|
2271
|
+
id: 'e2',
|
|
2272
|
+
title: 'Need support ?',
|
|
2273
|
+
status: 0,
|
|
2274
|
+
red: false,
|
|
2275
|
+
description: 'This area is dedicated to support you and your team. Support is made on Radar or Rain quality, ' +
|
|
2276
|
+
'or any feedback we can have about your production system. Our goal : improving your data.',
|
|
2277
|
+
created: new Date(),
|
|
2278
|
+
modified: new Date()
|
|
2279
|
+
}];
|
|
2280
|
+
demoNode.team = {
|
|
2281
|
+
id: 'p1',
|
|
2282
|
+
email: 'demo@demo.com',
|
|
2283
|
+
name: 'demo guy',
|
|
2284
|
+
description: 'the demo guy'
|
|
2285
|
+
};
|
|
2286
|
+
|
|
2287
|
+
*/
|
|
2288
|
+
return demoNode;
|
|
2191
2289
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2290
|
+
}
|
|
2291
|
+
class FidjStorageResult {
|
|
2292
|
+
}
|
|
2293
|
+
class FidjStorage {
|
|
2294
|
+
constructor(storage) {
|
|
2295
|
+
this.storage = storage;
|
|
2296
|
+
this.node = FidjStorageNode.getEmptyNode();
|
|
2297
|
+
this.fidjMetaResult = { data: new FidjStorageNode() };
|
|
2197
2298
|
}
|
|
2198
|
-
|
|
2199
|
-
var _a;
|
|
2299
|
+
storeData(fidjService, data) {
|
|
2200
2300
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2201
|
-
this.
|
|
2202
|
-
this.
|
|
2203
|
-
this.
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
const result = yield this.profileService.getIndicators(this.rainNode.id);
|
|
2207
|
-
this.qualityIndicators = result.indicators;
|
|
2301
|
+
this.node = JSON.parse(JSON.stringify(data));
|
|
2302
|
+
this.fidjMetaResult.data = this.node;
|
|
2303
|
+
if (this.isDemoMode) {
|
|
2304
|
+
this.storage.set('fidjMetaResult', JSON.stringify(this.fidjMetaResult));
|
|
2305
|
+
return;
|
|
2208
2306
|
}
|
|
2209
|
-
this.
|
|
2210
|
-
this.cdr.markForCheck();
|
|
2307
|
+
yield fidjService.put(this.fidjMetaResult);
|
|
2211
2308
|
});
|
|
2212
2309
|
}
|
|
2213
|
-
|
|
2214
|
-
this.showQualityModal = false;
|
|
2215
|
-
}
|
|
2216
|
-
formatDate(dateStr) {
|
|
2217
|
-
const date = new Date(dateStr);
|
|
2218
|
-
return (date.toLocaleDateString() +
|
|
2219
|
-
' ' +
|
|
2220
|
-
date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
|
|
2221
|
-
}
|
|
2222
|
-
fetchData(focusDate, focusRange) {
|
|
2310
|
+
getRefreshedNodeCopy(fidjService) {
|
|
2223
2311
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
style: 'bar',
|
|
2232
|
-
values,
|
|
2233
|
-
},
|
|
2234
|
-
{
|
|
2235
|
-
label: '% Images',
|
|
2236
|
-
style: 'bar',
|
|
2237
|
-
values,
|
|
2238
|
-
},
|
|
2239
|
-
// {
|
|
2240
|
-
// label: '% Quality',
|
|
2241
|
-
// style: 'line',
|
|
2242
|
-
// values,
|
|
2243
|
-
// },
|
|
2244
|
-
];
|
|
2245
|
-
const range = mapDateRangeToString(focusRange);
|
|
2246
|
-
let data = fakeData;
|
|
2247
|
-
if (!this.rainNode) {
|
|
2248
|
-
return data;
|
|
2312
|
+
if (this.isDemoMode) {
|
|
2313
|
+
const fidjMetaResult = this.storage.get('fidjMetaResult');
|
|
2314
|
+
if (fidjMetaResult) {
|
|
2315
|
+
this.fidjMetaResult = JSON.parse(fidjMetaResult);
|
|
2316
|
+
this.node = this.fidjMetaResult.data;
|
|
2317
|
+
}
|
|
2318
|
+
return JSON.parse(JSON.stringify(this.node));
|
|
2249
2319
|
}
|
|
2250
|
-
|
|
2251
|
-
|
|
2320
|
+
const firstDemoData = () => __awaiter(this, void 0, void 0, function* () {
|
|
2321
|
+
this.node = FidjStorageNode.getDemoNode();
|
|
2322
|
+
yield this.storeData(fidjService, this.node);
|
|
2323
|
+
});
|
|
2324
|
+
yield fidjService.sync(firstDemoData);
|
|
2325
|
+
const fidjFindAllResults = yield fidjService.findAll();
|
|
2326
|
+
if (fidjFindAllResults && fidjFindAllResults.length > 0) {
|
|
2327
|
+
if (fidjFindAllResults[0].data) {
|
|
2328
|
+
this.fidjMetaResult = fidjFindAllResults[0];
|
|
2329
|
+
this.node = this.fidjMetaResult.data;
|
|
2330
|
+
}
|
|
2252
2331
|
}
|
|
2253
|
-
|
|
2254
|
-
const hourCounts = yield this.profileService.getCountsHour(this.rainNode.id, {
|
|
2255
|
-
periodBegin: focusDate,
|
|
2256
|
-
});
|
|
2257
|
-
data = [
|
|
2258
|
-
{
|
|
2259
|
-
label: 'Rainy Count',
|
|
2260
|
-
style: 'line',
|
|
2261
|
-
values: hourCounts.rainyCount.map(RaainDetailsComponent.MapCountToDateValue),
|
|
2262
|
-
},
|
|
2263
|
-
{
|
|
2264
|
-
label: '% Images',
|
|
2265
|
-
style: 'bar',
|
|
2266
|
-
values: hourCounts.percentImages.map(RaainDetailsComponent.MapCountToDateValue),
|
|
2267
|
-
},
|
|
2268
|
-
{
|
|
2269
|
-
label: 'Rainy Sum',
|
|
2270
|
-
style: 'line',
|
|
2271
|
-
values: hourCounts.rainySum.map(RaainDetailsComponent.MapCountToDateValue),
|
|
2272
|
-
},
|
|
2273
|
-
];
|
|
2274
|
-
}
|
|
2275
|
-
else {
|
|
2276
|
-
const counts = yield this.profileService.getCounts(this.rainNode.id, {
|
|
2277
|
-
range,
|
|
2278
|
-
periodBegin: focusDate,
|
|
2279
|
-
});
|
|
2280
|
-
data = [
|
|
2281
|
-
{
|
|
2282
|
-
label: '% Rainy',
|
|
2283
|
-
style: 'bar',
|
|
2284
|
-
values: counts.percentRainy.map(RaainDetailsComponent.MapCountToDateValue),
|
|
2285
|
-
},
|
|
2286
|
-
{
|
|
2287
|
-
label: '% Images',
|
|
2288
|
-
style: 'bar',
|
|
2289
|
-
values: counts.percentImages.map(RaainDetailsComponent.MapCountToDateValue),
|
|
2290
|
-
},
|
|
2291
|
-
];
|
|
2292
|
-
}
|
|
2293
|
-
// console.log(`fetchData DONE ${range}`, data);
|
|
2294
|
-
return data;
|
|
2332
|
+
return JSON.parse(JSON.stringify(this.node));
|
|
2295
2333
|
});
|
|
2296
2334
|
}
|
|
2297
|
-
|
|
2298
|
-
this.
|
|
2335
|
+
setDemoMode(isDemo) {
|
|
2336
|
+
this.isDemoMode = isDemo;
|
|
2299
2337
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
class ProfileService {
|
|
2341
|
+
constructor(storage, fidjService, httpClient, router) {
|
|
2342
|
+
this.storage = storage;
|
|
2343
|
+
this.fidjService = fidjService;
|
|
2344
|
+
this.httpClient = httpClient;
|
|
2345
|
+
this.router = router;
|
|
2346
|
+
this.email = this.storage.get('raain-email');
|
|
2347
|
+
this.asTeamId = this.storage.get('raain-asTeamId');
|
|
2348
|
+
this.readyForSync = new BehaviorSubject(false);
|
|
2349
|
+
this.roles = [];
|
|
2350
|
+
this.fidjStorage = new FidjStorage(storage);
|
|
2351
|
+
this.isDemoMode = false;
|
|
2302
2352
|
}
|
|
2303
|
-
|
|
2353
|
+
get isDemoMode() {
|
|
2354
|
+
return this.isDemo;
|
|
2355
|
+
}
|
|
2356
|
+
set isDemoMode(mode) {
|
|
2357
|
+
this.isDemo = mode ? mode : true;
|
|
2358
|
+
this.fidjStorage.setDemoMode(this.isDemo);
|
|
2359
|
+
}
|
|
2360
|
+
get defaultUrlForAPI() {
|
|
2361
|
+
return this.storage.get('raain-urlForAPI');
|
|
2362
|
+
}
|
|
2363
|
+
set defaultUrlForAPI(url) {
|
|
2364
|
+
this.storage.set('raain-urlForAPI', url);
|
|
2365
|
+
}
|
|
2366
|
+
refreshProfile() {
|
|
2304
2367
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2305
|
-
|
|
2306
|
-
this.
|
|
2368
|
+
try {
|
|
2369
|
+
this.nodeData = yield this.fidjStorage.getRefreshedNodeCopy(this.fidjService);
|
|
2370
|
+
this.setRoles(yield this.fidjService.getRoles());
|
|
2371
|
+
return this.nodeData;
|
|
2372
|
+
}
|
|
2373
|
+
catch (e) {
|
|
2374
|
+
yield this.checkError(e);
|
|
2307
2375
|
}
|
|
2308
2376
|
});
|
|
2309
2377
|
}
|
|
2310
|
-
|
|
2311
|
-
var _a, _b;
|
|
2378
|
+
isConnected() {
|
|
2312
2379
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2313
|
-
|
|
2314
|
-
this.periodBegin = new Date(newValue);
|
|
2315
|
-
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
2316
|
-
this.storage.set('raain-periodBegin', this.periodBegin);
|
|
2317
|
-
yield this.onPeriodDurationChange(event);
|
|
2380
|
+
return this.fidjService.isConnected();
|
|
2318
2381
|
});
|
|
2319
2382
|
}
|
|
2320
|
-
|
|
2321
|
-
var _a
|
|
2322
|
-
return
|
|
2323
|
-
const newValue = (_b = (_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : this.periodEndAsString;
|
|
2324
|
-
this.periodEnd = new Date(newValue);
|
|
2325
|
-
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
2326
|
-
this.storage.set('raain-periodEnd', this.periodEnd);
|
|
2327
|
-
this.periodBegin = new Date(this.periodEnd.getTime() - this.getDurationInHours() * RaainDetailsComponent.HOUR_MS);
|
|
2328
|
-
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
2329
|
-
this.storage.set('raain-periodBegin', this.periodBegin);
|
|
2330
|
-
this.updateRefreshManagerPeriod();
|
|
2331
|
-
});
|
|
2383
|
+
getEmail() {
|
|
2384
|
+
var _a;
|
|
2385
|
+
return (_a = this.email) !== null && _a !== void 0 ? _a : this.storage.get('raain-email', this.email);
|
|
2332
2386
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
this.periodEnd = new Date(this.periodBegin.getTime() + durationInHours * RaainDetailsComponent.HOUR_MS);
|
|
2337
|
-
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
2338
|
-
this.storage.set('raain-periodEnd', this.periodEnd);
|
|
2339
|
-
this.storage.set('raain-periodDurationInHours', durationInHours);
|
|
2340
|
-
this.updateRefreshManagerPeriod();
|
|
2341
|
-
this.updateCumulativeDurationInMinutes();
|
|
2342
|
-
});
|
|
2387
|
+
setEmail(email) {
|
|
2388
|
+
this.email = email;
|
|
2389
|
+
this.storage.set('raain-email', this.email);
|
|
2343
2390
|
}
|
|
2344
|
-
|
|
2391
|
+
logout(fidjKey, fidjProd) {
|
|
2345
2392
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2393
|
+
// this.storage.remove('raain-email');
|
|
2394
|
+
if (!fidjKey) {
|
|
2395
|
+
try {
|
|
2396
|
+
yield this.fidjService.loginInDemoMode();
|
|
2397
|
+
this.readyForSync.next(true);
|
|
2398
|
+
}
|
|
2399
|
+
catch (err) {
|
|
2400
|
+
console.error('initFidj catch pb: ', err);
|
|
2401
|
+
}
|
|
2402
|
+
return;
|
|
2353
2403
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
this.
|
|
2357
|
-
|
|
2404
|
+
yield this.fidjService.logout(true);
|
|
2405
|
+
try {
|
|
2406
|
+
yield this.fidjService.init(fidjKey, {
|
|
2407
|
+
logLevel: LoggerLevelEnum.WARN,
|
|
2408
|
+
crypto: false,
|
|
2409
|
+
prod: fidjProd,
|
|
2410
|
+
useDB: false,
|
|
2411
|
+
});
|
|
2412
|
+
this.readyForSync.next(true);
|
|
2413
|
+
}
|
|
2414
|
+
catch (err) {
|
|
2415
|
+
console.error('initFidj catch pb: ', err);
|
|
2358
2416
|
}
|
|
2359
|
-
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
2360
2417
|
});
|
|
2361
2418
|
}
|
|
2362
|
-
|
|
2419
|
+
checkError(error) {
|
|
2363
2420
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2421
|
+
if (error.code === 401) {
|
|
2422
|
+
console.warn('Pb on auth');
|
|
2423
|
+
if (this.router.url.indexOf('login') < 0) {
|
|
2424
|
+
try {
|
|
2425
|
+
yield this.fidjService.logout();
|
|
2426
|
+
}
|
|
2427
|
+
catch (ignored) {
|
|
2428
|
+
// Ignore logout errors as we're redirecting to logout page anyway
|
|
2429
|
+
}
|
|
2430
|
+
return this.gotoLout();
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2367
2433
|
});
|
|
2368
2434
|
}
|
|
2369
|
-
|
|
2435
|
+
gotoLout() {
|
|
2370
2436
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2371
|
-
|
|
2437
|
+
try {
|
|
2438
|
+
if (this.router.url.indexOf('login') > -1) {
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
yield this.router.navigateByUrl('/logout', {
|
|
2442
|
+
skipLocationChange: true,
|
|
2443
|
+
replaceUrl: true,
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
catch (e) {
|
|
2447
|
+
console.error('gotoLout error: ', e);
|
|
2448
|
+
}
|
|
2372
2449
|
});
|
|
2373
2450
|
}
|
|
2374
|
-
|
|
2451
|
+
gotoLogin() {
|
|
2375
2452
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2376
|
-
|
|
2377
|
-
if (gaugesFiltered.length !== 1) {
|
|
2453
|
+
if (this.router.url.indexOf('login') > -1) {
|
|
2378
2454
|
return;
|
|
2379
2455
|
}
|
|
2380
|
-
|
|
2381
|
-
yield this.
|
|
2382
|
-
yield this.compareManager.selectGauge(gaugeSelected.id, 0);
|
|
2456
|
+
// await this.router.navigateByUrl('/', {skipLocationChange: true});
|
|
2457
|
+
yield this.router.navigate(['/login']);
|
|
2383
2458
|
});
|
|
2384
2459
|
}
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
gaugeValueShowBegin.setHours(0, 0);
|
|
2390
|
-
gaugeValueShowEnd.setHours(23, 59);
|
|
2391
|
-
const gaugeMeasures = yield this.profileService.getGaugeMeasures(gaugeSelected.id, gaugeValueShowBegin, gaugeValueShowEnd);
|
|
2392
|
-
const gaugeValues = gaugeMeasures.map((gm) => {
|
|
2393
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2394
|
-
const cartesianMeasureValue = new CartesianMeasureValue(gm.values[0]);
|
|
2395
|
-
return {
|
|
2396
|
-
date: gm.date,
|
|
2397
|
-
value: cartesianMeasureValue.getCartesianValues()[0].value,
|
|
2398
|
-
};
|
|
2399
|
-
});
|
|
2400
|
-
this.gaugeSelectedPoints = [
|
|
2401
|
-
{
|
|
2402
|
-
label: gaugeSelected.name,
|
|
2403
|
-
style: 'bar',
|
|
2404
|
-
values: gaugeValues,
|
|
2405
|
-
},
|
|
2406
|
-
];
|
|
2407
|
-
this.cdr.markForCheck();
|
|
2408
|
-
});
|
|
2460
|
+
isLoggedIn() {
|
|
2461
|
+
const loggedIn = this.fidjService.isLoggedIn();
|
|
2462
|
+
console.log('isLoggedIn: ', loggedIn);
|
|
2463
|
+
return loggedIn;
|
|
2409
2464
|
}
|
|
2410
|
-
|
|
2411
|
-
return
|
|
2412
|
-
yield this.refreshGaugeValues({ id: e.point.id, name: e.point.name });
|
|
2413
|
-
yield this.compareManager.selectGauge(e.point.id, e.compareIndex);
|
|
2414
|
-
});
|
|
2465
|
+
needsConnectionRefresh() {
|
|
2466
|
+
return this.fidjService.needsRefresh();
|
|
2415
2467
|
}
|
|
2416
|
-
|
|
2468
|
+
connectionRefresh() {
|
|
2417
2469
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2418
|
-
|
|
2419
|
-
// await this.refreshMap();
|
|
2420
|
-
// }
|
|
2470
|
+
yield this.fidjService.refresh();
|
|
2421
2471
|
});
|
|
2422
2472
|
}
|
|
2423
|
-
|
|
2424
|
-
// Toggle is bound to showPixelMarkers, raain-map handles marker display
|
|
2425
|
-
}
|
|
2426
|
-
toggleCumulativeChanged(_$event) {
|
|
2473
|
+
storeAll() {
|
|
2427
2474
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2428
|
-
this.
|
|
2429
|
-
this.dateShown = this.getDateBasedOnCumulativeMode(this.timeframeDates);
|
|
2430
|
-
this.updateCumulativeDurationInMinutes();
|
|
2431
|
-
if (this.toggleMap) {
|
|
2432
|
-
this.updateRefreshManagerPeriod(); // Update cumulative flag before refresh
|
|
2433
|
-
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
2434
|
-
}
|
|
2475
|
+
return this.fidjStorage.storeData(this.fidjService, this.nodeData);
|
|
2435
2476
|
});
|
|
2436
2477
|
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
// Cumulative mode: use period duration
|
|
2440
|
-
this.cumulativeDurationInMinutes = parseFloat(this.periodDurationAsString) * 60;
|
|
2441
|
-
}
|
|
2442
|
-
else {
|
|
2443
|
-
// Granular mode: use selectedTimeStep
|
|
2444
|
-
this.cumulativeDurationInMinutes = this.selectedTimeStep || 10;
|
|
2445
|
-
}
|
|
2478
|
+
isAdmin() {
|
|
2479
|
+
return this.roles.indexOf('admin') > -1;
|
|
2446
2480
|
}
|
|
2447
|
-
|
|
2448
|
-
|
|
2481
|
+
// === Notifications ===
|
|
2482
|
+
createNotification(rainId, message) {
|
|
2449
2483
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2484
|
+
const data = {
|
|
2485
|
+
rain: rainId,
|
|
2486
|
+
message,
|
|
2487
|
+
};
|
|
2488
|
+
try {
|
|
2489
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2490
|
+
key: 'notifications',
|
|
2491
|
+
verb: 'POST',
|
|
2492
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/notifications',
|
|
2493
|
+
data,
|
|
2494
|
+
});
|
|
2495
|
+
return new EventNode(resp.data);
|
|
2496
|
+
}
|
|
2497
|
+
catch (e) {
|
|
2498
|
+
yield this.checkError(e);
|
|
2499
|
+
}
|
|
2500
|
+
return null;
|
|
2453
2501
|
});
|
|
2454
2502
|
}
|
|
2455
|
-
|
|
2456
|
-
var _a;
|
|
2503
|
+
getNotifications(rainId) {
|
|
2457
2504
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2505
|
+
try {
|
|
2506
|
+
const params = { rain: rainId };
|
|
2507
|
+
const queryString = BuildQueryString(params);
|
|
2508
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2509
|
+
key: 'notifications',
|
|
2510
|
+
verb: 'GET',
|
|
2511
|
+
relativePath: queryString ? '?' + queryString : '',
|
|
2512
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/notifications',
|
|
2513
|
+
});
|
|
2514
|
+
return resp.data.notifications.map((n) => new EventNode(n));
|
|
2515
|
+
}
|
|
2516
|
+
catch (e) {
|
|
2517
|
+
yield this.checkError(e);
|
|
2518
|
+
}
|
|
2519
|
+
return null;
|
|
2462
2520
|
});
|
|
2463
2521
|
}
|
|
2464
|
-
|
|
2465
|
-
var _a;
|
|
2522
|
+
getAllNotifications() {
|
|
2466
2523
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2467
|
-
if (!((_a = this.rainNode) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
2468
|
-
return;
|
|
2469
|
-
}
|
|
2470
2524
|
try {
|
|
2471
|
-
const
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
(this.availableProviders.length > 0 ? this.availableProviders[0] : 'Raain');
|
|
2478
|
-
this.selectedTimeStep =
|
|
2479
|
-
this.storage.get('raain-selectedTimeStep') ||
|
|
2480
|
-
(this.availableTimeSteps.length > 0 ? this.availableTimeSteps[0] : 10);
|
|
2525
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2526
|
+
key: 'notifications',
|
|
2527
|
+
verb: 'GET',
|
|
2528
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/notifications',
|
|
2529
|
+
});
|
|
2530
|
+
return resp.data.notifications.map((n) => new EventNode(n));
|
|
2481
2531
|
}
|
|
2482
2532
|
catch (e) {
|
|
2483
|
-
|
|
2484
|
-
this.availableProviders = ['Raain'];
|
|
2485
|
-
this.availableTimeSteps = [5, 10, 15, 30, 60];
|
|
2486
|
-
this.selectedProvider = 'Raain';
|
|
2487
|
-
this.selectedTimeStep = 10;
|
|
2488
|
-
}
|
|
2489
|
-
// Set provider and timeStep on refreshManager
|
|
2490
|
-
if (this.refreshManager) {
|
|
2491
|
-
this.refreshManager.provider = this.selectedProvider;
|
|
2492
|
-
this.refreshManager.timeStepInMinutes = this.selectedTimeStep;
|
|
2533
|
+
yield this.checkError(e);
|
|
2493
2534
|
}
|
|
2494
|
-
|
|
2535
|
+
return [];
|
|
2495
2536
|
});
|
|
2496
2537
|
}
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
return '';
|
|
2500
|
-
}
|
|
2501
|
-
updateTruncatedError() {
|
|
2502
|
-
var _a;
|
|
2503
|
-
const error = ((_a = this.refreshManager) === null || _a === void 0 ? void 0 : _a.lastError) || '';
|
|
2504
|
-
if (error.length <= 80) {
|
|
2505
|
-
this.truncatedError = error;
|
|
2506
|
-
return;
|
|
2507
|
-
}
|
|
2508
|
-
this.truncatedError = error.substring(0, 80) + '...';
|
|
2509
|
-
}
|
|
2510
|
-
updateCachedValues() {
|
|
2511
|
-
this.updatePercentOfImages();
|
|
2512
|
-
this.updatePercentOfComputations();
|
|
2513
|
-
this.updateTruncatedError();
|
|
2514
|
-
this.updateCumulativeDurationInMinutes();
|
|
2515
|
-
}
|
|
2516
|
-
refreshMap() {
|
|
2538
|
+
// === Teams ===
|
|
2539
|
+
getTeams() {
|
|
2517
2540
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2541
|
+
const teams = [];
|
|
2542
|
+
const params = {};
|
|
2543
|
+
const queryString = BuildQueryString(params);
|
|
2544
|
+
try {
|
|
2545
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2546
|
+
key: 'teams',
|
|
2547
|
+
verb: 'GET',
|
|
2548
|
+
relativePath: queryString ? '?' + queryString : '',
|
|
2549
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/teams',
|
|
2550
|
+
});
|
|
2551
|
+
for (const team of resp.data.teams) {
|
|
2552
|
+
teams.push(new TeamNode(team));
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
catch (e) {
|
|
2556
|
+
yield this.checkError(e);
|
|
2557
|
+
}
|
|
2558
|
+
return teams;
|
|
2525
2559
|
});
|
|
2526
2560
|
}
|
|
2527
|
-
|
|
2561
|
+
getTeam(teamId) {
|
|
2528
2562
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
}
|
|
2538
|
-
updatePercentOfImages() {
|
|
2539
|
-
var _a, _b;
|
|
2540
|
-
if (!((_b = (_a = this.countsPeriod) === null || _a === void 0 ? void 0 : _a.percentImages) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
2541
|
-
this.percentOfImages = 0;
|
|
2542
|
-
return;
|
|
2543
|
-
}
|
|
2544
|
-
const duringPeriod = this.countsPeriod.percentImages.filter((a) => this.periodBegin.getTime() <= new Date(a.name).getTime() &&
|
|
2545
|
-
new Date(a.name).getTime() <= this.periodEnd.getTime());
|
|
2546
|
-
const summed = duringPeriod.reduce((a, b) => { var _a; return a + ((_a = b.x) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
2547
|
-
this.percentOfImages = Math.round(summed / duringPeriod.length);
|
|
2548
|
-
}
|
|
2549
|
-
updatePercentOfComputations() {
|
|
2550
|
-
var _a;
|
|
2551
|
-
const timeline = (_a = this.refreshManager) === null || _a === void 0 ? void 0 : _a.getTimelineFrameSet();
|
|
2552
|
-
if (!(timeline === null || timeline === void 0 ? void 0 : timeline.length)) {
|
|
2553
|
-
this.percentOfComputations = 0;
|
|
2554
|
-
return;
|
|
2555
|
-
}
|
|
2556
|
-
const timelineWithComputation = timeline.filter((a) => !!a.rainComputationCumulativeId || !!a.rainComputationId);
|
|
2557
|
-
const ratio = timelineWithComputation.length / timeline.length;
|
|
2558
|
-
this.percentOfComputations = Math.round(ratio * 100);
|
|
2559
|
-
}
|
|
2560
|
-
getDateBasedOnCumulativeMode(fallbackDates) {
|
|
2561
|
-
if ((fallbackDates === null || fallbackDates === void 0 ? void 0 : fallbackDates.length) > 0) {
|
|
2562
|
-
const dateExists = fallbackDates.some((d) => { var _a; return d.getTime() === ((_a = this.dateShown) === null || _a === void 0 ? void 0 : _a.getTime()); });
|
|
2563
|
-
if (!dateExists) {
|
|
2564
|
-
return this.toggleCumulative
|
|
2565
|
-
? fallbackDates[fallbackDates.length - 1]
|
|
2566
|
-
: fallbackDates[0];
|
|
2563
|
+
try {
|
|
2564
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2565
|
+
key: 'teams',
|
|
2566
|
+
verb: 'GET',
|
|
2567
|
+
relativePath: teamId,
|
|
2568
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/teams',
|
|
2569
|
+
});
|
|
2570
|
+
return new TeamNode(resp.data);
|
|
2567
2571
|
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
}
|
|
2574
|
-
getDurationInHours() {
|
|
2575
|
-
return parseFloat(this.periodDurationAsString);
|
|
2576
|
-
}
|
|
2577
|
-
updateRefreshManagerPeriod() {
|
|
2578
|
-
this.refreshManager.cumulative = this.toggleCumulative;
|
|
2579
|
-
// Align dates to 5-minute boundaries (floor) for consistency with raain-ground
|
|
2580
|
-
const alignTo5minFloor = (date) => {
|
|
2581
|
-
const minutes = date.getMinutes();
|
|
2582
|
-
const alignedMinutes = Math.floor(minutes / 5) * 5;
|
|
2583
|
-
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), alignedMinutes, 0, 0);
|
|
2584
|
-
};
|
|
2585
|
-
this.refreshManager.period = {
|
|
2586
|
-
begin: alignTo5minFloor(this.periodBegin),
|
|
2587
|
-
end: alignTo5minFloor(this.periodEnd),
|
|
2588
|
-
};
|
|
2572
|
+
catch (e) {
|
|
2573
|
+
yield this.checkError(e);
|
|
2574
|
+
}
|
|
2575
|
+
return null;
|
|
2576
|
+
});
|
|
2589
2577
|
}
|
|
2590
|
-
|
|
2578
|
+
// === Radars ===
|
|
2579
|
+
getRadars(name) {
|
|
2591
2580
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2581
|
+
const radars = [];
|
|
2582
|
+
const params = {};
|
|
2583
|
+
if (name) {
|
|
2584
|
+
params.name = name;
|
|
2585
|
+
}
|
|
2586
|
+
const queryString = BuildQueryString(params);
|
|
2587
|
+
try {
|
|
2588
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2589
|
+
key: 'radars',
|
|
2590
|
+
verb: 'GET',
|
|
2591
|
+
relativePath: queryString ? '?' + queryString : '',
|
|
2592
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/radars',
|
|
2593
|
+
});
|
|
2594
|
+
for (const r of resp.data.radars) {
|
|
2595
|
+
const radar = new RadarNode(r);
|
|
2596
|
+
radars.push(radar);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
catch (e) {
|
|
2600
|
+
yield this.checkError(e);
|
|
2601
|
+
}
|
|
2602
|
+
return radars;
|
|
2595
2603
|
});
|
|
2596
2604
|
}
|
|
2597
|
-
|
|
2605
|
+
getRadar(id) {
|
|
2598
2606
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2599
|
-
|
|
2600
|
-
|
|
2607
|
+
try {
|
|
2608
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2609
|
+
key: 'radars',
|
|
2610
|
+
verb: 'GET',
|
|
2611
|
+
relativePath: id,
|
|
2612
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/radars',
|
|
2613
|
+
});
|
|
2614
|
+
return new RadarNode(resp.data);
|
|
2601
2615
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2616
|
+
catch (e) {
|
|
2617
|
+
yield this.checkError(e);
|
|
2618
|
+
}
|
|
2619
|
+
return null;
|
|
2605
2620
|
});
|
|
2606
2621
|
}
|
|
2607
|
-
|
|
2608
|
-
var _a, _b;
|
|
2609
|
-
this.borders = [];
|
|
2610
|
-
this.isAdmin = false;
|
|
2611
|
-
this.timeframeContainers = new TimeframeContainers([]);
|
|
2612
|
-
this.currentTimeframeTarget = null;
|
|
2613
|
-
this.timeframeDates = [];
|
|
2614
|
-
this.countPoints = [];
|
|
2615
|
-
this.countsPeriod = { progress: 0, queueRunning: 0, percentImages: [] };
|
|
2616
|
-
this.gaugeSelectedPoints = [];
|
|
2617
|
-
this.toggleHistory = false;
|
|
2618
|
-
this.toggleMap = true;
|
|
2619
|
-
this.toggleCompare = false;
|
|
2620
|
-
this.toggleGaugeMeasures = false;
|
|
2621
|
-
this.toggleRemoveCompareDuplicate = true;
|
|
2622
|
-
this.toggleCumulative = this.storage.get('raain-toggleCumulative');
|
|
2623
|
-
this.periodBegin = new Date(this.storage.get('raain-periodBegin'));
|
|
2624
|
-
this.periodEnd = new Date(this.storage.get('raain-periodEnd'));
|
|
2625
|
-
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
2626
|
-
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
2627
|
-
const durationMs = this.periodEnd.getTime() - this.periodBegin.getTime();
|
|
2628
|
-
this.periodDurationAsString = '' + durationMs / RaainDetailsComponent.HOUR_MS;
|
|
2629
|
-
this.dateShown = this.getDateBasedOnCumulativeMode();
|
|
2630
|
-
this.refreshInProgress = false;
|
|
2631
|
-
this.showFullError = false;
|
|
2632
|
-
this.showQualityModal = false;
|
|
2633
|
-
this.qualityIndicators = [];
|
|
2634
|
-
this.qualityIndicatorsLoading = false;
|
|
2635
|
-
(_a = this.compareManager) === null || _a === void 0 ? void 0 : _a.cleanAll();
|
|
2636
|
-
(_b = this.refreshManager) === null || _b === void 0 ? void 0 : _b.cleanAll();
|
|
2637
|
-
}
|
|
2638
|
-
init() {
|
|
2639
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2640
|
-
this.cleanAll();
|
|
2641
|
-
this.updateCachedValues();
|
|
2642
|
-
yield this.initRain();
|
|
2643
|
-
this.cdr.markForCheck();
|
|
2644
|
-
});
|
|
2645
|
-
}
|
|
2646
|
-
initRain() {
|
|
2622
|
+
putRadar(radarNode) {
|
|
2647
2623
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2648
|
-
|
|
2649
|
-
|
|
2624
|
+
const data = {
|
|
2625
|
+
name: radarNode.name,
|
|
2626
|
+
};
|
|
2627
|
+
try {
|
|
2628
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2629
|
+
key: 'radars',
|
|
2630
|
+
relativePath: radarNode.id,
|
|
2631
|
+
verb: 'PUT',
|
|
2632
|
+
data,
|
|
2633
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/radars/',
|
|
2634
|
+
});
|
|
2635
|
+
return new RadarNode(resp.data);
|
|
2650
2636
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
this.compareManager.rainNode = this.rainNode;
|
|
2654
|
-
// Load providers and set on refreshManager
|
|
2655
|
-
yield this.loadProviders();
|
|
2656
|
-
this.refreshManager.setMethods(this.onRefreshInProgress.bind(this), this.onRefreshDone.bind(this), this.onFetchDone.bind(this));
|
|
2657
|
-
const center = this.rainNode.getCenter();
|
|
2658
|
-
this.coordinates = new MapLatLng(center.lat, center.lng);
|
|
2659
|
-
this.teamNode = yield this.profileService.getTeam(this.rainNode.getLink(TeamNode.TYPE).getId());
|
|
2660
|
-
if (this.periodBegin && this.periodEnd) {
|
|
2661
|
-
this.updateRefreshManagerPeriod();
|
|
2662
|
-
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
2637
|
+
catch (e) {
|
|
2638
|
+
yield this.checkError(e);
|
|
2663
2639
|
}
|
|
2664
2640
|
});
|
|
2665
2641
|
}
|
|
2666
|
-
|
|
2642
|
+
getLonelyRadars(rains) {
|
|
2667
2643
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2644
|
+
try {
|
|
2645
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2646
|
+
key: 'radars',
|
|
2647
|
+
verb: 'GET',
|
|
2648
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/radars',
|
|
2649
|
+
});
|
|
2650
|
+
const lonelyRadars = [];
|
|
2651
|
+
const radars = resp.data.radars;
|
|
2652
|
+
radars.forEach((radar) => {
|
|
2653
|
+
let found = false;
|
|
2654
|
+
rains.forEach((rain) => {
|
|
2655
|
+
const rdId = rain.getLink(RadarNode.TYPE).getId();
|
|
2656
|
+
if (rdId === radar.id) {
|
|
2657
|
+
found = true;
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
if (!found) {
|
|
2661
|
+
lonelyRadars.push(new RadarNode(radar));
|
|
2662
|
+
}
|
|
2663
|
+
});
|
|
2664
|
+
return lonelyRadars;
|
|
2665
|
+
}
|
|
2666
|
+
catch (e) {
|
|
2667
|
+
yield this.checkError(e);
|
|
2668
|
+
}
|
|
2669
|
+
return [];
|
|
2673
2670
|
});
|
|
2674
2671
|
}
|
|
2675
|
-
|
|
2672
|
+
getRainTimeframe(rainId, begin, end, forced = false, provider = 'Raain', timeStepInMinutes = 10, windowInMinutes = 0) {
|
|
2676
2673
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2674
|
+
try {
|
|
2675
|
+
const params = {
|
|
2676
|
+
format: 'timeframeCumulative',
|
|
2677
|
+
provider,
|
|
2678
|
+
timeStepInMinutes: String(timeStepInMinutes),
|
|
2679
|
+
begin: begin === null || begin === void 0 ? void 0 : begin.toISOString(),
|
|
2680
|
+
end: end === null || end === void 0 ? void 0 : end.toISOString(),
|
|
2681
|
+
};
|
|
2682
|
+
if (forced) {
|
|
2683
|
+
params.forced = 'true';
|
|
2685
2684
|
}
|
|
2685
|
+
params.windowInMinutes = String(windowInMinutes);
|
|
2686
|
+
const queryString = BuildQueryString(params);
|
|
2687
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2688
|
+
key: 'rains',
|
|
2689
|
+
verb: 'GET',
|
|
2690
|
+
relativePath: rainId + (queryString ? '?' + queryString : ''),
|
|
2691
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2692
|
+
});
|
|
2693
|
+
const rainNode = new RainNode(resp.data.timeframeCumulative);
|
|
2694
|
+
rainNode.name += '.radar.timeframeCumulative';
|
|
2695
|
+
return rainNode;
|
|
2696
|
+
}
|
|
2697
|
+
catch (e) {
|
|
2698
|
+
yield this.checkError(e);
|
|
2686
2699
|
}
|
|
2700
|
+
return null;
|
|
2687
2701
|
});
|
|
2688
2702
|
}
|
|
2689
|
-
|
|
2703
|
+
// === Rains ===
|
|
2704
|
+
getRains(name) {
|
|
2690
2705
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2691
|
-
|
|
2692
|
-
|
|
2706
|
+
const rains = [];
|
|
2707
|
+
const params = {};
|
|
2708
|
+
if (name) {
|
|
2709
|
+
params.name = name;
|
|
2693
2710
|
}
|
|
2694
|
-
|
|
2711
|
+
const queryString = BuildQueryString(params);
|
|
2712
|
+
try {
|
|
2713
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2714
|
+
key: 'rains',
|
|
2715
|
+
verb: 'GET',
|
|
2716
|
+
relativePath: queryString ? '?' + queryString : '',
|
|
2717
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2718
|
+
});
|
|
2719
|
+
for (const rain of resp.data.rains) {
|
|
2720
|
+
rains.push(new RainNode(rain));
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
catch (e) {
|
|
2724
|
+
yield this.checkError(e);
|
|
2725
|
+
}
|
|
2726
|
+
return rains;
|
|
2695
2727
|
});
|
|
2696
2728
|
}
|
|
2697
|
-
|
|
2729
|
+
getRain(id) {
|
|
2698
2730
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2699
|
-
|
|
2731
|
+
try {
|
|
2732
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2733
|
+
key: 'rains',
|
|
2734
|
+
relativePath: id,
|
|
2735
|
+
verb: 'GET',
|
|
2736
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains/',
|
|
2737
|
+
});
|
|
2738
|
+
return new RainNode(resp.data);
|
|
2739
|
+
}
|
|
2740
|
+
catch (e) {
|
|
2741
|
+
yield this.checkError(e);
|
|
2742
|
+
}
|
|
2743
|
+
return null;
|
|
2700
2744
|
});
|
|
2701
2745
|
}
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
RaainDetailsComponent.DAY_MS = 24 * 60 * 60 * 1000;
|
|
2705
|
-
RaainDetailsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RaainDetailsComponent, deps: [{ token: Storage }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2706
|
-
RaainDetailsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: RaainDetailsComponent, selector: "raain-details", inputs: { toggleAdmin: "toggleAdmin", rainNode: "rainNode", compareManager: "compareManager", refreshManager: "refreshManager", profileService: "profileService", radarService: "radarService" }, usesOnChanges: true, ngImport: i0, template: "<!-- Main content container -->\n<div *ngIf=\"rainNode\" class=\"raain-details-container\">\n\n <!-- Period selection section -->\n <ion-card class=\"period-card\">\n <ion-card-content>\n <div class=\"period-controls\">\n <div class=\"period-row\">\n\n <ion-button (click)=\"toggleHistory = !toggleHistory; onEnableCountHistoryTab(rainNode)\"\n fill=\"outline\">\n <ion-icon name=\"calendar-clear-outline\" slot=\"start\"></ion-icon>\n <ion-icon [name]=\"toggleHistory ? 'chevron-down' : 'chevron-forward'\" slot=\"end\"></ion-icon>\n </ion-button>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodBeginChange($event)\"\n [disabled]=\"toggleCumulative\"\n [value]=\"periodBeginAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"period-duration ion-hide-md-down\">\n <ion-select (ionDismiss)=\"onPeriodDurationChange($event)\"\n [(ngModel)]=\"periodDurationAsString\"\n class=\"duration-select\"\n id=\"periodDuration\"\n interface=\"popover\">\n <ion-select-option value=\"0.25\">15 minutes</ion-select-option>\n <ion-select-option value=\"0.5\">30 minutes</ion-select-option>\n <ion-select-option value=\"1\">1 hour</ion-select-option>\n <ion-select-option value=\"2\">2 hours</ion-select-option>\n <ion-select-option value=\"3\">3 hours</ion-select-option>\n <ion-select-option value=\"4\">4 hours</ion-select-option>\n <ion-select-option value=\"5\">5 hours</ion-select-option>\n <ion-select-option value=\"6\">6 hours</ion-select-option>\n <ion-select-option value=\"8\">8 hours</ion-select-option>\n <ion-select-option value=\"10\">10 hours</ion-select-option>\n <ion-select-option value=\"12\">12 hours</ion-select-option>\n <ion-select-option *ngIf=\"isAdmin\" value=\"24\">24 hours</ion-select-option>\n </ion-select>\n </div>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodEndChange($event)\"\n [disabled]=\"!toggleCumulative\"\n [value]=\"periodEndAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"toggle-cumulative\">\n <ion-label [class.text-primary]=\"toggleCumulative\">\n {{ toggleCumulative ? 'Cumulative' : 'Granular' }}\n </ion-label>\n <ion-toggle (ionChange)=\"toggleCumulativeChanged($event)\"\n [(ngModel)]=\"toggleCumulative\"\n [checked]=\"toggleCumulative\">\n </ion-toggle>\n </div>\n </div>\n\n <!-- Hidden label for change detection (uncomment to debug)\n <div class=\"hidden-label\">{{ onChangeDetectionTest(rainNode) }}</div>\n -->\n </div>\n\n <!-- Historical map section -->\n <div *ngIf=\"toggleHistory\" class=\"period-controls\">\n <raain-date-dynamic (changedDate)=\"onDateChangeInCount($event)\"\n [currentHeight]=\"300\"\n [fetchData]=\"fetchDataWrapper\"\n [points]=\"countPoints\">\n </raain-date-dynamic>\n </div>\n </ion-card-content>\n </ion-card>\n\n <!-- Map performance -->\n <ion-grid class=\"map-performance\">\n <ion-row id=\"progressAndRefresh\">\n <ion-col class=\"provider-selection\" size=\"12\" size-md=\"6\">\n <ion-button (click)=\"openQualityModal()\" class=\"quality-info-button\" fill=\"clear\">\n {{ refreshManager.rainComputationMapVersion }}\n <ion-icon name=\"help-circle-outline\" slot=\"end\"></ion-icon>\n </ion-button>\n <div *ngIf=\"availableProviders.length > 0 || availableTimeSteps.length > 0\" class=\"selection-row\">\n <ion-select (ionChange)=\"onProviderChanged($event)\"\n [value]=\"selectedProvider\"\n interface=\"popover\"\n label=\"Provider\"\n placeholder=\"Select Provider\">\n <ion-select-option *ngFor=\"let provider of availableProviders\" [value]=\"provider\">\n {{ provider }}\n </ion-select-option>\n </ion-select>\n\n <ion-select (ionChange)=\"onTimeStepChanged($event)\"\n [value]=\"selectedTimeStep\"\n interface=\"popover\"\n label=\"Time Step\"\n placeholder=\"Select Time Step\">\n <ion-select-option *ngFor=\"let step of availableTimeSteps\" [value]=\"step\">\n {{ step }} min\n </ion-select-option>\n </ion-select>\n </div>\n </ion-col>\n <ion-col class=\"ion-text-right\" size=\"12\" size-md=\"6\">\n <ion-label class=\"ion-margin-end\">\n <span *ngIf=\"percentOfComputations\">\n {{ percentOfComputations }}% Images\n <i *ngIf=\"countsPeriod.progress\" class=\"progress-indicator\">\n In Progress: {{ countsPeriod.progress }}...\n </i>\n </span>\n <span *ngIf=\"!percentOfComputations\">\n No image available\n </span>\n\n </ion-label>\n\n <ion-button (click)=\"refreshMap()\" [disabled]=\"refreshInProgress\" class=\"refresh-button\">\n Refresh Map\n </ion-button>\n </ion-col>\n </ion-row>\n\n <!-- status update row -->\n <ion-row>\n <!-- Progress col -->\n <ion-progress-bar\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [style.visibility]=\"refreshInProgress ? 'visible' : 'hidden'\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n\n <!-- Error col -->\n <ion-col (click)=\"showFullError = !showFullError\" *ngIf=\"refreshManager.lastError\" class=\"error-row\"\n size=\"12\">\n <div class=\"error-content\">\n <ion-icon class=\"error-icon\" name=\"warning-outline\"></ion-icon>\n <span [class.expanded]=\"showFullError\" class=\"error-text\">\n {{ showFullError ? refreshManager.lastError : truncatedError }}\n </span>\n <ion-icon [name]=\"showFullError ? 'chevron-up' : 'chevron-down'\" class=\"error-caret\"></ion-icon>\n </div>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Map section -->\n <ion-card class=\"map-card\">\n <ion-card-content class=\"map-content\">\n <ion-grid>\n <ion-row *ngIf=\"toggleMap && percentOfImages\">\n <!-- Map component -->\n <ion-col class=\"map-column\" size-lg=\"7\" size-md=\"12\">\n <div class=\"map-container\">\n <raain-map #raainMapRef\n (changedDate)=\"onDateChangeInMap($event)\"\n (changedSum)=\"onSumChangeInMap($event)\"\n (selectedMarker)=\"onGaugeSelectInMap($event)\"\n [coordinates]=\"coordinates\"\n [cumulativeDurationInMinutes]=\"cumulativeDurationInMinutes\"\n [currentHeight]=\"500\"\n [defaultDate]=\"dateShown\"\n [markers]=\"{\n borders,\n gauges: compareManager.gaugesInMap,\n gaugesInCompare: compareManager.gaugesInCompare,\n selectedGauges: compareManager.selectedGauges,\n pixels: compareManager.selectedPixels,\n pixelsSolution: compareManager.pixelsSolutions?.length ? compareManager.pixelsSolutions[0] : [],\n speeds: compareManager.speeds\n }\"\n [showCumulative]=\"toggleCumulative\"\n [showVisiblePixelMarkers]=\"showPixelMarkers\"\n [sumFn]=\"refreshManager.sumFn\"\n [sumValues]=\"refreshManager.sumValues\"\n [timeframeContainers]=\"timeframeContainers\"\n [timeframeDates]=\"timeframeDates\">\n </raain-map>\n </div>\n\n <div class=\"data-column\">\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Image Details</summary>\n <div class=\"details-content\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Date:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDate?.toISOString() }}\n | {{ refreshManager.rainComputationMapDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Water in the map:</span>\n <span class=\"detail-value\">{{ sumDetails }}</span>\n <ion-toggle\n (ionChange)=\"onTogglePixelMarkers()\"\n [(ngModel)]=\"showPixelMarkers\"\n style=\"margin-left: 8px; transform: scale(0.7);\">\n </ion-toggle>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ min:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMin }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ max:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMax }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n\n <!-- Data panel -->\n <ion-col *ngIf=\"!!compareManager.compareVersion\" class=\"data-column\" size-lg=\"5\" size-md=\"12\">\n <div class=\"data-panel\">\n <!-- Compare stack component -->\n <div class=\"compare-stack\">\n <raain-compare-stack\n (selectedPoint)=\"onGaugeSelectInCompare($event)\"\n [compareManager]=\"compareManager\"\n [cumulative]=\"toggleCumulative\">\n </raain-compare-stack>\n </div>\n\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Compare Details</summary>\n <div class=\"details-content\">\n <div [ngClass]=\"{'warning': refreshManager.rainComputationMapDoneDate?.getTime() > compareManager.currentQualityDoneDate?.getTime() + 60000}\"\n class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ compareManager.compareVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Gauges:</span>\n <span class=\"detail-value\">{{ compareManager.gaugesInCompare.length }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Points:</span>\n <span class=\"detail-value\">{{ compareManager.globalComparePoints.length }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n </ion-row>\n <ion-row>\n <!-- Bottom progress bar -->\n <ion-progress-bar *ngIf=\"refreshInProgress\"\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n\n <!-- Gauge values section -->\n <ion-card *ngIf=\"gaugeSelectedPoints.length && gaugeSelectedPoints[0].values.length\" class=\"gauge-card\">\n <ion-card-header>\n <ion-card-title>\n <ion-icon name=\"analytics-outline\"></ion-icon>\n Selected Gauge Data\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <raain-date-focus\n [currentHeight]=\"300\"\n [focusDate]=\"periodBegin\"\n [focusRange]=\"DateRange.DAY\"\n [points]=\"gaugeSelectedPoints\"\n [withoutAll]=\"true\">\n </raain-date-focus>\n </ion-card-content>\n </ion-card>\n\n <!-- Quality Performance Modal -->\n <div (click)=\"closeQualityModal()\" *ngIf=\"showQualityModal\" class=\"quality-modal-overlay\">\n <div (click)=\"$event.stopPropagation()\" class=\"quality-modal-content\">\n <div class=\"quality-modal-header\">\n <h2>Model Quality Performance</h2>\n <ion-button (click)=\"closeQualityModal()\" fill=\"clear\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n <div class=\"quality-modal-body\">\n <div *ngIf=\"qualityIndicatorsLoading\" class=\"quality-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <span>Loading indicators...</span>\n </div>\n <div *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length === 0\" class=\"quality-empty\">\n No quality indicators available for this year.\n </div>\n <table *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length > 0\" class=\"quality-table\">\n <thead>\n <tr>\n <th>Model</th>\n <th>Compare</th>\n <th>Gauges</th>\n <th>Period</th>\n <th>Avg Quality</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let indicator of qualityIndicators\">\n <td>{{ indicator.computingVersion }}</td>\n <td>{{ indicator.qualityVersion }}</td>\n <td>{{ indicator.provider }}<br>{{ indicator.timeStepInMinutes }} min</td>\n <td>{{ formatDate(indicator.startDate) }}<br>{{ formatDate(indicator.endDate) }}</td>\n <td>{{ indicator.averageQuality | number:'1.2-2' }}</td>\n <td>{{ formatDate(indicator.lastUpdatedAt) }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".raain-details-container{max-width:var(--app-max-width);margin:0 auto;padding:0 0 24px}.raain-details-card{width:100%;margin-bottom:20px}.raain-details-card ion-card-header{border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.raain-details-card ion-card-header ion-card-title{display:flex;align-items:center}.raain-details-card ion-card-header ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary)}.node-info-card{background-color:var(--ion-color-light)}.node-info-card .node-header{display:flex;align-items:center}.node-info-card .node-header .node-status{margin-right:16px}.node-info-card .node-header .node-status ion-icon{font-size:24px}.node-info-card .node-header .node-titles{flex:1}.node-info-card .node-header .node-titles ion-card-title{margin:0;font-size:1.4rem;font-weight:600;color:var(--ion-color-dark)}.node-info-card .node-header .node-titles ion-card-subtitle{padding-left:0;margin:4px 0 0;font-size:.9rem;color:var(--ion-color-medium)}.count-map-card,.period-card{background-color:var(--ion-color-light)}.period-card ion-card-title{display:flex;align-items:center}.period-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.period-row{display:flex;flex-wrap:wrap;align-items:center;gap:16px;position:relative}.now-button{min-width:100px}#all-dates{display:flex;align-items:center;margin-left:auto}#all-dates .toggle-label{margin-right:8px;white-space:nowrap}.refresh-button ion-icon{margin-right:4px;color:var(--ion-color-light)}.provider-selection{display:flex;align-items:center}.quality-info-button{--padding-start: 8px;--padding-end: 8px;font-size:.85rem;color:var(--ion-color-medium)}.quality-info-button ion-icon{font-size:18px;margin-left:4px}.selection-row{display:flex;flex-direction:row;align-items:center;gap:12px}.selection-row ion-select{flex:0 0 auto;min-width:100px;margin-bottom:0;padding:4px 8px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light)}.period-start,.period-duration{display:flex;align-items:center}.toggle-cumulative{display:flex;align-items:center;gap:8px;margin-left:auto}.toggle-cumulative .text-primary{color:var(--ion-color-primary);font-weight:600}.hidden-label{display:none}.datetime-input,#periodDuration,.duration-select{padding:8px 12px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light);font-family:var(--ion-font-family)}.datetime-input:focus,#periodDuration:focus,.duration-select:focus{outline:none;border-color:var(--ion-color-primary)}#periodDuration,.duration-select{min-width:150px}.gauge-card{background-color:var(--ion-color-light)}.gauge-card ion-card-title{display:flex;align-items:center}.gauge-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.error-row{cursor:pointer;background-color:rgba(var(--ion-color-danger-rgb),.1);border-left:3px solid var(--ion-color-danger);margin-top:8px;border-radius:4px;transition:background-color .2s ease}.error-row:hover{background-color:rgba(var(--ion-color-danger-rgb),.15)}.error-row .error-content{display:flex;align-items:flex-start;padding:8px 12px;gap:8px}.error-row .error-icon{color:var(--ion-color-danger);font-size:18px;flex-shrink:0;margin-top:2px}.error-row .error-text{flex:1;color:var(--ion-color-danger-shade);font-size:.9rem;word-break:break-word}.error-row .error-text.expanded{white-space:pre-wrap}.error-row .error-caret{color:var(--ion-color-danger);font-size:16px;flex-shrink:0;margin-top:2px}raain-compare-stack{width:100%;display:block}@media (max-width: 768px){.period-row{flex-direction:row;justify-content:space-between;align-items:center}#all-dates{margin-left:auto;padding-left:8px}#all-dates .toggle-label{font-size:.9rem}.map-header{flex-direction:row;justify-content:space-between;align-items:center;gap:16px}}.quality-modal-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:9999}.quality-modal-content{background-color:var(--ion-background-color, #fff);border-radius:12px;width:95%;max-width:900px;max-height:80vh;overflow:auto;box-shadow:0 4px 24px #0003}.quality-modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-modal-header h2{margin:0;font-size:1.25rem;font-weight:600;color:var(--ion-color-dark)}.quality-modal-header ion-button{--padding-start: 8px;--padding-end: 8px}.quality-modal-body{padding:20px}.quality-table{width:100%;border-collapse:collapse;margin-top:16px;table-layout:fixed}.quality-table th,.quality-table td{width:16.66%;padding:12px 16px;text-align:center;vertical-align:top;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-table th{background-color:rgba(var(--ion-color-primary-rgb),.1);font-weight:600;color:var(--ion-color-dark)}.quality-table td{color:var(--ion-color-dark-tint);font-size:.7rem}.quality-table tbody tr:hover{background-color:rgba(var(--ion-color-primary-rgb),.05)}.quality-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--ion-color-medium)}.quality-loading ion-spinner{margin-bottom:12px}.quality-empty{text-align:center;padding:40px 20px;color:var(--ion-color-medium);font-style:italic}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i4.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i4.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i4.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i4.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i4.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i4.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i4.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i4.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i4.IonProgressBar, selector: "ion-progress-bar", inputs: ["buffer", "color", "mode", "reversed", "type", "value"] }, { kind: "component", type: i4.IonRow, selector: "ion-row" }, { kind: "component", type: i4.IonSelect, selector: "ion-select", inputs: ["cancelText", "compareWith", "disabled", "interface", "interfaceOptions", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "value"] }, { kind: "component", type: i4.IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: i4.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i4.IonToggle, selector: "ion-toggle", inputs: ["checked", "color", "disabled", "mode", "name", "value"] }, { kind: "directive", type: i4.BooleanValueAccessor, selector: "ion-checkbox,ion-toggle" }, { kind: "directive", type: i4.SelectValueAccessor, selector: "ion-range, ion-select, ion-radio-group, ion-segment, ion-datetime" }, { kind: "component", type: RaainMapComponent, selector: "raain-map", inputs: ["coordinates", "markers", "timeframeContainers", "autoplay", "showMarkers", "showSpeedMarkers", "showVisiblePixelMarkers", "showCumulative", "cumulativeDurationInMinutes", "currentHeight", "timeframeDates", "defaultDate", "sumValues", "sumFn"], outputs: ["selectedMarker", "changedDate", "changedSum"] }, { kind: "component", type: RaainCompareStackComponent, selector: "raain-compare-stack", inputs: ["compareManager", "currentHeight", "cumulative"], outputs: ["selectedPoint"] }, { kind: "component", type: RaainDateFocusComponent, selector: "raain-date-focus", inputs: ["points", "focusDate", "focusRange", "withoutAll", "currentHeight"] }, { kind: "component", type: RaainDateDynamicComponent, selector: "raain-date-dynamic", inputs: ["points", "focusDate", "focusRange", "withoutAll", "currentHeight", "fetchData"], outputs: ["changedDate"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i2.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2707
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RaainDetailsComponent, decorators: [{
|
|
2708
|
-
type: Component,
|
|
2709
|
-
args: [{ selector: 'raain-details', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Main content container -->\n<div *ngIf=\"rainNode\" class=\"raain-details-container\">\n\n <!-- Period selection section -->\n <ion-card class=\"period-card\">\n <ion-card-content>\n <div class=\"period-controls\">\n <div class=\"period-row\">\n\n <ion-button (click)=\"toggleHistory = !toggleHistory; onEnableCountHistoryTab(rainNode)\"\n fill=\"outline\">\n <ion-icon name=\"calendar-clear-outline\" slot=\"start\"></ion-icon>\n <ion-icon [name]=\"toggleHistory ? 'chevron-down' : 'chevron-forward'\" slot=\"end\"></ion-icon>\n </ion-button>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodBeginChange($event)\"\n [disabled]=\"toggleCumulative\"\n [value]=\"periodBeginAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"period-duration ion-hide-md-down\">\n <ion-select (ionDismiss)=\"onPeriodDurationChange($event)\"\n [(ngModel)]=\"periodDurationAsString\"\n class=\"duration-select\"\n id=\"periodDuration\"\n interface=\"popover\">\n <ion-select-option value=\"0.25\">15 minutes</ion-select-option>\n <ion-select-option value=\"0.5\">30 minutes</ion-select-option>\n <ion-select-option value=\"1\">1 hour</ion-select-option>\n <ion-select-option value=\"2\">2 hours</ion-select-option>\n <ion-select-option value=\"3\">3 hours</ion-select-option>\n <ion-select-option value=\"4\">4 hours</ion-select-option>\n <ion-select-option value=\"5\">5 hours</ion-select-option>\n <ion-select-option value=\"6\">6 hours</ion-select-option>\n <ion-select-option value=\"8\">8 hours</ion-select-option>\n <ion-select-option value=\"10\">10 hours</ion-select-option>\n <ion-select-option value=\"12\">12 hours</ion-select-option>\n <ion-select-option *ngIf=\"isAdmin\" value=\"24\">24 hours</ion-select-option>\n </ion-select>\n </div>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodEndChange($event)\"\n [disabled]=\"!toggleCumulative\"\n [value]=\"periodEndAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"toggle-cumulative\">\n <ion-label [class.text-primary]=\"toggleCumulative\">\n {{ toggleCumulative ? 'Cumulative' : 'Granular' }}\n </ion-label>\n <ion-toggle (ionChange)=\"toggleCumulativeChanged($event)\"\n [(ngModel)]=\"toggleCumulative\"\n [checked]=\"toggleCumulative\">\n </ion-toggle>\n </div>\n </div>\n\n <!-- Hidden label for change detection (uncomment to debug)\n <div class=\"hidden-label\">{{ onChangeDetectionTest(rainNode) }}</div>\n -->\n </div>\n\n <!-- Historical map section -->\n <div *ngIf=\"toggleHistory\" class=\"period-controls\">\n <raain-date-dynamic (changedDate)=\"onDateChangeInCount($event)\"\n [currentHeight]=\"300\"\n [fetchData]=\"fetchDataWrapper\"\n [points]=\"countPoints\">\n </raain-date-dynamic>\n </div>\n </ion-card-content>\n </ion-card>\n\n <!-- Map performance -->\n <ion-grid class=\"map-performance\">\n <ion-row id=\"progressAndRefresh\">\n <ion-col class=\"provider-selection\" size=\"12\" size-md=\"6\">\n <ion-button (click)=\"openQualityModal()\" class=\"quality-info-button\" fill=\"clear\">\n {{ refreshManager.rainComputationMapVersion }}\n <ion-icon name=\"help-circle-outline\" slot=\"end\"></ion-icon>\n </ion-button>\n <div *ngIf=\"availableProviders.length > 0 || availableTimeSteps.length > 0\" class=\"selection-row\">\n <ion-select (ionChange)=\"onProviderChanged($event)\"\n [value]=\"selectedProvider\"\n interface=\"popover\"\n label=\"Provider\"\n placeholder=\"Select Provider\">\n <ion-select-option *ngFor=\"let provider of availableProviders\" [value]=\"provider\">\n {{ provider }}\n </ion-select-option>\n </ion-select>\n\n <ion-select (ionChange)=\"onTimeStepChanged($event)\"\n [value]=\"selectedTimeStep\"\n interface=\"popover\"\n label=\"Time Step\"\n placeholder=\"Select Time Step\">\n <ion-select-option *ngFor=\"let step of availableTimeSteps\" [value]=\"step\">\n {{ step }} min\n </ion-select-option>\n </ion-select>\n </div>\n </ion-col>\n <ion-col class=\"ion-text-right\" size=\"12\" size-md=\"6\">\n <ion-label class=\"ion-margin-end\">\n <span *ngIf=\"percentOfComputations\">\n {{ percentOfComputations }}% Images\n <i *ngIf=\"countsPeriod.progress\" class=\"progress-indicator\">\n In Progress: {{ countsPeriod.progress }}...\n </i>\n </span>\n <span *ngIf=\"!percentOfComputations\">\n No image available\n </span>\n\n </ion-label>\n\n <ion-button (click)=\"refreshMap()\" [disabled]=\"refreshInProgress\" class=\"refresh-button\">\n Refresh Map\n </ion-button>\n </ion-col>\n </ion-row>\n\n <!-- status update row -->\n <ion-row>\n <!-- Progress col -->\n <ion-progress-bar\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [style.visibility]=\"refreshInProgress ? 'visible' : 'hidden'\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n\n <!-- Error col -->\n <ion-col (click)=\"showFullError = !showFullError\" *ngIf=\"refreshManager.lastError\" class=\"error-row\"\n size=\"12\">\n <div class=\"error-content\">\n <ion-icon class=\"error-icon\" name=\"warning-outline\"></ion-icon>\n <span [class.expanded]=\"showFullError\" class=\"error-text\">\n {{ showFullError ? refreshManager.lastError : truncatedError }}\n </span>\n <ion-icon [name]=\"showFullError ? 'chevron-up' : 'chevron-down'\" class=\"error-caret\"></ion-icon>\n </div>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Map section -->\n <ion-card class=\"map-card\">\n <ion-card-content class=\"map-content\">\n <ion-grid>\n <ion-row *ngIf=\"toggleMap && percentOfImages\">\n <!-- Map component -->\n <ion-col class=\"map-column\" size-lg=\"7\" size-md=\"12\">\n <div class=\"map-container\">\n <raain-map #raainMapRef\n (changedDate)=\"onDateChangeInMap($event)\"\n (changedSum)=\"onSumChangeInMap($event)\"\n (selectedMarker)=\"onGaugeSelectInMap($event)\"\n [coordinates]=\"coordinates\"\n [cumulativeDurationInMinutes]=\"cumulativeDurationInMinutes\"\n [currentHeight]=\"500\"\n [defaultDate]=\"dateShown\"\n [markers]=\"{\n borders,\n gauges: compareManager.gaugesInMap,\n gaugesInCompare: compareManager.gaugesInCompare,\n selectedGauges: compareManager.selectedGauges,\n pixels: compareManager.selectedPixels,\n pixelsSolution: compareManager.pixelsSolutions?.length ? compareManager.pixelsSolutions[0] : [],\n speeds: compareManager.speeds\n }\"\n [showCumulative]=\"toggleCumulative\"\n [showVisiblePixelMarkers]=\"showPixelMarkers\"\n [sumFn]=\"refreshManager.sumFn\"\n [sumValues]=\"refreshManager.sumValues\"\n [timeframeContainers]=\"timeframeContainers\"\n [timeframeDates]=\"timeframeDates\">\n </raain-map>\n </div>\n\n <div class=\"data-column\">\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Image Details</summary>\n <div class=\"details-content\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Date:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDate?.toISOString() }}\n | {{ refreshManager.rainComputationMapDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Water in the map:</span>\n <span class=\"detail-value\">{{ sumDetails }}</span>\n <ion-toggle\n (ionChange)=\"onTogglePixelMarkers()\"\n [(ngModel)]=\"showPixelMarkers\"\n style=\"margin-left: 8px; transform: scale(0.7);\">\n </ion-toggle>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ min:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMin }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ max:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMax }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n\n <!-- Data panel -->\n <ion-col *ngIf=\"!!compareManager.compareVersion\" class=\"data-column\" size-lg=\"5\" size-md=\"12\">\n <div class=\"data-panel\">\n <!-- Compare stack component -->\n <div class=\"compare-stack\">\n <raain-compare-stack\n (selectedPoint)=\"onGaugeSelectInCompare($event)\"\n [compareManager]=\"compareManager\"\n [cumulative]=\"toggleCumulative\">\n </raain-compare-stack>\n </div>\n\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Compare Details</summary>\n <div class=\"details-content\">\n <div [ngClass]=\"{'warning': refreshManager.rainComputationMapDoneDate?.getTime() > compareManager.currentQualityDoneDate?.getTime() + 60000}\"\n class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ compareManager.compareVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Gauges:</span>\n <span class=\"detail-value\">{{ compareManager.gaugesInCompare.length }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Points:</span>\n <span class=\"detail-value\">{{ compareManager.globalComparePoints.length }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n </ion-row>\n <ion-row>\n <!-- Bottom progress bar -->\n <ion-progress-bar *ngIf=\"refreshInProgress\"\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n\n <!-- Gauge values section -->\n <ion-card *ngIf=\"gaugeSelectedPoints.length && gaugeSelectedPoints[0].values.length\" class=\"gauge-card\">\n <ion-card-header>\n <ion-card-title>\n <ion-icon name=\"analytics-outline\"></ion-icon>\n Selected Gauge Data\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <raain-date-focus\n [currentHeight]=\"300\"\n [focusDate]=\"periodBegin\"\n [focusRange]=\"DateRange.DAY\"\n [points]=\"gaugeSelectedPoints\"\n [withoutAll]=\"true\">\n </raain-date-focus>\n </ion-card-content>\n </ion-card>\n\n <!-- Quality Performance Modal -->\n <div (click)=\"closeQualityModal()\" *ngIf=\"showQualityModal\" class=\"quality-modal-overlay\">\n <div (click)=\"$event.stopPropagation()\" class=\"quality-modal-content\">\n <div class=\"quality-modal-header\">\n <h2>Model Quality Performance</h2>\n <ion-button (click)=\"closeQualityModal()\" fill=\"clear\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n <div class=\"quality-modal-body\">\n <div *ngIf=\"qualityIndicatorsLoading\" class=\"quality-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <span>Loading indicators...</span>\n </div>\n <div *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length === 0\" class=\"quality-empty\">\n No quality indicators available for this year.\n </div>\n <table *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length > 0\" class=\"quality-table\">\n <thead>\n <tr>\n <th>Model</th>\n <th>Compare</th>\n <th>Gauges</th>\n <th>Period</th>\n <th>Avg Quality</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let indicator of qualityIndicators\">\n <td>{{ indicator.computingVersion }}</td>\n <td>{{ indicator.qualityVersion }}</td>\n <td>{{ indicator.provider }}<br>{{ indicator.timeStepInMinutes }} min</td>\n <td>{{ formatDate(indicator.startDate) }}<br>{{ formatDate(indicator.endDate) }}</td>\n <td>{{ indicator.averageQuality | number:'1.2-2' }}</td>\n <td>{{ formatDate(indicator.lastUpdatedAt) }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n\n</div>\n", styles: [".raain-details-container{max-width:var(--app-max-width);margin:0 auto;padding:0 0 24px}.raain-details-card{width:100%;margin-bottom:20px}.raain-details-card ion-card-header{border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.raain-details-card ion-card-header ion-card-title{display:flex;align-items:center}.raain-details-card ion-card-header ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary)}.node-info-card{background-color:var(--ion-color-light)}.node-info-card .node-header{display:flex;align-items:center}.node-info-card .node-header .node-status{margin-right:16px}.node-info-card .node-header .node-status ion-icon{font-size:24px}.node-info-card .node-header .node-titles{flex:1}.node-info-card .node-header .node-titles ion-card-title{margin:0;font-size:1.4rem;font-weight:600;color:var(--ion-color-dark)}.node-info-card .node-header .node-titles ion-card-subtitle{padding-left:0;margin:4px 0 0;font-size:.9rem;color:var(--ion-color-medium)}.count-map-card,.period-card{background-color:var(--ion-color-light)}.period-card ion-card-title{display:flex;align-items:center}.period-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.period-row{display:flex;flex-wrap:wrap;align-items:center;gap:16px;position:relative}.now-button{min-width:100px}#all-dates{display:flex;align-items:center;margin-left:auto}#all-dates .toggle-label{margin-right:8px;white-space:nowrap}.refresh-button ion-icon{margin-right:4px;color:var(--ion-color-light)}.provider-selection{display:flex;align-items:center}.quality-info-button{--padding-start: 8px;--padding-end: 8px;font-size:.85rem;color:var(--ion-color-medium)}.quality-info-button ion-icon{font-size:18px;margin-left:4px}.selection-row{display:flex;flex-direction:row;align-items:center;gap:12px}.selection-row ion-select{flex:0 0 auto;min-width:100px;margin-bottom:0;padding:4px 8px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light)}.period-start,.period-duration{display:flex;align-items:center}.toggle-cumulative{display:flex;align-items:center;gap:8px;margin-left:auto}.toggle-cumulative .text-primary{color:var(--ion-color-primary);font-weight:600}.hidden-label{display:none}.datetime-input,#periodDuration,.duration-select{padding:8px 12px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light);font-family:var(--ion-font-family)}.datetime-input:focus,#periodDuration:focus,.duration-select:focus{outline:none;border-color:var(--ion-color-primary)}#periodDuration,.duration-select{min-width:150px}.gauge-card{background-color:var(--ion-color-light)}.gauge-card ion-card-title{display:flex;align-items:center}.gauge-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.error-row{cursor:pointer;background-color:rgba(var(--ion-color-danger-rgb),.1);border-left:3px solid var(--ion-color-danger);margin-top:8px;border-radius:4px;transition:background-color .2s ease}.error-row:hover{background-color:rgba(var(--ion-color-danger-rgb),.15)}.error-row .error-content{display:flex;align-items:flex-start;padding:8px 12px;gap:8px}.error-row .error-icon{color:var(--ion-color-danger);font-size:18px;flex-shrink:0;margin-top:2px}.error-row .error-text{flex:1;color:var(--ion-color-danger-shade);font-size:.9rem;word-break:break-word}.error-row .error-text.expanded{white-space:pre-wrap}.error-row .error-caret{color:var(--ion-color-danger);font-size:16px;flex-shrink:0;margin-top:2px}raain-compare-stack{width:100%;display:block}@media (max-width: 768px){.period-row{flex-direction:row;justify-content:space-between;align-items:center}#all-dates{margin-left:auto;padding-left:8px}#all-dates .toggle-label{font-size:.9rem}.map-header{flex-direction:row;justify-content:space-between;align-items:center;gap:16px}}.quality-modal-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:9999}.quality-modal-content{background-color:var(--ion-background-color, #fff);border-radius:12px;width:95%;max-width:900px;max-height:80vh;overflow:auto;box-shadow:0 4px 24px #0003}.quality-modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-modal-header h2{margin:0;font-size:1.25rem;font-weight:600;color:var(--ion-color-dark)}.quality-modal-header ion-button{--padding-start: 8px;--padding-end: 8px}.quality-modal-body{padding:20px}.quality-table{width:100%;border-collapse:collapse;margin-top:16px;table-layout:fixed}.quality-table th,.quality-table td{width:16.66%;padding:12px 16px;text-align:center;vertical-align:top;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-table th{background-color:rgba(var(--ion-color-primary-rgb),.1);font-weight:600;color:var(--ion-color-dark)}.quality-table td{color:var(--ion-color-dark-tint);font-size:.7rem}.quality-table tbody tr:hover{background-color:rgba(var(--ion-color-primary-rgb),.05)}.quality-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--ion-color-medium)}.quality-loading ion-spinner{margin-bottom:12px}.quality-empty{text-align:center;padding:40px 20px;color:var(--ion-color-medium);font-style:italic}\n"] }]
|
|
2710
|
-
}], ctorParameters: function () { return [{ type: Storage }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { toggleAdmin: [{
|
|
2711
|
-
type: Input
|
|
2712
|
-
}], rainNode: [{
|
|
2713
|
-
type: Input
|
|
2714
|
-
}], compareManager: [{
|
|
2715
|
-
type: Input
|
|
2716
|
-
}], refreshManager: [{
|
|
2717
|
-
type: Input
|
|
2718
|
-
}], profileService: [{
|
|
2719
|
-
type: Input
|
|
2720
|
-
}], radarService: [{
|
|
2721
|
-
type: Input
|
|
2722
|
-
}] } });
|
|
2723
|
-
|
|
2724
|
-
class Cache {
|
|
2725
|
-
constructor() {
|
|
2726
|
-
this._cache = {};
|
|
2727
|
-
}
|
|
2728
|
-
getValue(key, asyncGetter) {
|
|
2746
|
+
// === Count
|
|
2747
|
+
getCounts(rainId, options) {
|
|
2729
2748
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2749
|
+
try {
|
|
2750
|
+
const params = {
|
|
2751
|
+
range: options.range,
|
|
2752
|
+
begin: options.periodBegin.toISOString(),
|
|
2753
|
+
};
|
|
2754
|
+
const queryString = BuildQueryString(params);
|
|
2755
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2756
|
+
key: 'rains',
|
|
2757
|
+
relativePath: rainId + '/counts' + (queryString ? '?' + queryString : ''),
|
|
2758
|
+
verb: 'GET',
|
|
2759
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains/',
|
|
2760
|
+
});
|
|
2761
|
+
const counts = resp.data.counts.result;
|
|
2762
|
+
const percentImages = [], percentRainy = [], percentQ = [];
|
|
2763
|
+
counts.forEach((c) => {
|
|
2764
|
+
var _a, _b, _c;
|
|
2765
|
+
const label = this.setDateComponents(options.periodBegin, c);
|
|
2766
|
+
percentImages.push(new XYType((_a = c.percentImages) !== null && _a !== void 0 ? _a : 0, NaN, NaN, label));
|
|
2767
|
+
percentRainy.push(new XYType((_b = c.percentRainy) !== null && _b !== void 0 ? _b : 0, NaN, NaN, label));
|
|
2768
|
+
percentQ.push(new XYType((_c = c.percentQ) !== null && _c !== void 0 ? _c : 0, NaN, NaN, label));
|
|
2769
|
+
});
|
|
2770
|
+
return {
|
|
2771
|
+
percentImages,
|
|
2772
|
+
percentRainy,
|
|
2773
|
+
percentQ,
|
|
2774
|
+
queueRunning: resp.data.queueRunning,
|
|
2775
|
+
};
|
|
2733
2776
|
}
|
|
2734
|
-
|
|
2735
|
-
|
|
2777
|
+
catch (e) {
|
|
2778
|
+
yield this.checkError(e);
|
|
2736
2779
|
}
|
|
2737
|
-
return
|
|
2780
|
+
return null;
|
|
2738
2781
|
});
|
|
2739
2782
|
}
|
|
2740
|
-
|
|
2741
|
-
this
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2783
|
+
getCountsHour(rainId, options) {
|
|
2784
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2785
|
+
try {
|
|
2786
|
+
const params = {
|
|
2787
|
+
range: 'hour',
|
|
2788
|
+
begin: options.periodBegin.toISOString(),
|
|
2789
|
+
};
|
|
2790
|
+
const queryString = BuildQueryString(params);
|
|
2791
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
2792
|
+
key: 'rains',
|
|
2793
|
+
relativePath: rainId + '/counts' + (queryString ? '?' + queryString : ''),
|
|
2794
|
+
verb: 'GET',
|
|
2795
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains/',
|
|
2796
|
+
});
|
|
2797
|
+
const counts = resp.data.counts.result;
|
|
2798
|
+
const percentImages = [], rainySum = [], rainyCount = [];
|
|
2799
|
+
counts.forEach((c) => {
|
|
2800
|
+
var _a, _b, _c;
|
|
2801
|
+
const label = this.setDateComponents(options.periodBegin, c);
|
|
2802
|
+
percentImages.push(new XYType((_a = c.percentImages) !== null && _a !== void 0 ? _a : 0, NaN, NaN, label));
|
|
2803
|
+
rainyCount.push(new XYType((_b = c.rainyCount) !== null && _b !== void 0 ? _b : 0, NaN, NaN, label));
|
|
2804
|
+
rainySum.push(new XYType((_c = c.rainySum) !== null && _c !== void 0 ? _c : 0, NaN, NaN, label));
|
|
2805
|
+
});
|
|
2806
|
+
return {
|
|
2807
|
+
percentImages,
|
|
2808
|
+
rainyCount,
|
|
2809
|
+
rainySum,
|
|
2810
|
+
queueRunning: resp.data.queueRunning,
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
catch (e) {
|
|
2814
|
+
yield this.checkError(e);
|
|
2815
|
+
}
|
|
2816
|
+
return null;
|
|
2817
|
+
});
|
|
2767
2818
|
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
links: []
|
|
2793
|
-
}),
|
|
2794
|
-
new RadarNode({
|
|
2795
|
-
id: 'dr4',
|
|
2796
|
-
name: 'demo radar D',
|
|
2797
|
-
latitude: 0.14,
|
|
2798
|
-
longitude: -0.74,
|
|
2799
|
-
links: []
|
|
2800
|
-
})];
|
|
2801
|
-
demoNode.rains = [
|
|
2802
|
-
new RainNode({
|
|
2803
|
-
id: '5efd04569cb1f4161bd69dc8',
|
|
2804
|
-
name: 'Demo rain zone A',
|
|
2805
|
-
links: [new Link('radar', 'https://demo/api/radars/5efcfe619cb1f4161bd69dc3')],
|
|
2806
|
-
status: 0,
|
|
2807
|
-
quality: 75,
|
|
2808
|
-
latitude: 48.774569,
|
|
2809
|
-
longitude: 2.008407
|
|
2810
|
-
}),
|
|
2811
|
-
new RainNode({
|
|
2812
|
-
id: 'dz2',
|
|
2813
|
-
name: 'Demo rain zone B',
|
|
2814
|
-
radars: [demoNode.radars[0], demoNode.radars[1]],
|
|
2815
|
-
status: 1,
|
|
2816
|
-
quality: 50,
|
|
2817
|
-
latitude: 48.774569,
|
|
2818
|
-
longitude: 2.008407
|
|
2819
|
-
}),
|
|
2820
|
-
new RainNode({
|
|
2821
|
-
id: 'dz3',
|
|
2822
|
-
name: 'Demo rain zone C',
|
|
2823
|
-
radars: [demoNode.radars[0], demoNode.radars[1]],
|
|
2824
|
-
status: 2,
|
|
2825
|
-
quality: 75,
|
|
2826
|
-
latitude: 48.774569,
|
|
2827
|
-
longitude: 2.008407
|
|
2828
|
-
}),
|
|
2829
|
-
new RainNode({
|
|
2830
|
-
id: 'dz4',
|
|
2831
|
-
name: 'Demo rain zone D',
|
|
2832
|
-
radars: [demoNode.radars[0], demoNode.radars[1]],
|
|
2833
|
-
status: 3,
|
|
2834
|
-
quality: 95,
|
|
2835
|
-
latitude: 48.774569,
|
|
2836
|
-
longitude: 2.008407
|
|
2837
|
-
})];
|
|
2838
|
-
|
|
2839
|
-
demoNode.gauges = [
|
|
2840
|
-
new GaugeNode({
|
|
2841
|
-
id: 'g1',
|
|
2842
|
-
name: 'Gauge A',
|
|
2843
|
-
latitude: 48.7748,
|
|
2844
|
-
longitude: 2.28407,
|
|
2845
|
-
}),
|
|
2846
|
-
new GaugeNode({
|
|
2847
|
-
id: 'g2',
|
|
2848
|
-
name: 'Gauge B',
|
|
2849
|
-
latitude: 48.874569,
|
|
2850
|
-
longitude: 2.108407,
|
|
2851
|
-
})];
|
|
2852
|
-
demoNode.events = [{
|
|
2853
|
-
id: 'e2',
|
|
2854
|
-
title: 'Need support ?',
|
|
2855
|
-
status: 0,
|
|
2856
|
-
red: false,
|
|
2857
|
-
description: 'This area is dedicated to support you and your team. Support is made on Radar or Rain quality, ' +
|
|
2858
|
-
'or any feedback we can have about your production system. Our goal : improving your data.',
|
|
2859
|
-
created: new Date(),
|
|
2860
|
-
modified: new Date()
|
|
2861
|
-
}];
|
|
2862
|
-
demoNode.team = {
|
|
2863
|
-
id: 'p1',
|
|
2864
|
-
email: 'demo@demo.com',
|
|
2865
|
-
name: 'demo guy',
|
|
2866
|
-
description: 'the demo guy'
|
|
2867
|
-
};
|
|
2868
|
-
|
|
2869
|
-
*/
|
|
2870
|
-
return demoNode;
|
|
2819
|
+
// === Computing ===
|
|
2820
|
+
getRainComputationCumulativeCartesianMapById(rainId, rainComputationCumulativeId) {
|
|
2821
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2822
|
+
const params = { format: 'cartesian-map' };
|
|
2823
|
+
const queryString = BuildQueryString(params);
|
|
2824
|
+
try {
|
|
2825
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2826
|
+
key: 'rains',
|
|
2827
|
+
verb: 'GET',
|
|
2828
|
+
relativePath: `${rainId}/cumulatives/${rainComputationCumulativeId}?${queryString}`,
|
|
2829
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2830
|
+
});
|
|
2831
|
+
if (!response.data['cartesian-map']) {
|
|
2832
|
+
return null;
|
|
2833
|
+
}
|
|
2834
|
+
const rainComputationMap = new RainComputationMap(response.data['cartesian-map']);
|
|
2835
|
+
rainComputationMap.name = rainId + '.rain.cartesian-map';
|
|
2836
|
+
return rainComputationMap;
|
|
2837
|
+
}
|
|
2838
|
+
catch (e) {
|
|
2839
|
+
yield this.checkError(e);
|
|
2840
|
+
}
|
|
2841
|
+
return null;
|
|
2842
|
+
});
|
|
2871
2843
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
}
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2844
|
+
getRainComputationCumulativeCumulativesMapById(rainId, rainComputationCumulativeId, cumulativeHours) {
|
|
2845
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2846
|
+
const queryPath = `${rainId}/cumulatives/${rainComputationCumulativeId}/cumulative/${cumulativeHours}`;
|
|
2847
|
+
try {
|
|
2848
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2849
|
+
key: 'rains',
|
|
2850
|
+
verb: 'GET',
|
|
2851
|
+
relativePath: queryPath,
|
|
2852
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2853
|
+
});
|
|
2854
|
+
if (!response.data['cumulative']) {
|
|
2855
|
+
return null;
|
|
2856
|
+
}
|
|
2857
|
+
const rainComputationMap = new RainComputationMap(response.data['cumulative']);
|
|
2858
|
+
rainComputationMap.name = rainId + '.rain.cumulative-cumulative';
|
|
2859
|
+
return rainComputationMap;
|
|
2860
|
+
}
|
|
2861
|
+
catch (e) {
|
|
2862
|
+
yield this.checkError(e);
|
|
2863
|
+
}
|
|
2864
|
+
return null;
|
|
2865
|
+
});
|
|
2880
2866
|
}
|
|
2881
|
-
|
|
2867
|
+
getRainCumulativeCompareByDate(rainNode, rainComputationCumulativeId, date) {
|
|
2882
2868
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
this.
|
|
2887
|
-
|
|
2869
|
+
const params = { date: date.toISOString() };
|
|
2870
|
+
const queryString = BuildQueryString(params);
|
|
2871
|
+
try {
|
|
2872
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2873
|
+
key: 'rains',
|
|
2874
|
+
verb: 'GET',
|
|
2875
|
+
relativePath: `${rainNode.id}/cumulatives/${rainComputationCumulativeId}/compares?${queryString}`,
|
|
2876
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2877
|
+
});
|
|
2878
|
+
const qualityJson = response.data.qualities[0];
|
|
2879
|
+
const rainComputationQuality = new RainComputationQuality(qualityJson);
|
|
2880
|
+
rainComputationQuality.qualitySpeedMatrixContainer =
|
|
2881
|
+
SpeedMatrixContainer.CreateFromJson(rainComputationQuality.qualitySpeedMatrixContainer);
|
|
2882
|
+
return rainComputationQuality;
|
|
2883
|
+
}
|
|
2884
|
+
catch (e) {
|
|
2885
|
+
yield this.checkError(e);
|
|
2886
|
+
}
|
|
2887
|
+
return null;
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
getRainCumulativeCumulativesCompareByDate(rainNode, rainComputationCumulativeId, date, cumulativeHours) {
|
|
2891
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2892
|
+
const params = {
|
|
2893
|
+
date: date.toISOString(),
|
|
2894
|
+
cumulativeHours,
|
|
2895
|
+
};
|
|
2896
|
+
const queryString = BuildQueryString(params);
|
|
2897
|
+
try {
|
|
2898
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2899
|
+
key: 'rains',
|
|
2900
|
+
verb: 'GET',
|
|
2901
|
+
relativePath: `${rainNode.id}/cumulatives/${rainComputationCumulativeId}/compares?${queryString}`,
|
|
2902
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2903
|
+
});
|
|
2904
|
+
const qualityJson = response.data.qualities[0];
|
|
2905
|
+
const rainComputationQuality = new RainComputationQuality(qualityJson);
|
|
2906
|
+
rainComputationQuality.qualitySpeedMatrixContainer =
|
|
2907
|
+
SpeedMatrixContainer.CreateFromJson(rainComputationQuality.qualitySpeedMatrixContainer);
|
|
2908
|
+
return rainComputationQuality;
|
|
2909
|
+
}
|
|
2910
|
+
catch (e) {
|
|
2911
|
+
yield this.checkError(e);
|
|
2912
|
+
}
|
|
2913
|
+
return null;
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
getRainProgress(rainId) {
|
|
2917
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2918
|
+
try {
|
|
2919
|
+
const queryPath = '' + rainId + '/progress';
|
|
2920
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2921
|
+
key: 'rains',
|
|
2922
|
+
verb: 'GET',
|
|
2923
|
+
relativePath: queryPath,
|
|
2924
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2925
|
+
});
|
|
2926
|
+
// return response.data.inProgress;
|
|
2927
|
+
return response.data.inQueue;
|
|
2928
|
+
}
|
|
2929
|
+
catch (e) {
|
|
2930
|
+
yield this.checkError(e);
|
|
2931
|
+
}
|
|
2932
|
+
return 0;
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
getIndicators(rainId) {
|
|
2936
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2937
|
+
try {
|
|
2938
|
+
const params = {
|
|
2939
|
+
cumulative: 'true',
|
|
2940
|
+
};
|
|
2941
|
+
const queryString = BuildQueryString(params);
|
|
2942
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2943
|
+
key: 'rains',
|
|
2944
|
+
verb: 'GET',
|
|
2945
|
+
relativePath: rainId + '/indicators' + (queryString ? '?' + queryString : ''),
|
|
2946
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2947
|
+
});
|
|
2948
|
+
return response.data;
|
|
2949
|
+
}
|
|
2950
|
+
catch (e) {
|
|
2951
|
+
yield this.checkError(e);
|
|
2952
|
+
}
|
|
2953
|
+
return { indicators: [] };
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
// GET /rains/:rainId/cumulatives - List available cumulative periods
|
|
2957
|
+
getCumulativePeriods(rainId, filters) {
|
|
2958
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2959
|
+
try {
|
|
2960
|
+
const params = {};
|
|
2961
|
+
if ((filters === null || filters === void 0 ? void 0 : filters.windowInMinutes) !== undefined) {
|
|
2962
|
+
params.windowInMinutes = filters.windowInMinutes;
|
|
2963
|
+
}
|
|
2964
|
+
if (filters === null || filters === void 0 ? void 0 : filters.provider) {
|
|
2965
|
+
params.provider = filters.provider;
|
|
2966
|
+
}
|
|
2967
|
+
if ((filters === null || filters === void 0 ? void 0 : filters.isReady) !== undefined) {
|
|
2968
|
+
params.isReady = filters.isReady;
|
|
2969
|
+
}
|
|
2970
|
+
if ((filters === null || filters === void 0 ? void 0 : filters.forced) !== undefined) {
|
|
2971
|
+
params.forced = filters.forced;
|
|
2972
|
+
}
|
|
2973
|
+
const queryString = BuildQueryString(params);
|
|
2974
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
2975
|
+
key: 'rains',
|
|
2976
|
+
verb: 'GET',
|
|
2977
|
+
relativePath: rainId + '/cumulatives' + (queryString ? '?' + queryString : ''),
|
|
2978
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
2979
|
+
});
|
|
2980
|
+
return response.data;
|
|
2981
|
+
}
|
|
2982
|
+
catch (e) {
|
|
2983
|
+
yield this.checkError(e);
|
|
2984
|
+
}
|
|
2985
|
+
return { periods: [], total: 0 };
|
|
2986
|
+
});
|
|
2987
|
+
}
|
|
2988
|
+
// POST /rains/:rainId/cumulatives - Trigger cumulative computation
|
|
2989
|
+
createCumulativePeriod(rainId, params) {
|
|
2990
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2991
|
+
try {
|
|
2992
|
+
const body = {
|
|
2993
|
+
periodBegin: params.periodBegin.toISOString(),
|
|
2994
|
+
periodEnd: params.periodEnd.toISOString(),
|
|
2995
|
+
provider: params.provider,
|
|
2996
|
+
confName: params.confName || 'admin',
|
|
2997
|
+
timeStepInMinutes: params.timeStepInMinutes || 5,
|
|
2998
|
+
};
|
|
2999
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
3000
|
+
key: 'rains',
|
|
3001
|
+
verb: 'POST',
|
|
3002
|
+
relativePath: rainId + '/cumulatives',
|
|
3003
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3004
|
+
data: body,
|
|
3005
|
+
});
|
|
3006
|
+
return response.data;
|
|
3007
|
+
}
|
|
3008
|
+
catch (e) {
|
|
3009
|
+
yield this.checkError(e);
|
|
3010
|
+
}
|
|
3011
|
+
return null;
|
|
3012
|
+
});
|
|
3013
|
+
}
|
|
3014
|
+
// === Gauges ===
|
|
3015
|
+
getGauge(gaugeId) {
|
|
3016
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3017
|
+
try {
|
|
3018
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3019
|
+
key: 'gauges',
|
|
3020
|
+
verb: 'GET',
|
|
3021
|
+
relativePath: gaugeId,
|
|
3022
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/gauges',
|
|
3023
|
+
});
|
|
3024
|
+
return new GaugeNode(resp.data);
|
|
3025
|
+
}
|
|
3026
|
+
catch (e) {
|
|
3027
|
+
yield this.checkError(e);
|
|
2888
3028
|
}
|
|
2889
|
-
yield fidjService.put(this.fidjMetaResult);
|
|
2890
3029
|
});
|
|
2891
3030
|
}
|
|
2892
|
-
|
|
3031
|
+
getGauges(rainId, aroundLatLng, pageCount = 1) {
|
|
2893
3032
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
return JSON.parse(JSON.stringify(this.node));
|
|
3033
|
+
const baseParams = {
|
|
3034
|
+
aroundLatLng: `${aroundLatLng.lat},${aroundLatLng.lng}`,
|
|
3035
|
+
rainId,
|
|
3036
|
+
};
|
|
3037
|
+
if (this.asTeamId) {
|
|
3038
|
+
baseParams.teamId = this.asTeamId;
|
|
2901
3039
|
}
|
|
2902
|
-
const
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
3040
|
+
const gauges = [];
|
|
3041
|
+
try {
|
|
3042
|
+
for (let count = 1; count <= pageCount; count++) {
|
|
3043
|
+
const params = Object.assign(Object.assign({}, baseParams), { page: count });
|
|
3044
|
+
const queryString = BuildQueryString(params);
|
|
3045
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3046
|
+
key: 'gauges',
|
|
3047
|
+
verb: 'GET',
|
|
3048
|
+
relativePath: queryString ? '?' + queryString : '',
|
|
3049
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/gauges',
|
|
3050
|
+
});
|
|
3051
|
+
for (const gauge of resp.data.gauges) {
|
|
3052
|
+
gauges.push(new GaugeNodeFilter(gauge));
|
|
3053
|
+
}
|
|
2912
3054
|
}
|
|
2913
3055
|
}
|
|
2914
|
-
|
|
3056
|
+
catch (e) {
|
|
3057
|
+
yield this.checkError(e);
|
|
3058
|
+
}
|
|
3059
|
+
return gauges;
|
|
2915
3060
|
});
|
|
2916
3061
|
}
|
|
2917
|
-
|
|
2918
|
-
this
|
|
3062
|
+
getGaugeMeasures(gaugeId, begin, end) {
|
|
3063
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3064
|
+
const params = {
|
|
3065
|
+
begin: begin.toISOString(),
|
|
3066
|
+
end: end.toISOString(),
|
|
3067
|
+
};
|
|
3068
|
+
const queryString = BuildQueryString(params);
|
|
3069
|
+
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3070
|
+
key: 'gauges',
|
|
3071
|
+
verb: 'GET',
|
|
3072
|
+
relativePath: gaugeId + '/measures' + (queryString ? '?' + queryString : ''),
|
|
3073
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/gauges',
|
|
3074
|
+
});
|
|
3075
|
+
const gaugeMeasures = [];
|
|
3076
|
+
for (const gaugeMeasure of resp.data.gaugeMeasures) {
|
|
3077
|
+
gaugeMeasures.push(new GaugeMeasure(gaugeMeasure));
|
|
3078
|
+
}
|
|
3079
|
+
return gaugeMeasures;
|
|
3080
|
+
});
|
|
2919
3081
|
}
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
3082
|
+
getProviders(rainId) {
|
|
3083
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3084
|
+
try {
|
|
3085
|
+
const response = yield this.fidjService.sendOnEndpoint({
|
|
3086
|
+
verb: 'GET',
|
|
3087
|
+
key: 'rains',
|
|
3088
|
+
relativePath: rainId + '/providers',
|
|
3089
|
+
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3090
|
+
});
|
|
3091
|
+
return {
|
|
3092
|
+
providers: response.data.providers || [],
|
|
3093
|
+
timeStepInMinutes: response.data.timeStepInMinutes || [5, 10, 15, 30, 60],
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
catch (e) {
|
|
3097
|
+
console.error('getProviders error:', e);
|
|
3098
|
+
return {
|
|
3099
|
+
providers: [],
|
|
3100
|
+
timeStepInMinutes: [5, 10, 15, 30, 60],
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
});
|
|
2934
3104
|
}
|
|
2935
|
-
|
|
2936
|
-
|
|
3105
|
+
setRoles(roles) {
|
|
3106
|
+
this.roles = roles;
|
|
2937
3107
|
}
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
3108
|
+
setDateComponents(date, c) {
|
|
3109
|
+
const dateToShow = new Date(date);
|
|
3110
|
+
if (c.year !== undefined) {
|
|
3111
|
+
dateToShow.setUTCFullYear(c.year);
|
|
3112
|
+
}
|
|
3113
|
+
if (c.month !== undefined) {
|
|
3114
|
+
dateToShow.setUTCMonth(c.month - 1);
|
|
3115
|
+
}
|
|
3116
|
+
if (c.day !== undefined) {
|
|
3117
|
+
dateToShow.setUTCDate(c.day);
|
|
3118
|
+
}
|
|
3119
|
+
if (c.hour !== undefined) {
|
|
3120
|
+
dateToShow.setUTCHours(c.hour);
|
|
3121
|
+
}
|
|
3122
|
+
if (c.minute !== undefined) {
|
|
3123
|
+
dateToShow.setUTCMinutes(c.minute);
|
|
3124
|
+
}
|
|
3125
|
+
return dateToShow.toISOString();
|
|
2941
3126
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
3127
|
+
}
|
|
3128
|
+
ProfileService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ProfileService, deps: [{ token: Storage }, { token: i2$1.FidjService }, { token: i3$1.HttpClient }, { token: i4$1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3129
|
+
ProfileService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ProfileService, providedIn: 'root' });
|
|
3130
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ProfileService, decorators: [{
|
|
3131
|
+
type: Injectable,
|
|
3132
|
+
args: [{
|
|
3133
|
+
providedIn: 'root',
|
|
3134
|
+
}]
|
|
3135
|
+
}], ctorParameters: function () { return [{ type: Storage }, { type: i2$1.FidjService }, { type: i3$1.HttpClient }, { type: i4$1.Router }]; } });
|
|
3136
|
+
|
|
3137
|
+
class CumulativeSelectorComponent {
|
|
3138
|
+
constructor(profileService, cdr) {
|
|
3139
|
+
this.profileService = profileService;
|
|
3140
|
+
this.cdr = cdr;
|
|
3141
|
+
this.timeStepInMinutes = 5;
|
|
3142
|
+
this.isAdmin = false;
|
|
3143
|
+
this.periodSelected = new EventEmitter();
|
|
3144
|
+
this.cancelled = new EventEmitter();
|
|
3145
|
+
this.availablePeriods = [];
|
|
3146
|
+
this.baseCumulatives = null;
|
|
3147
|
+
this.loading = true;
|
|
3148
|
+
this.creating = false;
|
|
3149
|
+
this.creationProgress = '';
|
|
3150
|
+
this.errorMessage = '';
|
|
3151
|
+
this.coveragePercent = 0;
|
|
3152
|
+
this.canCreateNew = false;
|
|
3153
|
+
this.currentWindowMinutes = 0;
|
|
3154
|
+
this.POLL_TIMEOUT_MS = 900000; // 900 seconds
|
|
3155
|
+
this.POLL_INTERVAL_MS = 3000;
|
|
2944
3156
|
}
|
|
2945
|
-
|
|
2946
|
-
this
|
|
3157
|
+
ngOnInit() {
|
|
3158
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3159
|
+
this.currentWindowMinutes = Math.round((this.currentPeriodEnd.getTime() - this.currentPeriodBegin.getTime()) / 60000);
|
|
3160
|
+
yield this.loadAvailablePeriods();
|
|
3161
|
+
});
|
|
2947
3162
|
}
|
|
2948
|
-
|
|
3163
|
+
loadAvailablePeriods() {
|
|
2949
3164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3165
|
+
this.loading = true;
|
|
3166
|
+
this.errorMessage = '';
|
|
3167
|
+
this.cdr.markForCheck();
|
|
2950
3168
|
try {
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
3169
|
+
// Fetch all cumulative periods (admin sees all, non-admin sees only customer-launched)
|
|
3170
|
+
const response = yield this.profileService.getCumulativePeriods(this.rainId, {
|
|
3171
|
+
provider: this.provider,
|
|
3172
|
+
forced: this.isAdmin,
|
|
3173
|
+
});
|
|
3174
|
+
// Filter for existing custom cumulatives (window > 0)
|
|
3175
|
+
this.availablePeriods = response.periods.filter((p) => p.windowInMinutes > 0);
|
|
3176
|
+
// Get base cumulatives (window = 0) to check coverage
|
|
3177
|
+
this.baseCumulatives = response.periods.find((p) => p.windowInMinutes === 0);
|
|
3178
|
+
this.calculateCoverage();
|
|
2954
3179
|
}
|
|
2955
3180
|
catch (e) {
|
|
2956
|
-
|
|
3181
|
+
this.errorMessage = 'Failed to load cumulative periods';
|
|
3182
|
+
console.error('Error loading cumulative periods:', e);
|
|
2957
3183
|
}
|
|
3184
|
+
this.loading = false;
|
|
3185
|
+
this.cdr.markForCheck();
|
|
2958
3186
|
});
|
|
2959
3187
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
3188
|
+
calculateCoverage() {
|
|
3189
|
+
if (!this.baseCumulatives) {
|
|
3190
|
+
this.coveragePercent = 0;
|
|
3191
|
+
this.canCreateNew = false;
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
3194
|
+
const baseBegin = new Date(this.baseCumulatives.periodBegin);
|
|
3195
|
+
const baseEnd = new Date(this.baseCumulatives.periodEnd);
|
|
3196
|
+
// Check if current period is within base coverage
|
|
3197
|
+
const currentInRange = this.currentPeriodBegin >= baseBegin && this.currentPeriodEnd <= baseEnd;
|
|
3198
|
+
if (!currentInRange) {
|
|
3199
|
+
this.coveragePercent = 0;
|
|
3200
|
+
this.canCreateNew = false;
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
// Calculate expected number of base cumulatives needed
|
|
3204
|
+
const expectedCount = Math.ceil(this.currentWindowMinutes / this.timeStepInMinutes);
|
|
3205
|
+
// Check if enough base cumulatives exist
|
|
3206
|
+
// We assume coverage is complete if count >= expected (simplified check)
|
|
3207
|
+
if (this.baseCumulatives.count >= expectedCount) {
|
|
3208
|
+
this.coveragePercent = 100;
|
|
3209
|
+
this.canCreateNew = true;
|
|
3210
|
+
}
|
|
3211
|
+
else {
|
|
3212
|
+
this.coveragePercent = Math.round((this.baseCumulatives.count / expectedCount) * 100);
|
|
3213
|
+
this.canCreateNew = this.coveragePercent >= 100;
|
|
3214
|
+
}
|
|
2968
3215
|
}
|
|
2969
|
-
|
|
2970
|
-
this.
|
|
2971
|
-
|
|
3216
|
+
selectPeriod(period) {
|
|
3217
|
+
this.periodSelected.emit({
|
|
3218
|
+
periodBegin: new Date(period.periodBegin),
|
|
3219
|
+
periodEnd: new Date(period.periodEnd),
|
|
3220
|
+
windowInMinutes: period.windowInMinutes,
|
|
3221
|
+
});
|
|
2972
3222
|
}
|
|
2973
|
-
|
|
3223
|
+
createNewPeriod() {
|
|
2974
3224
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2975
|
-
|
|
2976
|
-
if (!fidjKey) {
|
|
2977
|
-
try {
|
|
2978
|
-
yield this.fidjService.loginInDemoMode();
|
|
2979
|
-
this.readyForSync.next(true);
|
|
2980
|
-
}
|
|
2981
|
-
catch (err) {
|
|
2982
|
-
console.error('initFidj catch pb: ', err);
|
|
2983
|
-
}
|
|
3225
|
+
if (!this.canCreateNew || this.creating) {
|
|
2984
3226
|
return;
|
|
2985
3227
|
}
|
|
2986
|
-
|
|
3228
|
+
this.creating = true;
|
|
3229
|
+
this.creationProgress = 'Starting cumulative computation...';
|
|
3230
|
+
this.errorMessage = '';
|
|
3231
|
+
this.cdr.markForCheck();
|
|
2987
3232
|
try {
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
3233
|
+
// Trigger cumulative creation
|
|
3234
|
+
const result = yield this.profileService.createCumulativePeriod(this.rainId, {
|
|
3235
|
+
periodBegin: this.currentPeriodBegin,
|
|
3236
|
+
periodEnd: this.currentPeriodEnd,
|
|
3237
|
+
provider: this.provider,
|
|
3238
|
+
timeStepInMinutes: this.timeStepInMinutes,
|
|
2993
3239
|
});
|
|
2994
|
-
|
|
3240
|
+
if (!result) {
|
|
3241
|
+
throw new Error('Failed to trigger cumulative computation');
|
|
3242
|
+
}
|
|
3243
|
+
this.creationProgress = `Jobs queued. Polling for completion...`;
|
|
3244
|
+
this.cdr.markForCheck();
|
|
3245
|
+
// Poll for completion
|
|
3246
|
+
const success = yield this.pollForCompletion();
|
|
3247
|
+
if (success) {
|
|
3248
|
+
this.periodSelected.emit({
|
|
3249
|
+
periodBegin: this.currentPeriodBegin,
|
|
3250
|
+
periodEnd: this.currentPeriodEnd,
|
|
3251
|
+
windowInMinutes: this.currentWindowMinutes,
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
else {
|
|
3255
|
+
this.errorMessage = 'Timeout waiting for cumulative computation';
|
|
3256
|
+
}
|
|
2995
3257
|
}
|
|
2996
|
-
catch (
|
|
2997
|
-
|
|
3258
|
+
catch (e) {
|
|
3259
|
+
this.errorMessage = `Error: ${e.message || 'Unknown error'}`;
|
|
3260
|
+
console.error('Error creating cumulative:', e);
|
|
2998
3261
|
}
|
|
3262
|
+
this.creating = false;
|
|
3263
|
+
this.cdr.markForCheck();
|
|
2999
3264
|
});
|
|
3000
3265
|
}
|
|
3001
|
-
|
|
3266
|
+
pollForCompletion() {
|
|
3002
3267
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3268
|
+
const startTime = Date.now();
|
|
3269
|
+
while (Date.now() - startTime < this.POLL_TIMEOUT_MS) {
|
|
3270
|
+
try {
|
|
3271
|
+
const progress = yield this.profileService.getRainProgress(this.rainId);
|
|
3272
|
+
if (progress === 0) {
|
|
3273
|
+
// Queue is empty, check if cumulative exists (admin sees all cumulatives)
|
|
3274
|
+
const response = yield this.profileService.getCumulativePeriods(this.rainId, {
|
|
3275
|
+
provider: this.provider,
|
|
3276
|
+
windowInMinutes: this.currentWindowMinutes,
|
|
3277
|
+
forced: this.isAdmin,
|
|
3278
|
+
});
|
|
3279
|
+
const found = response.periods.find((p) => p.windowInMinutes === this.currentWindowMinutes);
|
|
3280
|
+
if (found) {
|
|
3281
|
+
return true;
|
|
3282
|
+
}
|
|
3011
3283
|
}
|
|
3012
|
-
|
|
3284
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
3285
|
+
this.creationProgress = `Computing... (${elapsed}s, queue: ${progress})`;
|
|
3286
|
+
this.cdr.markForCheck();
|
|
3287
|
+
yield this.sleep(this.POLL_INTERVAL_MS);
|
|
3288
|
+
}
|
|
3289
|
+
catch (e) {
|
|
3290
|
+
console.warn('Poll error:', e);
|
|
3291
|
+
yield this.sleep(this.POLL_INTERVAL_MS);
|
|
3013
3292
|
}
|
|
3014
3293
|
}
|
|
3294
|
+
return false;
|
|
3015
3295
|
});
|
|
3016
3296
|
}
|
|
3017
|
-
|
|
3018
|
-
return
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
}
|
|
3297
|
+
sleep(ms) {
|
|
3298
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3299
|
+
}
|
|
3300
|
+
cancel() {
|
|
3301
|
+
this.cancelled.emit();
|
|
3302
|
+
}
|
|
3303
|
+
formatPeriod(period) {
|
|
3304
|
+
const begin = new Date(period.periodBegin);
|
|
3305
|
+
const end = new Date(period.periodEnd);
|
|
3306
|
+
return `${begin.toLocaleString()} → ${end.toLocaleString()}`;
|
|
3307
|
+
}
|
|
3308
|
+
formatWindow(minutes) {
|
|
3309
|
+
if (minutes < 60) {
|
|
3310
|
+
return `${minutes} min`;
|
|
3311
|
+
}
|
|
3312
|
+
const hours = minutes / 60;
|
|
3313
|
+
return hours === 1 ? '1 hour' : `${hours} hours`;
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
CumulativeSelectorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CumulativeSelectorComponent, deps: [{ token: ProfileService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3317
|
+
CumulativeSelectorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CumulativeSelectorComponent, selector: "cumulative-selector", inputs: { rainId: "rainId", currentPeriodBegin: "currentPeriodBegin", currentPeriodEnd: "currentPeriodEnd", provider: "provider", timeStepInMinutes: "timeStepInMinutes", isAdmin: "isAdmin" }, outputs: { periodSelected: "periodSelected", cancelled: "cancelled" }, ngImport: i0, template: "<div class=\"cumulative-selector-overlay\">\n <div class=\"cumulative-selector-modal\">\n <div class=\"modal-header\">\n <h2>Select Cumulative Period</h2>\n <ion-button fill=\"clear\" (click)=\"cancel()\">\n <ion-icon name=\"close\"></ion-icon>\n </ion-button>\n </div>\n\n <div class=\"modal-content\">\n <!-- Loading state -->\n <div *ngIf=\"loading\" class=\"loading-state\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <p>Loading available periods...</p>\n </div>\n\n <!-- Error message -->\n <div *ngIf=\"errorMessage\" class=\"error-message\">\n <ion-icon name=\"warning-outline\"></ion-icon>\n <span>{{ errorMessage }}</span>\n </div>\n\n <!-- Available periods list -->\n <div *ngIf=\"!loading && availablePeriods.length > 0\" class=\"periods-section\">\n <h3>Available Cumulative Periods</h3>\n <ion-list>\n <ion-item *ngFor=\"let period of availablePeriods\"\n button\n (click)=\"selectPeriod(period)\"\n [disabled]=\"creating\">\n <ion-icon name=\"layers-outline\" slot=\"start\"></ion-icon>\n <ion-label>\n <h2>{{ formatWindow(period.windowInMinutes) }}</h2>\n <p>{{ formatPeriod(period) }}</p>\n <p class=\"count-info\">{{ period.count }} cumulative(s)</p>\n </ion-label>\n <ion-icon name=\"chevron-forward\" slot=\"end\"></ion-icon>\n </ion-item>\n </ion-list>\n </div>\n\n <!-- No periods available -->\n <div *ngIf=\"!loading && availablePeriods.length === 0\" class=\"no-periods\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>No cumulative periods available yet.</p>\n </div>\n\n <!-- Create new section (admin only) -->\n <div *ngIf=\"!loading && isAdmin\" class=\"create-section\">\n <h3>Create New Cumulative</h3>\n <div class=\"create-info\">\n <p>\n <strong>Period:</strong>\n {{ currentPeriodBegin?.toLocaleString() }} \u2192 {{ currentPeriodEnd?.toLocaleString() }}\n </p>\n <p>\n <strong>Window:</strong> {{ formatWindow(currentWindowMinutes) }}\n </p>\n <p *ngIf=\"baseCumulatives\" class=\"coverage-info\"\n [class.coverage-ok]=\"coveragePercent >= 100\"\n [class.coverage-warn]=\"coveragePercent > 0 && coveragePercent < 100\"\n [class.coverage-error]=\"coveragePercent === 0\">\n <ion-icon [name]=\"coveragePercent >= 100 ? 'checkmark-circle' : 'alert-circle'\"></ion-icon>\n Base data coverage: {{ coveragePercent }}%\n </p>\n <p *ngIf=\"!baseCumulatives\" class=\"coverage-error\">\n <ion-icon name=\"alert-circle\"></ion-icon>\n No base cumulatives available\n </p>\n </div>\n\n <!-- Creation progress -->\n <div *ngIf=\"creating\" class=\"creation-progress\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <span>{{ creationProgress }}</span>\n </div>\n\n <ion-button [disabled]=\"!canCreateNew || creating\"\n expand=\"block\"\n (click)=\"createNewPeriod()\">\n <ion-icon name=\"add-circle-outline\" slot=\"start\"></ion-icon>\n Create {{ formatWindow(currentWindowMinutes) }} Cumulative\n </ion-button>\n\n <p *ngIf=\"!canCreateNew && !creating\" class=\"create-hint\">\n Create is disabled because base 5-min cumulatives don't fully cover the selected period.\n </p>\n </div>\n </div>\n\n <div class=\"modal-footer\">\n <ion-button fill=\"outline\" (click)=\"cancel()\" [disabled]=\"creating\">\n Cancel\n </ion-button>\n </div>\n </div>\n</div>\n", styles: [".cumulative-selector-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;z-index:1000}.cumulative-selector-modal{background:var(--ion-background-color, #fff);border-radius:12px;max-width:500px;width:90%;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 4px 20px #0000004d}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--ion-border-color, #ddd)}.modal-header h2{margin:0;font-size:1.25rem;font-weight:600}.modal-header ion-button{--padding-start: 8px;--padding-end: 8px}.modal-content{flex:1;overflow-y:auto;padding:16px 20px}.loading-state{display:flex;flex-direction:column;align-items:center;padding:40px 0}.loading-state ion-spinner{margin-bottom:16px}.loading-state p{color:var(--ion-color-medium)}.error-message{display:flex;align-items:center;gap:8px;padding:12px;background:var(--ion-color-danger-tint);color:var(--ion-color-danger);border-radius:8px;margin-bottom:16px}.error-message ion-icon{font-size:1.25rem}.periods-section{margin-bottom:24px}.periods-section h3{font-size:1rem;font-weight:600;margin-bottom:12px;color:var(--ion-color-dark)}.periods-section ion-list{border-radius:8px;overflow:hidden}.periods-section ion-item{--padding-start: 12px;--padding-end: 12px}.periods-section ion-item ion-label h2{font-weight:500}.periods-section ion-item ion-label p{font-size:.85rem;color:var(--ion-color-medium)}.periods-section ion-item ion-label .count-info{font-size:.75rem;color:var(--ion-color-primary)}.no-periods{display:flex;flex-direction:column;align-items:center;padding:24px;text-align:center;color:var(--ion-color-medium)}.no-periods ion-icon{font-size:2rem;margin-bottom:8px}.create-section{border-top:1px solid var(--ion-border-color, #ddd);padding-top:16px}.create-section h3{font-size:1rem;font-weight:600;margin-bottom:12px;color:var(--ion-color-dark)}.create-section .create-info{background:var(--ion-color-light);padding:12px;border-radius:8px;margin-bottom:16px}.create-section .create-info p{margin:4px 0;font-size:.9rem}.create-section .coverage-info{display:flex;align-items:center;gap:6px}.create-section .coverage-info ion-icon{font-size:1.1rem}.create-section .coverage-ok{color:var(--ion-color-success)}.create-section .coverage-warn{color:var(--ion-color-warning)}.create-section .coverage-error{color:var(--ion-color-danger)}.create-section .creation-progress{display:flex;align-items:center;gap:12px;padding:16px;background:var(--ion-color-primary-tint);border-radius:8px;margin-bottom:16px}.create-section .creation-progress ion-spinner{--color: var(--ion-color-primary)}.create-section .creation-progress span{color:var(--ion-color-primary);font-size:.9rem}.create-section .create-hint{font-size:.8rem;color:var(--ion-color-medium);text-align:center;margin-top:8px}.modal-footer{padding:16px 20px;border-top:1px solid var(--ion-border-color, #ddd);display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i4.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i4.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i4.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i4.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i4.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CumulativeSelectorComponent, decorators: [{
|
|
3319
|
+
type: Component,
|
|
3320
|
+
args: [{ selector: 'cumulative-selector', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"cumulative-selector-overlay\">\n <div class=\"cumulative-selector-modal\">\n <div class=\"modal-header\">\n <h2>Select Cumulative Period</h2>\n <ion-button fill=\"clear\" (click)=\"cancel()\">\n <ion-icon name=\"close\"></ion-icon>\n </ion-button>\n </div>\n\n <div class=\"modal-content\">\n <!-- Loading state -->\n <div *ngIf=\"loading\" class=\"loading-state\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <p>Loading available periods...</p>\n </div>\n\n <!-- Error message -->\n <div *ngIf=\"errorMessage\" class=\"error-message\">\n <ion-icon name=\"warning-outline\"></ion-icon>\n <span>{{ errorMessage }}</span>\n </div>\n\n <!-- Available periods list -->\n <div *ngIf=\"!loading && availablePeriods.length > 0\" class=\"periods-section\">\n <h3>Available Cumulative Periods</h3>\n <ion-list>\n <ion-item *ngFor=\"let period of availablePeriods\"\n button\n (click)=\"selectPeriod(period)\"\n [disabled]=\"creating\">\n <ion-icon name=\"layers-outline\" slot=\"start\"></ion-icon>\n <ion-label>\n <h2>{{ formatWindow(period.windowInMinutes) }}</h2>\n <p>{{ formatPeriod(period) }}</p>\n <p class=\"count-info\">{{ period.count }} cumulative(s)</p>\n </ion-label>\n <ion-icon name=\"chevron-forward\" slot=\"end\"></ion-icon>\n </ion-item>\n </ion-list>\n </div>\n\n <!-- No periods available -->\n <div *ngIf=\"!loading && availablePeriods.length === 0\" class=\"no-periods\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>No cumulative periods available yet.</p>\n </div>\n\n <!-- Create new section (admin only) -->\n <div *ngIf=\"!loading && isAdmin\" class=\"create-section\">\n <h3>Create New Cumulative</h3>\n <div class=\"create-info\">\n <p>\n <strong>Period:</strong>\n {{ currentPeriodBegin?.toLocaleString() }} \u2192 {{ currentPeriodEnd?.toLocaleString() }}\n </p>\n <p>\n <strong>Window:</strong> {{ formatWindow(currentWindowMinutes) }}\n </p>\n <p *ngIf=\"baseCumulatives\" class=\"coverage-info\"\n [class.coverage-ok]=\"coveragePercent >= 100\"\n [class.coverage-warn]=\"coveragePercent > 0 && coveragePercent < 100\"\n [class.coverage-error]=\"coveragePercent === 0\">\n <ion-icon [name]=\"coveragePercent >= 100 ? 'checkmark-circle' : 'alert-circle'\"></ion-icon>\n Base data coverage: {{ coveragePercent }}%\n </p>\n <p *ngIf=\"!baseCumulatives\" class=\"coverage-error\">\n <ion-icon name=\"alert-circle\"></ion-icon>\n No base cumulatives available\n </p>\n </div>\n\n <!-- Creation progress -->\n <div *ngIf=\"creating\" class=\"creation-progress\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <span>{{ creationProgress }}</span>\n </div>\n\n <ion-button [disabled]=\"!canCreateNew || creating\"\n expand=\"block\"\n (click)=\"createNewPeriod()\">\n <ion-icon name=\"add-circle-outline\" slot=\"start\"></ion-icon>\n Create {{ formatWindow(currentWindowMinutes) }} Cumulative\n </ion-button>\n\n <p *ngIf=\"!canCreateNew && !creating\" class=\"create-hint\">\n Create is disabled because base 5-min cumulatives don't fully cover the selected period.\n </p>\n </div>\n </div>\n\n <div class=\"modal-footer\">\n <ion-button fill=\"outline\" (click)=\"cancel()\" [disabled]=\"creating\">\n Cancel\n </ion-button>\n </div>\n </div>\n</div>\n", styles: [".cumulative-selector-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;z-index:1000}.cumulative-selector-modal{background:var(--ion-background-color, #fff);border-radius:12px;max-width:500px;width:90%;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 4px 20px #0000004d}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--ion-border-color, #ddd)}.modal-header h2{margin:0;font-size:1.25rem;font-weight:600}.modal-header ion-button{--padding-start: 8px;--padding-end: 8px}.modal-content{flex:1;overflow-y:auto;padding:16px 20px}.loading-state{display:flex;flex-direction:column;align-items:center;padding:40px 0}.loading-state ion-spinner{margin-bottom:16px}.loading-state p{color:var(--ion-color-medium)}.error-message{display:flex;align-items:center;gap:8px;padding:12px;background:var(--ion-color-danger-tint);color:var(--ion-color-danger);border-radius:8px;margin-bottom:16px}.error-message ion-icon{font-size:1.25rem}.periods-section{margin-bottom:24px}.periods-section h3{font-size:1rem;font-weight:600;margin-bottom:12px;color:var(--ion-color-dark)}.periods-section ion-list{border-radius:8px;overflow:hidden}.periods-section ion-item{--padding-start: 12px;--padding-end: 12px}.periods-section ion-item ion-label h2{font-weight:500}.periods-section ion-item ion-label p{font-size:.85rem;color:var(--ion-color-medium)}.periods-section ion-item ion-label .count-info{font-size:.75rem;color:var(--ion-color-primary)}.no-periods{display:flex;flex-direction:column;align-items:center;padding:24px;text-align:center;color:var(--ion-color-medium)}.no-periods ion-icon{font-size:2rem;margin-bottom:8px}.create-section{border-top:1px solid var(--ion-border-color, #ddd);padding-top:16px}.create-section h3{font-size:1rem;font-weight:600;margin-bottom:12px;color:var(--ion-color-dark)}.create-section .create-info{background:var(--ion-color-light);padding:12px;border-radius:8px;margin-bottom:16px}.create-section .create-info p{margin:4px 0;font-size:.9rem}.create-section .coverage-info{display:flex;align-items:center;gap:6px}.create-section .coverage-info ion-icon{font-size:1.1rem}.create-section .coverage-ok{color:var(--ion-color-success)}.create-section .coverage-warn{color:var(--ion-color-warning)}.create-section .coverage-error{color:var(--ion-color-danger)}.create-section .creation-progress{display:flex;align-items:center;gap:12px;padding:16px;background:var(--ion-color-primary-tint);border-radius:8px;margin-bottom:16px}.create-section .creation-progress ion-spinner{--color: var(--ion-color-primary)}.create-section .creation-progress span{color:var(--ion-color-primary);font-size:.9rem}.create-section .create-hint{font-size:.8rem;color:var(--ion-color-medium);text-align:center;margin-top:8px}.modal-footer{padding:16px 20px;border-top:1px solid var(--ion-border-color, #ddd);display:flex;justify-content:flex-end}\n"] }]
|
|
3321
|
+
}], ctorParameters: function () { return [{ type: ProfileService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { rainId: [{
|
|
3322
|
+
type: Input
|
|
3323
|
+
}], currentPeriodBegin: [{
|
|
3324
|
+
type: Input
|
|
3325
|
+
}], currentPeriodEnd: [{
|
|
3326
|
+
type: Input
|
|
3327
|
+
}], provider: [{
|
|
3328
|
+
type: Input
|
|
3329
|
+
}], timeStepInMinutes: [{
|
|
3330
|
+
type: Input
|
|
3331
|
+
}], isAdmin: [{
|
|
3332
|
+
type: Input
|
|
3333
|
+
}], periodSelected: [{
|
|
3334
|
+
type: Output
|
|
3335
|
+
}], cancelled: [{
|
|
3336
|
+
type: Output
|
|
3337
|
+
}] } });
|
|
3338
|
+
|
|
3339
|
+
let TEST_DETECTION = 0;
|
|
3340
|
+
class RaainDetailsComponent {
|
|
3341
|
+
constructor(storage, cdr) {
|
|
3342
|
+
this.storage = storage;
|
|
3343
|
+
this.cdr = cdr;
|
|
3344
|
+
this.availableProviders = [];
|
|
3345
|
+
this.availableTimeSteps = [];
|
|
3346
|
+
this.showPixelMarkers = false;
|
|
3347
|
+
this.qualityIndicators = [];
|
|
3348
|
+
this.qualityIndicatorsLoading = false;
|
|
3349
|
+
this.showCumulativeSelector = false;
|
|
3350
|
+
// Cached computed values (to avoid method calls in templates)
|
|
3351
|
+
this.percentOfComputations = 0;
|
|
3352
|
+
this.percentOfImages = 0;
|
|
3353
|
+
this.truncatedError = '';
|
|
3354
|
+
this.cumulativeDurationInMinutes = 10;
|
|
3355
|
+
this.DateRange = DateRange;
|
|
3356
|
+
// Wrapper function that preserves the async nature of fetchData
|
|
3357
|
+
this.fetchDataWrapper = (focusDate, focusRange) => __awaiter(this, void 0, void 0, function* () {
|
|
3358
|
+
return yield this.fetchData(focusDate, focusRange);
|
|
3031
3359
|
});
|
|
3032
3360
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3361
|
+
static PeriodDisplay(date) {
|
|
3362
|
+
let d = new Date();
|
|
3363
|
+
if (date) {
|
|
3364
|
+
d = new Date(date);
|
|
3365
|
+
const userTimezoneOffset = d.getTimezoneOffset() * 60000;
|
|
3366
|
+
d = new Date(d.getTime() - userTimezoneOffset);
|
|
3367
|
+
}
|
|
3368
|
+
const exampleFormattedDate = '2017-06-01T08:30';
|
|
3369
|
+
return d.toISOString().substring(0, exampleFormattedDate.length);
|
|
3041
3370
|
}
|
|
3042
|
-
|
|
3043
|
-
const
|
|
3044
|
-
|
|
3045
|
-
|
|
3371
|
+
static DateUTC(date) {
|
|
3372
|
+
const hasISOFormat = date.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
|
3373
|
+
let d = new Date(date);
|
|
3374
|
+
if (!hasISOFormat) {
|
|
3375
|
+
const userTimezoneOffset = d.getTimezoneOffset() * 60000;
|
|
3376
|
+
d = new Date(d.getTime() - userTimezoneOffset);
|
|
3377
|
+
}
|
|
3378
|
+
return d;
|
|
3046
3379
|
}
|
|
3047
|
-
|
|
3048
|
-
return
|
|
3380
|
+
static MapCountToDateValue(c) {
|
|
3381
|
+
return {
|
|
3382
|
+
date: RaainDetailsComponent.DateUTC(c.name),
|
|
3383
|
+
value: Math.min(100, c.x),
|
|
3384
|
+
};
|
|
3049
3385
|
}
|
|
3050
|
-
|
|
3386
|
+
openQualityModal() {
|
|
3387
|
+
var _a;
|
|
3051
3388
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3052
|
-
|
|
3389
|
+
this.showQualityModal = true;
|
|
3390
|
+
this.qualityIndicatorsLoading = true;
|
|
3391
|
+
this.qualityIndicators = [];
|
|
3392
|
+
this.cdr.markForCheck();
|
|
3393
|
+
if ((_a = this.rainNode) === null || _a === void 0 ? void 0 : _a.id) {
|
|
3394
|
+
const result = yield this.profileService.getIndicators(this.rainNode.id);
|
|
3395
|
+
this.qualityIndicators = result.indicators;
|
|
3396
|
+
}
|
|
3397
|
+
this.qualityIndicatorsLoading = false;
|
|
3398
|
+
this.cdr.markForCheck();
|
|
3053
3399
|
});
|
|
3054
3400
|
}
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
return this.fidjStorage.storeData(this.fidjService, this.nodeData);
|
|
3058
|
-
});
|
|
3401
|
+
closeQualityModal() {
|
|
3402
|
+
this.showQualityModal = false;
|
|
3059
3403
|
}
|
|
3060
|
-
|
|
3061
|
-
|
|
3404
|
+
formatDate(dateStr) {
|
|
3405
|
+
const date = new Date(dateStr);
|
|
3406
|
+
return (date.toLocaleDateString() +
|
|
3407
|
+
' ' +
|
|
3408
|
+
date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
|
|
3062
3409
|
}
|
|
3063
|
-
|
|
3064
|
-
createNotification(rainId, message) {
|
|
3410
|
+
fetchData(focusDate, focusRange) {
|
|
3065
3411
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3066
|
-
const
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
};
|
|
3070
|
-
try {
|
|
3071
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3072
|
-
key: 'notifications',
|
|
3073
|
-
verb: 'POST',
|
|
3074
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/notifications',
|
|
3075
|
-
data,
|
|
3076
|
-
});
|
|
3077
|
-
return new EventNode(resp.data);
|
|
3412
|
+
const values = [];
|
|
3413
|
+
for (let i = 0; i < 10; i++) {
|
|
3414
|
+
values.push({ date: new Date(2020 + i, 0), value: 10 });
|
|
3078
3415
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3416
|
+
const fakeData = [
|
|
3417
|
+
{
|
|
3418
|
+
label: '% Rainy',
|
|
3419
|
+
style: 'bar',
|
|
3420
|
+
values,
|
|
3421
|
+
},
|
|
3422
|
+
{
|
|
3423
|
+
label: '% Images',
|
|
3424
|
+
style: 'bar',
|
|
3425
|
+
values,
|
|
3426
|
+
},
|
|
3427
|
+
// {
|
|
3428
|
+
// label: '% Quality',
|
|
3429
|
+
// style: 'line',
|
|
3430
|
+
// values,
|
|
3431
|
+
// },
|
|
3432
|
+
];
|
|
3433
|
+
const range = mapDateRangeToString(focusRange);
|
|
3434
|
+
let data = fakeData;
|
|
3435
|
+
if (!this.rainNode) {
|
|
3436
|
+
return data;
|
|
3081
3437
|
}
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
const params = { rain: rainId };
|
|
3089
|
-
const queryString = BuildQueryString(params);
|
|
3090
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3091
|
-
key: 'notifications',
|
|
3092
|
-
verb: 'GET',
|
|
3093
|
-
relativePath: queryString ? '?' + queryString : '',
|
|
3094
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/notifications',
|
|
3438
|
+
if (focusRange === DateRange.CENTURY) {
|
|
3439
|
+
// fake
|
|
3440
|
+
}
|
|
3441
|
+
else if (focusRange === DateRange.HOUR) {
|
|
3442
|
+
const hourCounts = yield this.profileService.getCountsHour(this.rainNode.id, {
|
|
3443
|
+
periodBegin: focusDate,
|
|
3095
3444
|
});
|
|
3096
|
-
|
|
3445
|
+
data = [
|
|
3446
|
+
{
|
|
3447
|
+
label: 'Rainy Count',
|
|
3448
|
+
style: 'line',
|
|
3449
|
+
values: hourCounts.rainyCount.map(RaainDetailsComponent.MapCountToDateValue),
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
label: '% Images',
|
|
3453
|
+
style: 'bar',
|
|
3454
|
+
values: hourCounts.percentImages.map(RaainDetailsComponent.MapCountToDateValue),
|
|
3455
|
+
},
|
|
3456
|
+
{
|
|
3457
|
+
label: 'Rainy Sum',
|
|
3458
|
+
style: 'line',
|
|
3459
|
+
values: hourCounts.rainySum.map(RaainDetailsComponent.MapCountToDateValue),
|
|
3460
|
+
},
|
|
3461
|
+
];
|
|
3097
3462
|
}
|
|
3098
|
-
|
|
3099
|
-
yield this.
|
|
3463
|
+
else {
|
|
3464
|
+
const counts = yield this.profileService.getCounts(this.rainNode.id, {
|
|
3465
|
+
range,
|
|
3466
|
+
periodBegin: focusDate,
|
|
3467
|
+
});
|
|
3468
|
+
data = [
|
|
3469
|
+
{
|
|
3470
|
+
label: '% Rainy',
|
|
3471
|
+
style: 'bar',
|
|
3472
|
+
values: counts.percentRainy.map(RaainDetailsComponent.MapCountToDateValue),
|
|
3473
|
+
},
|
|
3474
|
+
{
|
|
3475
|
+
label: '% Images',
|
|
3476
|
+
style: 'bar',
|
|
3477
|
+
values: counts.percentImages.map(RaainDetailsComponent.MapCountToDateValue),
|
|
3478
|
+
},
|
|
3479
|
+
];
|
|
3100
3480
|
}
|
|
3101
|
-
|
|
3481
|
+
// console.log(`fetchData DONE ${range}`, data);
|
|
3482
|
+
return data;
|
|
3102
3483
|
});
|
|
3103
3484
|
}
|
|
3104
|
-
|
|
3485
|
+
ngOnChanges(changes) {
|
|
3486
|
+
this.change(changes).then((ignored) => { });
|
|
3487
|
+
}
|
|
3488
|
+
ngOnDestroy() {
|
|
3489
|
+
this.cleanAll();
|
|
3490
|
+
}
|
|
3491
|
+
onEnableCountHistoryTab(rain) {
|
|
3105
3492
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
key: 'notifications',
|
|
3109
|
-
verb: 'GET',
|
|
3110
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/notifications',
|
|
3111
|
-
});
|
|
3112
|
-
return resp.data.notifications.map((n) => new EventNode(n));
|
|
3113
|
-
}
|
|
3114
|
-
catch (e) {
|
|
3115
|
-
yield this.checkError(e);
|
|
3493
|
+
if (!this.toggleHistory) {
|
|
3494
|
+
this.countPoints = [];
|
|
3116
3495
|
}
|
|
3117
|
-
return [];
|
|
3118
3496
|
});
|
|
3119
3497
|
}
|
|
3120
|
-
|
|
3121
|
-
|
|
3498
|
+
onPeriodBeginChange(event) {
|
|
3499
|
+
var _a, _b;
|
|
3122
3500
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3123
|
-
const
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
key: 'teams',
|
|
3129
|
-
verb: 'GET',
|
|
3130
|
-
relativePath: queryString ? '?' + queryString : '',
|
|
3131
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/teams',
|
|
3132
|
-
});
|
|
3133
|
-
for (const team of resp.data.teams) {
|
|
3134
|
-
teams.push(new TeamNode(team));
|
|
3135
|
-
}
|
|
3136
|
-
}
|
|
3137
|
-
catch (e) {
|
|
3138
|
-
yield this.checkError(e);
|
|
3139
|
-
}
|
|
3140
|
-
return teams;
|
|
3501
|
+
const newValue = (_b = (_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : this.periodBeginAsString;
|
|
3502
|
+
this.periodBegin = new Date(newValue);
|
|
3503
|
+
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
3504
|
+
this.storage.set('raain-periodBegin', this.periodBegin);
|
|
3505
|
+
yield this.onPeriodDurationChange(event);
|
|
3141
3506
|
});
|
|
3142
3507
|
}
|
|
3143
|
-
|
|
3508
|
+
onPeriodEndChange(event) {
|
|
3509
|
+
var _a, _b;
|
|
3144
3510
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3511
|
+
const newValue = (_b = (_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : this.periodEndAsString;
|
|
3512
|
+
this.periodEnd = new Date(newValue);
|
|
3513
|
+
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
3514
|
+
this.storage.set('raain-periodEnd', this.periodEnd);
|
|
3515
|
+
this.periodBegin = new Date(this.periodEnd.getTime() - this.getDurationInHours() * RaainDetailsComponent.HOUR_MS);
|
|
3516
|
+
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
3517
|
+
this.storage.set('raain-periodBegin', this.periodBegin);
|
|
3518
|
+
this.updateRefreshManagerPeriod();
|
|
3519
|
+
});
|
|
3520
|
+
}
|
|
3521
|
+
onPeriodDurationChange(_event) {
|
|
3522
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3523
|
+
const durationInHours = this.getDurationInHours();
|
|
3524
|
+
this.periodEnd = new Date(this.periodBegin.getTime() + durationInHours * RaainDetailsComponent.HOUR_MS);
|
|
3525
|
+
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
3526
|
+
this.storage.set('raain-periodEnd', this.periodEnd);
|
|
3527
|
+
this.storage.set('raain-periodDurationInHours', durationInHours);
|
|
3528
|
+
this.updateRefreshManagerPeriod();
|
|
3529
|
+
this.updateCumulativeDurationInMinutes();
|
|
3158
3530
|
});
|
|
3159
3531
|
}
|
|
3160
|
-
|
|
3161
|
-
getRadars(name) {
|
|
3532
|
+
onDateChangeInCount(dateChanged) {
|
|
3162
3533
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3163
|
-
const
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3171
|
-
key: 'radars',
|
|
3172
|
-
verb: 'GET',
|
|
3173
|
-
relativePath: queryString ? '?' + queryString : '',
|
|
3174
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/radars',
|
|
3175
|
-
});
|
|
3176
|
-
for (const r of resp.data.radars) {
|
|
3177
|
-
const radar = new RadarNode(r);
|
|
3178
|
-
radars.push(radar);
|
|
3179
|
-
}
|
|
3534
|
+
const dateString = dateChanged.toISOString().substring(0, 11) +
|
|
3535
|
+
dateChanged.toLocaleTimeString().substring(0, 5);
|
|
3536
|
+
this.periodDurationAsString = '1';
|
|
3537
|
+
if (this.toggleCumulative) {
|
|
3538
|
+
// Cumulative: select periodEnd
|
|
3539
|
+
this.periodEndAsString = dateString;
|
|
3540
|
+
yield this.onPeriodEndChange(null);
|
|
3180
3541
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3542
|
+
else {
|
|
3543
|
+
// Granular: select periodBegin
|
|
3544
|
+
this.periodBeginAsString = dateString;
|
|
3545
|
+
yield this.onPeriodBeginChange(null);
|
|
3183
3546
|
}
|
|
3184
|
-
|
|
3547
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3185
3548
|
});
|
|
3186
3549
|
}
|
|
3187
|
-
|
|
3550
|
+
onDateChangeInMap(dateShown) {
|
|
3188
3551
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
verb: 'GET',
|
|
3193
|
-
relativePath: id,
|
|
3194
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/radars',
|
|
3195
|
-
});
|
|
3196
|
-
return new RadarNode(resp.data);
|
|
3197
|
-
}
|
|
3198
|
-
catch (e) {
|
|
3199
|
-
yield this.checkError(e);
|
|
3200
|
-
}
|
|
3201
|
-
return null;
|
|
3552
|
+
this.dateShown = dateShown;
|
|
3553
|
+
yield this.fetchAndUpdateMap();
|
|
3554
|
+
yield this.refreshManager.setReportPeriod(this.dateShown);
|
|
3202
3555
|
});
|
|
3203
3556
|
}
|
|
3204
|
-
|
|
3557
|
+
onSumChangeInMap(sum) {
|
|
3205
3558
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3206
|
-
|
|
3207
|
-
name: radarNode.name,
|
|
3208
|
-
};
|
|
3209
|
-
try {
|
|
3210
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3211
|
-
key: 'radars',
|
|
3212
|
-
relativePath: radarNode.id,
|
|
3213
|
-
verb: 'PUT',
|
|
3214
|
-
data,
|
|
3215
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/radars/',
|
|
3216
|
-
});
|
|
3217
|
-
return new RadarNode(resp.data);
|
|
3218
|
-
}
|
|
3219
|
-
catch (e) {
|
|
3220
|
-
yield this.checkError(e);
|
|
3221
|
-
}
|
|
3559
|
+
this.sumDetails = sum;
|
|
3222
3560
|
});
|
|
3223
3561
|
}
|
|
3224
|
-
|
|
3562
|
+
onGaugeSelectInMap(mapLatLng) {
|
|
3225
3563
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
verb: 'GET',
|
|
3230
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/radars',
|
|
3231
|
-
});
|
|
3232
|
-
const lonelyRadars = [];
|
|
3233
|
-
const radars = resp.data.radars;
|
|
3234
|
-
radars.forEach((radar) => {
|
|
3235
|
-
let found = false;
|
|
3236
|
-
rains.forEach((rain) => {
|
|
3237
|
-
const rdId = rain.getLink(RadarNode.TYPE).getId();
|
|
3238
|
-
if (rdId === radar.id) {
|
|
3239
|
-
found = true;
|
|
3240
|
-
}
|
|
3241
|
-
});
|
|
3242
|
-
if (!found) {
|
|
3243
|
-
lonelyRadars.push(new RadarNode(radar));
|
|
3244
|
-
}
|
|
3245
|
-
});
|
|
3246
|
-
return lonelyRadars;
|
|
3247
|
-
}
|
|
3248
|
-
catch (e) {
|
|
3249
|
-
yield this.checkError(e);
|
|
3564
|
+
const gaugesFiltered = this.compareManager.gaugesInMap.filter((g) => g.lat === mapLatLng.lat && g.lng === mapLatLng.lng);
|
|
3565
|
+
if (gaugesFiltered.length !== 1) {
|
|
3566
|
+
return;
|
|
3250
3567
|
}
|
|
3251
|
-
|
|
3568
|
+
const gaugeSelected = gaugesFiltered[0];
|
|
3569
|
+
yield this.refreshGaugeValues({ id: gaugeSelected.id, name: gaugeSelected.name });
|
|
3570
|
+
yield this.compareManager.selectGauge(gaugeSelected.id, 0);
|
|
3252
3571
|
});
|
|
3253
3572
|
}
|
|
3254
|
-
|
|
3573
|
+
refreshGaugeValues(gaugeSelected) {
|
|
3255
3574
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3575
|
+
const gaugeValueShowBegin = new Date(this.periodBegin.getTime() - RaainDetailsComponent.DAY_MS);
|
|
3576
|
+
const gaugeValueShowEnd = new Date(this.periodEnd.getTime() + RaainDetailsComponent.DAY_MS);
|
|
3577
|
+
gaugeValueShowBegin.setHours(0, 0);
|
|
3578
|
+
gaugeValueShowEnd.setHours(23, 59);
|
|
3579
|
+
const gaugeMeasures = yield this.profileService.getGaugeMeasures(gaugeSelected.id, gaugeValueShowBegin, gaugeValueShowEnd);
|
|
3580
|
+
const gaugeValues = gaugeMeasures.map((gm) => {
|
|
3581
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3582
|
+
const cartesianMeasureValue = new CartesianMeasureValue(gm.values[0]);
|
|
3583
|
+
return {
|
|
3584
|
+
date: gm.date,
|
|
3585
|
+
value: cartesianMeasureValue.getCartesianValues()[0].value,
|
|
3263
3586
|
};
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3274
|
-
});
|
|
3275
|
-
const rainNode = new RainNode(resp.data.timeframeCumulative);
|
|
3276
|
-
rainNode.name += '.radar.timeframeCumulative';
|
|
3277
|
-
return rainNode;
|
|
3278
|
-
}
|
|
3279
|
-
catch (e) {
|
|
3280
|
-
yield this.checkError(e);
|
|
3281
|
-
}
|
|
3282
|
-
return null;
|
|
3587
|
+
});
|
|
3588
|
+
this.gaugeSelectedPoints = [
|
|
3589
|
+
{
|
|
3590
|
+
label: gaugeSelected.name,
|
|
3591
|
+
style: 'bar',
|
|
3592
|
+
values: gaugeValues,
|
|
3593
|
+
},
|
|
3594
|
+
];
|
|
3595
|
+
this.cdr.markForCheck();
|
|
3283
3596
|
});
|
|
3284
3597
|
}
|
|
3285
|
-
|
|
3286
|
-
getRains(name) {
|
|
3598
|
+
onGaugeSelectInCompare(e) {
|
|
3287
3599
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
if (name) {
|
|
3291
|
-
params.name = name;
|
|
3292
|
-
}
|
|
3293
|
-
const queryString = BuildQueryString(params);
|
|
3294
|
-
try {
|
|
3295
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3296
|
-
key: 'rains',
|
|
3297
|
-
verb: 'GET',
|
|
3298
|
-
relativePath: queryString ? '?' + queryString : '',
|
|
3299
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3300
|
-
});
|
|
3301
|
-
for (const rain of resp.data.rains) {
|
|
3302
|
-
rains.push(new RainNode(rain));
|
|
3303
|
-
}
|
|
3304
|
-
}
|
|
3305
|
-
catch (e) {
|
|
3306
|
-
yield this.checkError(e);
|
|
3307
|
-
}
|
|
3308
|
-
return rains;
|
|
3600
|
+
yield this.refreshGaugeValues({ id: e.point.id, name: e.point.name });
|
|
3601
|
+
yield this.compareManager.selectGauge(e.point.id, e.compareIndex);
|
|
3309
3602
|
});
|
|
3310
3603
|
}
|
|
3311
|
-
|
|
3604
|
+
onToggleMap($event) {
|
|
3312
3605
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3606
|
+
// if (this.toggleMap) {
|
|
3607
|
+
// await this.refreshMap();
|
|
3608
|
+
// }
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
onTogglePixelMarkers() {
|
|
3612
|
+
// Toggle is bound to showPixelMarkers, raain-map handles marker display
|
|
3613
|
+
}
|
|
3614
|
+
onCumulativeToggleClick($event) {
|
|
3615
|
+
var _a;
|
|
3616
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3617
|
+
console.log('[CumulativeToggle] click event', {
|
|
3618
|
+
currentValue: this.toggleCumulative,
|
|
3619
|
+
eventType: $event.type,
|
|
3620
|
+
target: (_a = $event.target) === null || _a === void 0 ? void 0 : _a.tagName,
|
|
3621
|
+
});
|
|
3622
|
+
$event.preventDefault();
|
|
3623
|
+
$event.stopPropagation();
|
|
3624
|
+
if (!this.toggleCumulative) {
|
|
3625
|
+
console.log('[CumulativeToggle] showing popup, toggle stays OFF');
|
|
3626
|
+
// Currently OFF, user wants to enable - show selector popup
|
|
3627
|
+
this.showCumulativeSelector = true;
|
|
3628
|
+
// Force reset the toggle visual state after ion-toggle's internal handler
|
|
3629
|
+
setTimeout(() => {
|
|
3630
|
+
if (this.cumulativeToggleRef) {
|
|
3631
|
+
this.cumulativeToggleRef.checked = false;
|
|
3632
|
+
console.log('[CumulativeToggle] force reset toggle to OFF');
|
|
3633
|
+
}
|
|
3634
|
+
}, 0);
|
|
3635
|
+
this.cdr.markForCheck();
|
|
3321
3636
|
}
|
|
3322
|
-
|
|
3323
|
-
|
|
3637
|
+
else {
|
|
3638
|
+
console.log('[CumulativeToggle] turning OFF');
|
|
3639
|
+
// Currently ON, user wants to disable - direct toggle off
|
|
3640
|
+
this.toggleCumulative = false;
|
|
3641
|
+
this.storage.set('raain-toggleCumulative', false);
|
|
3642
|
+
this.dateShown = this.getDateBasedOnCumulativeMode(this.timeframeDates);
|
|
3643
|
+
this.updateCumulativeDurationInMinutes();
|
|
3644
|
+
if (this.toggleMap) {
|
|
3645
|
+
this.updateRefreshManagerPeriod();
|
|
3646
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3647
|
+
}
|
|
3648
|
+
this.cdr.markForCheck();
|
|
3324
3649
|
}
|
|
3325
|
-
|
|
3650
|
+
console.log('[CumulativeToggle] after handler, toggleCumulative =', this.toggleCumulative);
|
|
3326
3651
|
});
|
|
3327
3652
|
}
|
|
3328
|
-
|
|
3329
|
-
getCounts(rainId, options) {
|
|
3653
|
+
onCumulativePeriodSelected(selection) {
|
|
3330
3654
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
percentRainy,
|
|
3355
|
-
percentQ,
|
|
3356
|
-
queueRunning: resp.data.queueRunning,
|
|
3357
|
-
};
|
|
3358
|
-
}
|
|
3359
|
-
catch (e) {
|
|
3360
|
-
yield this.checkError(e);
|
|
3655
|
+
this.showCumulativeSelector = false;
|
|
3656
|
+
// Update period to match selection
|
|
3657
|
+
this.periodBegin = selection.periodBegin;
|
|
3658
|
+
this.periodEnd = selection.periodEnd;
|
|
3659
|
+
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
3660
|
+
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
3661
|
+
// Calculate duration in hours
|
|
3662
|
+
const durationMs = this.periodEnd.getTime() - this.periodBegin.getTime();
|
|
3663
|
+
const durationHours = durationMs / RaainDetailsComponent.HOUR_MS;
|
|
3664
|
+
this.periodDurationAsString = String(durationHours);
|
|
3665
|
+
// Store cumulative period
|
|
3666
|
+
this.storage.set('raain-periodBegin', this.periodBegin);
|
|
3667
|
+
this.storage.set('raain-periodEnd', this.periodEnd);
|
|
3668
|
+
this.storage.set('raain-periodDurationInHours', durationHours);
|
|
3669
|
+
// Enable cumulative mode
|
|
3670
|
+
this.toggleCumulative = true;
|
|
3671
|
+
this.storage.set('raain-toggleCumulative', true);
|
|
3672
|
+
this.dateShown = this.getDateBasedOnCumulativeMode(this.timeframeDates);
|
|
3673
|
+
this.updateCumulativeDurationInMinutes();
|
|
3674
|
+
// Refresh map
|
|
3675
|
+
if (this.toggleMap) {
|
|
3676
|
+
this.updateRefreshManagerPeriod();
|
|
3677
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3361
3678
|
}
|
|
3362
|
-
|
|
3679
|
+
this.cdr.markForCheck();
|
|
3363
3680
|
});
|
|
3364
3681
|
}
|
|
3365
|
-
|
|
3682
|
+
onCumulativeSelectorCancelled() {
|
|
3683
|
+
console.log('[CumulativeToggle] cancelled, toggleCumulative =', this.toggleCumulative);
|
|
3684
|
+
this.showCumulativeSelector = false;
|
|
3685
|
+
this.cdr.markForCheck();
|
|
3686
|
+
}
|
|
3687
|
+
updateCumulativeDurationInMinutes() {
|
|
3688
|
+
if (this.toggleCumulative) {
|
|
3689
|
+
// Cumulative mode: use period duration
|
|
3690
|
+
this.cumulativeDurationInMinutes = parseFloat(this.periodDurationAsString) * 60;
|
|
3691
|
+
}
|
|
3692
|
+
else {
|
|
3693
|
+
// Granular mode: use selectedTimeStep
|
|
3694
|
+
this.cumulativeDurationInMinutes = this.selectedTimeStep || 10;
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
onProviderChanged($event) {
|
|
3698
|
+
var _a;
|
|
3366
3699
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
begin: options.periodBegin.toISOString(),
|
|
3371
|
-
};
|
|
3372
|
-
const queryString = BuildQueryString(params);
|
|
3373
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3374
|
-
key: 'rains',
|
|
3375
|
-
relativePath: rainId + '/counts' + (queryString ? '?' + queryString : ''),
|
|
3376
|
-
verb: 'GET',
|
|
3377
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains/',
|
|
3378
|
-
});
|
|
3379
|
-
const counts = resp.data.counts.result;
|
|
3380
|
-
const percentImages = [], rainySum = [], rainyCount = [];
|
|
3381
|
-
counts.forEach((c) => {
|
|
3382
|
-
var _a, _b, _c;
|
|
3383
|
-
const label = this.setDateComponents(options.periodBegin, c);
|
|
3384
|
-
percentImages.push(new XYType((_a = c.percentImages) !== null && _a !== void 0 ? _a : 0, NaN, NaN, label));
|
|
3385
|
-
rainyCount.push(new XYType((_b = c.rainyCount) !== null && _b !== void 0 ? _b : 0, NaN, NaN, label));
|
|
3386
|
-
rainySum.push(new XYType((_c = c.rainySum) !== null && _c !== void 0 ? _c : 0, NaN, NaN, label));
|
|
3387
|
-
});
|
|
3388
|
-
return {
|
|
3389
|
-
percentImages,
|
|
3390
|
-
rainyCount,
|
|
3391
|
-
rainySum,
|
|
3392
|
-
queueRunning: resp.data.queueRunning,
|
|
3393
|
-
};
|
|
3394
|
-
}
|
|
3395
|
-
catch (e) {
|
|
3396
|
-
yield this.checkError(e);
|
|
3397
|
-
}
|
|
3398
|
-
return null;
|
|
3700
|
+
this.selectedProvider = (_a = $event === null || $event === void 0 ? void 0 : $event.detail) === null || _a === void 0 ? void 0 : _a.value;
|
|
3701
|
+
this.storage.set('raain-selectedProvider', this.selectedProvider);
|
|
3702
|
+
yield this.applyRefreshManagerSettings();
|
|
3399
3703
|
});
|
|
3400
3704
|
}
|
|
3401
|
-
|
|
3402
|
-
|
|
3705
|
+
onTimeStepChanged($event) {
|
|
3706
|
+
var _a;
|
|
3403
3707
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
key: 'rains',
|
|
3409
|
-
verb: 'GET',
|
|
3410
|
-
relativePath: `${rainId}/cumulatives/${rainComputationCumulativeId}?${queryString}`,
|
|
3411
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3412
|
-
});
|
|
3413
|
-
if (!response.data['cartesian-map']) {
|
|
3414
|
-
return null;
|
|
3415
|
-
}
|
|
3416
|
-
const rainComputationMap = new RainComputationMap(response.data['cartesian-map']);
|
|
3417
|
-
rainComputationMap.name = rainId + '.rain.cartesian-map';
|
|
3418
|
-
return rainComputationMap;
|
|
3419
|
-
}
|
|
3420
|
-
catch (e) {
|
|
3421
|
-
yield this.checkError(e);
|
|
3422
|
-
}
|
|
3423
|
-
return null;
|
|
3708
|
+
this.selectedTimeStep = (_a = $event === null || $event === void 0 ? void 0 : $event.detail) === null || _a === void 0 ? void 0 : _a.value;
|
|
3709
|
+
this.storage.set('raain-selectedTimeStep', this.selectedTimeStep);
|
|
3710
|
+
this.updateCumulativeDurationInMinutes();
|
|
3711
|
+
yield this.applyRefreshManagerSettings();
|
|
3424
3712
|
});
|
|
3425
3713
|
}
|
|
3426
|
-
|
|
3714
|
+
loadProviders() {
|
|
3715
|
+
var _a;
|
|
3427
3716
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3428
|
-
|
|
3717
|
+
if (!((_a = this.rainNode) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
3718
|
+
return;
|
|
3719
|
+
}
|
|
3429
3720
|
try {
|
|
3430
|
-
const
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
rainComputationMap.name = rainId + '.rain.cumulative-cumulative';
|
|
3441
|
-
return rainComputationMap;
|
|
3721
|
+
const result = yield this.profileService.getProviders(this.rainNode.id);
|
|
3722
|
+
this.availableProviders = result.providers;
|
|
3723
|
+
this.availableTimeSteps = result.timeStepInMinutes;
|
|
3724
|
+
// Load saved selections or use defaults
|
|
3725
|
+
this.selectedProvider =
|
|
3726
|
+
this.storage.get('raain-selectedProvider') ||
|
|
3727
|
+
(this.availableProviders.length > 0 ? this.availableProviders[0] : 'Raain');
|
|
3728
|
+
this.selectedTimeStep =
|
|
3729
|
+
this.storage.get('raain-selectedTimeStep') ||
|
|
3730
|
+
(this.availableTimeSteps.length > 0 ? this.availableTimeSteps[0] : 10);
|
|
3442
3731
|
}
|
|
3443
3732
|
catch (e) {
|
|
3444
|
-
|
|
3733
|
+
// Set fallback values
|
|
3734
|
+
this.availableProviders = ['Raain'];
|
|
3735
|
+
this.availableTimeSteps = [5, 10, 15, 30, 60];
|
|
3736
|
+
this.selectedProvider = 'Raain';
|
|
3737
|
+
this.selectedTimeStep = 10;
|
|
3445
3738
|
}
|
|
3446
|
-
|
|
3739
|
+
// Set provider and timeStep on refreshManager
|
|
3740
|
+
if (this.refreshManager) {
|
|
3741
|
+
this.refreshManager.provider = this.selectedProvider;
|
|
3742
|
+
this.refreshManager.timeStepInMinutes = this.selectedTimeStep;
|
|
3743
|
+
}
|
|
3744
|
+
this.cdr.markForCheck();
|
|
3447
3745
|
});
|
|
3448
3746
|
}
|
|
3449
|
-
|
|
3747
|
+
onChangeDetectionTest(rainNode) {
|
|
3748
|
+
console.log(TEST_DETECTION++, 'onChangeDetectionTest');
|
|
3749
|
+
return '';
|
|
3750
|
+
}
|
|
3751
|
+
updateTruncatedError() {
|
|
3752
|
+
var _a;
|
|
3753
|
+
const error = ((_a = this.refreshManager) === null || _a === void 0 ? void 0 : _a.lastError) || '';
|
|
3754
|
+
if (error.length <= 80) {
|
|
3755
|
+
this.truncatedError = error;
|
|
3756
|
+
return;
|
|
3757
|
+
}
|
|
3758
|
+
this.truncatedError = error.substring(0, 80) + '...';
|
|
3759
|
+
}
|
|
3760
|
+
updateCachedValues() {
|
|
3761
|
+
this.updatePercentOfImages();
|
|
3762
|
+
this.updatePercentOfComputations();
|
|
3763
|
+
this.updateTruncatedError();
|
|
3764
|
+
this.updateCumulativeDurationInMinutes();
|
|
3765
|
+
}
|
|
3766
|
+
refreshMap() {
|
|
3450
3767
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3459
|
-
});
|
|
3460
|
-
const qualityJson = response.data.qualities[0];
|
|
3461
|
-
const rainComputationQuality = new RainComputationQuality(qualityJson);
|
|
3462
|
-
rainComputationQuality.qualitySpeedMatrixContainer =
|
|
3463
|
-
SpeedMatrixContainer.CreateFromJson(rainComputationQuality.qualitySpeedMatrixContainer);
|
|
3464
|
-
return rainComputationQuality;
|
|
3465
|
-
}
|
|
3466
|
-
catch (e) {
|
|
3467
|
-
yield this.checkError(e);
|
|
3468
|
-
}
|
|
3469
|
-
return null;
|
|
3768
|
+
this.gaugeSelectedPoints = [];
|
|
3769
|
+
this.dateShown = this.getDateBasedOnCumulativeMode();
|
|
3770
|
+
this.borders = [];
|
|
3771
|
+
this.compareManager.cleanAll();
|
|
3772
|
+
yield this.compareManager.setGaugesInMap();
|
|
3773
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3774
|
+
this.cdr.markForCheck();
|
|
3470
3775
|
});
|
|
3471
3776
|
}
|
|
3472
|
-
|
|
3777
|
+
setPeriodOfNow() {
|
|
3473
3778
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3474
|
-
const
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3779
|
+
const last30mn = new Date();
|
|
3780
|
+
last30mn.setMinutes(last30mn.getMinutes() - 30);
|
|
3781
|
+
this.periodBeginAsString =
|
|
3782
|
+
last30mn.toISOString().substring(0, 11) + last30mn.toLocaleTimeString().substring(0, 5);
|
|
3783
|
+
this.periodDurationAsString = '1';
|
|
3784
|
+
yield this.onPeriodBeginChange(null);
|
|
3785
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
updatePercentOfImages() {
|
|
3789
|
+
var _a, _b;
|
|
3790
|
+
if (!((_b = (_a = this.countsPeriod) === null || _a === void 0 ? void 0 : _a.percentImages) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
3791
|
+
this.percentOfImages = 0;
|
|
3792
|
+
return;
|
|
3793
|
+
}
|
|
3794
|
+
const duringPeriod = this.countsPeriod.percentImages.filter((a) => this.periodBegin.getTime() <= new Date(a.name).getTime() &&
|
|
3795
|
+
new Date(a.name).getTime() <= this.periodEnd.getTime());
|
|
3796
|
+
const summed = duringPeriod.reduce((a, b) => { var _a; return a + ((_a = b.x) !== null && _a !== void 0 ? _a : 0); }, 0);
|
|
3797
|
+
this.percentOfImages = Math.round(summed / duringPeriod.length);
|
|
3798
|
+
}
|
|
3799
|
+
updatePercentOfComputations() {
|
|
3800
|
+
var _a;
|
|
3801
|
+
const timeline = (_a = this.refreshManager) === null || _a === void 0 ? void 0 : _a.getTimelineFrameSet();
|
|
3802
|
+
if (!(timeline === null || timeline === void 0 ? void 0 : timeline.length)) {
|
|
3803
|
+
this.percentOfComputations = 0;
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
const timelineWithComputation = timeline.filter((a) => !!a.rainComputationCumulativeId || !!a.rainComputationId);
|
|
3807
|
+
const ratio = timelineWithComputation.length / timeline.length;
|
|
3808
|
+
this.percentOfComputations = Math.round(ratio * 100);
|
|
3809
|
+
}
|
|
3810
|
+
getDateBasedOnCumulativeMode(fallbackDates) {
|
|
3811
|
+
if ((fallbackDates === null || fallbackDates === void 0 ? void 0 : fallbackDates.length) > 0) {
|
|
3812
|
+
const dateExists = fallbackDates.some((d) => { var _a; return d.getTime() === ((_a = this.dateShown) === null || _a === void 0 ? void 0 : _a.getTime()); });
|
|
3813
|
+
if (!dateExists) {
|
|
3814
|
+
return this.toggleCumulative
|
|
3815
|
+
? fallbackDates[fallbackDates.length - 1]
|
|
3816
|
+
: fallbackDates[0];
|
|
3494
3817
|
}
|
|
3495
|
-
|
|
3818
|
+
}
|
|
3819
|
+
return this.toggleCumulative ? this.periodEnd : this.periodBegin;
|
|
3820
|
+
}
|
|
3821
|
+
getCumulativeHours() {
|
|
3822
|
+
return this.toggleCumulative ? parseFloat(this.periodDurationAsString) : 0;
|
|
3823
|
+
}
|
|
3824
|
+
getDurationInHours() {
|
|
3825
|
+
return parseFloat(this.periodDurationAsString);
|
|
3826
|
+
}
|
|
3827
|
+
updateRefreshManagerPeriod() {
|
|
3828
|
+
this.refreshManager.cumulative = this.toggleCumulative;
|
|
3829
|
+
// Align dates to 5-minute boundaries (floor) for consistency with raain-ground
|
|
3830
|
+
const alignTo5minFloor = (date) => {
|
|
3831
|
+
const minutes = date.getMinutes();
|
|
3832
|
+
const alignedMinutes = Math.floor(minutes / 5) * 5;
|
|
3833
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), alignedMinutes, 0, 0);
|
|
3834
|
+
};
|
|
3835
|
+
this.refreshManager.period = {
|
|
3836
|
+
begin: alignTo5minFloor(this.periodBegin),
|
|
3837
|
+
end: alignTo5minFloor(this.periodEnd),
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
fetchAndUpdateMap() {
|
|
3841
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3842
|
+
yield this.refreshManager.fetch(this.dateShown, this.toggleGaugeMeasures, this.getCumulativeHours());
|
|
3843
|
+
this.currentTimeframeTarget = this.refreshManager.getTimelineSelectedFrameSet();
|
|
3844
|
+
this.cdr.markForCheck();
|
|
3496
3845
|
});
|
|
3497
3846
|
}
|
|
3498
|
-
|
|
3847
|
+
applyRefreshManagerSettings() {
|
|
3499
3848
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
const response = yield this.fidjService.sendOnEndpoint({
|
|
3503
|
-
key: 'rains',
|
|
3504
|
-
verb: 'GET',
|
|
3505
|
-
relativePath: queryPath,
|
|
3506
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3507
|
-
});
|
|
3508
|
-
// return response.data.inProgress;
|
|
3509
|
-
return response.data.inQueue;
|
|
3510
|
-
}
|
|
3511
|
-
catch (e) {
|
|
3512
|
-
yield this.checkError(e);
|
|
3849
|
+
if (!this.refreshManager) {
|
|
3850
|
+
return;
|
|
3513
3851
|
}
|
|
3514
|
-
|
|
3852
|
+
this.refreshManager.provider = this.selectedProvider;
|
|
3853
|
+
this.refreshManager.timeStepInMinutes = this.selectedTimeStep;
|
|
3854
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3515
3855
|
});
|
|
3516
3856
|
}
|
|
3517
|
-
|
|
3857
|
+
cleanAll() {
|
|
3858
|
+
var _a, _b;
|
|
3859
|
+
this.borders = [];
|
|
3860
|
+
this.isAdmin = false;
|
|
3861
|
+
this.timeframeContainers = new TimeframeContainers([]);
|
|
3862
|
+
this.currentTimeframeTarget = null;
|
|
3863
|
+
this.timeframeDates = [];
|
|
3864
|
+
this.countPoints = [];
|
|
3865
|
+
this.countsPeriod = { progress: 0, queueRunning: 0, percentImages: [] };
|
|
3866
|
+
this.gaugeSelectedPoints = [];
|
|
3867
|
+
this.toggleHistory = false;
|
|
3868
|
+
this.toggleMap = true;
|
|
3869
|
+
this.toggleCompare = false;
|
|
3870
|
+
this.toggleGaugeMeasures = false;
|
|
3871
|
+
this.toggleRemoveCompareDuplicate = true;
|
|
3872
|
+
this.toggleCumulative = this.storage.get('raain-toggleCumulative');
|
|
3873
|
+
this.periodBegin = new Date(this.storage.get('raain-periodBegin'));
|
|
3874
|
+
this.periodEnd = new Date(this.storage.get('raain-periodEnd'));
|
|
3875
|
+
this.periodBeginAsString = RaainDetailsComponent.PeriodDisplay(this.periodBegin);
|
|
3876
|
+
this.periodEndAsString = RaainDetailsComponent.PeriodDisplay(this.periodEnd);
|
|
3877
|
+
const durationMs = this.periodEnd.getTime() - this.periodBegin.getTime();
|
|
3878
|
+
this.periodDurationAsString = '' + durationMs / RaainDetailsComponent.HOUR_MS;
|
|
3879
|
+
this.dateShown = this.getDateBasedOnCumulativeMode();
|
|
3880
|
+
this.refreshInProgress = false;
|
|
3881
|
+
this.showFullError = false;
|
|
3882
|
+
this.showQualityModal = false;
|
|
3883
|
+
this.qualityIndicators = [];
|
|
3884
|
+
this.qualityIndicatorsLoading = false;
|
|
3885
|
+
(_a = this.compareManager) === null || _a === void 0 ? void 0 : _a.cleanAll();
|
|
3886
|
+
(_b = this.refreshManager) === null || _b === void 0 ? void 0 : _b.cleanAll();
|
|
3887
|
+
}
|
|
3888
|
+
init() {
|
|
3518
3889
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
const queryString = BuildQueryString(params);
|
|
3524
|
-
const response = yield this.fidjService.sendOnEndpoint({
|
|
3525
|
-
key: 'rains',
|
|
3526
|
-
verb: 'GET',
|
|
3527
|
-
relativePath: rainId + '/indicators' + (queryString ? '?' + queryString : ''),
|
|
3528
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/rains',
|
|
3529
|
-
});
|
|
3530
|
-
return response.data;
|
|
3531
|
-
}
|
|
3532
|
-
catch (e) {
|
|
3533
|
-
yield this.checkError(e);
|
|
3534
|
-
}
|
|
3535
|
-
return { indicators: [] };
|
|
3890
|
+
this.cleanAll();
|
|
3891
|
+
this.updateCachedValues();
|
|
3892
|
+
yield this.initRain();
|
|
3893
|
+
this.cdr.markForCheck();
|
|
3536
3894
|
});
|
|
3537
3895
|
}
|
|
3538
|
-
|
|
3539
|
-
getGauge(gaugeId) {
|
|
3896
|
+
initRain() {
|
|
3540
3897
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
key: 'gauges',
|
|
3544
|
-
verb: 'GET',
|
|
3545
|
-
relativePath: gaugeId,
|
|
3546
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/gauges',
|
|
3547
|
-
});
|
|
3548
|
-
return new GaugeNode(resp.data);
|
|
3898
|
+
if (!this.rainNode) {
|
|
3899
|
+
return;
|
|
3549
3900
|
}
|
|
3550
|
-
|
|
3551
|
-
|
|
3901
|
+
this.isAdmin = this.profileService.isAdmin();
|
|
3902
|
+
this.refreshManager.rainNode = this.rainNode;
|
|
3903
|
+
this.compareManager.rainNode = this.rainNode;
|
|
3904
|
+
// Load providers and set on refreshManager
|
|
3905
|
+
yield this.loadProviders();
|
|
3906
|
+
this.refreshManager.setMethods(this.onRefreshInProgress.bind(this), this.onRefreshDone.bind(this), this.onFetchDone.bind(this));
|
|
3907
|
+
const center = this.rainNode.getCenter();
|
|
3908
|
+
this.coordinates = new MapLatLng(center.lat, center.lng);
|
|
3909
|
+
this.teamNode = yield this.profileService.getTeam(this.rainNode.getLink(TeamNode.TYPE).getId());
|
|
3910
|
+
// Load all gauges linked to the rainNode on map
|
|
3911
|
+
yield this.compareManager.setGaugesInMap();
|
|
3912
|
+
if (this.periodBegin && this.periodEnd) {
|
|
3913
|
+
this.updateRefreshManagerPeriod();
|
|
3914
|
+
yield this.refreshManager.refresh(false, this.toggleAdmin);
|
|
3552
3915
|
}
|
|
3553
3916
|
});
|
|
3554
3917
|
}
|
|
3555
|
-
|
|
3918
|
+
onRefreshInProgress(countPeriods, timeframeDates) {
|
|
3556
3919
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
});
|
|
3575
|
-
for (const gauge of resp.data.gauges) {
|
|
3576
|
-
gauges.push(new GaugeNodeFilter(gauge));
|
|
3577
|
-
}
|
|
3920
|
+
this.refreshInProgress = true;
|
|
3921
|
+
this.countsPeriod = countPeriods;
|
|
3922
|
+
this.timeframeDates = timeframeDates;
|
|
3923
|
+
this.updateCachedValues();
|
|
3924
|
+
this.cdr.markForCheck();
|
|
3925
|
+
});
|
|
3926
|
+
}
|
|
3927
|
+
onRefreshDone(timeframeDates) {
|
|
3928
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3929
|
+
this.timeframeDates = timeframeDates;
|
|
3930
|
+
this.refreshInProgress = false;
|
|
3931
|
+
this.updateCachedValues();
|
|
3932
|
+
this.cdr.markForCheck();
|
|
3933
|
+
if (this.toggleMap && timeframeDates.length > 0) {
|
|
3934
|
+
this.dateShown = this.getDateBasedOnCumulativeMode(timeframeDates);
|
|
3935
|
+
if (this.dateShown) {
|
|
3936
|
+
yield this.fetchAndUpdateMap();
|
|
3578
3937
|
}
|
|
3579
3938
|
}
|
|
3580
|
-
catch (e) {
|
|
3581
|
-
yield this.checkError(e);
|
|
3582
|
-
}
|
|
3583
|
-
return gauges;
|
|
3584
3939
|
});
|
|
3585
3940
|
}
|
|
3586
|
-
|
|
3941
|
+
onFetchDone(timeframeContainers) {
|
|
3587
3942
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
end: end.toISOString(),
|
|
3591
|
-
};
|
|
3592
|
-
const queryString = BuildQueryString(params);
|
|
3593
|
-
const resp = yield this.fidjService.sendOnEndpoint({
|
|
3594
|
-
key: 'gauges',
|
|
3595
|
-
verb: 'GET',
|
|
3596
|
-
relativePath: gaugeId + '/measures' + (queryString ? '?' + queryString : ''),
|
|
3597
|
-
defaultKeyUrl: this.defaultUrlForAPI + '/gauges',
|
|
3598
|
-
});
|
|
3599
|
-
const gaugeMeasures = [];
|
|
3600
|
-
for (const gaugeMeasure of resp.data.gaugeMeasures) {
|
|
3601
|
-
gaugeMeasures.push(new GaugeMeasure(gaugeMeasure));
|
|
3943
|
+
if (timeframeContainers) {
|
|
3944
|
+
this.timeframeContainers = timeframeContainers;
|
|
3602
3945
|
}
|
|
3603
|
-
|
|
3946
|
+
this.cdr.markForCheck();
|
|
3604
3947
|
});
|
|
3605
3948
|
}
|
|
3606
|
-
|
|
3949
|
+
change(_changes) {
|
|
3607
3950
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
return {
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
};
|
|
3951
|
+
yield this.init();
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
RaainDetailsComponent.HOUR_MS = 60 * 60000;
|
|
3956
|
+
RaainDetailsComponent.DAY_MS = 24 * 60 * 60 * 1000;
|
|
3957
|
+
RaainDetailsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RaainDetailsComponent, deps: [{ token: Storage }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3958
|
+
RaainDetailsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: RaainDetailsComponent, selector: "raain-details", inputs: { toggleAdmin: "toggleAdmin", rainNode: "rainNode", compareManager: "compareManager", refreshManager: "refreshManager", profileService: "profileService", radarService: "radarService" }, viewQueries: [{ propertyName: "cumulativeToggleRef", first: true, predicate: ["cumulativeToggle"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Main content container -->\n<div *ngIf=\"rainNode\" class=\"raain-details-container\">\n\n <!-- Period selection section -->\n <ion-card class=\"period-card\">\n <ion-card-content>\n <div class=\"period-controls\">\n <div class=\"period-row\">\n\n <ion-button (click)=\"toggleHistory = !toggleHistory; onEnableCountHistoryTab(rainNode)\"\n fill=\"outline\">\n <ion-icon name=\"calendar-clear-outline\" slot=\"start\"></ion-icon>\n <ion-icon [name]=\"toggleHistory ? 'chevron-down' : 'chevron-forward'\" slot=\"end\"></ion-icon>\n </ion-button>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodBeginChange($event)\"\n [disabled]=\"toggleCumulative\"\n [value]=\"periodBeginAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"period-duration ion-hide-md-down\">\n <ion-select (ionDismiss)=\"onPeriodDurationChange($event)\"\n [(ngModel)]=\"periodDurationAsString\"\n [disabled]=\"toggleCumulative\"\n class=\"duration-select\"\n id=\"periodDuration\"\n interface=\"popover\">\n <ion-select-option value=\"0.25\">15 minutes</ion-select-option>\n <ion-select-option value=\"0.5\">30 minutes</ion-select-option>\n <ion-select-option value=\"1\">1 hour</ion-select-option>\n <ion-select-option value=\"2\">2 hours</ion-select-option>\n <ion-select-option value=\"3\">3 hours</ion-select-option>\n <ion-select-option value=\"4\">4 hours</ion-select-option>\n <ion-select-option value=\"5\">5 hours</ion-select-option>\n <ion-select-option value=\"6\">6 hours</ion-select-option>\n <ion-select-option value=\"8\">8 hours</ion-select-option>\n <ion-select-option value=\"10\">10 hours</ion-select-option>\n <ion-select-option value=\"12\">12 hours</ion-select-option>\n <ion-select-option *ngIf=\"isAdmin\" value=\"24\">24 hours</ion-select-option>\n </ion-select>\n </div>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodEndChange($event)\"\n [disabled]=\"toggleCumulative\"\n [value]=\"periodEndAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"toggle-cumulative\" (click)=\"onCumulativeToggleClick($event)\">\n <ion-label [class.text-primary]=\"toggleCumulative\">\n {{ toggleCumulative ? 'Cumulative' : 'Granular' }}\n </ion-label>\n <ion-toggle #cumulativeToggle [checked]=\"toggleCumulative\">\n </ion-toggle>\n </div>\n </div>\n\n <!-- Hidden label for change detection (uncomment to debug)\n <div class=\"hidden-label\">{{ onChangeDetectionTest(rainNode) }}</div>\n -->\n </div>\n\n <!-- Historical map section -->\n <div *ngIf=\"toggleHistory\" class=\"period-controls\">\n <raain-date-dynamic (changedDate)=\"onDateChangeInCount($event)\"\n [currentHeight]=\"300\"\n [fetchData]=\"fetchDataWrapper\"\n [points]=\"countPoints\">\n </raain-date-dynamic>\n </div>\n </ion-card-content>\n </ion-card>\n\n <!-- Map performance -->\n <ion-grid class=\"map-performance\">\n <ion-row id=\"progressAndRefresh\">\n <ion-col class=\"provider-selection\" size=\"12\" size-md=\"6\">\n <ion-button (click)=\"openQualityModal()\" class=\"quality-info-button\" fill=\"clear\">\n {{ refreshManager.rainComputationMapVersion }}\n <ion-icon name=\"help-circle-outline\" slot=\"end\"></ion-icon>\n </ion-button>\n <div *ngIf=\"availableProviders.length > 0 || availableTimeSteps.length > 0\" class=\"selection-row\">\n <ion-select (ionChange)=\"onProviderChanged($event)\"\n [value]=\"selectedProvider\"\n interface=\"popover\"\n label=\"Provider\"\n placeholder=\"Select Provider\">\n <ion-select-option *ngFor=\"let provider of availableProviders\" [value]=\"provider\">\n {{ provider }}\n </ion-select-option>\n </ion-select>\n\n <ion-select (ionChange)=\"onTimeStepChanged($event)\"\n [value]=\"selectedTimeStep\"\n interface=\"popover\"\n label=\"Time Step\"\n placeholder=\"Select Time Step\">\n <ion-select-option *ngFor=\"let step of availableTimeSteps\" [value]=\"step\">\n {{ step }} min\n </ion-select-option>\n </ion-select>\n </div>\n </ion-col>\n <ion-col class=\"ion-text-right\" size=\"12\" size-md=\"6\">\n <ion-label class=\"ion-margin-end\">\n <span *ngIf=\"percentOfComputations\">\n {{ percentOfComputations }}% Images\n <i *ngIf=\"countsPeriod.progress\" class=\"progress-indicator\">\n In Progress: {{ countsPeriod.progress }}...\n </i>\n </span>\n <span *ngIf=\"!percentOfComputations\">\n No image available\n </span>\n\n </ion-label>\n\n <ion-button (click)=\"refreshMap()\" [disabled]=\"refreshInProgress\" class=\"refresh-button\">\n Refresh Map\n </ion-button>\n </ion-col>\n </ion-row>\n\n <!-- status update row -->\n <ion-row>\n <!-- Progress col -->\n <ion-progress-bar\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [style.visibility]=\"refreshInProgress ? 'visible' : 'hidden'\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n\n <!-- Error col -->\n <ion-col (click)=\"showFullError = !showFullError\" *ngIf=\"refreshManager.lastError\" class=\"error-row\"\n size=\"12\">\n <div class=\"error-content\">\n <ion-icon class=\"error-icon\" name=\"warning-outline\"></ion-icon>\n <span [class.expanded]=\"showFullError\" class=\"error-text\">\n {{ showFullError ? refreshManager.lastError : truncatedError }}\n </span>\n <ion-icon [name]=\"showFullError ? 'chevron-up' : 'chevron-down'\" class=\"error-caret\"></ion-icon>\n </div>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Map section -->\n <ion-card class=\"map-card\">\n <ion-card-content class=\"map-content\">\n <ion-grid>\n <ion-row *ngIf=\"toggleMap && percentOfImages\">\n <!-- Map component -->\n <ion-col class=\"map-column\" size-lg=\"7\" size-md=\"12\">\n <div class=\"map-container\">\n <raain-map #raainMapRef\n (changedDate)=\"onDateChangeInMap($event)\"\n (changedSum)=\"onSumChangeInMap($event)\"\n (selectedMarker)=\"onGaugeSelectInMap($event)\"\n [coordinates]=\"coordinates\"\n [cumulativeDurationInMinutes]=\"cumulativeDurationInMinutes\"\n [currentHeight]=\"500\"\n [defaultDate]=\"dateShown\"\n [markers]=\"{\n borders,\n gauges: compareManager.gaugesInMap,\n gaugesInCompare: compareManager.gaugesInCompare,\n selectedGauges: compareManager.selectedGauges,\n pixels: compareManager.selectedPixels,\n pixelsSolution: compareManager.pixelsSolutions?.length ? compareManager.pixelsSolutions[0] : [],\n speeds: compareManager.speeds\n }\"\n [showCumulative]=\"toggleCumulative\"\n [showVisiblePixelMarkers]=\"showPixelMarkers\"\n [sumFn]=\"refreshManager.sumFn\"\n [sumValues]=\"refreshManager.sumValues\"\n [timeframeContainers]=\"timeframeContainers\"\n [timeframeDates]=\"timeframeDates\">\n </raain-map>\n </div>\n\n <div class=\"data-column\">\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Image Details</summary>\n <div class=\"details-content\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Date:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDate?.toISOString() }}\n | {{ refreshManager.rainComputationMapDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Water in the map:</span>\n <span class=\"detail-value\">{{ sumDetails }}</span>\n <ion-toggle\n (ionChange)=\"onTogglePixelMarkers()\"\n [(ngModel)]=\"showPixelMarkers\"\n style=\"margin-left: 8px; transform: scale(0.7);\">\n </ion-toggle>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ min:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMin }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ max:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMax }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n\n <!-- Data panel -->\n <ion-col *ngIf=\"!!compareManager.compareVersion\" class=\"data-column\" size-lg=\"5\" size-md=\"12\">\n <div class=\"data-panel\">\n <!-- Compare stack component -->\n <div class=\"compare-stack\">\n <raain-compare-stack\n (selectedPoint)=\"onGaugeSelectInCompare($event)\"\n [compareManager]=\"compareManager\"\n [cumulative]=\"toggleCumulative\">\n </raain-compare-stack>\n </div>\n\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Compare Details</summary>\n <div class=\"details-content\">\n <div [ngClass]=\"{'warning': refreshManager.rainComputationMapDoneDate?.getTime() > compareManager.currentQualityDoneDate?.getTime() + 60000}\"\n class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ compareManager.compareVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Gauges:</span>\n <span class=\"detail-value\">{{ compareManager.gaugesInCompare.length }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Points:</span>\n <span class=\"detail-value\">{{ compareManager.globalComparePoints.length }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n </ion-row>\n <ion-row>\n <!-- Bottom progress bar -->\n <ion-progress-bar *ngIf=\"refreshInProgress\"\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n\n <!-- Gauge values section -->\n <ion-card *ngIf=\"gaugeSelectedPoints.length && gaugeSelectedPoints[0].values.length\" class=\"gauge-card\">\n <ion-card-header>\n <ion-card-title>\n <ion-icon name=\"analytics-outline\"></ion-icon>\n Selected Gauge Data\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <raain-date-focus\n [currentHeight]=\"300\"\n [focusDate]=\"periodBegin\"\n [focusRange]=\"DateRange.DAY\"\n [points]=\"gaugeSelectedPoints\"\n [withoutAll]=\"true\">\n </raain-date-focus>\n </ion-card-content>\n </ion-card>\n\n <!-- Quality Performance Modal -->\n <div (click)=\"closeQualityModal()\" *ngIf=\"showQualityModal\" class=\"quality-modal-overlay\">\n <div (click)=\"$event.stopPropagation()\" class=\"quality-modal-content\">\n <div class=\"quality-modal-header\">\n <h2>Model Quality Performance</h2>\n <ion-button (click)=\"closeQualityModal()\" fill=\"clear\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n <div class=\"quality-modal-body\">\n <div *ngIf=\"qualityIndicatorsLoading\" class=\"quality-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <span>Loading indicators...</span>\n </div>\n <div *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length === 0\" class=\"quality-empty\">\n No quality indicators available for this year.\n </div>\n <table *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length > 0\" class=\"quality-table\">\n <thead>\n <tr>\n <th>Model</th>\n <th>Compare</th>\n <th>Gauges</th>\n <th>Period</th>\n <th>Avg Quality</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let indicator of qualityIndicators\">\n <td>{{ indicator.computingVersion }}</td>\n <td>{{ indicator.qualityVersion }}</td>\n <td>{{ indicator.provider }}<br>{{ indicator.timeStepInMinutes }} min</td>\n <td>{{ formatDate(indicator.startDate) }}<br>{{ formatDate(indicator.endDate) }}</td>\n <td>{{ indicator.averageQuality | number:'1.2-2' }}</td>\n <td>{{ formatDate(indicator.lastUpdatedAt) }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n\n <!-- Cumulative Selector Modal -->\n <cumulative-selector *ngIf=\"showCumulativeSelector\"\n [rainId]=\"rainNode?.id\"\n [currentPeriodBegin]=\"periodBegin\"\n [currentPeriodEnd]=\"periodEnd\"\n [provider]=\"selectedProvider\"\n [timeStepInMinutes]=\"selectedTimeStep\"\n [isAdmin]=\"toggleAdmin\"\n (periodSelected)=\"onCumulativePeriodSelected($event)\"\n (cancelled)=\"onCumulativeSelectorCancelled()\">\n </cumulative-selector>\n\n</div>\n", styles: [".raain-details-container{max-width:var(--app-max-width);margin:0 auto;padding:0 0 24px}.raain-details-card{width:100%;margin-bottom:20px}.raain-details-card ion-card-header{border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.raain-details-card ion-card-header ion-card-title{display:flex;align-items:center}.raain-details-card ion-card-header ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary)}.node-info-card{background-color:var(--ion-color-light)}.node-info-card .node-header{display:flex;align-items:center}.node-info-card .node-header .node-status{margin-right:16px}.node-info-card .node-header .node-status ion-icon{font-size:24px}.node-info-card .node-header .node-titles{flex:1}.node-info-card .node-header .node-titles ion-card-title{margin:0;font-size:1.4rem;font-weight:600;color:var(--ion-color-dark)}.node-info-card .node-header .node-titles ion-card-subtitle{padding-left:0;margin:4px 0 0;font-size:.9rem;color:var(--ion-color-medium)}.count-map-card,.period-card{background-color:var(--ion-color-light)}.period-card ion-card-title{display:flex;align-items:center}.period-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.period-row{display:flex;flex-wrap:wrap;align-items:center;gap:16px;position:relative}.now-button{min-width:100px}#all-dates{display:flex;align-items:center;margin-left:auto}#all-dates .toggle-label{margin-right:8px;white-space:nowrap}.refresh-button ion-icon{margin-right:4px;color:var(--ion-color-light)}.provider-selection{display:flex;align-items:center}.quality-info-button{--padding-start: 8px;--padding-end: 8px;font-size:.85rem;color:var(--ion-color-medium)}.quality-info-button ion-icon{font-size:18px;margin-left:4px}.selection-row{display:flex;flex-direction:row;align-items:center;gap:12px}.selection-row ion-select{flex:0 0 auto;min-width:100px;margin-bottom:0;padding:4px 8px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light)}.period-start,.period-duration{display:flex;align-items:center}.toggle-cumulative{display:flex;align-items:center;gap:8px;margin-left:auto}.toggle-cumulative .text-primary{color:var(--ion-color-primary);font-weight:600}.hidden-label{display:none}.datetime-input,#periodDuration,.duration-select{padding:8px 12px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light);font-family:var(--ion-font-family)}.datetime-input:focus,#periodDuration:focus,.duration-select:focus{outline:none;border-color:var(--ion-color-primary)}#periodDuration,.duration-select{min-width:150px}.gauge-card{background-color:var(--ion-color-light)}.gauge-card ion-card-title{display:flex;align-items:center}.gauge-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.error-row{cursor:pointer;background-color:rgba(var(--ion-color-danger-rgb),.1);border-left:3px solid var(--ion-color-danger);margin-top:8px;border-radius:4px;transition:background-color .2s ease}.error-row:hover{background-color:rgba(var(--ion-color-danger-rgb),.15)}.error-row .error-content{display:flex;align-items:flex-start;padding:8px 12px;gap:8px}.error-row .error-icon{color:var(--ion-color-danger);font-size:18px;flex-shrink:0;margin-top:2px}.error-row .error-text{flex:1;color:var(--ion-color-danger-shade);font-size:.9rem;word-break:break-word}.error-row .error-text.expanded{white-space:pre-wrap}.error-row .error-caret{color:var(--ion-color-danger);font-size:16px;flex-shrink:0;margin-top:2px}raain-compare-stack{width:100%;display:block}@media (max-width: 768px){.period-row{flex-direction:row;justify-content:space-between;align-items:center}#all-dates{margin-left:auto;padding-left:8px}#all-dates .toggle-label{font-size:.9rem}.map-header{flex-direction:row;justify-content:space-between;align-items:center;gap:16px}}.quality-modal-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:9999}.quality-modal-content{background-color:var(--ion-background-color, #fff);border-radius:12px;width:95%;max-width:900px;max-height:80vh;overflow:auto;box-shadow:0 4px 24px #0003}.quality-modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-modal-header h2{margin:0;font-size:1.25rem;font-weight:600;color:var(--ion-color-dark)}.quality-modal-header ion-button{--padding-start: 8px;--padding-end: 8px}.quality-modal-body{padding:20px}.quality-table{width:100%;border-collapse:collapse;margin-top:16px;table-layout:fixed}.quality-table th,.quality-table td{width:16.66%;padding:12px 16px;text-align:center;vertical-align:top;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-table th{background-color:rgba(var(--ion-color-primary-rgb),.1);font-weight:600;color:var(--ion-color-dark)}.quality-table td{color:var(--ion-color-dark-tint);font-size:.7rem}.quality-table tbody tr:hover{background-color:rgba(var(--ion-color-primary-rgb),.05)}.quality-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--ion-color-medium)}.quality-loading ion-spinner{margin-bottom:12px}.quality-empty{text-align:center;padding:40px 20px;color:var(--ion-color-medium);font-style:italic}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i4.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i4.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i4.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i4.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i4.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i4.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i4.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i4.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i4.IonProgressBar, selector: "ion-progress-bar", inputs: ["buffer", "color", "mode", "reversed", "type", "value"] }, { kind: "component", type: i4.IonRow, selector: "ion-row" }, { kind: "component", type: i4.IonSelect, selector: "ion-select", inputs: ["cancelText", "compareWith", "disabled", "interface", "interfaceOptions", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "value"] }, { kind: "component", type: i4.IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: i4.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i4.IonToggle, selector: "ion-toggle", inputs: ["checked", "color", "disabled", "mode", "name", "value"] }, { kind: "directive", type: i4.BooleanValueAccessor, selector: "ion-checkbox,ion-toggle" }, { kind: "directive", type: i4.SelectValueAccessor, selector: "ion-range, ion-select, ion-radio-group, ion-segment, ion-datetime" }, { kind: "component", type: RaainMapComponent, selector: "raain-map", inputs: ["coordinates", "markers", "timeframeContainers", "autoplay", "showMarkers", "showSpeedMarkers", "showVisiblePixelMarkers", "showCumulative", "cumulativeDurationInMinutes", "currentHeight", "timeframeDates", "defaultDate", "sumValues", "sumFn"], outputs: ["selectedMarker", "changedDate", "changedSum"] }, { kind: "component", type: RaainCompareStackComponent, selector: "raain-compare-stack", inputs: ["compareManager", "currentHeight", "cumulative"], outputs: ["selectedPoint"] }, { kind: "component", type: RaainDateFocusComponent, selector: "raain-date-focus", inputs: ["points", "focusDate", "focusRange", "withoutAll", "currentHeight"] }, { kind: "component", type: RaainDateDynamicComponent, selector: "raain-date-dynamic", inputs: ["points", "focusDate", "focusRange", "withoutAll", "currentHeight", "fetchData"], outputs: ["changedDate"] }, { kind: "component", type: CumulativeSelectorComponent, selector: "cumulative-selector", inputs: ["rainId", "currentPeriodBegin", "currentPeriodEnd", "provider", "timeStepInMinutes", "isAdmin"], outputs: ["periodSelected", "cancelled"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i2.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3959
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: RaainDetailsComponent, decorators: [{
|
|
3960
|
+
type: Component,
|
|
3961
|
+
args: [{ selector: 'raain-details', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Main content container -->\n<div *ngIf=\"rainNode\" class=\"raain-details-container\">\n\n <!-- Period selection section -->\n <ion-card class=\"period-card\">\n <ion-card-content>\n <div class=\"period-controls\">\n <div class=\"period-row\">\n\n <ion-button (click)=\"toggleHistory = !toggleHistory; onEnableCountHistoryTab(rainNode)\"\n fill=\"outline\">\n <ion-icon name=\"calendar-clear-outline\" slot=\"start\"></ion-icon>\n <ion-icon [name]=\"toggleHistory ? 'chevron-down' : 'chevron-forward'\" slot=\"end\"></ion-icon>\n </ion-button>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodBeginChange($event)\"\n [disabled]=\"toggleCumulative\"\n [value]=\"periodBeginAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"period-duration ion-hide-md-down\">\n <ion-select (ionDismiss)=\"onPeriodDurationChange($event)\"\n [(ngModel)]=\"periodDurationAsString\"\n [disabled]=\"toggleCumulative\"\n class=\"duration-select\"\n id=\"periodDuration\"\n interface=\"popover\">\n <ion-select-option value=\"0.25\">15 minutes</ion-select-option>\n <ion-select-option value=\"0.5\">30 minutes</ion-select-option>\n <ion-select-option value=\"1\">1 hour</ion-select-option>\n <ion-select-option value=\"2\">2 hours</ion-select-option>\n <ion-select-option value=\"3\">3 hours</ion-select-option>\n <ion-select-option value=\"4\">4 hours</ion-select-option>\n <ion-select-option value=\"5\">5 hours</ion-select-option>\n <ion-select-option value=\"6\">6 hours</ion-select-option>\n <ion-select-option value=\"8\">8 hours</ion-select-option>\n <ion-select-option value=\"10\">10 hours</ion-select-option>\n <ion-select-option value=\"12\">12 hours</ion-select-option>\n <ion-select-option *ngIf=\"isAdmin\" value=\"24\">24 hours</ion-select-option>\n </ion-select>\n </div>\n\n <div class=\"period-start ion-hide-md-down\">\n <input (change)=\"onPeriodEndChange($event)\"\n [disabled]=\"toggleCumulative\"\n [value]=\"periodEndAsString\"\n class=\"datetime-input\"\n type=\"datetime-local\">\n </div>\n\n <div class=\"toggle-cumulative\" (click)=\"onCumulativeToggleClick($event)\">\n <ion-label [class.text-primary]=\"toggleCumulative\">\n {{ toggleCumulative ? 'Cumulative' : 'Granular' }}\n </ion-label>\n <ion-toggle #cumulativeToggle [checked]=\"toggleCumulative\">\n </ion-toggle>\n </div>\n </div>\n\n <!-- Hidden label for change detection (uncomment to debug)\n <div class=\"hidden-label\">{{ onChangeDetectionTest(rainNode) }}</div>\n -->\n </div>\n\n <!-- Historical map section -->\n <div *ngIf=\"toggleHistory\" class=\"period-controls\">\n <raain-date-dynamic (changedDate)=\"onDateChangeInCount($event)\"\n [currentHeight]=\"300\"\n [fetchData]=\"fetchDataWrapper\"\n [points]=\"countPoints\">\n </raain-date-dynamic>\n </div>\n </ion-card-content>\n </ion-card>\n\n <!-- Map performance -->\n <ion-grid class=\"map-performance\">\n <ion-row id=\"progressAndRefresh\">\n <ion-col class=\"provider-selection\" size=\"12\" size-md=\"6\">\n <ion-button (click)=\"openQualityModal()\" class=\"quality-info-button\" fill=\"clear\">\n {{ refreshManager.rainComputationMapVersion }}\n <ion-icon name=\"help-circle-outline\" slot=\"end\"></ion-icon>\n </ion-button>\n <div *ngIf=\"availableProviders.length > 0 || availableTimeSteps.length > 0\" class=\"selection-row\">\n <ion-select (ionChange)=\"onProviderChanged($event)\"\n [value]=\"selectedProvider\"\n interface=\"popover\"\n label=\"Provider\"\n placeholder=\"Select Provider\">\n <ion-select-option *ngFor=\"let provider of availableProviders\" [value]=\"provider\">\n {{ provider }}\n </ion-select-option>\n </ion-select>\n\n <ion-select (ionChange)=\"onTimeStepChanged($event)\"\n [value]=\"selectedTimeStep\"\n interface=\"popover\"\n label=\"Time Step\"\n placeholder=\"Select Time Step\">\n <ion-select-option *ngFor=\"let step of availableTimeSteps\" [value]=\"step\">\n {{ step }} min\n </ion-select-option>\n </ion-select>\n </div>\n </ion-col>\n <ion-col class=\"ion-text-right\" size=\"12\" size-md=\"6\">\n <ion-label class=\"ion-margin-end\">\n <span *ngIf=\"percentOfComputations\">\n {{ percentOfComputations }}% Images\n <i *ngIf=\"countsPeriod.progress\" class=\"progress-indicator\">\n In Progress: {{ countsPeriod.progress }}...\n </i>\n </span>\n <span *ngIf=\"!percentOfComputations\">\n No image available\n </span>\n\n </ion-label>\n\n <ion-button (click)=\"refreshMap()\" [disabled]=\"refreshInProgress\" class=\"refresh-button\">\n Refresh Map\n </ion-button>\n </ion-col>\n </ion-row>\n\n <!-- status update row -->\n <ion-row>\n <!-- Progress col -->\n <ion-progress-bar\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [style.visibility]=\"refreshInProgress ? 'visible' : 'hidden'\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n\n <!-- Error col -->\n <ion-col (click)=\"showFullError = !showFullError\" *ngIf=\"refreshManager.lastError\" class=\"error-row\"\n size=\"12\">\n <div class=\"error-content\">\n <ion-icon class=\"error-icon\" name=\"warning-outline\"></ion-icon>\n <span [class.expanded]=\"showFullError\" class=\"error-text\">\n {{ showFullError ? refreshManager.lastError : truncatedError }}\n </span>\n <ion-icon [name]=\"showFullError ? 'chevron-up' : 'chevron-down'\" class=\"error-caret\"></ion-icon>\n </div>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Map section -->\n <ion-card class=\"map-card\">\n <ion-card-content class=\"map-content\">\n <ion-grid>\n <ion-row *ngIf=\"toggleMap && percentOfImages\">\n <!-- Map component -->\n <ion-col class=\"map-column\" size-lg=\"7\" size-md=\"12\">\n <div class=\"map-container\">\n <raain-map #raainMapRef\n (changedDate)=\"onDateChangeInMap($event)\"\n (changedSum)=\"onSumChangeInMap($event)\"\n (selectedMarker)=\"onGaugeSelectInMap($event)\"\n [coordinates]=\"coordinates\"\n [cumulativeDurationInMinutes]=\"cumulativeDurationInMinutes\"\n [currentHeight]=\"500\"\n [defaultDate]=\"dateShown\"\n [markers]=\"{\n borders,\n gauges: compareManager.gaugesInMap,\n gaugesInCompare: compareManager.gaugesInCompare,\n selectedGauges: compareManager.selectedGauges,\n pixels: compareManager.selectedPixels,\n pixelsSolution: compareManager.pixelsSolutions?.length ? compareManager.pixelsSolutions[0] : [],\n speeds: compareManager.speeds\n }\"\n [showCumulative]=\"toggleCumulative\"\n [showVisiblePixelMarkers]=\"showPixelMarkers\"\n [sumFn]=\"refreshManager.sumFn\"\n [sumValues]=\"refreshManager.sumValues\"\n [timeframeContainers]=\"timeframeContainers\"\n [timeframeDates]=\"timeframeDates\">\n </raain-map>\n </div>\n\n <div class=\"data-column\">\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Image Details</summary>\n <div class=\"details-content\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Date:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapDate?.toISOString() }}\n | {{ refreshManager.rainComputationMapDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Water in the map:</span>\n <span class=\"detail-value\">{{ sumDetails }}</span>\n <ion-toggle\n (ionChange)=\"onTogglePixelMarkers()\"\n [(ngModel)]=\"showPixelMarkers\"\n style=\"margin-left: 8px; transform: scale(0.7);\">\n </ion-toggle>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ min:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMin }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Source DBZ max:</span>\n <span class=\"detail-value\">{{ refreshManager.rainComputationMapOriginalMax }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n\n <!-- Data panel -->\n <ion-col *ngIf=\"!!compareManager.compareVersion\" class=\"data-column\" size-lg=\"5\" size-md=\"12\">\n <div class=\"data-panel\">\n <!-- Compare stack component -->\n <div class=\"compare-stack\">\n <raain-compare-stack\n (selectedPoint)=\"onGaugeSelectInCompare($event)\"\n [compareManager]=\"compareManager\"\n [cumulative]=\"toggleCumulative\">\n </raain-compare-stack>\n </div>\n\n <!-- Technical details (collapsible for cleaner UI) -->\n <details class=\"technical-details\">\n <summary>Compare Details</summary>\n <div class=\"details-content\">\n <div [ngClass]=\"{'warning': refreshManager.rainComputationMapDoneDate?.getTime() > compareManager.currentQualityDoneDate?.getTime() + 60000}\"\n class=\"detail-row\">\n <span class=\"detail-label\">Computed:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityDoneDate | date:'yyyy-MM-dd HH:mm' }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Version:</span>\n <span class=\"detail-value\">{{ compareManager.compareVersion }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Launched by:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityLaunchedBy }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Time spent:</span>\n <span class=\"detail-value\">{{ compareManager.currentQualityTimeSpentInMs }}\n ms</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Gauges:</span>\n <span class=\"detail-value\">{{ compareManager.gaugesInCompare.length }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Points:</span>\n <span class=\"detail-value\">{{ compareManager.globalComparePoints.length }}</span>\n </div>\n </div>\n </details>\n </div>\n </ion-col>\n </ion-row>\n <ion-row>\n <!-- Bottom progress bar -->\n <ion-progress-bar *ngIf=\"refreshInProgress\"\n [buffer]=\"(countsPeriod.progress / ((timeframeDates?.length || 10) +3))+0.01\"\n [value]=\"countsPeriod.progress / ((timeframeDates?.length || 10) +3)\"\n color=\"primary\">\n </ion-progress-bar>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n\n <!-- Gauge values section -->\n <ion-card *ngIf=\"gaugeSelectedPoints.length && gaugeSelectedPoints[0].values.length\" class=\"gauge-card\">\n <ion-card-header>\n <ion-card-title>\n <ion-icon name=\"analytics-outline\"></ion-icon>\n Selected Gauge Data\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <raain-date-focus\n [currentHeight]=\"300\"\n [focusDate]=\"periodBegin\"\n [focusRange]=\"DateRange.DAY\"\n [points]=\"gaugeSelectedPoints\"\n [withoutAll]=\"true\">\n </raain-date-focus>\n </ion-card-content>\n </ion-card>\n\n <!-- Quality Performance Modal -->\n <div (click)=\"closeQualityModal()\" *ngIf=\"showQualityModal\" class=\"quality-modal-overlay\">\n <div (click)=\"$event.stopPropagation()\" class=\"quality-modal-content\">\n <div class=\"quality-modal-header\">\n <h2>Model Quality Performance</h2>\n <ion-button (click)=\"closeQualityModal()\" fill=\"clear\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n <div class=\"quality-modal-body\">\n <div *ngIf=\"qualityIndicatorsLoading\" class=\"quality-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <span>Loading indicators...</span>\n </div>\n <div *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length === 0\" class=\"quality-empty\">\n No quality indicators available for this year.\n </div>\n <table *ngIf=\"!qualityIndicatorsLoading && qualityIndicators.length > 0\" class=\"quality-table\">\n <thead>\n <tr>\n <th>Model</th>\n <th>Compare</th>\n <th>Gauges</th>\n <th>Period</th>\n <th>Avg Quality</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let indicator of qualityIndicators\">\n <td>{{ indicator.computingVersion }}</td>\n <td>{{ indicator.qualityVersion }}</td>\n <td>{{ indicator.provider }}<br>{{ indicator.timeStepInMinutes }} min</td>\n <td>{{ formatDate(indicator.startDate) }}<br>{{ formatDate(indicator.endDate) }}</td>\n <td>{{ indicator.averageQuality | number:'1.2-2' }}</td>\n <td>{{ formatDate(indicator.lastUpdatedAt) }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n\n <!-- Cumulative Selector Modal -->\n <cumulative-selector *ngIf=\"showCumulativeSelector\"\n [rainId]=\"rainNode?.id\"\n [currentPeriodBegin]=\"periodBegin\"\n [currentPeriodEnd]=\"periodEnd\"\n [provider]=\"selectedProvider\"\n [timeStepInMinutes]=\"selectedTimeStep\"\n [isAdmin]=\"toggleAdmin\"\n (periodSelected)=\"onCumulativePeriodSelected($event)\"\n (cancelled)=\"onCumulativeSelectorCancelled()\">\n </cumulative-selector>\n\n</div>\n", styles: [".raain-details-container{max-width:var(--app-max-width);margin:0 auto;padding:0 0 24px}.raain-details-card{width:100%;margin-bottom:20px}.raain-details-card ion-card-header{border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.raain-details-card ion-card-header ion-card-title{display:flex;align-items:center}.raain-details-card ion-card-header ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary)}.node-info-card{background-color:var(--ion-color-light)}.node-info-card .node-header{display:flex;align-items:center}.node-info-card .node-header .node-status{margin-right:16px}.node-info-card .node-header .node-status ion-icon{font-size:24px}.node-info-card .node-header .node-titles{flex:1}.node-info-card .node-header .node-titles ion-card-title{margin:0;font-size:1.4rem;font-weight:600;color:var(--ion-color-dark)}.node-info-card .node-header .node-titles ion-card-subtitle{padding-left:0;margin:4px 0 0;font-size:.9rem;color:var(--ion-color-medium)}.count-map-card,.period-card{background-color:var(--ion-color-light)}.period-card ion-card-title{display:flex;align-items:center}.period-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.period-row{display:flex;flex-wrap:wrap;align-items:center;gap:16px;position:relative}.now-button{min-width:100px}#all-dates{display:flex;align-items:center;margin-left:auto}#all-dates .toggle-label{margin-right:8px;white-space:nowrap}.refresh-button ion-icon{margin-right:4px;color:var(--ion-color-light)}.provider-selection{display:flex;align-items:center}.quality-info-button{--padding-start: 8px;--padding-end: 8px;font-size:.85rem;color:var(--ion-color-medium)}.quality-info-button ion-icon{font-size:18px;margin-left:4px}.selection-row{display:flex;flex-direction:row;align-items:center;gap:12px}.selection-row ion-select{flex:0 0 auto;min-width:100px;margin-bottom:0;padding:4px 8px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light)}.period-start,.period-duration{display:flex;align-items:center}.toggle-cumulative{display:flex;align-items:center;gap:8px;margin-left:auto}.toggle-cumulative .text-primary{color:var(--ion-color-primary);font-weight:600}.hidden-label{display:none}.datetime-input,#periodDuration,.duration-select{padding:8px 12px;border:1px solid rgba(var(--ion-color-medium-rgb),.3);border-radius:var(--ion-border-radius);background-color:var(--ion-color-light);font-family:var(--ion-font-family)}.datetime-input:focus,#periodDuration:focus,.duration-select:focus{outline:none;border-color:var(--ion-color-primary)}#periodDuration,.duration-select{min-width:150px}.gauge-card{background-color:var(--ion-color-light)}.gauge-card ion-card-title{display:flex;align-items:center}.gauge-card ion-card-title ion-icon{margin-right:8px;color:var(--ion-color-primary);font-size:20px}.error-row{cursor:pointer;background-color:rgba(var(--ion-color-danger-rgb),.1);border-left:3px solid var(--ion-color-danger);margin-top:8px;border-radius:4px;transition:background-color .2s ease}.error-row:hover{background-color:rgba(var(--ion-color-danger-rgb),.15)}.error-row .error-content{display:flex;align-items:flex-start;padding:8px 12px;gap:8px}.error-row .error-icon{color:var(--ion-color-danger);font-size:18px;flex-shrink:0;margin-top:2px}.error-row .error-text{flex:1;color:var(--ion-color-danger-shade);font-size:.9rem;word-break:break-word}.error-row .error-text.expanded{white-space:pre-wrap}.error-row .error-caret{color:var(--ion-color-danger);font-size:16px;flex-shrink:0;margin-top:2px}raain-compare-stack{width:100%;display:block}@media (max-width: 768px){.period-row{flex-direction:row;justify-content:space-between;align-items:center}#all-dates{margin-left:auto;padding-left:8px}#all-dates .toggle-label{font-size:.9rem}.map-header{flex-direction:row;justify-content:space-between;align-items:center;gap:16px}}.quality-modal-overlay{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:9999}.quality-modal-content{background-color:var(--ion-background-color, #fff);border-radius:12px;width:95%;max-width:900px;max-height:80vh;overflow:auto;box-shadow:0 4px 24px #0003}.quality-modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-modal-header h2{margin:0;font-size:1.25rem;font-weight:600;color:var(--ion-color-dark)}.quality-modal-header ion-button{--padding-start: 8px;--padding-end: 8px}.quality-modal-body{padding:20px}.quality-table{width:100%;border-collapse:collapse;margin-top:16px;table-layout:fixed}.quality-table th,.quality-table td{width:16.66%;padding:12px 16px;text-align:center;vertical-align:top;border-bottom:1px solid rgba(var(--ion-color-medium-rgb),.2)}.quality-table th{background-color:rgba(var(--ion-color-primary-rgb),.1);font-weight:600;color:var(--ion-color-dark)}.quality-table td{color:var(--ion-color-dark-tint);font-size:.7rem}.quality-table tbody tr:hover{background-color:rgba(var(--ion-color-primary-rgb),.05)}.quality-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:var(--ion-color-medium)}.quality-loading ion-spinner{margin-bottom:12px}.quality-empty{text-align:center;padding:40px 20px;color:var(--ion-color-medium);font-style:italic}\n"] }]
|
|
3962
|
+
}], ctorParameters: function () { return [{ type: Storage }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { toggleAdmin: [{
|
|
3963
|
+
type: Input
|
|
3964
|
+
}], rainNode: [{
|
|
3965
|
+
type: Input
|
|
3966
|
+
}], compareManager: [{
|
|
3967
|
+
type: Input
|
|
3968
|
+
}], refreshManager: [{
|
|
3969
|
+
type: Input
|
|
3970
|
+
}], profileService: [{
|
|
3971
|
+
type: Input
|
|
3972
|
+
}], radarService: [{
|
|
3973
|
+
type: Input
|
|
3974
|
+
}], cumulativeToggleRef: [{
|
|
3975
|
+
type: ViewChild,
|
|
3976
|
+
args: ['cumulativeToggle']
|
|
3977
|
+
}] } });
|
|
3978
|
+
|
|
3979
|
+
class Cache {
|
|
3980
|
+
constructor() {
|
|
3981
|
+
this._cache = {};
|
|
3982
|
+
}
|
|
3983
|
+
getValue(key, asyncGetter) {
|
|
3984
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3985
|
+
if (!Object.prototype.hasOwnProperty.call(this._cache, key)) {
|
|
3986
|
+
console.log('cache not done: ', key);
|
|
3987
|
+
this.putValue(key, yield asyncGetter());
|
|
3619
3988
|
}
|
|
3620
|
-
|
|
3621
|
-
console.
|
|
3622
|
-
return {
|
|
3623
|
-
providers: [],
|
|
3624
|
-
timeStepInMinutes: [5, 10, 15, 30, 60],
|
|
3625
|
-
};
|
|
3989
|
+
else {
|
|
3990
|
+
console.log('cache done: ', key);
|
|
3626
3991
|
}
|
|
3992
|
+
return this._cache[key];
|
|
3627
3993
|
});
|
|
3628
3994
|
}
|
|
3629
|
-
|
|
3630
|
-
this.
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
if (c.year !== undefined) {
|
|
3635
|
-
dateToShow.setUTCFullYear(c.year);
|
|
3636
|
-
}
|
|
3637
|
-
if (c.month !== undefined) {
|
|
3638
|
-
dateToShow.setUTCMonth(c.month - 1);
|
|
3639
|
-
}
|
|
3640
|
-
if (c.day !== undefined) {
|
|
3641
|
-
dateToShow.setUTCDate(c.day);
|
|
3642
|
-
}
|
|
3643
|
-
if (c.hour !== undefined) {
|
|
3644
|
-
dateToShow.setUTCHours(c.hour);
|
|
3645
|
-
}
|
|
3646
|
-
if (c.minute !== undefined) {
|
|
3647
|
-
dateToShow.setUTCMinutes(c.minute);
|
|
3995
|
+
putValue(key, value) {
|
|
3996
|
+
this._cache[key] = value;
|
|
3997
|
+
const length = Object.getOwnPropertyNames(this._cache).length;
|
|
3998
|
+
if (length > 30) {
|
|
3999
|
+
console.warn('Pb on cache size exceed ? do a restart ?', length);
|
|
3648
4000
|
}
|
|
3649
|
-
return dateToShow.toISOString();
|
|
3650
4001
|
}
|
|
3651
4002
|
}
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type:
|
|
4003
|
+
Cache.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: Cache, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4004
|
+
Cache.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: Cache, providedIn: 'root' });
|
|
4005
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: Cache, decorators: [{
|
|
3655
4006
|
type: Injectable,
|
|
3656
4007
|
args: [{
|
|
3657
4008
|
providedIn: 'root',
|
|
3658
4009
|
}]
|
|
3659
|
-
}], ctorParameters: function () { return [
|
|
4010
|
+
}], ctorParameters: function () { return []; } });
|
|
3660
4011
|
|
|
3661
4012
|
class RadarService {
|
|
3662
4013
|
constructor(profileService) {
|
|
@@ -4089,7 +4440,8 @@ SharedModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
|
4089
4440
|
RaainSpeedComponent,
|
|
4090
4441
|
RaainGlobeComponent,
|
|
4091
4442
|
ProfileIconDirective,
|
|
4092
|
-
RaainDetailsComponent
|
|
4443
|
+
RaainDetailsComponent,
|
|
4444
|
+
CumulativeSelectorComponent], imports: [CommonModule, FormsModule, IonicModule, NgOptimizedImage, PipesModule], exports: [CommonModule,
|
|
4093
4445
|
NgStyle,
|
|
4094
4446
|
PipesModule,
|
|
4095
4447
|
RaainMapComponent,
|
|
@@ -4101,7 +4453,8 @@ SharedModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
|
4101
4453
|
RaainSpeedComponent,
|
|
4102
4454
|
RaainGlobeComponent,
|
|
4103
4455
|
ProfileIconDirective,
|
|
4104
|
-
RaainDetailsComponent
|
|
4456
|
+
RaainDetailsComponent,
|
|
4457
|
+
CumulativeSelectorComponent] });
|
|
4105
4458
|
SharedModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, providers: [Storage, RadarService, ProfileService, Cache, IonRange], imports: [CommonModule, FormsModule, IonicModule, PipesModule, CommonModule,
|
|
4106
4459
|
PipesModule] });
|
|
4107
4460
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, decorators: [{
|
|
@@ -4118,6 +4471,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
4118
4471
|
RaainGlobeComponent,
|
|
4119
4472
|
ProfileIconDirective,
|
|
4120
4473
|
RaainDetailsComponent,
|
|
4474
|
+
CumulativeSelectorComponent,
|
|
4121
4475
|
],
|
|
4122
4476
|
imports: [CommonModule, FormsModule, IonicModule, NgOptimizedImage, PipesModule],
|
|
4123
4477
|
providers: [Storage, RadarService, ProfileService, Cache, IonRange],
|
|
@@ -4135,6 +4489,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
4135
4489
|
RaainGlobeComponent,
|
|
4136
4490
|
ProfileIconDirective,
|
|
4137
4491
|
RaainDetailsComponent,
|
|
4492
|
+
CumulativeSelectorComponent,
|
|
4138
4493
|
],
|
|
4139
4494
|
}]
|
|
4140
4495
|
}], ctorParameters: function () {
|
|
@@ -4149,5 +4504,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
4149
4504
|
* Generated bundle index. Do not edit.
|
|
4150
4505
|
*/
|
|
4151
4506
|
|
|
4152
|
-
export { AreInProgressPipe, AreReady, AreStopped, CONSTANTS, Cache, CompareManager, FidjStorage, FidjStorageNode, FrameSet, GaugeNodeFilter, HasGoodQuality, HasNotGoodQuality, HaveNotBeenRed, IsNotReady, IsReady, PipesModule, ProfileIconDirective, ProfileService, ProgressBuffer, ProgressValue, RaainCompareComponent, RaainCompareStackComponent, RaainConfigurationComponent, RaainDateDynamicComponent, RaainDateFocusComponent, RaainDetailsComponent, RaainGlobeComponent, RaainMapComponent, RaainSpeedComponent, RadarService, RefreshManager, SharedModule, Storage, WaitForValidation, XYType, mapDateRangeToString };
|
|
4507
|
+
export { AreInProgressPipe, AreReady, AreStopped, CONSTANTS, Cache, CompareManager, CumulativeSelectorComponent, FidjStorage, FidjStorageNode, FrameSet, GaugeNodeFilter, HasGoodQuality, HasNotGoodQuality, HaveNotBeenRed, IsNotReady, IsReady, PipesModule, ProfileIconDirective, ProfileService, ProgressBuffer, ProgressValue, RaainCompareComponent, RaainCompareStackComponent, RaainConfigurationComponent, RaainDateDynamicComponent, RaainDateFocusComponent, RaainDetailsComponent, RaainGlobeComponent, RaainMapComponent, RaainSpeedComponent, RadarService, RefreshManager, SharedModule, Storage, WaitForValidation, XYType, mapDateRangeToString };
|
|
4153
4508
|
//# sourceMappingURL=raain-app.mjs.map
|