diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/app.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/app.component.html index cc96faf4863cc775939491acf27205571d0e7dd1..bc7ed586bdd1d2a93cfd116180b1f526570d66d5 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/app.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/app.component.html @@ -18,12 +18,12 @@ <!-- Provisional model warning --> <nshmp-lib-ng-hazard-provisional-model - [name]="(facade.usageModelA$ | async)?.response?.model?.name" + [name]="facade.usageModelA()?.response?.model?.name" /> <!-- Provisional model warning --> <nshmp-lib-ng-hazard-provisional-model - [name]="(facade.usageModelB$ | async)?.response?.model?.name" + [name]="facade.usageModelB()?.response?.model?.name" /> <!-- About page --> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.html index 66789e92d33da64912291468d6bb9eccfcbc8468..cc0344672b48a9bc32f16ed24f197f5583b75f03 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.html @@ -7,17 +7,14 @@ </mat-tab> <!-- Data tab --> - <mat-tab label="Hazard Curve Data" [disabled]="(hasData$ | async) === false"> + <mat-tab label="Hazard Curve Data" [disabled]="hasData() === false"> <ng-template matTabContent> <app-hazard-data /> </ng-template> </mat-tab> <!-- Spectra tab --> - <mat-tab - label="Response Spectrum Data" - [disabled]="(hasData$ | async) === false" - > + <mat-tab label="Response Spectrum Data" [disabled]="hasData() === false"> <ng-template matTabContent> <app-spectra-data /> </ng-template> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.ts index 895cf79bb35adcb91fcf64d540b15d810474582d..d66be9f4bbe2c02e7ad7bb2621c82e7e0afd7161 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/content/content.component.ts @@ -1,7 +1,6 @@ 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 {map} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; import {HazardDataComponent} from '../hazard-data/hazard-data.component'; @@ -31,13 +30,14 @@ import {SpectraDataComponent} from '../spectra-data/spectra-data.component'; }) export class ContentComponent { /** Whether there is data */ - hasData$ = this.facade.serviceResponses$.pipe( - map( - models => - models.modelA?.hazardResponse !== undefined && - models.modelB?.hazardResponse !== undefined - ) - ); + hasData = computed(() => { + const {modelA, modelB} = this.facade.serviceResponses(); + + return ( + modelA?.hazardResponse !== undefined && + modelB?.hazardResponse !== undefined + ); + }); constructor(private facade: AppFacade) {} } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.html index 3bf9b85de0912e3c6704cfd4307b8cf381e37220..a1a3b1e134375a2639fbd258fc3d0b4f027bef89 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.html @@ -1,100 +1,95 @@ <!-- Control Panel --> -@if (formState$ | async; as formState) { - <form - class="height-full overflow-auto" - [ngrxFormState]="formState" - (submit)="onSubmit()" - > - <!-- Model --> - <nshmp-lib-ng-hazard-model-form - [controlState]="formState?.controls?.model" - [models]="facade.availableModels$ | async" - /> +<form + class="height-full overflow-auto" + [formGroup]="formGroup" + (submit)="onSubmit()" +> + <!-- Model --> + <nshmp-lib-no-ngrx-hazard-model-form + [modelControl]="formGroup.controls.model" + [models]="facade.availableModels()" + /> - <!-- Model Compare --> - <nshmp-lib-ng-hazard-model-form - [controlState]="formState?.controls?.modelCompare" - [models]="facade.comparableModels$ | async" - label="Model Compare" - placeHolderText="--- Select a Different Model ---" - /> + <!-- Model Compare --> + <nshmp-lib-no-ngrx-hazard-model-form + [modelControl]="formGroup.controls.modelCompare" + [models]="facade.comparableModels()" + label="Model Compare" + placeHolderText="--- Select a Different Model ---" + /> + @if (facade.usage()) { <!-- Latitude --> - <nshmp-lib-ng-hazard-location-form + <nshmp-lib-no-ngrx-hazard-location-form class="latitude-input" - [controlState]="formState?.controls?.latitude" + [locationControl]="formGroup.controls.latitude" + [modelControl]="formGroup.controls.model" label="Latitude" - [bounds]="(facade.usage$ | async)?.response?.latitude" + [bounds]="facade.usage().response.latitude" /> <!-- Longitude --> - <nshmp-lib-ng-hazard-location-form + <nshmp-lib-no-ngrx-hazard-location-form class="longitude-input" - [controlState]="formState?.controls?.longitude" + [locationControl]="formGroup.controls.longitude" + [modelControl]="formGroup.controls.model" label="Longitude" - [bounds]="(facade.usage$ | async)?.response?.longitude" + [bounds]="facade.usage().response.longitude" /> + } - <!-- Select site --> - <nshmp-lib-ng-map-select-site - [data]="selectSiteData$ | async" - (siteSelect)="facade.setLocation($event)" - /> + <!-- Select site --> + <nshmp-lib-no-ngrx-map-select-site + [data]="selectSiteData()" + (siteSelect)="facade.setLocation($event)" + /> - <!-- Imt --> - <mat-form-field class="grid-col-12"> - <mat-label>Intensity Measure Type</mat-label> - <mat-select [ngrxFormControlState]="formState?.controls?.imt"> - @for (imt of imts$ | async; track imt) { - <mat-option [value]="imt.value"> - {{ imt.display }} - </mat-option> - } - </mat-select> - </mat-form-field> + <!-- Imt --> + <mat-form-field class="grid-col-12"> + <mat-label>Intensity Measure Type</mat-label> + <mat-select [formControl]="formGroup.controls.imt"> + @for (imt of imts(); track imt) { + <mat-option [value]="imt.value"> + {{ imt.display }} + </mat-option> + } + </mat-select> + </mat-form-field> - <!-- Site Class --> - <div class="grid-row grid-gap-sm"> - <!-- Site classes --> - <nshmp-lib-ng-hazard-site-class-form - class="grid-col-7" - [controlState]="formState?.controls?.siteClass" - [siteClasses]="siteClasses$ | async" - formFieldClass="grid-col-12" + <!-- Site Class --> + <div class="grid-row grid-gap-sm"> + <!-- Site classes --> + @if (siteClasses()) { + <nshmp-lib-no-ngrx-hazard-site-class-form + [siteClassControl]="formGroup.controls.siteClass" + [siteClasses]="siteClasses()" + [modelControl]="formGroup.controls.model" + [vs30Control]="formGroup.controls.vs30" + [vs30Bounds]="facade.usage()?.response.vs30" /> + } + </div> - <!-- Vs30--> - <nshmp-lib-ng-hazard-vs30-form - class="grid-col-5" - [controlState]="formState?.controls?.vs30" - [bounds]="(facade.usage$ | async)?.vs30" - formFieldClass="grid-col-12" - /> - </div> + <!-- Return Period Controls --> + <nshmp-lib-no-ngrx-hazard-return-period-form + [returnPeriodControl]="formGroup.controls.returnPeriod" + [commonReturnPeriodControl]="formGroup.controls.commonReturnPeriods" + /> - <!-- Return Period Controls --> - <nshmp-lib-ng-hazard-return-period-form - [returnPeriodControlState]="formState?.controls?.returnPeriod" - [commonReturnPeriodControlState]=" - formState?.controls?.commonReturnPeriods - " - /> + <!-- Truncation --> + <nshmp-lib-no-ngrx-hazard-truncation-form + [truncationControl]="formGroup.controls.truncate" + /> - <!-- Truncation --> - <nshmp-lib-ng-hazard-truncation-form - [controlState]="formState?.controls?.truncate" - /> - - <!-- Max Direction --> - <nshmp-lib-ng-hazard-max-direction-form - [controlState]="formState?.controls?.maxDirection" - /> + <!-- Max Direction --> + <nshmp-lib-no-ngrx-hazard-max-direction-form + [maxDirectionControl]="formGroup.controls.maxDirection" + /> - <nshmp-lib-ng-control-panel-buttons - [plotDisabled]="formState?.isInvalid" - [serviceCallInfo]="facade.serviceCallInfo$ | async" - [resetDisabled]="formState?.isPristine" - (resetClick)="facade.resetControlPanel()" - /> - </form> -} + <nshmp-lib-no-ngrx-control-panel-buttons + [plotDisabled]="formGroup.invalid" + [serviceCallInfo]="facade.serviceCallInfo()" + [resetDisabled]="formGroup.pristine" + (resetClick)="facade.resetControlPanel()" + /> +</form> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.ts index 1af22eebf94b378d005286ed8aa6ea3e60343e67..95f23833f37df2a2825e4d64685f7058f11fbd3a 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/control-panel/control-panel.component.ts @@ -1,34 +1,35 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy, OnInit, Signal} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import {MatOption} from '@angular/material/core'; -import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatError, MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; import {MatSelect} from '@angular/material/select'; import { - hazardUtils, NshmpLibNgHazardLocationFormComponent, NshmpLibNgHazardMaxDirectionFormComponent, NshmpLibNgHazardModelFormComponent, NshmpLibNgHazardReturnPeriodFormComponent, NshmpLibNgHazardSiteClassFormComponent, NshmpLibNgHazardTruncationFormComponent, - NshmpLibNgHazardVs30FormComponent, -} from '@ghsc/nshmp-lib-ng/hazard'; +} from '@ghsc/nshmp-lib-no-ngrx/hazard'; import { NshmpLibNgMapSelectSiteComponent, SelectSiteDialogData, -} from '@ghsc/nshmp-lib-ng/map'; +} from '@ghsc/nshmp-lib-no-ngrx/map'; import { NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, NshmpService, nshmpUtils, RETURN_PERIOD_BOUNDS, RETURN_PERIODS, -} from '@ghsc/nshmp-lib-ng/nshmp'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm'; import {environment} from 'projects/nshmp-apps/src/environments/environment'; -import {combineLatest, map, Observable} from 'rxjs'; +import {Subscription} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; +import {combineUsages} from '../../utils/response-handler.utils'; /** * Control panel with form fields to call dynamic hazard services. @@ -40,23 +41,24 @@ import {AppFacade} from '../../state/app.facade'; MatSelect, MatOption, AsyncPipe, + MatInput, NshmpLibNgHazardModelFormComponent, NshmpLibNgHazardLocationFormComponent, NshmpLibNgMapSelectSiteComponent, NshmpLibNgHazardSiteClassFormComponent, - NshmpLibNgHazardVs30FormComponent, NshmpLibNgHazardReturnPeriodFormComponent, NshmpLibNgHazardTruncationFormComponent, NshmpLibNgHazardMaxDirectionFormComponent, NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, + ReactiveFormsModule, + MatError, ], 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 { /** Max and min bounds for return periods */ returnPeriodBounds = RETURN_PERIOD_BOUNDS; /** List of return periods */ @@ -65,45 +67,85 @@ export class ControlPanelComponent { siteClassPlaceHolder = nshmpUtils.selectPlaceHolder(); /** Form field state */ - formState$ = this.facade.controlPanelForm$; + formGroup = this.facade.formGroup; /** List of available IMTs */ - imts$ = this.facade.usage$.pipe(map(usage => usage?.response?.model?.imts)); + imts = computed(() => this.facade.usage()?.response?.model?.imts); /** Data for select site component */ - selectSiteData$: Observable<SelectSiteDialogData> = combineLatest([ - this.facade.usage$, - this.facade.controlPanelForm$, - this.facade.nshmService$, - ]).pipe( - map(([usage, form, nshmService]) => { - if (usage === null || nshmService === undefined) { - return; - } - - const services = - environment.webServices.nshmpHazWs.services.curveServices; - - return { - mapUrl: `${nshmService.url}${services.map}?raw=true`, - nshm: form.value.model, - sitesUrl: `${nshmService.url}${services.sites}?raw=true`, - }; - }) - ); + selectSiteData: Signal<SelectSiteDialogData> = computed(() => { + const usage = this.facade.usage(); + const nshmService = this.facade.nshmService(); + + if (usage === null || nshmService === undefined) { + return; + } + + const services = environment.webServices.nshmpHazWs.services.curveServices; + + return { + mapUrl: `${nshmService.url}${services.map}?raw=true`, + nshm: this.facade.formGroup.getRawValue().model, + sitesUrl: `${nshmService.url}${services.sites}?raw=true`, + }; + }); /** List of site class `Parameter`s */ - siteClasses$ = this.facade.usage$.pipe( - map(usage => - hazardUtils.siteClassesToParameters(usage?.response?.model?.siteClasses) - ) + siteClasses = computed( + () => this.facade.usage()?.response?.model.siteClasses ); + private imtSubscription = new Subscription(); + private maxDirectionSubscription = new Subscription(); + private modelCompareSubscription = new Subscription(); + private modelSubscription = new Subscription(); + private truncateSubscription = new Subscription(); + private vs30Subscription = new Subscription(); + constructor( public facade: AppFacade, private nshmpService: NshmpService ) {} + ngOnInit(): void { + this.modelSubscription = + this.formGroup.controls.model.valueChanges.subscribe(() => { + this.facade.onModelChange(); + }); + + this.modelCompareSubscription = + this.formGroup.controls.modelCompare.valueChanges.subscribe( + modelCompare => this.onModelCompare(modelCompare) + ); + + this.imtSubscription = this.formGroup.controls.imt.valueChanges.subscribe( + () => this.facade.createPlots() + ); + + this.maxDirectionSubscription = + this.formGroup.controls.maxDirection.valueChanges.subscribe(() => + this.facade.createPlots() + ); + + this.truncateSubscription = + this.formGroup.controls.truncate.valueChanges.subscribe(() => + this.facade.createPlots() + ); + + this.vs30Subscription = this.formGroup.controls.vs30.valueChanges.subscribe( + () => this.facade.resetState() + ); + } + + ngOnDestroy(): void { + this.imtSubscription.unsubscribe(); + this.maxDirectionSubscription.unsubscribe(); + this.modelCompareSubscription.unsubscribe(); + this.modelSubscription.unsubscribe(); + this.truncateSubscription.unsubscribe(); + this.vs30Subscription.unsubscribe(); + } + /** * On form submit. */ @@ -111,4 +153,16 @@ export class ControlPanelComponent { this.facade.callServices(); this.nshmpService.selectPlotControl(); } + + private onModelCompare(modelCompare: NshmId): void { + if (modelCompare === null) { + this.facade.onModelChange(); + } else { + const usages = [this.facade.usageModelA(), this.facade.usageModelB()]; + + this.facade.updateState({ + combinedUsage: combineUsages(usages), + }); + } + } } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.html index 16a791208cc5f59fc2c18df32a23339e39ed7e56..786dd8646e3c858c78d961d5fa37a3d4b7e3c287 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.html @@ -1,20 +1,20 @@ <div class="grid-container-widescreen-xl margin-y-3"> <mat-accordion multi> <app-table-data-panel - [filename]="hazardFilename$ | async" - [tableData]="hazardTableData$ | async" + [filename]="hazardFilename" + [tableData]="hazardTableData()" panelTitle="Hazard Curves: Total" /> <app-table-data-panel - [filename]="hazardDiffFilename$ | async" - [tableData]="hazardDiffTableData$ | async" + [filename]="hazardDiffFilename" + [tableData]="hazardDiffTableData()" panelTitle="Hazard Curves: Percent Difference" /> <app-table-data-panel - [filename]="hazardComponentsFilename$ | async" - [tableData]="hazardComponentsTableData$ | async" + [filename]="hazardComponentsFilename" + [tableData]="hazardComponentsTableData()" panelTitle="Hazard Curves: Components" /> </mat-accordion> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.ts index 7f1bc6575be0f83b2d0436d23805faddd0b6746d..623e587be6e87934a50c515428569d63fc3854ac 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/hazard-data/hazard-data.component.ts @@ -1,10 +1,9 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatAccordion} from '@angular/material/expansion'; -import {PlotTableDataParams, plotUtils} from '@ghsc/nshmp-lib-ng/plot'; +import {PlotTableDataParams, plotUtils} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {Imt, imtToString} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm'; import {PlotData} from 'plotly.js'; -import {map} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; import {Plots} from '../../state/app.state'; @@ -22,19 +21,15 @@ import {TableDataPanelComponent} from '../table-data-panel/table-data-panel.comp }) export class HazardDataComponent { /** Hazard components plot data */ - private hazardComponentsPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD_COMPONENTS)) - ); + private hazardComponentsPlot = this.facade + .plots() + .get(Plots.HAZARD_COMPONENTS); /** Hazard % diff plot data */ - private hazardDiffPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD_DIFFERENCES)) - ); + private hazardDiffPlot = this.facade.plots().get(Plots.HAZARD_DIFFERENCES); /** Hazard plot data */ - private hazardPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD)) - ); + private hazardPlot = this.facade.plots().get(Plots.HAZARD); /** Table data parameters */ private tableParams: PlotTableDataParams = { @@ -49,71 +44,57 @@ export class HazardDataComponent { }; /** Model values */ - private models$ = this.facade.controlPanelForm$.pipe( - map(form => `${form.value.model}-${form.value.modelCompare}`) - ); + private models = `${this.facade.formGroup.getRawValue().model}-${this.facade.formGroup.getRawValue().modelCompare}`; /** Filename for hazard components export CSV */ - hazardComponentsFilename$ = this.models$.pipe( - map(models => `hazard-components-${models}.csv`) - ); + hazardComponentsFilename = `hazard-components-${this.models}.csv`; /** Filename for hazard export CSV */ - hazardFilename$ = this.models$.pipe( - map(models => `hazard-compare-${models}.csv`) - ); + hazardFilename = `hazard-compare-${this.models}.csv`; /** Filename for hazard difference export CSV */ - hazardDiffFilename$ = this.models$.pipe( - map(models => `hazard-%-diff-${models}.csv`) - ); + hazardDiffFilename = `hazard-%-diff-${this.models}.csv`; /** Hazard table data */ - hazardTableData$ = this.hazardPlot$.pipe( - map(plot => { - const data = [...plot.plotData.data]; - // Remove return period from data - data.shift(); + hazardTableData = computed(() => { + const data = [...this.hazardPlot.plotData.data]; + // Remove return period from data + data.shift(); - return plotUtils.plotDataToTableData( - { - ...plot, - plotData: { - ...plot.plotData, - data, - }, + return plotUtils.plotDataToTableData( + { + ...this.hazardPlot, + plotData: { + ...this.hazardPlot.plotData, + data, }, - this.tableParams - ); - }) - ); + }, + this.tableParams + ); + }); /** Hazard difference table data */ - hazardDiffTableData$ = this.hazardDiffPlot$.pipe( - map(plot => { - return plotUtils.plotDataToTableData(plot, this.tableParams); - }) + hazardDiffTableData = computed(() => + plotUtils.plotDataToTableData(this.hazardDiffPlot, this.tableParams) ); /** Hazard components table data */ - hazardComponentsTableData$ = this.hazardComponentsPlot$.pipe( - map(plot => { - const data = [...plot.plotData.data]; - // Remove return period from data - data.shift(); + hazardComponentsTableData = computed(() => { + const data = [...this.hazardComponentsPlot.plotData.data]; + // Remove return period from data + data.shift(); - return plotUtils.plotDataToTableData( - { - ...plot, - plotData: { - ...plot.plotData, - data, - }, + return plotUtils.plotDataToTableData( + { + ...this.hazardComponentsPlot, + plotData: { + ...this.hazardComponentsPlot.plotData, + data, }, - this.tableParams - ); - }) - ); + }, + this.tableParams + ); + }); constructor(private facade: AppFacade) {} } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.html index 2b1c1476e38f3faa9f6d22a094303d7ba01c8565..4689f8b4e662c99e5375b0b8d8d6913720f0e80c 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.html @@ -1,69 +1,65 @@ -@if (form$ | async; as form) { - <div class="grid-row parameter-summary"> - <div class="grid-col-12 tablet:grid-col-6"> - <mat-list class="parameter-list"> - <mat-list-item> - <span class="parameter">Model</span>: - {{ toDisplay(form.value.model, facade.availableModels$ | async) }} - </mat-list-item> +<div class="grid-row parameter-summary"> + <div class="grid-col-12 tablet:grid-col-6"> + <mat-list class="parameter-list"> + <mat-list-item> + <span class="parameter">Model</span>: + {{ toDisplay(form.value.model, facade.availableModels()) }} + </mat-list-item> - <mat-list-item> - <span class="parameter">Model Compare</span>: - {{ - toDisplay(form.value.modelCompare, facade.availableModels$ | async) - }} - </mat-list-item> + <mat-list-item> + <span class="parameter">Model Compare</span>: + {{ toDisplay(form.value.modelCompare, facade.availableModels()) }} + </mat-list-item> - <mat-list-item> - <span class="parameter">Latitude</span>: {{ form.value.latitude }}° - </mat-list-item> + <mat-list-item> + <span class="parameter">Latitude</span>: {{ form.value.latitude }}° + </mat-list-item> - <mat-list-item> - <span class="parameter">Longitude</span>: {{ form.value.longitude }}° - </mat-list-item> + <mat-list-item> + <span class="parameter">Longitude</span>: {{ form.value.longitude }}° + </mat-list-item> - <mat-list-item> - @if (form.value.siteClass !== selectPlaceHolder.value) { - <span class="parameter">Site Class</span>: - {{ toDisplay(form.value.siteClass, siteClasses$ | async) }} - } @else { - <span class="parameter">Vs30</span>: {{ form.value.vs30 }} m/s - } - </mat-list-item> - </mat-list> - </div> + <mat-list-item> + @if (form.value.siteClass !== selectPlaceHolder.value) { + <span class="parameter">Site Class</span>: + {{ toDisplay(form.value.siteClass, siteClasses$ | async) }} + } @else { + <span class="parameter">Vs30</span>: {{ form.value.vs30 }} m/s + } + </mat-list-item> + </mat-list> + </div> - <div class="grid-col-12 tablet:grid-col-6 print-flex-basis-half"> - <mat-list class="parameter-list"> - <mat-list-item> - <span class="parameter">IMT</span>: - {{ form.value.imt }} - </mat-list-item> + <div class="grid-col-12 tablet:grid-col-6 print-flex-basis-half"> + <mat-list class="parameter-list"> + <mat-list-item> + <span class="parameter">IMT</span>: + {{ form.value.imt }} + </mat-list-item> - <mat-list-item> - <span class="parameter">Return Period</span>: - @if (form.value.commonReturnPeriod !== selectPlaceHolder.value) { - {{ returnPeriodName[form.value.returnPeriod] }} - } @else { - {{ form.value.returnPeriod }} yr - } - </mat-list-item> + <mat-list-item> + <span class="parameter">Return Period</span>: + <!-- @if (form.getRawValue().returnPeriod !== selectPlaceHolder.value) { + {{ returnPeriodName[form.value.returnPeriod] }} + } @else { + {{ form.value.returnPeriod }} yr + } --> + </mat-list-item> - <mat-list-item> - <span class="parameter">Source Type</span>: - {{ form.value.sourceType }} - </mat-list-item> + <mat-list-item> + <span class="parameter">Source Type</span>: + {{ form.value.sourceType }} + </mat-list-item> - <mat-list-item> - <span class="parameter">Truncate</span>: - {{ form.value.truncate === true ? 'On' : 'Off' }} - </mat-list-item> + <mat-list-item> + <span class="parameter">Truncate</span>: + {{ form.value.truncate === true ? 'On' : 'Off' }} + </mat-list-item> - <mat-list-item> - <span class="parameter">Max Direction</span>: - {{ form.value.maxDirection === true ? 'On' : 'Off' }} - </mat-list-item> - </mat-list> - </div> + <mat-list-item> + <span class="parameter">Max Direction</span>: + {{ form.value.maxDirection === true ? 'On' : 'Off' }} + </mat-list-item> + </mat-list> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.ts index 9ed74b4711ccf5a74a39e008f1d0f86562a04261..17c3004aeefc4ca6c4a27250e027e4e89ed91e68 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/parameter-summary/parameter-summary.component.ts @@ -1,9 +1,8 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatList, MatListItem} from '@angular/material/list'; -import {hazardUtils} from '@ghsc/nshmp-lib-ng/hazard'; -import {nshmpUtils, returnPeriodAltName} from '@ghsc/nshmp-lib-ng/nshmp'; -import {map} from 'rxjs'; +import {hazardUtils} from '@ghsc/nshmp-lib-no-ngrx/hazard'; +import {nshmpUtils, returnPeriodAltName} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import {AppFacade} from '../../state/app.facade'; @@ -25,14 +24,20 @@ export class ParameterSummaryComponent { toDisplay = nshmpUtils.parameterToDisplay; /** Form field state */ - form$ = this.facade.controlPanelForm$; + form = this.facade.formGroup; /** Site classes from usage as `Parameter`s */ - siteClasses$ = this.facade.usage$.pipe( - map(usage => - hazardUtils.siteClassesToParameters(usage?.response?.model?.siteClasses) - ) - ); + siteClasses = computed(() => { + const usage = this.facade.usage(); - constructor(private facade: AppFacade) {} + if (usage === undefined || usage === null) { + return []; + } + + return hazardUtils.siteClassesToParameters( + usage.response.model.siteClasses + ); + }); + + constructor(public facade: AppFacade) {} } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.html index 31daed70807f3d906515b047eb2163bae00dd805..8dfad0ad328f5207a5aa9ecface99524146296a5 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.html @@ -5,7 +5,10 @@ <mat-expansion-panel-header> <mat-panel-title>{{ panel.title }}</mat-panel-title> </mat-expansion-panel-header> - <nshmp-lib-ng-plot-settings [plot]="panel.plot$ | async" /> + <nshmp-lib-no-ngrx-plot-settings + [plot]="panel.plot()" + (updatedPlot)="updatePlot(panel.id, $event)" + /> </mat-expansion-panel> } </mat-accordion> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.ts index 6156988a8fd66e4d091b81b5be994a4ac53a6d74..4b262db0941e5e8d02d85422f22fed00cc5c2dda 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plot-settings/plot-settings.component.ts @@ -1,5 +1,5 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import { MatAccordion, MatExpansionPanel, @@ -8,9 +8,9 @@ import { } from '@angular/material/expansion'; import { NshmpLibNgPlotSettingsComponent, - PlotSettingsPanel, -} from '@ghsc/nshmp-lib-ng/plot'; -import {map} from 'rxjs'; + NshmpPlot, +} from '@ghsc/nshmp-lib-no-ngrx/plot'; +import {PlotSettingsPanelId} from 'projects/nshmp-apps/src/shared/models/plot-settings-panel.model'; import {AppFacade} from '../../state/app.facade'; import {Plots} from '../../state/app.state'; @@ -34,62 +34,73 @@ import {Plots} from '../../state/app.state'; }) export class PlotSettingsComponent { /** Hazard plot data */ - private hazardPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD)) - ); + private hazardPlot = computed(() => this.facade.plots().get(Plots.HAZARD)); /** Hazard components plot data */ - private hazardComponentsPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD_COMPONENTS)) + private hazardComponentsPlot = computed(() => + this.facade.plots().get(Plots.HAZARD_COMPONENTS) ); /** Hazard % diff plot data */ - private hazardDiffPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD_DIFFERENCES)) + private hazardDiffPlot = computed(() => + this.facade.plots().get(Plots.HAZARD_DIFFERENCES) ); /** Spectra components plot data */ - private spectraComponentsPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.SPECTRUM_COMPONENTS)) + private spectraComponentsPlot = computed(() => + this.facade.plots().get(Plots.SPECTRUM_COMPONENTS) ); /** Spectra % diff plot data */ - private spectraDiffPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.SPECTRUM_DIFFERENCES)) + private spectraDiffPlot = computed(() => + this.facade.plots().get(Plots.SPECTRUM_DIFFERENCES) ); /** Spectra plot data */ - private spectraPlot$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.SPECTRUM)) - ); + private spectraPlot = computed(() => this.facade.plots().get(Plots.SPECTRUM)); /** The panels with plot settings */ - panels: PlotSettingsPanel[] = [ + panels: PlotSettingsPanelId[] = [ { - plot$: this.hazardPlot$, + id: Plots.HAZARD, + plot: this.hazardPlot, title: 'Hazard Curves Plot Settings', }, { - plot$: this.hazardDiffPlot$, + id: Plots.HAZARD_DIFFERENCES, + plot: this.hazardDiffPlot, title: 'Hazard % Difference Plot Settings', }, { - plot$: this.spectraPlot$, + id: Plots.SPECTRUM, + plot: this.spectraPlot, title: 'Spectra Plot Settings', }, { - plot$: this.spectraDiffPlot$, + id: Plots.SPECTRUM_DIFFERENCES, + plot: this.spectraDiffPlot, title: 'Spectra % Difference Plot Settings', }, { - plot$: this.hazardComponentsPlot$, + id: Plots.HAZARD_COMPONENTS, + plot: this.hazardComponentsPlot, title: 'Hazard Component Curves Plot Settings', }, { - plot$: this.spectraComponentsPlot$, + id: Plots.SPECTRUM_COMPONENTS, + plot: this.spectraComponentsPlot, title: 'Spectra Component Curves Plot Settings', }, ]; constructor(private facade: AppFacade) {} + + updatePlot(id: string, plot: NshmpPlot): void { + console.log(id, plot); + const plots = new Map(this.facade.state().plots); + plots.set(id, plot); + this.facade.updateState({ + plots, + }); + } } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.html index 184bd1f0660fb3885f5ec700efe7616fe15b1f06..256fa41fc70374bc3503878e0a0ebb16a660da5b 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.html @@ -1,5 +1,7 @@ -<nshmp-lib-ng-plots-container containerClass="grid-container-widescreen-xl"> - <nshmp-lib-ng-hazard-design-code-warning /> +<nshmp-lib-no-ngrx-plots-container + containerClass="grid-container-widescreen-xl" +> + <nshmp-lib-no-ngrx-hazard-design-code-warning /> <div class="grid-row"> @for (plot of plots; track plot) { @@ -16,13 +18,13 @@ <mat-panel-title>{{ plot.title }}</mat-panel-title> </mat-expansion-panel-header> <mat-divider /> - @if (plot.plotData$ | async; as plotData) { - <nshmp-lib-ng-plot [plot]="plotData" /> - } - @if (plot.diffPlotData$ | async; as diffPlotData) { + + <nshmp-lib-no-ngrx-plot [plot]="plot.plotData()" /> + + @if (plot.diffPlotData) { <div class="padding-top-4"> <mat-divider /> - <nshmp-lib-ng-plot [plot]="diffPlotData" /> + <nshmp-lib-no-ngrx-plot [plot]="plot.diffPlotData()" /> </div> } </mat-expansion-panel> @@ -49,9 +51,9 @@ </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> </div> </div> -</nshmp-lib-ng-plots-container> +</nshmp-lib-no-ngrx-plots-container> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.ts index 206fc18f6edc1a9ee46040af196ffea67a1bf748..89210f5d8fdaab491f0a0c290b29197e9664897d 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/plots/plots.component.ts @@ -1,5 +1,5 @@ import {AsyncPipe, NgClass} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, Signal} from '@angular/core'; import {MatDivider} from '@angular/material/divider'; import { MatAccordion, @@ -7,15 +7,15 @@ import { MatExpansionPanelHeader, MatExpansionPanelTitle, } from '@angular/material/expansion'; -import {NshmpLibNgHazardDesignCodeWarningComponent} from '@ghsc/nshmp-lib-ng/hazard'; -import {NshmpLibNgAppMetadataComponent} from '@ghsc/nshmp-lib-ng/nshmp'; +import {NshmpLibNgHazardDesignCodeWarningComponent} from '@ghsc/nshmp-lib-no-ngrx/hazard'; +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 {AppControlsService} from '@ghsc/nshmp-template'; import {PlotlyPlot} from '@ghsc/nshmp-utils-ts/libs/plotly'; -import {map, Observable} from 'rxjs'; +import {map} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; import {Plots} from '../../state/app.state'; @@ -26,12 +26,12 @@ import {ParameterSummaryComponent} from '../parameter-summary/parameter-summary. */ interface PlotInfo { /** Main plot data */ - plotData$: Observable<PlotlyPlot>; + plotData: Signal<PlotlyPlot>; /** Expansion panel title */ title: string; /** % diff plot data */ - diffPlotData$?: Observable<PlotlyPlot>; + diffPlotData?: Signal<PlotlyPlot>; } /** @@ -58,34 +58,34 @@ interface PlotInfo { templateUrl: './plots.component.html', }) export class PlotsComponent { - /** Hazard components plot data */ - private hazardComponentsPlotData$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD_COMPONENTS)?.plotData) + /** Hazard plot data */ + private hazardPlotData = computed( + () => this.facade.plots().get(Plots.HAZARD).plotData ); - /** Hazard % diff plot data */ - private hazardDiffPlotData$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD_DIFFERENCES)?.plotData) + /** Hazard components plot data */ + private hazardComponentsPlotData = computed( + () => this.facade.plots().get(Plots.HAZARD_COMPONENTS).plotData ); - /** Hazard plot data */ - private hazardPlotData$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.HAZARD)?.plotData) + /** Hazard % diff plot data */ + private hazardDiffPlotData = computed( + () => this.facade.plots().get(Plots.HAZARD_DIFFERENCES).plotData ); /** Spectra components plot data */ - private spectraComponentsPlotData$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.SPECTRUM_COMPONENTS)?.plotData) + private spectraComponentsPlotData = computed( + () => this.facade.plots().get(Plots.SPECTRUM_COMPONENTS).plotData ); /** Spectra % diff plot data */ - private spectraDiffPlotData$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.SPECTRUM_DIFFERENCES)?.plotData) + private spectraDiffPlotData = computed( + () => this.facade.plots().get(Plots.SPECTRUM_DIFFERENCES).plotData ); /** Spectra plot data */ - private spectraPlotData$ = this.facade.plots$.pipe( - map(plots => plots.get(Plots.SPECTRUM)?.plotData) + private spectraPlotData = computed( + () => this.facade.plots().get(Plots.SPECTRUM).plotData ); /** Wheather both the control and settings panels are closed */ @@ -109,29 +109,27 @@ export class PlotsComponent { /** The plots */ plots: PlotInfo[] = [ { - diffPlotData$: this.hazardDiffPlotData$, - plotData$: this.hazardPlotData$, + diffPlotData: this.hazardDiffPlotData, + plotData: this.hazardPlotData, title: 'Hazard Curves', }, { - diffPlotData$: this.spectraDiffPlotData$, - plotData$: this.spectraPlotData$, + diffPlotData: this.spectraDiffPlotData, + plotData: this.spectraPlotData, title: 'Spectra', }, { - plotData$: this.hazardComponentsPlotData$, + plotData: this.hazardComponentsPlotData, title: 'Hazard Components', }, { - plotData$: this.spectraComponentsPlotData$, + plotData: this.spectraComponentsPlotData, title: 'Spectra Components', }, ]; /** 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/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.html index dad4df7af0054e45762c5aac955114a5f4dbfadd..4926ad259f303a5802d370cf6228d361e9e4f444 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.html @@ -1,20 +1,20 @@ <div class="grid-container-widescreen-xl margin-y-3"> <mat-accordion multi> <app-table-data-panel - [filename]="spectraFilename$ | async" - [tableData]="spectraTotalTableData$ | async" + [filename]="spectraFilename" + [tableData]="spectraTotalTableData()" panelTitle="Response Spectra: Total" /> <app-table-data-panel - [filename]="spectraDiffFilename$ | async" - [tableData]="spectraDiffTableData$ | async" + [filename]="spectraDiffFilename" + [tableData]="spectraDiffTableData()" panelTitle="Response Spectra: Percent Difference" /> <app-table-data-panel - [filename]="spectraComponentsFilename$ | async" - [tableData]="spectraComponentsTableData$ | async" + [filename]="spectraComponentsFilename" + [tableData]="spectraComponentsTableData()" panelTitle="Response Spectra: Components" /> </mat-accordion> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.ts index c9192d6e93940b686dccf4468cd163b65989483e..44e977fed5eca498e092602d9307338b1bfd19e4 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/spectra-data/spectra-data.component.ts @@ -1,14 +1,13 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, Signal} from '@angular/core'; import {MatAccordion} from '@angular/material/expansion'; -import {TableData} from '@ghsc/nshmp-lib-ng/nshmp'; +import {TableData} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import { SourceType, sourceTypeToCapitalCase, } from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/model'; import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; import equal from 'fast-deep-equal'; -import {combineLatest, map, Observable} from 'rxjs'; import {AppFacade} from '../../state/app.facade'; import { @@ -62,98 +61,76 @@ interface ToTableDataOptions { }) export class SpectraDataComponent { /** Model values */ - private models$ = this.facade.controlPanelForm$.pipe( - map(form => `${form.value.model}-${form.value.modelCompare}`) - ); + private models = `${this.facade.formGroup.getRawValue().model}-${this.facade.formGroup.getRawValue().modelCompare}`; /** Filename for hazard components export CSV */ - spectraComponentsFilename$ = this.models$.pipe( - map(models => `spectra-components-${models}.csv`) - ); + spectraComponentsFilename = `spectra-components-${this.models}.csv`; /** Filename for hazard difference export CSV */ - spectraDiffFilename$ = this.models$.pipe( - map(models => `spectra-%-diff-${models}.csv`) - ); + spectraDiffFilename = `spectra-%-diff-${this.models}.csv`; /** Filename for hazard export CSV */ - spectraFilename$ = this.models$.pipe( - map(models => `spectra-compare-${models}.csv`) - ); + spectraFilename = `spectra-compare-${this.models}.csv`; /** Table data for response spectra components */ - spectraComponentsTableData$ = combineLatest([ - this.facade.serviceResponses$, - this.facade.controlPanelForm$, - this.facade.availableModels$, - ]).pipe( - map(([serviceResponses, form, availableModels]) => { - const imts = this.spectraImtRow(serviceResponses); - - const spectraFilter = (spectra: Spectra) => - spectra.sourceType !== SourceType.TOTAL; - - return [ - imts, - ...this.serviceResponsesToTableData({ - availableModels, - name: (modelInfo: Parameter, spectra: Spectra) => - `${modelInfo.display} - ${sourceTypeToCapitalCase( - spectra.sourceType - )}`, - returnPeriod: form.value.returnPeriod, - serviceResponses, - spectraFilter, - }), - ]; - }) - ); + spectraComponentsTableData = computed(() => { + const imts = this.spectraImtRow(this.facade.serviceResponses()); + + const spectraFilter = (spectra: Spectra) => + spectra.sourceType !== SourceType.TOTAL; + + return [ + imts, + ...this.serviceResponsesToTableData({ + availableModels: this.facade.availableModels(), + name: (modelInfo: Parameter, spectra: Spectra) => + `${modelInfo.display} - ${sourceTypeToCapitalCase( + spectra.sourceType + )}`, + returnPeriod: this.facade.formGroup.getRawValue().returnPeriod, + serviceResponses: this.facade.serviceResponses(), + spectraFilter, + }), + ]; + }); /** Table data for response spectra difference */ - spectraDiffTableData$: Observable<TableData[]> = combineLatest([ - this.facade.serviceResponses$, - this.facade.controlPanelForm$, - ]).pipe( - map(([serviceResponses, form]) => { - const spectraDiff = spectraPercentDifference( - serviceResponses, - form.value.returnPeriod - ); - - return [ - this.spectraImtRow(serviceResponses), - { - td: spectraDiff.percentDifference, - th: 'Percent Difference', - }, - ]; - }) - ); + spectraDiffTableData: Signal<TableData[]> = computed(() => { + const serviceResponses = this.facade.serviceResponses(); + + const spectraDiff = spectraPercentDifference( + serviceResponses, + this.facade.formGroup.getRawValue().returnPeriod + ); + + return [ + this.spectraImtRow(serviceResponses), + { + td: spectraDiff.percentDifference, + th: 'Percent Difference', + }, + ]; + }); /** Table data for response spectra */ - spectraTotalTableData$ = combineLatest([ - this.facade.serviceResponses$, - this.facade.controlPanelForm$, - this.facade.availableModels$, - ]).pipe( - map(([serviceResponses, form, availableModels]) => { - const imts = this.spectraImtRow(serviceResponses); - - const spectraFilter = (spectra: Spectra) => - spectra.sourceType === SourceType.TOTAL; - - return [ - imts, - ...this.serviceResponsesToTableData({ - availableModels, - name: (modelInfo: Parameter) => modelInfo.display, - returnPeriod: form.value.returnPeriod, - serviceResponses, - spectraFilter, - }), - ]; - }) - ); + spectraTotalTableData = computed(() => { + const serviceResponses = this.facade.serviceResponses(); + const imts = this.spectraImtRow(serviceResponses); + + const spectraFilter = (spectra: Spectra) => + spectra.sourceType === SourceType.TOTAL; + + return [ + imts, + ...this.serviceResponsesToTableData({ + availableModels: this.facade.availableModels(), + name: (modelInfo: Parameter) => modelInfo.display, + returnPeriod: this.facade.formGroup.getRawValue().returnPeriod, + serviceResponses, + spectraFilter, + }), + ]; + }); constructor(private facade: AppFacade) {} diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.html b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.html index b0b49ca7b35221d3a701444f50dcc4ce4e9a6846..d83e9cb1bac3cec10df3c384b1a32e174e51bc8f 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.html +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.html @@ -3,11 +3,11 @@ <mat-panel-title>{{ panelTitle }}</mat-panel-title> </mat-expansion-panel-header> - <nshmp-lib-ng-export-data-table + <nshmp-lib-no-ngrx-export-data-table [table]="tableData" [filename]="filename | lowercase" [buttonText]="buttonText" /> - <nshmp-lib-ng-data-table [table]="tableData" /> + <nshmp-lib-no-ngrx-data-table [table]="tableData" /> </mat-expansion-panel> diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.ts index 5a61dc77338d6e12ed34196a9a063ca33c9dd63e..43861892039a1f488c4c277018d0eb3a59611240 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/components/table-data-panel/table-data-panel.component.ts @@ -9,7 +9,7 @@ import { NshmpLibNgDataTableComponent, NshmpLibNgExportDataTableComponent, TableData, -} from '@ghsc/nshmp-lib-ng/nshmp'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; /** * Expansion panel with data table and export. diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.facade.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.facade.ts index 74e1ff942692b5d4c774ede23a40a78f4142f28e..df6176c68e6a986fad71b27dbd6c52e960e50b83 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.facade.ts @@ -1,18 +1,61 @@ -import {Injectable} from '@angular/core'; -import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; -import {HazardUsageResponse} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/hazard-service'; +import {computed, Injectable, Signal, signal} from '@angular/core'; +import {AbstractControl, FormBuilder, Validators} from '@angular/forms'; +import {ActivatedRoute, Router} from '@angular/router'; +import {HazardService, hazardUtils} from '@ghsc/nshmp-lib-no-ngrx/hazard'; +import { + NshmpService, + ServiceCallInfo, + SpinnerService, +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; +import { + HazardCalcResponse, + HazardRequestMetadata, + HazardResponse, + HazardUsageResponse, +} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/hazard-service'; import {NshmMetadata} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/nshm-service'; import {Location} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/geo'; +import {Imt, NehrpSiteClass} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm'; +import {sourceTypeFromPascalCase} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/model'; +import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm'; import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import {select, Store} from '@ngrx/store'; -import {FormGroupState} from 'ngrx-forms'; import {environment} from 'projects/nshmp-apps/src/environments/environment'; -import {Observable} from 'rxjs'; +import {devApps} from 'projects/nshmp-apps/src/shared/utils/applications.utils'; +import { + redrawPlots, + resetPlotSettings, + validateNan, +} from 'projects/nshmp-apps/src/shared/utils/facade.utils'; +import {catchError, forkJoin, Observable} from 'rxjs'; -import {appActions} from './app.actions'; -import {dynamicCompareAppFeature} from './app.reducer'; -import {ControlForm, ServiceResponses} from './app.state'; +import {defaultPlots} from '../utils/app.utils'; +import {createPlots} from '../utils/response-handler.utils'; +import * as responseHandler from '../utils/response-handler.utils'; +import { + AppState, + ControlForm, + controlPanelFormInitialState, + initialState, + ServiceResponses, + Spectra, +} from './app.state'; + +/** + * URL query. + */ +interface Query { + imt: string; + latitude: string; + longitude: string; + maxDirection: string; + model: string; + modelCompare: string; + returnPeriod: string; + siteClass: string; + truncate: string; + vs30: string; +} /** * Entrypoint to NGRX store for dynamic hazard compare application. @@ -21,128 +64,255 @@ import {ControlForm, ServiceResponses} from './app.state'; providedIn: 'root', }) export class AppFacade { + readonly state = signal<AppState>(initialState()); + + readonly formGroup = this.formBuilder.group<ControlForm>( + controlPanelFormInitialState() + ); + /** nshmp-haz-ws web config */ nshmpHazWs = environment.webServices.nshmpHazWs; /** Hazard endpoint */ serviceEndpoint = this.nshmpHazWs.services.curveServices.hazard; - constructor(private store: Store) {} + constructor( + private formBuilder: FormBuilder, + private spinnerService: SpinnerService, + private nshmpService: NshmpService, + private hazardService: HazardService, + private route: ActivatedRoute, + private router: Router + ) { + this.addValidators(); + this.formGroup.valueChanges.subscribe(() => this.updateUrl()); + } /** * Returns the available dynamic models. */ - get availableModels$(): Observable<Parameter[]> { - return this.store.pipe( - select(dynamicCompareAppFeature.selectAvailableModels) - ); + get availableModels(): Signal<Parameter[]> { + return computed(() => this.state().availableModels); } /** * Returns the models that can been compared with selected model. */ - get comparableModels$(): Observable<Parameter[]> { - return this.store.pipe( - select(dynamicCompareAppFeature.selectComparableModels) - ); - } - - /** - * Returns the control panel form state. - */ - get controlPanelForm$(): Observable<FormGroupState<ControlForm>> { - return this.store.pipe( - select(dynamicCompareAppFeature.selectControlPanelForm) - ); + get comparableModels(): Signal<Parameter[]> { + return computed(() => this.state().comparableModels); } /** * Returns the `Map` of the plots. */ - get plots$(): Observable<Map<string, NshmpPlot>> { - return this.store.pipe(select(dynamicCompareAppFeature.selectPlots)); + get plots(): Signal<Map<string, NshmpPlot>> { + return computed(() => this.state().plots); } /** * Returns the metadata of the NSHM observable. */ - get nshmService$(): Observable<NshmMetadata> { - return this.store.pipe(select(dynamicCompareAppFeature.selectNshmService)); + get nshmService(): Signal<NshmMetadata> { + return computed(() => + this.state().nshmServices.find( + nshmService => nshmService.model === this.formGroup.getRawValue().model + ) + ); } /** * Returns the service call info. */ - get serviceCallInfo$(): Observable<ServiceCallInfo> { - return this.store.pipe( - select(dynamicCompareAppFeature.selectServiceCallInfo) - ); + get serviceCallInfo(): Signal<ServiceCallInfo> { + return computed(() => this.state().serviceCallInfo); } /** * Returns the service responses. */ - get serviceResponses$(): Observable<ServiceResponses> { - return this.store.pipe( - select(dynamicCompareAppFeature.selectServiceResponses) - ); + get serviceResponses(): Signal<ServiceResponses> { + return computed(() => this.state().serviceResponses); } /** * Return the usage for the selected model. */ - get usage$(): Observable<HazardUsageResponse> { - return this.store.pipe( - select(dynamicCompareAppFeature.selectCombinedUsage) - ); + get usage(): Signal<HazardUsageResponse> { + return computed(() => this.state().combinedUsage); } /** * Return the usage for the selected model. */ - get usageModelA$(): Observable<HazardUsageResponse> { - return this.store.pipe(select(dynamicCompareAppFeature.selectUsageModelA)); + get usageModelA(): Signal<HazardUsageResponse> { + return computed(() => + this.state().usageResponses?.get(this.formGroup.getRawValue().model) + ); } /** * Return the usage for the selected model. */ - get usageModelB$(): Observable<HazardUsageResponse> { - return this.store.pipe(select(dynamicCompareAppFeature.selectUsageModelB)); + get usageModelB(): Signal<HazardUsageResponse> { + return computed(() => + this.state().usageResponses?.get( + this.formGroup.getRawValue().modelCompare + ) + ); } /** * Call the hazard service. */ callServices(): void { - this.store.dispatch(appActions.callServices()); + this.spinnerService.show(SpinnerService.MESSAGE_SERVICE); + const form = this.formGroup.getRawValue(); + + const modelUrl = this.serviceCallUrl( + form.model, + form, + this.state().nshmServices + ); + + const modelCompareUrl = this.serviceCallUrl( + form.modelCompare, + form, + this.state().nshmServices + ); + + const serviceCallInfo: ServiceCallInfo = { + ...this.state().serviceCallInfo, + serviceCalls: [modelUrl, modelCompareUrl], + }; + + const calls: Observable<HazardCalcResponse>[] = [ + modelUrl, + modelCompareUrl, + ].map(url => this.nshmpService.callService$(url)); + + forkJoin(calls) + .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) + .subscribe(hazardResponses => { + if (hazardResponses.length !== 2) { + throw new Error('Does not contain two responses'); + } + + const modelA = hazardResponses[0]; + const modelB = hazardResponses[1]; + + const serviceResponses: ServiceResponses = { + modelA: { + hazardResponse: modelA, + model: form.model, + spectra: this.responseSpectra(modelA.response.hazardCurves, form), + }, + modelB: { + hazardResponse: modelB, + model: form.modelCompare, + spectra: this.responseSpectra(modelB.response.hazardCurves, form), + }, + }; + + this.updateState({serviceCallInfo, serviceResponses}); + this.createPlots(); + this.spinnerService.remove(); + }); } /** * Initialize applicaiton. */ init(): void { - this.store.dispatch(appActions.init()); + this.spinnerService.show(SpinnerService.MESSAGE_METADATA); + + this.hazardService + .dynamicNshms$<HazardRequestMetadata>( + `${this.nshmpHazWs.url}${this.nshmpHazWs.services.nshms}`, + this.serviceEndpoint + ) + .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) + .subscribe(({models, nshmServices, usageResponses}) => { + this.spinnerService.remove(); + const serviceCallInfo: ServiceCallInfo = { + ...this.state().serviceCallInfo, + usage: nshmServices.map( + service => `${service.url}${this.serviceEndpoint}` + ), + }; + + this.updateState({ + availableModels: models, + nshmServices, + serviceCallInfo, + usageResponses, + }); + + this.onModelChange(); + this.initialFormSet(); + }); + } + + /** + * Returns the updated state for a model change. + * + * @param state The current application state + */ + onModelChange(): void { + const comparableModels = this.getComparableModels(); + const defaultComparableModel = [...comparableModels]?.pop()?.value ?? null; + const combinedUsage = this.getCombinedUsage( + this.state().usageResponses, + this.formGroup.getRawValue().model, + defaultComparableModel + ); + + this.formGroup.patchValue({modelCompare: defaultComparableModel as NshmId}); + + this.updateState({ + combinedUsage, + comparableModels, + plots: initialState().plots, + serviceCallInfo: { + ...this.state().serviceCallInfo, + serviceCalls: [], + }, + serviceResponses: initialState().serviceResponses, + }); } /** * Redraw the plots. */ plotRedraw(): void { - this.store.dispatch(appActions.plotRedraw()); + redrawPlots(this.state().plots); } /** * Reset the control panel. */ resetControlPanel(): void { - this.store.dispatch(appActions.resetControlPanel()); + this.formGroup.reset(controlPanelFormInitialState()); + this.resetState(); + this.onModelChange(); } /** * Reset the plot settings. */ resetSettings(): void { - this.store.dispatch(appActions.resetSettings()); + resetPlotSettings({ + currentPlots: this.state().plots, + defaultPlots: defaultPlots(), + }); + } + + resetState(): void { + this.updateState({ + plots: initialState().plots, + serviceCallInfo: { + ...this.state().serviceCallInfo, + serviceCalls: [], + }, + }); } /** @@ -176,6 +346,248 @@ export class AppFacade { * @param location The location */ setLocation(location: Location): void { - this.store.dispatch(appActions.setLocation({location})); + this.formGroup.patchValue({ + latitude: location.latitude, + longitude: location.longitude, + }); + } + + updateState(state: Partial<AppState>): void { + this.state.set({ + ...this.state(), + ...state, + }); + } + + createPlots(): void { + const plots = createPlots(this.state(), this.formGroup); + this.updateState({plots}); + } + + /** + * Returns the IMT to use. + * + * @param query The query value + * @param defaultImt The default IMT + */ + private getImt(query: string, defaultImt: Imt): Imt { + return ( + Object.values(Imt).find(imt => imt.toString() === query) ?? defaultImt + ); + } + + /** + * Returns the model to use. + * + * @param query The query model + * @param defaultModel The default model + */ + private getModel(query: string, defaultModel: NshmId): NshmId { + return ( + Object.values(NshmId).find(model => model.toString() === query) ?? + defaultModel + ); + } + + /** + * Returns the site class to use. + * + * @param query The query value + * @param defaultSiteClass The default site class + */ + private getSiteClass( + query: string, + defaultSiteClass: NehrpSiteClass + ): NehrpSiteClass { + return ( + Object.values(NehrpSiteClass).find( + siteClass => siteClass.toString() === query + ) ?? defaultSiteClass + ); + } + + /** + * Create the response spectra for a specific model and for each source type available. + * + * @param hazardResponses The hazard responses + * @param form The control form values + */ + private responseSpectra( + hazardResponses: HazardResponse[], + form: ControlForm + ): Spectra[] { + const spectra: Spectra[] = []; + const sourceTypes = [...hazardResponses] + .shift() + .data.map(data => data.component); + + sourceTypes.forEach(sourceType => { + form.sourceType = sourceType; + spectra.push({ + responseSpectra: hazardUtils.responseSpectra(hazardResponses, form), + sourceType: sourceTypeFromPascalCase(sourceType), + }); + }); + + return spectra; + } + + /** + * Returns the URL to call for a specific model. + * + * @param model The NSHM to call + * @param formValues The control panel form values + * @param nshmServices The NSHM service metadata + */ + private serviceCallUrl( + model: NshmId, + formValues: ControlForm, + nshmServices: NshmMetadata[] + ): string { + const {latitude, longitude, vs30} = formValues; + const service = nshmServices.find(service => service.model === model); + return `${service.url}${this.serviceEndpoint}/${longitude}/${latitude}/${vs30}`; + } + + private initialFormSet(): void { + const query = this.route.snapshot.queryParams as Query; + const defaultValues = controlPanelFormInitialState(); + + const formValues: ControlForm = { + commonReturnPeriods: defaultValues.commonReturnPeriods, + imt: this.getImt(query.imt, defaultValues.imt), + latitude: query.latitude + ? Number.parseFloat(query.latitude) + : defaultValues.latitude, + longitude: query.longitude + ? Number.parseFloat(query.longitude) + : defaultValues.longitude, + maxDirection: + query.maxDirection !== undefined + ? (JSON.parse(query.maxDirection) as boolean) + : defaultValues.maxDirection, + model: this.getModel(query.model, defaultValues.model), + modelCompare: this.getModel( + query.modelCompare, + defaultValues.modelCompare + ), + returnPeriod: query.returnPeriod + ? Number.parseInt(query.returnPeriod, 10) + : defaultValues.returnPeriod, + siteClass: this.getSiteClass( + query.siteClass, + defaultValues.siteClass as NehrpSiteClass + ), + sourceType: null, + truncate: + query.truncate !== undefined + ? (JSON.parse(query.truncate) as boolean) + : defaultValues.truncate, + vs30: query.vs30 ? Number.parseFloat(query.vs30) : defaultValues.vs30, + }; + + this.formGroup.patchValue(formValues); + + if (this.formGroup.valid) { + this.nshmpService.selectPlotControl(); + this.callServices(); + } else if (this.formGroup.getRawValue() !== defaultValues) { + this.formGroup.markAsDirty(); + } + } + + private updateUrl(): void { + const value = this.formGroup.getRawValue(); + + const query: Query = { + imt: value.imt, + latitude: value.latitude?.toString(), + longitude: value.longitude?.toString(), + maxDirection: String(value.maxDirection), + model: value.model, + modelCompare: value.modelCompare, + returnPeriod: value.returnPeriod?.toString(), + siteClass: value.siteClass, + truncate: String(value.truncate), + vs30: value.vs30?.toString(), + }; + + this.router + .navigate([devApps().hazard.dynamicCompare.routerLink], { + queryParams: { + ...query, + }, + }) + .catch((error: Error) => this.nshmpService.throwError$(error)); + } + + /** + * Returns the combined usage from model and comparable model. + * + * @param usageResponses The usages + * @param model The current selected model + * @param comparableModel The current comparable model + */ + private getCombinedUsage( + usageResponses: Map<string, HazardUsageResponse>, + model: NshmId, + comparableModel: string | null + ): HazardUsageResponse { + let combinedUsage = usageResponses.get(model); + + if (comparableModel !== null) { + combinedUsage = responseHandler.combineUsages([ + usageResponses.get(model), + usageResponses.get(comparableModel), + ]); + } + + return combinedUsage; + } + + /** + * Returns the available models that are comparable to the select model. + * + * @param state The application state + */ + private getComparableModels(): Parameter[] { + const formValues = this.formGroup.getRawValue(); + const state = this.state(); + + const currentNshmService = state.nshmServices.find( + nshmService => nshmService.model === formValues.model + ); + + const comparableServices = state.nshmServices.filter( + nshmService => + nshmService.model !== currentNshmService.model && + nshmService.project === currentNshmService.project + ); + + return state.availableModels.filter(model => + comparableServices.find( + service => service.model.toString() === model.value + ) + ); + } + + private addValidators(): void { + const controls = this.formGroup.controls; + + this.addRequiredValidator(controls.imt); + this.addRequiredValidator(controls.imt); + this.addRequiredValidator(controls.latitude); + this.addRequiredValidator(controls.longitude); + this.addRequiredValidator(controls.model); + this.addRequiredValidator(controls.modelCompare); + this.addRequiredValidator(controls.vs30); + this.addRequiredValidator(controls.returnPeriod); + + controls.latitude.addValidators(validateNan()); + controls.longitude.addValidators(validateNan()); + } + + private addRequiredValidator(control: AbstractControl): void { + control.addValidators(control => Validators.required(control)); } } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.state.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.state.ts index e493ca762039d97dc9ff076bb1d88da07bb94734..f96ab64ad0aa26c97fd95b3675579fc4670128ce 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.state.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/state/app.state.ts @@ -1,9 +1,10 @@ import { DynamicHazardControlForm, - hazardUtils, ResponseSpectra, } from '@ghsc/nshmp-lib-ng/hazard'; -import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; +import {hazardUtils} from '@ghsc/nshmp-lib-no-ngrx/hazard'; +import {ServiceCallInfo} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import { HazardCalcResponse, HazardUsageResponse, @@ -16,8 +17,6 @@ import { } from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/model'; import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm'; import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import {createFormGroupState, FormGroupState, FormState} from 'ngrx-forms'; -import {SharedAppState} from 'projects/nshmp-apps/src/shared/state/shared'; import {defaultPlots} from '../utils/app.utils'; @@ -39,17 +38,17 @@ export enum Plots { /** * Development dynamic hazard compare NGRX state. */ -export interface AppState extends SharedAppState { +export interface AppState { /** Available NSHMs */ availableModels: Parameter[]; /** Common usage for selected models */ combinedUsage: HazardUsageResponse; /** Models that can compare with a selected model */ comparableModels: Parameter[]; - /** Control panel form field state */ - controlPanelForm: FormState<ControlForm>; /** NSHM service metadata */ nshmServices: NshmMetadata[]; + /** The application's plots */ + plots: Map<string, NshmpPlot>; /** Service call info */ serviceCallInfo: ServiceCallInfo; /** Hazard service responses */ @@ -108,13 +107,12 @@ export function initialState(): AppState { availableModels: [], combinedUsage: null, comparableModels: [], - controlPanelForm: controlPanelFormInitialState(), nshmServices: [], plots: defaultPlots(), serviceCallInfo: { serviceCalls: [], serviceName: 'Dynamic Hazard Curves', - usage: null, + usage: [], }, serviceResponses: { modelA: null, @@ -127,12 +125,12 @@ export function initialState(): AppState { /** * Initial state for the control panel, */ -export function controlPanelFormInitialState(): FormGroupState<ControlForm> { - return createFormGroupState<ControlForm>(FORM_ID, { +export function controlPanelFormInitialState(): ControlForm { + return { ...hazardUtils.hazardDefaultFormValues(), imt: Imt.PGA, modelCompare: null, sourceType: sourceTypeToCapitalCase(SourceType.TOTAL), vs30: 760, - }); + }; } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/app.utils.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/app.utils.ts index e7f6b20a24512f9b0c6ddeeb07d30ed975d5b78b..fe276caedd5f5dd0c1de77f547c808d4c85e290f 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/app.utils.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/app.utils.ts @@ -1,38 +1,22 @@ -import {hazardUtils} from '@ghsc/nshmp-lib-ng/hazard'; -import { - NshmpPlot, - NshmpPlotSettings, - PlotOptions, - plotUtils, -} from '@ghsc/nshmp-lib-ng/plot'; +import {hazardUtils} from '@ghsc/nshmp-lib-no-ngrx/hazard'; +import {NshmpPlot, PlotOptions, plotUtils} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm'; import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import {createFormGroupState} from 'ngrx-forms'; import {Plots} from '../state/app.state'; -/** - * Plot ngrx-form ids. - */ -export enum PlotSettingsId { - HAZARD = 'ngrx-forms [Hazard Curves Settings]', - HAZARD_COMPONENTS = 'ngrx-forms [Hazard Component Curves Settings]', - HAZARD_DIFFERENCES = 'ngrx-forms [Hazard Difference Settings]', - SPECTRA = 'ngrx-forms [Spectra Settings]', - SPECTRA_COMPONENTS = 'ngrx-forms [Spectra Components Settings]', - SPECTRA_DIFFERENCES = 'ngrx-forms [Spectra Difference Settings]', -} - const differenceAspectRatio = '3:1'; const hazardXLabel = 'Ground Motion (g)'; const hazardYLabel = 'Annual Frequency of Exceedence'; const spectraXLabel = 'Spectral Period (s)'; const spectraYLabel = hazardXLabel; + const plotOptions: PlotOptions = { layout: { aspectRatio: hazardUtils.HAZARD_ASPECT_RATIO, }, }; + const diffPlotOptions: PlotOptions = { layout: { aspectRatio: differenceAspectRatio, @@ -113,13 +97,10 @@ function setHazardPlots(plots: Map<string, NshmpPlot>): void { plots.set(Plots.HAZARD, { label: 'Hazard Curves', plotData: hazardPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - PlotSettingsId.HAZARD, - { - config: hazardPlotData.config, - layout: plotUtils.plotlyLayoutToSettings(hazardPlotData.layout), - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup({ + config: hazardPlotData.config, + layout: plotUtils.plotlyLayoutToSettings(hazardPlotData.layout), + }), }); const hazardDiffPlotData = plotUtils.defaultPlot({ @@ -134,13 +115,10 @@ function setHazardPlots(plots: Map<string, NshmpPlot>): void { plots.set(Plots.HAZARD_DIFFERENCES, { label: 'Hazard Curve Differences', plotData: hazardDiffPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - PlotSettingsId.HAZARD_DIFFERENCES, - { - config: hazardDiffPlotData.config, - layout: plotUtils.plotlyLayoutToSettings(hazardDiffPlotData.layout), - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup({ + config: hazardDiffPlotData.config, + layout: plotUtils.plotlyLayoutToSettings(hazardDiffPlotData.layout), + }), }); const hazardComponentsPlotData = plotUtils.defaultPlot({ @@ -155,15 +133,10 @@ function setHazardPlots(plots: Map<string, NshmpPlot>): void { plots.set(Plots.HAZARD_COMPONENTS, { label: 'Hazard Component Curves', plotData: hazardComponentsPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - PlotSettingsId.HAZARD_COMPONENTS, - { - config: hazardComponentsPlotData.config, - layout: plotUtils.plotlyLayoutToSettings( - hazardComponentsPlotData.layout - ), - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup({ + config: hazardComponentsPlotData.config, + layout: plotUtils.plotlyLayoutToSettings(hazardComponentsPlotData.layout), + }), }); } @@ -185,13 +158,10 @@ function setSpectraPlots(plots: Map<string, NshmpPlot>): void { plots.set(Plots.SPECTRUM, { label: 'Spectrum', plotData: spectrumPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - PlotSettingsId.SPECTRA, - { - config: spectrumPlotData.config, - layout: plotUtils.plotlyLayoutToSettings(spectrumPlotData.layout), - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup({ + config: spectrumPlotData.config, + layout: plotUtils.plotlyLayoutToSettings(spectrumPlotData.layout), + }), }); const spectrumDiffPlotData = plotUtils.defaultPlot({ @@ -206,13 +176,10 @@ function setSpectraPlots(plots: Map<string, NshmpPlot>): void { plots.set(Plots.SPECTRUM_DIFFERENCES, { label: 'Spectrum Differences', plotData: spectrumDiffPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - PlotSettingsId.SPECTRA_DIFFERENCES, - { - config: spectrumDiffPlotData.config, - layout: plotUtils.plotlyLayoutToSettings(spectrumDiffPlotData.layout), - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup({ + config: spectrumDiffPlotData.config, + layout: plotUtils.plotlyLayoutToSettings(spectrumDiffPlotData.layout), + }), }); const spectraComponentsPlotData = plotUtils.defaultPlot({ @@ -227,14 +194,11 @@ function setSpectraPlots(plots: Map<string, NshmpPlot>): void { plots.set(Plots.SPECTRUM_COMPONENTS, { label: 'Spectra Component Curves', plotData: spectraComponentsPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - PlotSettingsId.SPECTRA_COMPONENTS, - { - config: spectraComponentsPlotData.config, - layout: plotUtils.plotlyLayoutToSettings( - spectraComponentsPlotData.layout - ), - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup({ + config: spectraComponentsPlotData.config, + layout: plotUtils.plotlyLayoutToSettings( + spectraComponentsPlotData.layout + ), + }), }); } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/hazard-plots.utils.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/hazard-plots.utils.ts index 1182fbcecb119d2ffc3eeffcfb52f49c72bdb2ab..e480a7c02610a797c626ae30918a27d71c49a522 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/hazard-plots.utils.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/hazard-plots.utils.ts @@ -1,6 +1,6 @@ -import {hazardUtils} from '@ghsc/nshmp-lib-ng/hazard'; -import {NumberBounds} from '@ghsc/nshmp-lib-ng/nshmp'; -import {NshmpPlot, plotUtils} from '@ghsc/nshmp-lib-ng/plot'; +import {hazardUtils} from '@ghsc/nshmp-lib-no-ngrx/hazard'; +import {FormGroupControls, NumberBounds} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot, plotUtils} from '@ghsc/nshmp-lib-no-ngrx/plot'; import { HazardCurve, HazardResponse, @@ -82,7 +82,10 @@ interface PlotDash { * * @param state The app state */ -export function createHazardPlots(state: AppState): Map<string, NshmpPlot> { +export function createHazardPlots( + state: AppState, + formGroup: FormGroupControls<ControlForm> +): Map<string, NshmpPlot> { const plots = new Map<string, NshmpPlot>(); const hazardPlot = state.plots.get(Plots.HAZARD); @@ -93,7 +96,7 @@ export function createHazardPlots(state: AppState): Map<string, NshmpPlot> { state.serviceResponses, state.availableModels, hazardPlot, - state.controlPanelForm.value + formGroup.getRawValue() ); plots.set(Plots.HAZARD, { @@ -103,7 +106,7 @@ export function createHazardPlots(state: AppState): Map<string, NshmpPlot> { const hazardCurveDifference = createHazardDiffPlot( state.serviceResponses, - state.controlPanelForm.value.imt, + formGroup.getRawValue().imt, hazardDiffPlot ); @@ -116,7 +119,7 @@ export function createHazardPlots(state: AppState): Map<string, NshmpPlot> { state.serviceResponses, state.availableModels, hazardComponentsPlot, - state.controlPanelForm.value + formGroup.getRawValue() ); plots.set(Plots.HAZARD_COMPONENTS, { diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/response-handler.utils.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/response-handler.utils.ts index 5de1076c701ac81a87bf7eee32de7dd0c3f5f92d..c393e7ad8cf154efc1593a5416255b1bb6253cb1 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/response-handler.utils.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/response-handler.utils.ts @@ -1,8 +1,9 @@ -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; +import {FormGroupControls} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {HazardUsageResponse} from '@ghsc/nshmp-utils-ts/libs/nshmp-haz/www/hazard-service'; import {Parameter} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import {AppState} from '../state/app.state'; +import {AppState, ControlForm} from '../state/app.state'; import {createHazardPlots} from './hazard-plots.utils'; import {createSpectraPlots} from './spectra-plots.utils'; @@ -59,7 +60,10 @@ export function combineUsages( * * @param state The app state */ -export function createPlots(state: AppState): Map<string, NshmpPlot> { +export function createPlots( + state: AppState, + formGroup: FormGroupControls<ControlForm> +): Map<string, NshmpPlot> { if ( state.serviceResponses.modelA === null || state.serviceResponses.modelB === null @@ -67,8 +71,8 @@ export function createPlots(state: AppState): Map<string, NshmpPlot> { return state.plots; } - const hazardPlots = createHazardPlots(state); - const spectraPlots = createSpectraPlots(state); + const hazardPlots = createHazardPlots(state, formGroup); + const spectraPlots = createSpectraPlots(state, formGroup); return new Map([...hazardPlots.entries(), ...spectraPlots.entries()]); } diff --git a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/spectra-plots.utils.ts b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/spectra-plots.utils.ts index 7f4d63fbc8de9544240f969a3f742a76a4f821e8..39cdd9962f0bb0cc6622d4beb0a5c141c6e1c5f2 100644 --- a/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/spectra-plots.utils.ts +++ b/projects/nshmp-apps/src/app/dev/hazard/dynamic-compare/utils/spectra-plots.utils.ts @@ -1,4 +1,5 @@ -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; +import {FormGroupControls} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {Imt, imtToPeriod} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm'; import { SourceType, @@ -110,7 +111,10 @@ interface PlotColors { * * @param state The app state */ -export function createSpectraPlots(state: AppState): Map<string, NshmpPlot> { +export function createSpectraPlots( + state: AppState, + formGroup: FormGroupControls<ControlForm> +): Map<string, NshmpPlot> { const plots = new Map<string, NshmpPlot>(); const spectraPlot = state.plots.get(Plots.SPECTRUM); @@ -120,7 +124,7 @@ export function createSpectraPlots(state: AppState): Map<string, NshmpPlot> { const spectraCurves = createSpectraCurvesPlot( state.serviceResponses, state.availableModels, - state.controlPanelForm.value, + formGroup.getRawValue(), spectraPlot ); @@ -132,7 +136,7 @@ export function createSpectraPlots(state: AppState): Map<string, NshmpPlot> { const spectraComponents = createSpectraComponentsPlot( state.serviceResponses, state.availableModels, - state.controlPanelForm.value, + formGroup.getRawValue(), spectraPlot ); @@ -144,7 +148,7 @@ export function createSpectraPlots(state: AppState): Map<string, NshmpPlot> { const spectraDiff = createSpectraDiffPlot( state.serviceResponses, state.availableModels, - state.controlPanelForm.value, + formGroup.getRawValue(), spectraDiffPlot ); diff --git a/projects/nshmp-apps/src/environments/environment.ts b/projects/nshmp-apps/src/environments/environment.ts index 9e5f90d516b5f90965c8a202e809a4e13ff04283..2cf15b89f0705c54d673933432345458f0ad848c 100644 --- a/projects/nshmp-apps/src/environments/environment.ts +++ b/projects/nshmp-apps/src/environments/environment.ts @@ -6,5 +6,5 @@ import {webServices} from './web-services'; */ export const environment: AngularEnvironment = { production: false, - webServices: webServices('https://earthquake.usgs.gov'), + webServices: webServices('https://staging-earthquake.usgs.gov'), }; diff --git a/projects/nshmp-apps/src/shared/models/plot-settings-panel.model.ts b/projects/nshmp-apps/src/shared/models/plot-settings-panel.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..641b80c49384b755340ea4f69414ebc8e3887c23 --- /dev/null +++ b/projects/nshmp-apps/src/shared/models/plot-settings-panel.model.ts @@ -0,0 +1,5 @@ +import {PlotSettingsPanel} from '@ghsc/nshmp-lib-no-ngrx/plot'; + +export interface PlotSettingsPanelId extends PlotSettingsPanel { + id: string; +} diff --git a/projects/nshmp-apps/src/shared/utils/facade.utils.ts b/projects/nshmp-apps/src/shared/utils/facade.utils.ts index a1ff3e61b0d01d38538a06ede5c14a3111661626..bb3ba5d49fa434844b96c449aef62e3c39ae0809 100644 --- a/projects/nshmp-apps/src/shared/utils/facade.utils.ts +++ b/projects/nshmp-apps/src/shared/utils/facade.utils.ts @@ -1,3 +1,4 @@ +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; export interface ResetPlotSettingsProps { @@ -34,3 +35,11 @@ export function redrawPlots( return updatedPlots; } + +export function validateNan(): ValidatorFn { + return ( + control: AbstractControl<number, number> + ): ValidationErrors | null => { + return isNaN(control.value) ? {nan: {value: control.value}} : null; + }; +}