diff --git a/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.ts b/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.ts index bc789827acc0462dba39e2f6451d85cbc873fcbd..8a71fe50ef4208780e5fae4cd5ede5470abc835e 100644 --- a/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.ts +++ b/projects/nshmp-apps/src/app/gmm/magnitude/components/site-parameters/site-parameters.component.ts @@ -57,7 +57,7 @@ export class SiteParametersComponent implements OnInit, OnDestroy { constructor(private facade: AppFacade) {} ngOnInit(): void { - combineLatest([ + this.valueSubscription = combineLatest([ this.controls.vs30Multi.valueChanges, this.controls.vs30.valueChanges, this.controls.z1p0.valueChanges, diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html index 3362dfb6b7cc650398c56f8fea8d35732713aa88..a317fdaa3f4be2bfbb8bf8b6cd1f2efaef37f1d3 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.html @@ -10,15 +10,15 @@ <mat-tab labelClass="medians-tab" label="Medians" - [disabled]="(hasData$ | async) === false" + [disabled]="hasData() === false" > <ng-template matTabContent> - <nshmp-lib-ng-export-data-table - [table]="spectraTable$ | async" + <nshmp-lib-no-ngrx-export-data-table + [table]="spectraTable()" filename="spectra-medians.csv" buttonText="Export Medians as CSV" /> - <nshmp-lib-ng-data-table [table]="spectraTable$ | async" /> + <nshmp-lib-no-ngrx-data-table [table]="spectraTable()" /> </ng-template> </mat-tab> @@ -26,15 +26,15 @@ <mat-tab labelClass="sigmas-tab" label="Sigmas" - [disabled]="(hasData$ | async) === false" + [disabled]="hasData() === false" > <ng-template matTabContent> - <nshmp-lib-ng-export-data-table - [table]="sigmaTable$ | async" + <nshmp-lib-no-ngrx-export-data-table + [table]="sigmaTable()" filename="spectra-sigmas.csv" buttonText="Export Sigmas as CSV" /> - <nshmp-lib-ng-data-table [table]="sigmaTable$ | async" /> + <nshmp-lib-no-ngrx-data-table [table]="sigmaTable()" /> </ng-template> </mat-tab> </mat-tab-group> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.ts index fc2321541ce217d23d1aae366ccf036f7a47b67b..9c4e7ecd980393f27c58433f1db7d956c36bcfdc 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/content/content.component.ts @@ -1,12 +1,11 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatTab, MatTabContent, MatTabGroup} from '@angular/material/tabs'; -import {gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpLibNgDataTableComponent, NshmpLibNgExportDataTableComponent, -} from '@ghsc/nshmp-lib-ng/nshmp'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import {AppFacade} from '../../state/app.facade'; import {PlotsComponent} from '../plots/plots.component'; @@ -43,35 +42,33 @@ export class ContentComponent { spectraExp = true; /** Whether service has been called and data exists */ - hasData$ = this.facade.serviceResponse$.pipe( - map(responseSpectra => responseSpectra?.length > 0) - ); + hasData = computed(() => this.facade.serviceResponse()?.length > 0); /** Table data for sigma */ - sigmaTable$ = this.facade.sigmaPlotState$.pipe( - map(plot => - gmmUtils.plotToTable( - plot.plotData, - this.sigmaExp, - this.commonXValues, - this.padValues, - plot.xValues - ) - ) - ); + sigmaTable = computed(() => { + const plot = this.facade.sigmaPlotState(); + + return gmmUtils.plotToTable( + plot.plotData, + this.sigmaExp, + this.commonXValues, + this.padValues, + plot.xValues + ); + }); /** Table data for mean */ - spectraTable$ = this.facade.spectraPlotState$.pipe( - map(plot => - gmmUtils.plotToTable( - plot.plotData, - this.spectraExp, - this.commonXValues, - this.padValues, - plot.xValues - ) - ) - ); + spectraTable = computed(() => { + const plot = this.facade.spectraPlotState(); + + return gmmUtils.plotToTable( + plot.plotData, + this.spectraExp, + this.commonXValues, + this.padValues, + plot.xValues + ); + }); constructor(public facade: AppFacade) {} } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html index b0a397aef4178dd4d77a320a81c4d05fd1d74e2f..b81fc6684b50efefc2e5197055daec7ba99f676c 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.html @@ -1,51 +1,47 @@ <!-- Response Spectra Conrol Panel --> -@if (form$ | async) { - <form - class="settings-section control-panel height-full overflow-auto" - [ngrxFormState]="form$ | async" - (submit)="onSubmit()" - novalidate - > - <mat-form-field - class="grid-col-12 multi-select-parameter-menu padding-top-1" - > - <mat-label>Multi-Selectable Parameter</mat-label> - <mat-select - [ngrxFormControlState]="(controls$ | async)?.multiSelectableParam" - > - <mat-option value="gmm">Ground Motion Model</mat-option> - <mat-option value="Mw">Magnitude</mat-option> - <mat-option value="vs30">Site Class</mat-option> - </mat-select> - </mat-form-field> - - <!-- GMM menu --> - <nshmp-lib-ng-gmm-menu - [gmmControlState]="(controls$ | async)?.gmmSource" - [gmmGroupTypeState]="(controls$ | async)?.gmmGroupType" - [multiple]="(controls$ | async)?.multiSelectableParam.value === 'gmm'" - [parameters]="parameters$ | async" +<form + class="settings-section control-panel height-full overflow-auto" + [formGroup]="form" + (submit)="onSubmit()" + novalidate +> + <mat-form-field class="grid-col-12 multi-select-parameter-menu padding-top-1"> + <mat-label>Multi-Selectable Parameter</mat-label> + <mat-select [formControl]="controls.multiSelectableParam"> + <mat-option value="gmm">Ground Motion Model</mat-option> + <mat-option value="Mw">Magnitude</mat-option> + <mat-option value="vs30">Site Class</mat-option> + </mat-select> + </mat-form-field> + + <!-- GMM menu --> + @if (parameters()) { + <nshmp-lib-no-ngrx-gmm-menu + [gmmControl]="controls.gmmSource" + [gmmGroupTypeControl]="controls.gmmGroupType" + [multiple]="controls.multiSelectableParam.value === 'gmm'" + [parameters]="parameters()" /> + } - <app-event-parameters /> + <app-event-parameters /> - <app-source-parameters /> + <app-source-parameters /> - <app-path-parameters /> + <app-path-parameters /> - <app-site-parameters /> + <app-site-parameters /> - <nshmp-lib-ng-gmm-plot-options-control-panel - [showEpistemicFormControl]="(controls$ | async)?.showEpistemicUncertainty" - /> + <nshmp-lib-no-ngrx-gmm-plot-options-control-panel + [showEpistemicFormControl]="controls.showEpistemicUncertainty" + /> - <div class="padding-y-3"></div> + <div class="padding-y-3"></div> - <nshmp-lib-ng-control-panel-buttons - [plotDisabled]="(form$ | async)?.isInvalid" - [serviceCallInfo]="serviceCallInfo$ | async" - [resetDisabled]="(form$ | async)?.isPristine" - (resetClick)="facade.resetControlPanel()" - /> - </form> -} + <nshmp-lib-no-ngrx-control-panel-buttons + [plotDisabled]="form.invalid" + [serviceCallInfo]="serviceCallInfo()" + [resetDisabled]="form.pristine" + (resetClick)="facade.resetControlPanel()" + /> +</form> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.ts index c98557c2f7da039620117875e2f4e32765e77529..e99636a3335c8ff4480dc6a1ee293964cef9b634 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/control-panel/control-panel.component.ts @@ -1,19 +1,19 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy, OnInit} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import {MatOption} from '@angular/material/core'; import {MatFormField, MatLabel} from '@angular/material/form-field'; import {MatSelect} from '@angular/material/select'; import { + GmmFormControlIds, NshmpLibNgGmmMenuComponent, NshmpLibNgGmmPlotOptionsControlPanelComponent, -} from '@ghsc/nshmp-lib-ng/gmm'; +} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, NshmpService, -} from '@ghsc/nshmp-lib-ng/nshmp'; -import {unbox} from 'ngrx-forms'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {Subscription} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; import {EventParametersComponent} from '../event-parameters/event-parameters.component'; @@ -38,38 +38,63 @@ import {SourceParametersComponent} from '../source-parameters/source-parameters. PathParametersComponent, SiteParametersComponent, AsyncPipe, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-control-panel', standalone: true, styleUrl: './control-panel.component.scss', templateUrl: './control-panel.component.html', }) -export class ControlPanelComponent { +export class ControlPanelComponent implements OnInit, OnDestroy { /** Form controls state */ - controls$ = this.facade.controlForm$.pipe(map(form => form.controls)); + controls = this.facade.formGroup.controls; /** Control panel form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** Whether a GMM has been selected */ - gmmSelected$ = this.controls$.pipe( - map(controls => unbox(controls.gmmSource.value).length > 0) - ); + gmmSelected = this.controls.gmmSource.getRawValue()?.length > 0; /** Usage parameters */ - parameters$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters) - ); + parameters = computed(() => this.facade.usage()?.response.parameters); /** Service call info state */ - serviceCallInfo$ = this.facade.serviceCallInfo$; + serviceCallInfo = this.facade.serviceCallInfo; + + /** Supported IMTs based on GMMs selected */ + supportedImts = this.facade.supportedImts; + + private subs: Subscription[] = []; constructor( public facade: AppFacade, public nshmpService: NshmpService ) {} + ngOnInit(): void { + this.subs.push( + this.controls.gmmSource.valueChanges.subscribe(() => { + this.onGmmSource(); + }) + ); + + this.subs.push( + this.controls.multiSelectableParam.valueChanges.subscribe(() => + this.onMultiSelectableParam() + ) + ); + + this.subs.push( + this.controls.showEpistemicUncertainty.valueChanges.subscribe(() => + this.onShowEpistemicUncertainty() + ) + ); + } + + ngOnDestroy(): void { + this.subs.forEach(sub => sub?.unsubscribe()); + } + /** * On form submit. */ @@ -77,4 +102,33 @@ export class ControlPanelComponent { this.facade.callService(); this.nshmpService.selectPlotControl(); } + + private onGmmSource() { + this.facade.resetState(); + } + + private onMultiSelectableParam(): void { + if (!this.facade.state().usageResponse) { + return; + } + + const {multiSelectableParam} = this.form.getRawValue(); + const parameters = this.facade.state().usageResponse.response.parameters; + + if (multiSelectableParam === GmmFormControlIds.MW) { + this.controls.Mw.setValue(parameters.Mw.value as number); + this.controls.MwMulti.setValue([]); + } else if (multiSelectableParam === GmmFormControlIds.VS30) { + this.controls.vs30.setValue(parameters.vs30.value as number); + this.controls.vs30Multi.setValue([]); + } + + this.controls.gmmSource.setValue([]); + this.controls.gmmSource.markAsPristine(); + this.facade.resetState(); + } + + private onShowEpistemicUncertainty(): void { + this.facade.createPlots(); + } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html index d86d6face2ada7ca82897e4b7ae46cd55ae846fa..cb3797a9d1948f86da47efe7df05922ccbe1ff0c 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.html @@ -1,92 +1,89 @@ <!-- Event parameters --> -@if (form$ | async; as form) { - <div class="settings-subsection control-panel"> - <mat-label class="settings-subsection--label">Event Parameters</mat-label> +<div class="settings-subsection control-panel"> + <mat-label class="settings-subsection--label">Event Parameters</mat-label> - <div class="settings-subsection--section"> - <div class="grid-row grid-gap-sm"> - <!-- Event parameters: Mw input --> - <nshmp-lib-ng-gmm-multi-select - class="grid-col-6 mw-input" - label="M<sub>W</sub>" - [multiple]="MwMultiple$ | async" - [numberControl]="form?.controls?.Mw" - [parameter]="(parameters$ | async)?.Mw" - placeholder="Select values..." - [selectControl]="form?.controls?.MwMulti" - [selectOptions]="MwCommonValues" - /> + <div class="settings-subsection--section"> + <div class="grid-row grid-gap-sm"> + <!-- Event parameters: Mw input --> + <nshmp-lib-no-ngrx-gmm-multi-select + class="grid-col-6 mw-input" + label="M<sub>W</sub>" + [multiple]="MwMultiple$ | async" + [numberControl]="form.controls.Mw" + [parameter]="parameters()?.Mw" + placeholder="Select values..." + [selectControl]="form.controls.MwMulti" + [selectOptions]="MwCommonValues" + /> - <!-- Event Parameters: Rake --> - <mat-form-field class="grid-col-6 rake-input"> - <mat-label>Rake (°)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.rake?.max" - [min]="(parameters$ | async)?.rake?.min" - [ngrxFormControlState]="form?.controls?.rake" - step="5" - type="number" - [value]="form?.controls?.rakeButton?.value" - /> - <mat-error> - [ - {{ (parameters$ | async)?.rake?.min }}, - {{ (parameters$ | async)?.rake?.max }} - ] - </mat-error> - </mat-form-field> - </div> + <!-- Event Parameters: Rake --> + <mat-form-field class="grid-col-6 rake-input"> + <mat-label>Rake (°)</mat-label> + <input + matInput + [max]="parameters()?.rake?.max" + [min]="parameters()?.rake?.min" + [formControl]="form.controls.rake" + step="5" + type="number" + [value]="form?.controls?.rakeButton?.value" + /> + <mat-error> + [ + {{ parameters()?.rake?.min }}, + {{ parameters()?.rake?.max }} + ] + </mat-error> + </mat-form-field> + </div> - <!-- Event Parameters: Rake Buttons --> - <mat-button-toggle-group - class="grid-col-12 rake-buttons" - [ngrxFormControlState]="form?.controls?.rakeButton" - [value]="form?.controls?.rake?.value" - > - <mat-button-toggle class="grid-col-4" [value]="0"> - Strike-Slip - </mat-button-toggle> - <mat-button-toggle class="grid-col-4" [value]="-90"> - Normal - </mat-button-toggle> - <mat-button-toggle class="grid-col-4" [value]="90"> - Reverse - </mat-button-toggle> - </mat-button-toggle-group> + <!-- Event Parameters: Rake Buttons --> + <mat-button-toggle-group + class="grid-col-12 rake-buttons" + [formControl]="form.controls.rakeButton" + [value]="form.controls.rake.value" + > + <mat-button-toggle class="grid-col-4" [value]="0"> + Strike-Slip + </mat-button-toggle> + <mat-button-toggle class="grid-col-4" [value]="-90"> + Normal + </mat-button-toggle> + <mat-button-toggle class="grid-col-4" [value]="90"> + Reverse + </mat-button-toggle> + </mat-button-toggle-group> - <!-- Event Parameters: zHyp --> - <div class="grid-row grid-gap-lg zhyp-input"> - <mat-form-field class="grid-col-4"> - <mat-label>Z<sub>HYP</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.zHyp?.max" - [min]="(parameters$ | async)?.zHyp?.min" - [ngrxFormControlState]="form?.controls?.zHyp" - step="0.5" - type="number" - [disabled]="form?.value?.zHypCentered" - /> - <mat-error> - [ - {{ (parameters$ | async)?.zHyp?.min }}, - {{ (parameters$ | async)?.zHyp?.max }} - ] - </mat-error> - </mat-form-field> + <!-- Event Parameters: zHyp --> + <div class="grid-row grid-gap-lg zhyp-input"> + <mat-form-field class="grid-col-4"> + <mat-label>Z<sub>HYP</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.zHyp?.max" + [min]="parameters()?.zHyp?.min" + [formControl]="form.controls.zHyp" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.zHyp?.min }}, + {{ parameters()?.zHyp?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Event Parameters: centered down dip --> - <div class="grid-col-8"> - <mat-checkbox - class="down-dip-checkbox" - color="primary" - [ngrxFormControlState]="form?.controls?.zHypCentered" - > - Centered down-dip - </mat-checkbox> - </div> + <!-- Event Parameters: centered down dip --> + <div class="grid-col-8"> + <mat-checkbox + class="down-dip-checkbox" + color="primary" + [formControl]="form.controls.zHypCentered" + > + Centered down-dip + </mat-checkbox> </div> </div> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.ts index 06354d9895575cbfdb843ff00d379cf88787a2fc..b4097b24c86fd3ad15e8af10f964d19ac42889d1 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/event-parameters/event-parameters.component.ts @@ -1,5 +1,12 @@ import {AsyncPipe} from '@angular/common'; -import {Component, ViewEncapsulation} from '@angular/core'; +import { + Component, + computed, + OnDestroy, + OnInit, + ViewEncapsulation, +} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import { MatButtonToggle, MatButtonToggleGroup, @@ -11,11 +18,12 @@ import { GmmFormControlIds, MW_COMMON_VALUES, NshmpLibNgGmmMultiSelectComponent, -} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpNgrxFormsModule} from '@ghsc/nshmp-lib-ng/nshmp'; +} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import {combineLatest, Subscription} from 'rxjs'; import {map} from 'rxjs/operators'; import {AppFacade} from '../../state/app.facade'; +import {calcHypocenterDepth} from '../../utils/app.utils'; /** * Control panel form fields for GMM event parameters. @@ -32,33 +40,66 @@ import {AppFacade} from '../../state/app.facade'; MatButtonToggle, MatCheckbox, AsyncPipe, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-event-parameters', standalone: true, styleUrl: './event-parameters.component.scss', templateUrl: './event-parameters.component.html', }) -export class EventParametersComponent { +export class EventParametersComponent implements OnInit, OnDestroy { /** Common Vs30 values */ MwCommonValues = MW_COMMON_VALUES; /** Control panel form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; - /** Whether Mw is multi-selectable */ - MwMultiple$ = this.facade.controlForm$.pipe( - map( - form => - form.controls.multiSelectableParam.value === - GmmFormControlIds.MW.toString() - ) - ); + /** Whether magnitudes are multi-selectable */ + MwMultiple$ = + this.facade.formGroup.controls.multiSelectableParam.valueChanges.pipe( + map( + multiSelectableParam => + multiSelectableParam.toString() === GmmFormControlIds.MW.toString() + ) + ); /** Usage parameters */ - parameters$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters) - ); + parameters = computed(() => this.facade.usage()?.response.parameters); + + private controls = this.form.controls; + + private subs: Subscription[] = []; constructor(public facade: AppFacade) {} + + ngOnInit(): void { + this.subs.push( + combineLatest([ + this.controls.Mw.valueChanges, + this.controls.rake.valueChanges, + this.controls.rakeButton.valueChanges, + this.controls.zHyp.valueChanges, + ]).subscribe(() => this.facade.resetState()) + ); + + this.subs.push( + this.controls.zHypCentered.valueChanges.subscribe(() => + this.onZHypCentered() + ) + ); + } + + ngOnDestroy(): void { + this.subs.forEach(sub => sub.unsubscribe()); + } + + private onZHypCentered(): void { + if (this.form.getRawValue().zHypCentered) { + this.controls.zHyp.disable(); + } else { + this.controls.zHyp.enable(); + } + + calcHypocenterDepth(this.form); + } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.html index 7f123557e6fa176ca95e9b6aac5d14df661f7c99..e35d058f4bb911531c9ee00daeee41f6c9a34067 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.html @@ -1,137 +1,135 @@ -@if (form$ | async; as form) { - <div class="grid-row parameter-summary"> - <div class="grid-col-12 tablet-lg:grid-col-6"> - <mat-list class="parameter-list"> - <mat-list-item> - <span class="parameter">Ground Motion Models</span>: - </mat-list-item> - - @for (gmmSource of unbox(form?.value?.gmmSource); track gmmSource) { - <mat-list-item class="indent-list font-small"> - {{ gmmSource.gmm.display }} - </mat-list-item> - } - </mat-list> - </div> - - <div class="grid-col-12 tablet-lg:grid-col-3"> - <mat-list class="parameter-list"> - @if (form.value.multiSelectableParam === GmmFormControlIds.MW) { - <mat-list-item> - <span class="parameter">Magnitude</span>: - </mat-list-item> - - @for (Mw of unbox(form?.value?.MwMulti); track Mw) { - <mat-list-item class="indent-list"> - {{ Mw }} - </mat-list-item> - } - } @else { - <mat-list-item> - <span class="parameter">Magnitude</span>: - {{ form?.value?.Mw }} - </mat-list-item> - } - - <mat-list-item> - <span class="parameter">Rake</span>: {{ form?.value?.rake }}° - </mat-list-item> - - <mat-list-item> - <span class="parameter"> Z<sub>HYP</sub> </span>: - {{ form?.value?.zHyp }} km - </mat-list-item> - - <mat-list-item> - <span class="parameter">Centered Down-Dip</span>: - {{ form.value.zHypCentered === true ? 'On' : 'Off' }} - </mat-list-item> - - <mat-list-item> - <span class="parameter"> Z<sub>TOR</sub> </span>: - {{ form?.value?.zTor }} km - </mat-list-item> - - <mat-list-item> - <span class="parameter">Dip</span>: {{ form?.value?.dip }}° - </mat-list-item> - - <mat-list-item> - <span class="parameter">Width</span>: {{ form?.value?.width }} km - </mat-list-item> - - <mat-list-item> - <span class="parameter"> R<sub>X</sub> </span>: - {{ form?.value?.rX }} - km - </mat-list-item> - </mat-list> - </div> - - <div class="grid-col-12 tablet-lg:grid-col-3"> - <mat-list class="parameter-list"> - <mat-list-item> - <span class="parameter"> R<sub>RUP</sub> </span>: - {{ form?.value?.rRup }} - km - </mat-list-item> - - <mat-list-item> - <span class="parameter"> R<sub>JB</sub> </span>: - {{ form?.value?.rJB }} - km +<div class="grid-row parameter-summary"> + <div class="grid-col-12 tablet-lg:grid-col-6"> + <mat-list class="parameter-list"> + <mat-list-item> + <span class="parameter">Ground Motion Models</span>: + </mat-list-item> + + @for (gmmSource of form?.value?.gmmSource; track gmmSource) { + <mat-list-item class="indent-list font-small"> + {{ gmmSource.gmm.display }} </mat-list-item> + } + </mat-list> + </div> + <div class="grid-col-12 tablet-lg:grid-col-3"> + <mat-list class="parameter-list"> + @if (form.value.multiSelectableParam === GmmFormControlIds.MW) { <mat-list-item> - <span class="parameter">Derive R<sub>JB</sub> & R<sub>RUP</sub></span - >: - {{ form.value.derivePathParams === true ? 'On' : 'Off' }} + <span class="parameter">Magnitude</span>: </mat-list-item> + @for (Mw of form?.value?.MwMulti; track Mw) { + <mat-list-item class="indent-list"> + {{ Mw }} + </mat-list-item> + } + } @else { <mat-list-item> - <span class="parameter">Wall</span>: - {{ form?.value?.hangingWall ? 'Hanging Wall' : 'Footwall' }} + <span class="parameter">Magnitude</span>: + {{ form?.value?.Mw }} </mat-list-item> + } + + <mat-list-item> + <span class="parameter">Rake</span>: {{ form?.value?.rake }}° + </mat-list-item> + + <mat-list-item> + <span class="parameter"> Z<sub>HYP</sub> </span>: + {{ form?.value?.zHyp }} km + </mat-list-item> + + <mat-list-item> + <span class="parameter">Centered Down-Dip</span>: + {{ form.value.zHypCentered === true ? 'On' : 'Off' }} + </mat-list-item> + + <mat-list-item> + <span class="parameter"> Z<sub>TOR</sub> </span>: + {{ form?.value?.zTor }} km + </mat-list-item> + + <mat-list-item> + <span class="parameter">Dip</span>: {{ form?.value?.dip }}° + </mat-list-item> + + <mat-list-item> + <span class="parameter">Width</span>: {{ form?.value?.width }} km + </mat-list-item> + + <mat-list-item> + <span class="parameter"> R<sub>X</sub> </span>: + {{ form?.value?.rX }} + km + </mat-list-item> + </mat-list> + </div> - @if (form.value.multiSelectableParam === GmmFormControlIds.VS30) { - <mat-list-item> <span class="parameter">Vs30</span>: </mat-list-item> - @for (vs30 of unbox(form?.value?.vs30Multi); track vs30) { - <mat-list-item class="indent-list"> - {{ vs30 }} - <sup> m</sup>/<sub>s</sub> - </mat-list-item> - } - } @else { - <mat-list-item> - <span class="parameter">Vs30</span>: {{ form?.value?.vs30 }} + <div class="grid-col-12 tablet-lg:grid-col-3"> + <mat-list class="parameter-list"> + <mat-list-item> + <span class="parameter"> R<sub>RUP</sub> </span>: + {{ form?.value?.rRup }} + km + </mat-list-item> + + <mat-list-item> + <span class="parameter"> R<sub>JB</sub> </span>: + {{ form?.value?.rJB }} + km + </mat-list-item> + + <mat-list-item> + <span class="parameter">Derive R<sub>JB</sub> & R<sub>RUP</sub></span + >: + {{ form.value.derivePathParams === true ? 'On' : 'Off' }} + </mat-list-item> + + <mat-list-item> + <span class="parameter">Wall</span>: + {{ form?.value?.hangingWall ? 'Hanging Wall' : 'Footwall' }} + </mat-list-item> + + @if (form.value.multiSelectableParam === GmmFormControlIds.VS30) { + <mat-list-item> <span class="parameter">Vs30</span>: </mat-list-item> + @for (vs30 of form?.value?.vs30Multi; track vs30) { + <mat-list-item class="indent-list"> + {{ vs30 }} <sup> m</sup>/<sub>s</sub> </mat-list-item> } - + } @else { <mat-list-item> - <span class="parameter"> Z<sub>1.0</sub> </span>: - {{ form?.value?.z1p0 }} - @if (form?.value?.z1p0) { - <span> km</span> - } + <span class="parameter">Vs30</span>: {{ form?.value?.vs30 }} + <sup> m</sup>/<sub>s</sub> </mat-list-item> + } - <mat-list-item> - <span class="parameter"> Z<sub>2.5</sub> </span>: - {{ form?.value?.z2p5 }} - @if (form?.value?.z2p5) { - <span> km</span> - } - </mat-list-item> + <mat-list-item> + <span class="parameter"> Z<sub>1.0</sub> </span>: + {{ form?.value?.z1p0 }} + @if (form?.value?.z1p0) { + <span> km</span> + } + </mat-list-item> - <mat-list-item> - <span class="parameter"> Z<sub>SED</sub> </span>: - {{ form?.value?.zSed }} - @if (form?.value?.zSed) { - <span> km</span> - } - </mat-list-item> - </mat-list> - </div> + <mat-list-item> + <span class="parameter"> Z<sub>2.5</sub> </span>: + {{ form?.value?.z2p5 }} + @if (form?.value?.z2p5) { + <span> km</span> + } + </mat-list-item> + + <mat-list-item> + <span class="parameter"> Z<sub>SED</sub> </span>: + {{ form?.value?.zSed }} + @if (form?.value?.zSed) { + <span> km</span> + } + </mat-list-item> + </mat-list> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.ts index 166e75ceceae0c498c77a8ad634b2e22b42325ea..ea07924b428e1863c7b6cdd4cf9d5a05fae85831 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/parameter-summary/parameter-summary.component.ts @@ -1,9 +1,7 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatList, MatListItem} from '@angular/material/list'; -import {GmmFormControlIds, gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {unbox} from 'ngrx-forms'; -import {map} from 'rxjs'; +import {GmmFormControlIds, gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import {AppFacade} from '../../state/app.facade'; @@ -23,16 +21,12 @@ export class ParameterSummaryComponent { /** Function to convert IMT id to display */ imtIdToDisplay = gmmUtils.imtIdToDisplay; - /** ngrx-forms unbox function */ - unbox = unbox; /** Control form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** List of GMMs */ - gmms$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters?.gmm.values) - ); + gmms = computed(() => this.facade.usage()?.response?.parameters.gmm.values); constructor(private facade: AppFacade) {} } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html index b527eaf74e1ccd1a1a5721710255b44b9bba3c02..06b0961a45fe468a0aa8c4a2ea9b61a219d79c60 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.html @@ -1,93 +1,89 @@ -@if (form$ | async; as form) { - <!-- Path Parameters --> - <div class="settings-subsection control-panel"> - <mat-label class="settings-subsection--label">Path Parameters</mat-label> +<!-- Path Parameters --> +<div class="settings-subsection control-panel"> + <mat-label class="settings-subsection--label">Path Parameters</mat-label> - <div class="settings-subsection--section"> - <div class="grid-row grid-gap-sm"> - <!-- Path Parameters: rX input --> - <mat-form-field class="grid-col-4 rx-input"> - <mat-label>R<sub>X</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.rX?.max" - [min]="(parameters$ | async)?.rX?.min" - [ngrxFormControlState]="form?.controls?.rX" - step="0.5" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.rX?.min }}, - {{ (parameters$ | async)?.rX?.max }} - ] - </mat-error> - </mat-form-field> + <div class="settings-subsection--section"> + <div class="grid-row grid-gap-sm"> + <!-- Path Parameters: rX input --> + <mat-form-field class="grid-col-4 rx-input"> + <mat-label>R<sub>X</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.rX?.max" + [min]="parameters()?.rX?.min" + [formControl]="form.controls.rX" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.rX?.min }}, + {{ parameters()?.rX?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Path Parameters: rRup input --> - <mat-form-field class="grid-col-4 rrup-input"> - <mat-label>R<sub>RUP</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.rRup?.max" - [min]="(parameters$ | async)?.rRup?.min" - [ngrxFormControlState]="form?.controls?.rRup" - step="0.5" - type="number" - [disabled]="form?.value?.derivePathParams" - /> - <mat-error> - [ - {{ (parameters$ | async)?.rRup?.min }}, - {{ (parameters$ | async)?.rRup?.max }} - ] - </mat-error> - </mat-form-field> + <!-- Path Parameters: rRup input --> + <mat-form-field class="grid-col-4 rrup-input"> + <mat-label>R<sub>RUP</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.rRup?.max" + [min]="parameters()?.rRup?.min" + [formControl]="form.controls.rRup" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.rRup?.min }}, + {{ parameters()?.rRup?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Path Parameters: rJB input --> - <mat-form-field class="grid-col-4 rjb-input"> - <mat-label>R<sub>JB</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.rJB?.max" - [min]="(parameters$ | async)?.rJB?.min" - [ngrxFormControlState]="form?.controls?.rJB" - step="0.5" - type="number" - [disabled]="form?.value?.derivePathParams" - /> - <mat-error> - [ - {{ (parameters$ | async)?.rJB?.min }}, - {{ (parameters$ | async)?.rJB?.max }} - ] - </mat-error> - </mat-form-field> - </div> - - <!-- Path Parameters: Derive rJB and rRup --> - <div class="grid-row"> - <mat-checkbox - class="grid-col-12 rjb-rrup-checkbox" - color="primary" - [ngrxFormControlState]="form?.controls?.derivePathParams" - > - Derive R<sub>JB</sub> and R<sub>RUP</sub> - </mat-checkbox> - </div> + <!-- Path Parameters: rJB input --> + <mat-form-field class="grid-col-4 rjb-input"> + <mat-label>R<sub>JB</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.rJB?.max" + [min]="parameters()?.rJB?.min" + [formControl]="form.controls.rJB" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.rJB?.min }}, + {{ parameters()?.rJB?.max }} + ] + </mat-error> + </mat-form-field> + </div> - <!-- Path Parameters: Hanging wall or footwall --> - <mat-button-toggle-group - class="grid-col-12 hanging-wall-button" - [ngrxFormControlState]="form?.controls?.hangingWall" + <!-- Path Parameters: Derive rJB and rRup --> + <div class="grid-row"> + <mat-checkbox + class="grid-col-12 rjb-rrup-checkbox" + color="primary" + [formControl]="form.controls.derivePathParams" > - <mat-button-toggle class="grid-col-6" [value]="true"> - Hanging Wall - </mat-button-toggle> - <mat-button-toggle class="grid-col-6" [value]="false"> - Footwall - </mat-button-toggle> - </mat-button-toggle-group> + Derive R<sub>JB</sub> and R<sub>RUP</sub> + </mat-checkbox> </div> + + <!-- Path Parameters: Hanging wall or footwall --> + <mat-button-toggle-group + class="grid-col-12 hanging-wall-button" + [formControl]="form.controls.hangingWall" + > + <mat-button-toggle class="grid-col-6" [value]="true"> + Hanging Wall + </mat-button-toggle> + <mat-button-toggle class="grid-col-6" [value]="false"> + Footwall + </mat-button-toggle> + </mat-button-toggle-group> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.ts index 0aa99a26e9a20670894cd843cb4312fc072a54d0..dda358e483b1a4b56404fb9396e7c64862430eb6 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/path-parameters/path-parameters.component.ts @@ -1,5 +1,5 @@ -import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy, OnInit} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import { MatButtonToggle, MatButtonToggleGroup, @@ -7,10 +7,10 @@ import { import {MatCheckbox} from '@angular/material/checkbox'; import {MatError, MatFormField, MatLabel} from '@angular/material/form-field'; import {MatInput} from '@angular/material/input'; -import {NshmpNgrxFormsModule} from '@ghsc/nshmp-lib-ng/nshmp'; -import {map} from 'rxjs/operators'; +import {Subscription} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; +import {calcPathParameters} from '../../utils/app.utils'; /** * Control panel form fields for GMM path parameters. @@ -24,25 +24,86 @@ import {AppFacade} from '../../state/app.facade'; MatCheckbox, MatButtonToggleGroup, MatButtonToggle, - AsyncPipe, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-path-parameters', standalone: true, styleUrl: './path-parameters.component.scss', templateUrl: './path-parameters.component.html', }) -export class PathParametersComponent { - /** Form controls state */ - controls$ = this.facade.controlForm$.pipe(map(form => form?.controls)); - +export class PathParametersComponent implements OnInit, OnDestroy { /** Control panel form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** Usage parameters */ - parameters$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters) - ); + parameters = computed(() => this.facade.usage()?.response.parameters); + + subs: Subscription[] = []; + + private controls = this.form.controls; constructor(private facade: AppFacade) {} + + ngOnInit(): void { + this.subs.push(this.controls.rX.valueChanges.subscribe(() => this.onRx())); + + this.subs.push( + this.controls.derivePathParams.valueChanges.subscribe(() => + this.onDerivePathParams() + ) + ); + + this.subs.push( + this.controls.hangingWall.valueChanges.subscribe(() => + this.onHangingWall() + ) + ); + } + + ngOnDestroy(): void { + this.subs.forEach(sub => sub.unsubscribe()); + } + + private onDerivePathParams(): void { + const {derivePathParams, hangingWall, rX} = this.form.getRawValue(); + + if (derivePathParams) { + if (rX >= 0 !== hangingWall) { + this.controls.hangingWall.setValue(rX >= 0); + } + this.controls.hangingWall.enable(); + this.controls.rJB.disable(); + this.controls.rRup.disable(); + calcPathParameters(this.form); + } else { + this.controls.hangingWall.setValue(null); + this.controls.hangingWall.disable(); + this.controls.rJB.enable(); + this.controls.rRup.enable(); + } + + this.facade.resetState(); + } + + private onHangingWall(): void { + const values = this.form.getRawValue(); + const rX = values.hangingWall ? Math.abs(values.rX) : -Math.abs(values.rX); + + if (values.rX !== rX) { + this.controls.rX.setValue(rX); + } + calcPathParameters(this.form); + this.facade.resetState(); + } + + private onRx(): void { + const {derivePathParams, hangingWall, rX} = this.form.getRawValue(); + + if (derivePathParams && rX >= 0 !== hangingWall) { + this.controls.hangingWall.patchValue(rX >= 0); + } + + calcPathParameters(this.form); + this.facade.resetState(); + } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html index cbc9234323f1cd016e4079196748915a531d91dd..afead4807c59a12b832d54be3f5b96ff84ae2ed9 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.html @@ -1,27 +1,26 @@ <div class="height-full overflow-auto"> - @if (spectraPlot$ | async) { - <nshmp-lib-ng-plot-settings + @if (spectraPlot()) { + <nshmp-lib-no-ngrx-plot-settings class="spectra-settings" - [plot]="spectraPlot$ | async" + [plot]="spectraPlot()" + (updatedPlot)="updatePlot(PlotIds.MEANS, $event)" /> } - @if (sigmaPlot$ | async) { + @if (sigmaPlot()) { <nshmp-lib-ng-plot-settings class="sigma-settings" - [plot]="sigmaPlot$ | async" + [plot]="sigmaPlot()" + (updatedPlot)="updatePlot(PlotIds.SIGMA, $event)" /> } <div class="padding-y-3"></div> - <nshmp-lib-ng-plot-reset-plot-settings + <nshmp-lib-no-ngrx-plot-reset-plot-settings (resetClick)="facade.resetPlotSettings()" [resetDisabled]=" - (spectraPlotSettings$ | async)?.isPristine && - (spectraPlotSettings$ | async)?.isUntouched && - (sigmaPlotSettings$ | async)?.isPristine && - (sigmaPlotSettings$ | async)?.isPristine + spectraPlotSettings()?.pristine && sigmaPlotSettings()?.pristine " /> </div> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.ts index 6b2ad32e2131a0b8865b4be56004a7c354d5eb02..f39fca909e907a766b588c7107883acdf87799ba 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/plots-settings/plots-settings.component.ts @@ -1,11 +1,12 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpLibNgPlotResetPlotSettingsComponent, NshmpLibNgPlotSettingsComponent, -} from '@ghsc/nshmp-lib-ng/plot'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/plot'; +import {SpectraPlot} from '../../models/spectra-plot.model'; import {AppFacade} from '../../state/app.facade'; /** @@ -23,19 +24,29 @@ import {AppFacade} from '../../state/app.facade'; templateUrl: './plots-settings.component.html', }) export class PlotsSettingsComponent { - /** Sigma plot state */ - sigmaPlot$ = this.facade.sigmaPlotState$; - /** Sigma plot settings */ - sigmaPlotSettings$ = this.facade.sigmaPlotState$.pipe( - map(plot => plot.settingsForm) - ); + PlotIds = gmmUtils.PlotType; - /** Spectra plot */ - spectraPlot$ = this.facade.spectraPlotState$; - /** Spectra plot settings */ - spectraPlotSettings$ = this.facade.spectraPlotState$.pipe( - map(plot => plot.settingsForm) + /** Mean plot state */ + spectraPlot = this.facade.spectraPlotState; + + /** Mean plot settings */ + spectraPlotSettings = computed( + () => this.facade.spectraPlotState().settingsForm ); + /** Sigma plot state */ + sigmaPlot = this.facade.sigmaPlotState; + + /** Sigma plot settings */ + sigmaPlotSettings = computed(() => this.facade.sigmaPlotState().settingsForm); + constructor(public facade: AppFacade) {} + + updatePlot(id: string, plot: SpectraPlot): void { + const plots = new Map(this.facade.state().plots); + plots.set(id, plot); + this.facade.updateState({ + plots, + }); + } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html index 6e73585f8d29ec594e684fab0e9bb5e9cd6ad60d..5c3419366f5f8bf8ab20c1a55e206e61ca7e6a23 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.html @@ -1,4 +1,4 @@ -<nshmp-lib-ng-plots-container> +<nshmp-lib-no-ngrx-plots-container> <mat-accordion multi> <!-- Spectra plot --> <mat-expansion-panel expanded> @@ -8,8 +8,11 @@ <mat-divider /> - @if (spectraPlotData$ | async; as spectraPlot) { - <nshmp-lib-ng-plot class="spectra-plot" [plot]="spectraPlot" /> + @if (spectraPlotData()) { + <nshmp-lib-no-ngrx-plot + class="spectra-plot" + [plot]="spectraPlotData()" + /> } </mat-expansion-panel> @@ -21,8 +24,8 @@ <mat-divider /> - @if (sigmaPlotData$ | async; as sigmaPlot) { - <nshmp-lib-ng-plot class="sigma-plot" [plot]="sigmaPlot" /> + @if (sigmaPlotData()) { + <nshmp-lib-no-ngrx-plot class="sigma-plot" [plot]="sigmaPlotData()" /> } </mat-expansion-panel> @@ -43,7 +46,7 @@ </mat-expansion-panel-header> <mat-divider /> - <nshmp-lib-ng-app-metadata [repositories]="repositories$ | async" /> + <nshmp-lib-no-ngrx-app-metadata [repositories]="repositories()" /> </mat-expansion-panel> </mat-accordion> -</nshmp-lib-ng-plots-container> +</nshmp-lib-no-ngrx-plots-container> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.ts index 5ef21065f4b79423c302b9d438da138644407a4f..6f493ca397500854a78f7311e17b94d134db0fd9 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/plots/plots.component.ts @@ -1,5 +1,5 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatDivider} from '@angular/material/divider'; import { MatAccordion, @@ -7,15 +7,14 @@ import { MatExpansionPanelHeader, MatExpansionPanelTitle, } from '@angular/material/expansion'; -import {gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpLibNgAppMetadataComponent} from '@ghsc/nshmp-lib-ng/nshmp'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import {NshmpLibNgAppMetadataComponent} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import { NshmpLibNgPlotComponent, NshmpLibNgPlotsContainerComponent, -} from '@ghsc/nshmp-lib-ng/plot'; +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {PlotlyPlot} from '@ghsc/nshmp-utils-ts/libs/plotly'; import {PlotData} from 'plotly.js'; -import {map} from 'rxjs/operators'; import {AppFacade} from '../../state/app.facade'; import {ParameterSummaryComponent} from '../parameter-summary/parameter-summary.component'; @@ -43,19 +42,17 @@ import {ParameterSummaryComponent} from '../parameter-summary/parameter-summary. }) export class PlotsComponent { /** Sigma plot data */ - sigmaPlotData$ = this.facade.sigmaPlotState$.pipe( - map(plot => this.filterData(plot.plotData)) + sigmaPlotData = computed(() => + this.filterData(this.facade.sigmaPlotState().plotData) ); /** Response spectra plot data */ - spectraPlotData$ = this.facade.spectraPlotState$.pipe( - map(plot => ({...this.filterData(plot.plotData)})) - ); + spectraPlotData = computed(() => ({ + ...this.filterData(this.facade.spectraPlotState().plotData), + })); /** Repo metadata */ - repositories$ = this.facade.usage$.pipe( - map(usage => usage?.metadata.repositories) - ); + repositories = computed(() => this.facade.usage()?.metadata?.repositories); constructor(private facade: AppFacade) {} diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html index ccd874a9f4b036f0dd4912f3eede85de0473e069..66e5d7c34e0f90dc63b7f9c0f3fd0b70962d7df3 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.html @@ -1,77 +1,75 @@ -@if (form$ | async; as form) { - <!-- Site & Basin--> - <div class="settings-subsection control-panel"> - <mat-label class="settings-subsection--label">Site & Basin</mat-label> +<!-- Site & Basin--> +<div class="settings-subsection control-panel"> + <mat-label class="settings-subsection--label">Site & Basin</mat-label> - <div class="settings-subsection--section grid-row grid-gap-sm"> - <!-- Site & Basin: Vs30 input --> - <nshmp-lib-ng-gmm-multi-select - class="grid-col-12 vs30-input" - label="V<sub>s30</sub> (<sup>m</sup>⁄<sub>s</sub>)" - [multiple]="vs30Multiple$ | async" - [numberControl]="form?.controls?.vs30" - [parameter]="(parameters$ | async)?.vs30" - placeholder="Select values..." - [selectControl]="form?.controls?.vs30Multi" - [selectOptions]="vs30CommonValues" - /> + <div class="settings-subsection--section grid-row grid-gap-sm"> + <!-- Site & Basin: Vs30 input --> + <nshmp-lib-no-ngrx-gmm-multi-select + class="grid-col-12 vs30-input" + label="V<sub>s30</sub> (<sup>m</sup>⁄<sub>s</sub>)" + [multiple]="vs30Multiple$ | async" + [numberControl]="form?.controls?.vs30" + [parameter]="parameters()?.vs30" + placeholder="Select values..." + [selectControl]="form.controls.vs30Multi" + [selectOptions]="vs30CommonValues" + /> - <!-- Site & Basin: Z 1.0 input --> - <mat-form-field class="grid-col-4 z1p0-input"> - <mat-label>Z<sub>1.0</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.z1p0?.max" - [min]="(parameters$ | async)?.z1p0?.min" - [ngrxFormControlState]="form?.controls?.z1p0" - step="1.0" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.z1p0?.min }}, - {{ (parameters$ | async)?.z1p0?.max }} - ] - </mat-error> - </mat-form-field> + <!-- Site & Basin: Z 1.0 input --> + <mat-form-field class="grid-col-4 z1p0-input"> + <mat-label>Z<sub>1.0</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.z1p0?.max" + [min]="parameters()?.z1p0?.min" + [formControl]="form?.controls?.z1p0" + step="1.0" + type="number" + /> + <mat-error> + [ + {{ parameters()?.z1p0?.min }}, + {{ parameters()?.z1p0?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Site & Basin: Z 2.5 input --> - <mat-form-field class="grid-col-4 z2p5-input"> - <mat-label>Z<sub>2.5</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.z2p5?.max" - [min]="(parameters$ | async)?.z2p5?.min" - [ngrxFormControlState]="form?.controls?.z2p5" - step="1.0" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.z2p5?.min }}, - {{ (parameters$ | async)?.z2p5?.max }} - ] - </mat-error> - </mat-form-field> + <!-- Site & Basin: Z 2.5 input --> + <mat-form-field class="grid-col-4 z2p5-input"> + <mat-label>Z<sub>2.5</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.z2p5?.max" + [min]="parameters()?.z2p5?.min" + [formControl]="form.controls.z2p5" + step="1.0" + type="number" + /> + <mat-error> + [ + {{ parameters()?.z2p5?.min }}, + {{ parameters()?.z2p5?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Site & Basin: zSed input --> - <mat-form-field class="grid-col-4 zsed-input"> - <mat-label>Z<sub>SED</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.zSed?.max" - [min]="(parameters$ | async)?.zSed?.min" - [ngrxFormControlState]="form?.controls?.zSed" - step="1.0" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.zSed?.min }}, - {{ (parameters$ | async)?.zSed?.max }} - ] - </mat-error> - </mat-form-field> - </div> + <!-- Site & Basin: zSed input --> + <mat-form-field class="grid-col-4 zsed-input"> + <mat-label>Z<sub>SED</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.zSed?.max" + [min]="parameters()?.zSed?.min" + [formControl]="form.controls.zSed" + step="1.0" + type="number" + /> + <mat-error> + [ + {{ parameters()?.zSed?.min }}, + {{ parameters()?.zSed?.max }} + ] + </mat-error> + </mat-form-field> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.ts index c4100d13ea041fb5cf9e6e30556c5aa42caa1edb..48ea9f9b3e414f282b7c9239635a72fd81c5bd5c 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/site-parameters/site-parameters.component.ts @@ -1,13 +1,14 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy, OnInit} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import {MatError, MatFormField, MatLabel} from '@angular/material/form-field'; import {MatInput} from '@angular/material/input'; import { GmmFormControlIds, NshmpLibNgGmmMultiSelectComponent, VS30_COMMON_VALUES, -} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpNgrxFormsModule} from '@ghsc/nshmp-lib-ng/nshmp'; +} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import {combineLatest, Subscription} from 'rxjs'; import {map} from 'rxjs/operators'; import {AppFacade} from '../../state/app.facade'; @@ -23,33 +24,49 @@ import {AppFacade} from '../../state/app.facade'; MatInput, MatError, AsyncPipe, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-site-parameters', standalone: true, styleUrl: './site-parameters.component.scss', templateUrl: './site-parameters.component.html', }) -export class SiteParametersComponent { +export class SiteParametersComponent implements OnInit, OnDestroy { + /** Form controls state */ + private controls = this.facade.formGroup.controls; + /** Common vs30 values */ vs30CommonValues = VS30_COMMON_VALUES; /** Control panel form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** Usage parameters */ - parameters$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters) - ); + parameters = computed(() => this.facade.usage()?.response.parameters); - /** Whether vs30 is multi-selectable */ - vs30Multiple$ = this.facade.controlForm$.pipe( + /** Whether Vs30 is multi-selectable */ + vs30Multiple$ = this.controls.multiSelectableParam.valueChanges.pipe( map( - form => - form.controls.multiSelectableParam.value === - GmmFormControlIds.VS30.toString() + multiSelectableParam => + multiSelectableParam.toString() === GmmFormControlIds.VS30.toString() ) ); + private valueSubscription = new Subscription(); + constructor(private facade: AppFacade) {} + + ngOnInit(): void { + this.valueSubscription = combineLatest([ + this.controls.vs30Multi.valueChanges, + this.controls.vs30.valueChanges, + this.controls.z1p0.valueChanges, + this.controls.z2p5.valueChanges, + this.controls.zSed.valueChanges, + ]).subscribe(() => this.facade.resetState()); + } + + ngOnDestroy(): void { + this.valueSubscription.unsubscribe(); + } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html b/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html index 4f1293cdedec01c099a755c3e403effbb71167c3..cc101f7f91ecb794ea27b792d3934cc1c35ece72 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.html @@ -1,65 +1,63 @@ -@if (form$ | async; as form) { - <!-- Source Parameters --> - <div class="settings-subsection control-panel"> - <mat-label class="settings-subsection--label">Source Geometry</mat-label> +<!-- Source Parameters --> +<div class="settings-subsection control-panel"> + <mat-label class="settings-subsection--label">Source Geometry</mat-label> - <div class="settings-subsection--section grid-row grid-gap-sm"> - <!-- Source Parameters: zTor input --> - <mat-form-field class="grid-col-4 ztor-input"> - <mat-label>Z<sub>TOR</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.zTor?.max" - [min]="(parameters$ | async)?.zTor?.min" - [ngrxFormControlState]="form?.controls?.zTor" - step="0.5" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.zTor?.min }}, - {{ (parameters$ | async)?.zTor?.max }} - ] - </mat-error> - </mat-form-field> + <div class="settings-subsection--section grid-row grid-gap-sm"> + <!-- Source Parameters: zTor input --> + <mat-form-field class="grid-col-4 ztor-input"> + <mat-label>Z<sub>TOR</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.zTor?.max" + [min]="parameters()?.zTor?.min" + [formControl]="form.controls.zTor" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.zTor?.min }}, + {{ parameters()?.zTor?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Source Parameters: Dip input --> - <mat-form-field class="grid-col-4 dip-input"> - <mat-label>Dip (°)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.dip?.max" - [min]="(parameters$ | async)?.dip?.min" - [ngrxFormControlState]="form?.controls?.dip" - step="5" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.dip?.min }}, - {{ (parameters$ | async)?.dip?.max }} - ] - </mat-error> - </mat-form-field> + <!-- Source Parameters: Dip input --> + <mat-form-field class="grid-col-4 dip-input"> + <mat-label>Dip (°)</mat-label> + <input + matInput + [max]="parameters()?.dip?.max" + [min]="parameters()?.dip?.min" + [formControl]="form.controls.dip" + step="5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.dip?.min }}, + {{ parameters()?.dip?.max }} + ] + </mat-error> + </mat-form-field> - <!-- Source Parameters: Width input --> - <mat-form-field class="grid-col-4 width-input"> - <mat-label>Width (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.width?.max" - [min]="(parameters$ | async)?.width?.min" - [ngrxFormControlState]="form?.controls?.width" - step="1" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.width?.min }}, - {{ (parameters$ | async)?.width?.max }} - ] - </mat-error> - </mat-form-field> - </div> + <!-- Source Parameters: Width input --> + <mat-form-field class="grid-col-4 width-input"> + <mat-label>Width (km)</mat-label> + <input + matInput + [max]="parameters()?.width?.max" + [min]="parameters()?.width?.min" + [formControl]="form.controls.width" + step="1" + type="number" + /> + <mat-error> + [ + {{ parameters()?.width?.min }}, + {{ parameters()?.width?.max }} + ] + </mat-error> + </mat-form-field> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.ts b/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.ts index f9a9e72920adc0ae69716f6cd552e005fe79554b..8bc99a98139fef0e3fe03979d76333244cb8ad44 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/components/source-parameters/source-parameters.component.ts @@ -1,11 +1,12 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy, OnInit} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import {MatError, MatFormField, MatLabel} from '@angular/material/form-field'; import {MatInput} from '@angular/material/input'; -import {NshmpNgrxFormsModule} from '@ghsc/nshmp-lib-ng/nshmp'; -import {map} from 'rxjs/operators'; +import {combineLatest, Subscription} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; +import {calcHypocenterDepth} from '../../utils/app.utils'; /** * Control panel form fields for GMM source parameters. @@ -17,21 +18,38 @@ import {AppFacade} from '../../state/app.facade'; MatInput, MatError, AsyncPipe, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-source-parameters', standalone: true, styleUrl: './source-parameters.component.scss', templateUrl: './source-parameters.component.html', }) -export class SourceParametersComponent { +export class SourceParametersComponent implements OnInit, OnDestroy { /** Control panel form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** Usage parameters */ - parameters$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters) - ); + parameters = computed(() => this.facade.usage()?.response?.parameters); + + private controls = this.form.controls; + + private valueSubscription = new Subscription(); constructor(private facade: AppFacade) {} + + ngOnInit(): void { + combineLatest([ + this.controls.dip.valueChanges, + this.controls.width.valueChanges, + this.controls.zTor.valueChanges, + ]).subscribe(() => { + this.facade.resetState(); + calcHypocenterDepth(this.form); + }); + } + + ngOnDestroy(): void { + this.valueSubscription.unsubscribe(); + } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/models/form-controls.model.ts b/projects/nshmp-apps/src/app/gmm/spectra/models/form-controls.model.ts index a2675afc8070099ac2dda910928d901680d25a94..baf700cb25d86b2eeca5d7bd83b62e8ff27e3ed8 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/models/form-controls.model.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/models/form-controls.model.ts @@ -1,4 +1,4 @@ -import {GmmFormControlIds, GmmFormControls} from '@ghsc/nshmp-lib-ng/gmm'; +import {GmmFormControls} from '@ghsc/nshmp-lib-no-ngrx/gmm'; /** * Control panel form fields. @@ -23,29 +23,3 @@ export interface FormControls extends GmmFormControls { /** Whether hypocenter is centered */ zHypCentered: boolean; } - -/** Control panel ids */ -enum GmmSpectraFormControlIds { - DERIVE_PATH_PARAMS = 'derivePathParams', - HANGING_WALL = 'hangingWall', - RAKE = 'rake', - RAKE_BUTTON = 'rakeButton', - RJB = 'rJB', - RRUP = 'rRup', - RX = 'rX', - ZHYP = 'zHyp', - ZHYP_CENTERED = 'zHypCentered', -} - -/** - * Application control panel form field ids. - */ -export const FormControlIds = { - ...GmmFormControlIds, - ...GmmSpectraFormControlIds, -}; - -/** - * Application control panel form field ids. - */ -export type FormControlIds = GmmFormControlIds | GmmSpectraFormControlIds; diff --git a/projects/nshmp-apps/src/app/gmm/spectra/models/spectra-plot.model.ts b/projects/nshmp-apps/src/app/gmm/spectra/models/spectra-plot.model.ts index 255351042acf234bea045bf79e646bbf99999607..ee59dc12e278c091ad5db3fcf6b8060a3a52c917 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/models/spectra-plot.model.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/models/spectra-plot.model.ts @@ -1,4 +1,4 @@ -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; /** * Spectra plot diff --git a/projects/nshmp-apps/src/app/gmm/spectra/state/app.facade.ts b/projects/nshmp-apps/src/app/gmm/spectra/state/app.facade.ts index 020ed44c40a8462ea8df9bd756ec510c7878a46a..ffbfa2bf25efe6b714ca19c2af2ab4eb437fdbfc 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/state/app.facade.ts @@ -1,18 +1,33 @@ -import {Injectable} from '@angular/core'; +import {computed, Injectable, Signal, signal} from '@angular/core'; +import {AbstractControl, FormBuilder, Validators} from '@angular/forms'; import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import { + FormGroupControls, + NshmpService, + SpinnerService, +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import { GmmSpectraResponse, GmmSpectraUsage, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; import {EnumParameterValues} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import {select, Store} from '@ngrx/store'; -import {FormGroupState} from 'ngrx-forms'; -import {Observable} from 'rxjs'; +import {environment} from 'projects/nshmp-apps/src/environments/environment'; +import { + redrawPlots, + resetPlotSettings, +} from 'projects/nshmp-apps/src/shared/utils/facade.utils'; +import {catchError} from 'rxjs'; import {FormControls} from '../models/form-controls.model'; import {SpectraPlot} from '../models/spectra-plot.model'; -import {actions} from './app.actions'; -import {spectraAppFeature} from './app.reducer'; +import { + DEFAULT_FORM_VALUES, + defaultPlots, + usageFormValues, +} from '../utils/app.default-values'; +import {serviceResponseToPlotData} from '../utils/response.handler'; +import {AppState, INITIAL_STATE} from './app.state'; /** * Entrypoint to NGRX store for response spectra application. @@ -21,89 +36,250 @@ import {spectraAppFeature} from './app.reducer'; providedIn: 'root', }) export class AppFacade { - constructor(private store: Store) {} + /** nshmp-ws base URL */ + baseUrl = environment.webServices.data.url; + /** GMM service URL */ + serviceUrl = `${this.baseUrl}${environment.webServices.data.services.gmmSpectra}`; - /** - * Returns the control panel form. - */ - get controlForm$(): Observable<FormGroupState<FormControls>> { - return this.store.pipe(select(spectraAppFeature.selectControlForm)); + readonly formGroup = this.formBuilder.group({ + ...DEFAULT_FORM_VALUES, + gmmSource: [], + MwMulti: [], + vs30Multi: [], + }) as FormGroupControls<FormControls>; + + readonly state = signal<AppState>(INITIAL_STATE); + + constructor( + private formBuilder: FormBuilder, + private nshmpService: NshmpService, + private spinnerService: SpinnerService + ) { + this.addValidators(this.formGroup); + this.formGroup.controls.gmmSource.setValue([]); + this.formGroup.controls.showEpistemicUncertainty.disable(); } /** - * Returns service call info observable. + * Returns the mean plot state. */ - get serviceCallInfo$(): Observable<ServiceCallInfo> { - return this.store.pipe(select(spectraAppFeature.selectServiceCallInfo)); + get spectraPlotState(): Signal<SpectraPlot> { + return computed(() => this.state().plots?.get(gmmUtils.PlotType.MEANS)); } /** - * Returns the Gmm distance service responses. + * Returns service call info observable. */ - get serviceResponse$(): Observable<GmmSpectraResponse[]> { - return this.store.pipe(select(spectraAppFeature.selectServiceResponses)); + get serviceCallInfo(): Signal<ServiceCallInfo> { + return computed(() => this.state().serviceCallInfo); } /** - * Returns the mean plot state. + * Returns the Gmm distance service responses. */ - get spectraPlotState$(): Observable<SpectraPlot> { - return this.store.pipe(select(spectraAppFeature.selectSpectraPlot)); + get serviceResponse(): Signal<GmmSpectraResponse[]> { + return computed(() => this.state().serviceResponses); } /** * Returns the mean plot state. */ - get sigmaPlotState$(): Observable<SpectraPlot> { - return this.store.pipe(select(spectraAppFeature.selectSigmaPlot)); + get sigmaPlotState(): Signal<SpectraPlot> { + return computed(() => this.state().plots?.get(gmmUtils.PlotType.SIGMA)); } /** * Returns supported IMTs observable. */ - get supportedImts$(): Observable<EnumParameterValues[]> { - return this.store.pipe(select(spectraAppFeature.selectSupportedImts)); + get supportedImts(): Signal<EnumParameterValues[]> { + return computed(() => this.state().supportedImts); } /** * Returns the Gmm distance usage response. */ - get usage$(): Observable<GmmSpectraUsage> { - return this.store.pipe(select(spectraAppFeature.selectUsageResponses)); + get usage(): Signal<GmmSpectraUsage> { + return computed(() => this.state().usageResponse); } /** * Calls the Gmm distance service. */ callService(): void { - this.store.dispatch(actions.callServices()); + if (this.formGroup.invalid) { + return; + } + + this.spinnerService.show(SpinnerService.MESSAGE_SERVICE); + + const urls = gmmUtils.serviceEndpoints( + this.serviceUrl, + this.formGroup.getRawValue(), + this.formGroup.getRawValue().multiSelectableParam + ); + + this.nshmpService + .callServices$<GmmSpectraResponse>(urls) + .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) + .subscribe(serviceResponses => + this.handleServiceResponses(serviceResponses) + ); + } + + /** + * Create plots based on current state and form group. + */ + createPlots(): void { + this.updateState({ + plots: serviceResponseToPlotData(this.state(), this.formGroup), + }); } /** * Initialize the application. */ init(): void { - this.store.dispatch(actions.init()); + this.spinnerService.show(SpinnerService.MESSAGE_METADATA); + + this.nshmpService + .callService$<GmmSpectraUsage>(this.serviceUrl) + .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) + .subscribe(usageResponse => this.handleUsageResponse(usageResponse)); } /** * Redraw the plot. */ plotRedraw(): void { - this.store.dispatch(actions.plotRedraw()); + this.updateState({ + plots: redrawPlots(this.state().plots), + }); } /** * Reset the control panel. */ resetControlPanel(): void { - this.store.dispatch(actions.resetControlPanel()); + this.formGroup.reset( + usageFormValues(this.state().usageResponse.response.parameters) + ); } /** * Reset the plot settings. */ resetPlotSettings(): void { - this.store.dispatch(actions.resetSettings()); + resetPlotSettings({ + currentPlots: this.state().plots, + defaultPlots: defaultPlots(), + }); + } + + /** + * Reset the state. + */ + resetState(): void { + const serviceCallInfo = gmmUtils.serviceCallInfo({ + multiSelectableParam: this.formGroup.getRawValue().multiSelectableParam, + serviceName: this.state().serviceCallInfo.serviceName, + serviceResponses: this.state().serviceResponses, + serviceUrl: this.serviceUrl, + values: this.formGroup.getRawValue(), + }); + + this.updateState({ + serviceCallInfo, + serviceResponses: null, + }); + + this.createPlots(); + } + + /** + * Update state. + * + * @param state Partial new state to update + */ + updateState(state: Partial<AppState>): void { + this.state.set({ + ...this.state(), + ...state, + }); + } + + /** + * Add validators to form controls. + * + * @param form The form group + */ + private addValidators(form: FormGroupControls<FormControls>): void { + const required = (control: AbstractControl) => Validators.required(control); + + form.controls.derivePathParams.addValidators(required); + form.controls.dip.addValidators(required); + form.controls.gmmSource.addValidators(required); + form.controls.hangingWall.addValidators(required); + form.controls.multiSelectableParam.addValidators(required); + form.controls.Mw.addValidators(required); + form.controls.vs30.addValidators(required); + form.controls.width.addValidators(required); + form.controls.zTor.addValidators(required); + form.controls.rake.addValidators(required); + form.controls.rJB.addValidators(required); + form.controls.rRup.addValidators(required); + form.controls.rX.addValidators(required); + form.controls.vs30.addValidators(required); + form.controls.width.addValidators(required); + form.controls.zTor.addValidators(required); + + form.updateValueAndValidity(); + } + + private handleServiceResponses(serviceResponses: GmmSpectraResponse[]): void { + const means = serviceResponses.map(s => s.response.means); + const sigmas = serviceResponses.map(s => s.response.sigmas); + const hasLogicTree = gmmUtils.hasTree([...means, ...sigmas]); + + if (hasLogicTree) { + this.formGroup.controls.showEpistemicUncertainty.enable(); + } else { + this.formGroup.controls.showEpistemicUncertainty.disable(); + } + + this.updateState({ + serviceCallInfo: gmmUtils.serviceCallInfo({ + multiSelectableParam: this.formGroup.getRawValue().multiSelectableParam, + serviceName: this.state().serviceCallInfo.serviceName, + serviceResponses, + serviceUrl: this.serviceUrl, + values: this.formGroup.getRawValue(), + }), + serviceResponses, + }); + + this.createPlots(); + + this.spinnerService.remove(); + } + + private handleUsageResponse(usageResponse: GmmSpectraUsage): void { + const parameters = usageResponse.response.parameters; + const values = usageFormValues(parameters); + + this.formGroup.patchValue({ + ...values, + }); + + const serviceCallInfo: ServiceCallInfo = { + ...this.state().serviceCallInfo, + usage: [this.serviceUrl], + }; + + this.updateState({ + serviceCallInfo, + usageResponse, + }); + + this.spinnerService.remove(); } } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/state/app.state.ts b/projects/nshmp-apps/src/app/gmm/spectra/state/app.state.ts index cb9dd0460da9b153fba8a12cf802ad7bb3e86610..290ca784420554c1f56577c5c498ac64cec52952 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/state/app.state.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/state/app.state.ts @@ -1,41 +1,17 @@ -import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; +import {ServiceCallInfo} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import { GmmSpectraResponse, GmmSpectraUsage, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; import {EnumParameterValues} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import { - createFormGroupState, - disable, - FormGroupState, - updateGroup, -} from 'ngrx-forms'; -import {SharedAppState} from 'projects/nshmp-apps/src/shared/state/shared'; -import {FormControls} from '../models/form-controls.model'; import {SpectraPlot} from '../models/spectra-plot.model'; -import {DEFAULT_FORM_VALUES, defaultPlots} from '../utils/app.default-values'; - -/** Control form id for ngrx-forms */ -export const CONTROL_FORM_ID = '[ngrx-forms] Response Spectra App Control Form'; - -const formState = createFormGroupState<FormControls>(CONTROL_FORM_ID, { - ...DEFAULT_FORM_VALUES, -}); - -/** - * Initial state for the control panel - */ -export const INITIAL_CONTROL_FORM_STATE = updateGroup<FormControls>({ - showEpistemicUncertainty: disable, -})(formState); +import {defaultPlots} from '../utils/app.default-values'; /** * Spectra application state. */ -export interface AppState extends SharedAppState { - /** Control panel form field state */ - controlForm: FormGroupState<FormControls>; +export interface AppState { /** Plots */ plots: Map<string, SpectraPlot>; /** Service call info */ @@ -45,21 +21,20 @@ export interface AppState extends SharedAppState { /** Supported IMTs */ supportedImts: EnumParameterValues[]; /** GMM usages */ - usageResponses: GmmSpectraUsage; + usageResponse: GmmSpectraUsage; } /** * Application initial state. */ export const INITIAL_STATE: AppState = { - controlForm: INITIAL_CONTROL_FORM_STATE, plots: defaultPlots(), serviceCallInfo: { serviceCalls: [], serviceName: 'Response Spectra', - usage: null, + usage: [], }, serviceResponses: [], supportedImts: [], - usageResponses: null, + usageResponse: null, }; diff --git a/projects/nshmp-apps/src/app/gmm/spectra/utils/app.default-values.ts b/projects/nshmp-apps/src/app/gmm/spectra/utils/app.default-values.ts index b5c1a07119148bb73188d7f35fc235a5a0401181..34752e16fadeac616b19497121ae072a9546b2f8 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/utils/app.default-values.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/utils/app.default-values.ts @@ -1,15 +1,14 @@ -import {GmmFormControlIds, GmmSource, gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; +import {GmmFormControlIds, gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpPlot, NshmpPlotSettings, PlotOptions, plotUtils, -} from '@ghsc/nshmp-lib-ng/plot'; +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import { GmmGroupType, GmmSpectraUsageParameters, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; -import {box, createFormGroupState} from 'ngrx-forms'; import {FormControls} from '../models/form-controls.model'; import {SpectraPlot} from '../models/spectra-plot.model'; @@ -26,11 +25,11 @@ export const DEFAULT_FORM_VALUES: FormControls = { derivePathParams: true, dip: null, gmmGroupType: GmmGroupType.ACTIVE_CRUST, - gmmSource: box([] as GmmSource[]), + gmmSource: [], hangingWall: true, multiSelectableParam: GmmFormControlIds.GMM, Mw: null, - MwMulti: box([] as number[]), + MwMulti: [], rake: 0, rakeButton: 0, rJB: null, @@ -38,7 +37,7 @@ export const DEFAULT_FORM_VALUES: FormControls = { rX: null, showEpistemicUncertainty: false, vs30: null, - vs30Multi: box([] as number[]), + vs30Multi: [], width: null, z1p0: null, z2p5: null, @@ -53,20 +52,20 @@ export const DEFAULT_FORM_VALUES: FormControls = { * * @param parameters The service parameters */ -export function defaultFormValues( +export function usageFormValues( parameters: GmmSpectraUsageParameters ): FormControls { return { ...DEFAULT_FORM_VALUES, dip: parameters.dip.value as number, Mw: parameters.Mw.value as number, - MwMulti: box([parameters.Mw.value as number]), + MwMulti: [parameters.Mw.value as number], rake: parameters.rake.value as number, rJB: parameters.rJB.value as number, rRup: parameters.rRup.value as number, rX: parameters.rX.value as number, vs30: parameters.vs30.value as number, - vs30Multi: box([parameters.vs30.value as number]), + vs30Multi: [parameters.vs30.value as number], width: parameters.width.value as number, z1p0: parameters.z1p0.value as number, z2p5: parameters.z2p5.value as number, @@ -84,23 +83,13 @@ export function defaultPlots(): Map<string, SpectraPlot> { plots.set(gmmUtils.PlotType.MEANS, { label: 'Response Spectra', plotData: meanPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - SPECTRA_PLOT_SETTING_ID, - { - ...meanSettingsForm, - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup(meanSettingsForm), }); plots.set(gmmUtils.PlotType.SIGMA, { label: 'Standard Deviation', plotData: sigmaPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - SIGMA_PLOT_SETTING_ID, - { - ...sigmaSettingsForm, - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup(sigmaSettingsForm), }); return new Map(plots); diff --git a/projects/nshmp-apps/src/app/gmm/spectra/utils/app.utils.ts b/projects/nshmp-apps/src/app/gmm/spectra/utils/app.utils.ts index 0929955f287e3a067f4a171425f524f05ad8a1aa..aaeb3a02070f01a2814050f0ddab7b40483c014a 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/utils/app.utils.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/utils/app.utils.ts @@ -1,11 +1,6 @@ +import {FormControl} from '@angular/forms'; +import {FormGroupControls} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import {Maths} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/calc'; -import { - disable, - FormControlState, - FormGroupState, - setValue, - updateGroup, -} from 'ngrx-forms'; import {FormControls} from '../models/form-controls.model'; @@ -18,19 +13,18 @@ const precision = 3; * @param state The application state */ export function calcHypocenterDepth( - state: FormGroupState<FormControls> -): FormGroupState<FormControls> { - if (!state.value.zHypCentered) { - return state; + form: FormGroupControls<FormControls> +): void { + const values = form.getRawValue(); + + if (!values.zHypCentered) { + return; } - const values = state.value; const dip = toRadians(values.dip); const zHyp = values.zTor + (Math.sin(dip) * values.width) / 2.0; - return updateGroup<FormControls>({ - zHyp: control => updateControl(control, zHyp, values.zHypCentered), - })(state); + updateControl(form.controls.zHyp, zHyp, values.zHypCentered); } /** @@ -39,20 +33,22 @@ export function calcHypocenterDepth( * @param state Application state */ export function calcPathParameters( - state: FormGroupState<FormControls> -): FormGroupState<FormControls> { - if (!state.value.derivePathParams) { - return state; + form: FormGroupControls<FormControls> +): void { + const values = form.getRawValue(); + + if (!values.derivePathParams) { + return; } - const values = state.value; const rX = values.rX; const zTor = values.zTor; const footwall = !values.hangingWall; let rRup = Math.hypot(rX, zTor); if (footwall) { - return updatePathFormValues(state, {rJB: Math.abs(rX), rRup, rX}); + updatePathFormValues(form, {rJB: Math.abs(rX), rRup, rX}); + return; } const δ = toRadians(values.dip); @@ -66,7 +62,8 @@ export function calcPathParameters( const rCut1 = h1 * sinδ; if (rX < rCut1) { - return updatePathFormValues(state, {rJB, rRup, rX}); + updatePathFormValues(form, {rJB, rRup, rX}); + return; } const zBot = zTor + Wz; @@ -75,7 +72,8 @@ export function calcPathParameters( if (rX >= rCut2) { rRup = Math.hypot(zBot, rJB); - return updatePathFormValues(state, {rJB, rRup, rX}); + updatePathFormValues(form, {rJB, rRup, rX}); + return; } /* @@ -83,7 +81,7 @@ export function calcPathParameters( * to top and bottom of fault. */ rRup = h1 + (h2 - h1) * ((rX - rCut1) / (rCut2 - rCut1)); - return updatePathFormValues(state, {rJB, rRup, rX}); + updatePathFormValues(form, {rJB, rRup, rX}); } /** @@ -103,20 +101,18 @@ function toRadians(value: number): number { * @param path Path parameters */ function updatePathFormValues( - state: FormGroupState<FormControls>, + form: FormGroupControls<FormControls>, path: { rJB: number; rRup: number; rX: number; } -): FormGroupState<FormControls> { - return updateGroup<FormControls>({ - rJB: control => - updateControl(control, path.rJB, state.value.derivePathParams), - rRup: control => - updateControl(control, path.rRup, state.value.derivePathParams), - rX: control => updateControl(control, path.rX), - })(state); +): void { + const values = form.getRawValue(); + + updateControl(form.controls.rJB, path.rJB, values.derivePathParams); + updateControl(form.controls.rRup, path.rRup, values.derivePathParams); + updateControl(form.controls.rX, path.rX); } /** @@ -127,13 +123,17 @@ function updatePathFormValues( * @param disabled Whether control is disabled */ function updateControl( - control: FormControlState<number>, + control: FormControl<number>, value: number, disabled = false -): FormControlState<number> { - control = setValue(control, value ? Maths.round(value, precision) : value); +): void { + value = value ? Maths.round(value, precision) : value; + + if (!isNaN(value) && control.value !== value) { + control.setValue(value); + } + if (disabled) { - control = disable(control); + control.disable(); } - return control; } diff --git a/projects/nshmp-apps/src/app/gmm/spectra/utils/response.handler.ts b/projects/nshmp-apps/src/app/gmm/spectra/utils/response.handler.ts index ba7262480c89b4a71468e25858bbe09afa7cb38f..85a808715e4b4027d5b64e24a5139ad63104743b 100644 --- a/projects/nshmp-apps/src/app/gmm/spectra/utils/response.handler.ts +++ b/projects/nshmp-apps/src/app/gmm/spectra/utils/response.handler.ts @@ -1,4 +1,5 @@ -import {gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import {FormGroupControls} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import { GmmSeries, GmmSpectraResponse, @@ -6,8 +7,10 @@ import { TreeValues, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; +import {FormControls} from '../models/form-controls.model'; import {SpectraPlot} from '../models/spectra-plot.model'; import {AppState} from '../state/app.state'; +import {defaultPlots} from './app.default-values'; /** * Transform Gmm distance service responses to plots. @@ -15,16 +18,17 @@ import {AppState} from '../state/app.state'; * @param state The application state */ export function serviceResponseToPlotData( - state: AppState + state: AppState, + form: FormGroupControls<FormControls> ): Map<string, SpectraPlot> { - if (state.serviceResponses.length === 0) { - return state.plots; + if (state.serviceResponses === null || state.serviceResponses?.length === 0) { + return defaultPlots(); } const plots = new Map<string, SpectraPlot>(); const meanPlot = state.plots.get(gmmUtils.PlotType.MEANS); const sigmaPlot = state.plots.get(gmmUtils.PlotType.SIGMA); - const formValues = state.controlForm.value; + const formValues = form.getRawValue(); const xValues = state.serviceResponses .map(serviceResponse =>