diff --git a/projects/nshmp-apps/src/app/source/rates/app.component.ts b/projects/nshmp-apps/src/app/source/rates/app.component.ts index cbb78b382184643054e18abfc2db33bc6f460440..e535b749916710247cac5017b98ff36e89cf2d14 100644 --- a/projects/nshmp-apps/src/app/source/rates/app.component.ts +++ b/projects/nshmp-apps/src/app/source/rates/app.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import {NshmpLibNgAboutPageComponent} from '@ghsc/nshmp-lib-ng/about'; import {NshmpLibNgHazardProvisionalModelComponent} from '@ghsc/nshmp-lib-ng/hazard'; import {NshmpLibNgTemplateComponent} from '@ghsc/nshmp-lib-ng/nshmp'; @@ -15,6 +15,7 @@ import {AboutComponent} from './components/about/about.component'; import {ContentComponent} from './components/content/content.component'; import {ControlPanelComponent} from './components/control-panel/control-panel.component'; import {PlotSettingsPanelComponent} from './components/plot-settings-panel/plot-settings-panel.component'; +import {AppService} from './services/app.service'; @Component({ imports: [ @@ -35,9 +36,15 @@ import {PlotSettingsPanelComponent} from './components/plot-settings-panel/plot- styleUrl: './app.component.scss', templateUrl: './app.component.html', }) -export class AppComponent { +export class AppComponent implements OnInit { /** Navigation list for menu */ navigationList = navigation(); /** Application title */ title = apps().source.rateAndProbability.display; + + constructor(private service: AppService) {} + + ngOnInit(): void { + this.service.init(); + } } diff --git a/projects/nshmp-apps/src/app/source/rates/models/control-form.model.ts b/projects/nshmp-apps/src/app/source/rates/models/control-form.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..901e600ee7ec617af30eaacd0a8acad16dd68115 --- /dev/null +++ b/projects/nshmp-apps/src/app/source/rates/models/control-form.model.ts @@ -0,0 +1,9 @@ +import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm'; + +export interface ControlForm { + distance: number; + latitude: number; + longitude: number; + model: NshmId; + timespan: number; +} diff --git a/projects/nshmp-apps/src/app/source/rates/models/plots.model.ts b/projects/nshmp-apps/src/app/source/rates/models/plots.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc6c671ce0983e2d283192a37a17f31c55adad5f --- /dev/null +++ b/projects/nshmp-apps/src/app/source/rates/models/plots.model.ts @@ -0,0 +1,6 @@ +import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; + +export interface RatePlots { + probability: NshmpPlot; + rate: NshmpPlot; +} diff --git a/projects/nshmp-apps/src/app/source/rates/models/state.model.ts b/projects/nshmp-apps/src/app/source/rates/models/state.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..852559b3884bce3fd51891180cc760af3e7d6ab8 --- /dev/null +++ b/projects/nshmp-apps/src/app/source/rates/models/state.model.ts @@ -0,0 +1,22 @@ +import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; +import {NshmMetadata} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/nshm-service'; +import {ProbabilityUsage} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/probability-service'; +import {RateResponse} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/rates-service'; +import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; + +import {RatePlots} from './plots.model'; + +export interface AppState { + /** Available NSHMs */ + availableModels: Parameter[]; + /** NSHM service metadata */ + nshmServices: NshmMetadata[]; + /** Hazard plots */ + plots: RatePlots; + /** Service call info */ + serviceCallInfo: ServiceCallInfo; + /** Rate service responses */ + serviceResponse: RateResponse; + /** Rate usage responses */ + usageResponses: Map<string, ProbabilityUsage>; +} diff --git a/projects/nshmp-apps/src/app/source/rates/services/app.service.ts b/projects/nshmp-apps/src/app/source/rates/services/app.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..9d48be5055c1a5c95fbec32b10015e9c115714f8 --- /dev/null +++ b/projects/nshmp-apps/src/app/source/rates/services/app.service.ts @@ -0,0 +1,170 @@ +import {computed, Injectable, Signal, signal} from '@angular/core'; +import {FormBuilder} from '@angular/forms'; +import {HazardService} from '@ghsc/nshmp-lib-ng/hazard'; +import {NshmpService, SpinnerService} from '@ghsc/nshmp-lib-ng/nshmp'; +import {NshmMetadata} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/nshm-service'; +import { + ProbabilityRequestMetadata, + ProbabilityUsage, +} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/probability-service'; +import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm'; +import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; +import deepEqual from 'deep-equal'; +import {environment} from 'projects/nshmp-apps/src/environments/environment'; +import {AppServiceModel} from 'projects/nshmp-apps/src/shared/models/app-service.model'; +import {SharedService} from 'projects/nshmp-apps/src/shared/services/shared.service'; +import {catchError} from 'rxjs'; + +import {ControlForm} from '../models/control-form.model'; +import {RatePlots} from '../models/plots.model'; +import {AppState} from '../models/state.model'; + +@Injectable({ + providedIn: 'root', +}) +export class AppService + extends SharedService + implements AppServiceModel<AppState, ControlForm> +{ + /** nshmp-haz-ws web config */ + private nshmpHazWs = environment.webServices.nshmpHazWs; + /** Probability endpoint */ + private probabilityEndpoint = + this.nshmpHazWs.services.curveServices.probability; + /** Rate endpoint */ + private rateEndpoint = this.nshmpHazWs.services.curveServices.rate; + + state = signal<AppState>(this.initialState()); + formGroup = this.formBuilder.group<ControlForm>(this.defaultFormValues()); + + constructor( + private formBuilder: FormBuilder, + private nshmpService: NshmpService, + private spinnerService: SpinnerService, + private hazardService: HazardService + ) { + super(); + this.addValidators(); + } + + /** + * Returns the available models. + */ + get availableModels(): Signal<Parameter[]> { + return computed(() => this.state().availableModels); + } + + /** + * Returns the metadata of the NSHM observable. + */ + get nshmService(): Signal<NshmMetadata> { + return computed(() => + this.state().nshmServices.find( + nshmService => nshmService.model === this.formGroup.getRawValue().model + ) + ); + } + + /** + * Return the usage for the selected model. + */ + get usage(): Signal<ProbabilityUsage> { + return computed(() => + this.state().usageResponses?.get(this.formGroup.getRawValue().model) + ); + } + + addValidators(): void { + const controls = this.formGroup.controls; + + controls.distance.addValidators([this.validateRequired()]); + controls.latitude.addValidators([ + this.validateRequired(), + this.validateNan(), + ]); + controls.longitude.addValidators([ + this.validateRequired(), + this.validateNan(), + ]); + controls.timespan.addValidators([this.validateRequired()]); + } + + callService(): void { + throw new Error('Method not implemented.'); + } + + defaultFormValues(): ControlForm { + return { + distance: null, + latitude: null, + longitude: null, + model: NshmId.CONUS_2018, + timespan: null, + }; + } + + init(): void { + const spinnerRef = this.spinnerService.show( + SpinnerService.MESSAGE_METADATA + ); + + this.hazardService + .dynamicNshms$<ProbabilityRequestMetadata>( + `${this.nshmpHazWs.url}${this.nshmpHazWs.services.nshms}`, + this.probabilityEndpoint + ) + .pipe( + catchError((error: Error) => { + spinnerRef.close(); + return this.nshmpService.throwError$(error); + }) + ) + .subscribe(({models, nshmServices, usageResponses}) => { + spinnerRef.close(); + + this.updateState({ + availableModels: models, + nshmServices, + usageResponses, + }); + + console.log(this.state()); + }); + } + + initialState(): AppState { + return { + availableModels: [], + nshmServices: [], + plots: this.defaultPlots(), + serviceCallInfo: { + serviceCalls: [], + serviceName: '', + usage: [], + }, + serviceResponse: null, + usageResponses: null, + }; + } + + updateState(state: Partial<AppState>): void { + const updatedState = { + ...this.state(), + ...state, + }; + + if (!deepEqual(updatedState, this.state())) { + this.state.set({ + ...this.state(), + ...state, + }); + } + } + + private defaultPlots(): RatePlots { + return { + probability: null, + rate: null, + }; + } +} diff --git a/projects/nshmp-apps/src/shared/services/shared.service.ts b/projects/nshmp-apps/src/shared/services/shared.service.ts index bfd795a98de760cf6622bb8a2be212983524cfbf..effe8ddbc2d01f503f45c3b669675a86122c66a0 100644 --- a/projects/nshmp-apps/src/shared/services/shared.service.ts +++ b/projects/nshmp-apps/src/shared/services/shared.service.ts @@ -1,4 +1,9 @@ -import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; +import { + AbstractControl, + ValidationErrors, + ValidatorFn, + Validators, +} from '@angular/forms'; import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; export interface ResetPlotSettingsProps { @@ -28,4 +33,8 @@ export class SharedService { return isNaN(control.value) ? {nan: {value: control.value}} : null; }; } + + validateRequired(): ValidatorFn { + return (control: AbstractControl) => Validators.required(control); + } }