services/app.service.ts
Entrypoint to store for GM vs distance application.
SharedService
Properties |
|
Methods |
Accessors |
constructor(formBuilder: FormBuilder, nshmpService: NshmpService, spinnerService: SpinnerService, route: ActivatedRoute, location: LocationService)
|
||||||||||||||||||
Defined in services/app.service.ts:66
|
||||||||||||||||||
Parameters :
|
addValidators |
addValidators()
|
Defined in services/app.service.ts:115
|
Add validators to form controls.
Returns :
void
|
callService |
callService()
|
Defined in services/app.service.ts:155
|
Calls the Gmm distance service.
Returns :
void
|
createPlots |
createPlots()
|
Defined in services/app.service.ts:180
|
Create plots based on current state and form group.
Returns :
void
|
defaultFormValues |
defaultFormValues()
|
Defined in services/app.service.ts:189
|
Default values for control panel.
Returns :
FormControls
|
defaultPlots |
defaultPlots()
|
Defined in services/app.service.ts:214
|
Returns the default plots.
Returns :
Map<string, NshmpPlot>
|
Private handleServiceResponses | ||||||
handleServiceResponses(serviceResponses: GmmDistanceResponse[])
|
||||||
Defined in services/app.service.ts:346
|
||||||
Parameters :
Returns :
void
|
Private handleUsageResponse | ||||||
handleUsageResponse(usageResponse: GmmDistanceUsage)
|
||||||
Defined in services/app.service.ts:373
|
||||||
Parameters :
Returns :
void
|
init |
init()
|
Defined in services/app.service.ts:245
|
Initialize the application.
Returns :
void
|
Private initialFormSet |
initialFormSet()
|
Defined in services/app.service.ts:392
|
Returns :
void
|
initialState |
initialState()
|
Defined in services/app.service.ts:263
|
GMM distance app inital state
Returns :
AppState
|
resetControlPanel |
resetControlPanel()
|
Defined in services/app.service.ts:280
|
Reset the control panel.
Returns :
void
|
resetPlotSettings |
resetPlotSettings()
|
Defined in services/app.service.ts:289
|
Reset the plot settings.
Returns :
void
|
resetState |
resetState()
|
Defined in services/app.service.ts:299
|
Reset the state.
Returns :
void
|
Private serviceResponseToPlotData | ||||||||||||
serviceResponseToPlotData(state: AppState, form: FormGroupControls<FormControls>)
|
||||||||||||
Defined in services/app.service.ts:453
|
||||||||||||
Transform Gmm distance service responses to plots.
Parameters :
Returns :
Map<string, NshmpPlot>
|
updateState | ||||||||
updateState(state: Partial<AppState>)
|
||||||||
Defined in services/app.service.ts:321
|
||||||||
Update state.
Parameters :
Returns :
void
|
Private updateUrl |
updateUrl()
|
Defined in services/app.service.ts:509
|
Returns :
void
|
usageFormValues | ||||||||
usageFormValues(parameters: GmmDistanceUsageParameters)
|
||||||||
Defined in services/app.service.ts:333
|
||||||||
Returns the default from values from parameters.
Parameters :
Returns :
FormControls
|
Private baseUrl |
Default value : environment.webServices.data.url
|
Defined in services/app.service.ts:55
|
nshmp-ws base URL |
Readonly formGroup |
Default value : this.formBuilder.group({
...this.defaultFormValues(),
gmmSource: [],
MwMulti: [],
vs30Multi: [],
}) as FormGroupControls<FormControls>
|
Defined in services/app.service.ts:59
|
Private serviceUrl |
Default value : `${this.baseUrl}${environment.webServices.data.services.gmmDistance}`
|
Defined in services/app.service.ts:57
|
GMM service URL |
Readonly state |
Default value : signal<AppState>(this.initialState())
|
Defined in services/app.service.ts:66
|
meanPlotState |
getmeanPlotState()
|
Defined in services/app.service.ts:84
|
Returns the mean plot state.
Returns :
Signal<NshmpPlot>
|
serviceCallInfo |
getserviceCallInfo()
|
Defined in services/app.service.ts:88
|
serviceResponse |
getserviceResponse()
|
Defined in services/app.service.ts:95
|
Returns the Gmm distance service responses.
Returns :
Signal<GmmDistanceResponse[]>
|
supportedImts |
getsupportedImts()
|
Defined in services/app.service.ts:99
|
usage |
getusage()
|
Defined in services/app.service.ts:106
|
Returns the Gmm distance usage response.
Returns :
Signal<GmmDistanceUsage>
|
import {Location as LocationService} from '@angular/common';
import {HttpParams} from '@angular/common/http';
import {computed, Injectable, Signal, signal} from '@angular/core';
import {AbstractControl, FormBuilder, Validators} from '@angular/forms';
import {ActivatedRoute} from '@angular/router';
import {
GmmAppQueryImt,
gmmUtils,
MultiSelectableParam,
} from '@ghsc/nshmp-lib-ng/gmm';
import {
FormGroupControls,
NshmpService,
nshmpUtils,
ServiceCallInfo,
SpinnerService,
} from '@ghsc/nshmp-lib-ng/nshmp';
import {NshmpPlot, NshmpPlotSettings, plotUtils} from '@ghsc/nshmp-lib-ng/plot';
import {XySequence} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/data';
import {Imt, imtToString} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm';
import {
GmmDistanceResponse,
GmmDistanceUsage,
GmmDistanceUsageParameters,
GmmGroupType,
} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services';
import {EnumParameterValues} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata';
import {environment} from 'projects/nshmp-apps/src/environments/environment';
import {AppServiceModel} from 'projects/nshmp-apps/src/shared/models/app-service.model';
import {SharedService} from 'projects/nshmp-apps/src/shared/services/shared.service';
import {apps} from 'projects/nshmp-apps/src/shared/utils/applications.utils';
import {catchError} from 'rxjs';
import {FormControls} from '../models/form-controls.model';
import {AppState} from '../models/state.model';
interface Query extends GmmAppQueryImt {
/** Max distance */
rMax: string;
/** Min distance */
rMin: string;
}
/**
* Entrypoint to store for GM vs distance application.
*/
@Injectable({
providedIn: 'root',
})
export class AppService
extends SharedService
implements AppServiceModel<AppState, FormControls>
{
/** nshmp-ws base URL */
private baseUrl = environment.webServices.data.url;
/** GMM service URL */
private serviceUrl = `${this.baseUrl}${environment.webServices.data.services.gmmDistance}`;
readonly formGroup = this.formBuilder.group({
...this.defaultFormValues(),
gmmSource: [],
MwMulti: [],
vs30Multi: [],
}) as FormGroupControls<FormControls>;
readonly state = signal<AppState>(this.initialState());
constructor(
private formBuilder: FormBuilder,
private nshmpService: NshmpService,
private spinnerService: SpinnerService,
private route: ActivatedRoute,
private location: LocationService,
) {
super();
this.addValidators();
this.formGroup.controls.gmmSource.setValue([]);
this.formGroup.controls.showEpistemicUncertainty.disable();
}
/**
* Returns the mean plot state.
*/
get meanPlotState(): Signal<NshmpPlot> {
return computed(() => this.state().plots?.get(gmmUtils.PlotType.MEANS));
}
get serviceCallInfo(): Signal<ServiceCallInfo> {
return computed(() => this.state().serviceCallInfo);
}
/**
* Returns the Gmm distance service responses.
*/
get serviceResponse(): Signal<GmmDistanceResponse[]> {
return computed(() => this.state().serviceResponses);
}
get supportedImts(): Signal<EnumParameterValues[]> {
return computed(() => this.state().supportedImts);
}
/**
* Returns the Gmm distance usage response.
*/
get usage(): Signal<GmmDistanceUsage> {
return computed(() => this.state().usageResponse);
}
/**
* Add validators to form controls.
*
* @param form The form group
*/
addValidators(): void {
const required = (control: AbstractControl) => Validators.required(control);
this.formGroup.controls.Mw.addValidators(required);
this.formGroup.controls.dip.addValidators(required);
this.formGroup.controls.gmmSource.addValidators(required);
this.formGroup.controls.imt.addValidators(required);
this.formGroup.controls.multiSelectableParam.addValidators(required);
this.formGroup.controls.vs30.addValidators(required);
this.formGroup.controls.width.addValidators(required);
this.formGroup.controls.zTor.addValidators(required);
this.formGroup.controls.MwMulti.addValidators(control => {
if (
this.formGroup.getRawValue().multiSelectableParam ===
MultiSelectableParam.MW
) {
return Validators.required(control);
} else {
return Validators.nullValidator(control);
}
});
this.formGroup.controls.vs30Multi.addValidators(control => {
if (
this.formGroup.getRawValue().multiSelectableParam ===
MultiSelectableParam.VS30
) {
return Validators.required(control);
} else {
return Validators.nullValidator(control);
}
});
this.formGroup.updateValueAndValidity();
}
/**
* Calls the Gmm distance service.
*/
callService(): void {
if (this.formGroup.invalid) {
return;
}
const spinnerRef = this.spinnerService.show(SpinnerService.MESSAGE_SERVICE);
const urls = gmmUtils.serviceEndpoints(
this.serviceUrl,
this.formGroup.getRawValue(),
this.formGroup.getRawValue().multiSelectableParam,
);
this.nshmpService
.callServices$<GmmDistanceResponse>(urls)
.pipe(catchError((error: Error) => this.nshmpService.throwError$(error)))
.subscribe(serviceResponses => {
this.handleServiceResponses(serviceResponses);
spinnerRef.close();
});
}
/**
* Create plots based on current state and form group.
*/
createPlots(): void {
this.updateState({
plots: this.serviceResponseToPlotData(this.state(), this.formGroup),
});
}
/**
* Default values for control panel.
*/
defaultFormValues(): FormControls {
return {
dip: null,
gmmGroupType: GmmGroupType.ACTIVE_CRUST,
gmmSource: [],
imt: 'default',
multiSelectableParam: MultiSelectableParam.GMM,
Mw: null,
MwMulti: [],
rMax: 300,
rMin: 0.1,
showEpistemicUncertainty: false,
vs30: null,
vs30Multi: [],
width: null,
z1p0: null,
z2p5: null,
zSed: null,
zTor: null,
};
}
/**
* Returns the default plots.
*/
defaultPlots(): Map<string, NshmpPlot> {
const plots = new Map<string, NshmpPlot>();
const meansPlotData = plotUtils.defaultPlot({
id: gmmUtils.PlotType.MEANS,
options: {
layout: {
aspectRatio: gmmUtils.MEAN_ASPECT_RATIO,
},
},
title: 'Ground Motion vs. Distance',
xLabel: 'Distance (km)',
yLabel: 'Median ground motion (g)',
});
const meansPlotSettingsForm: NshmpPlotSettings = {
config: meansPlotData.config,
layout: plotUtils.plotlyLayoutToSettings(meansPlotData.layout),
};
plots.set(gmmUtils.PlotType.MEANS, {
label: 'Means',
plotData: meansPlotData,
settingsForm: plotUtils.plotSettingsToFormGroup(meansPlotSettingsForm),
});
return new Map(plots);
}
/**
* Initialize the application.
*/
init(): void {
const spinnerRef = this.spinnerService.show(
SpinnerService.MESSAGE_METADATA,
);
this.nshmpService
.callService$<GmmDistanceUsage>(this.serviceUrl)
.pipe(catchError((error: Error) => this.nshmpService.throwError$(error)))
.subscribe(usageResponse => {
this.handleUsageResponse(usageResponse);
this.initialFormSet();
spinnerRef.close();
});
}
/**
* GMM distance app inital state
*/
initialState(): AppState {
return {
plots: this.defaultPlots(),
serviceCallInfo: {
serviceCalls: [],
serviceName: 'Ground Motion vs. Distance',
usage: [],
},
serviceResponses: [],
supportedImts: [],
usageResponse: null,
};
}
/**
* Reset the control panel.
*/
resetControlPanel(): void {
this.formGroup.reset(
this.usageFormValues(this.state().usageResponse.response.parameters),
);
}
/**
* Reset the plot settings.
*/
resetPlotSettings(): void {
super.resetPlotSettings({
currentPlots: this.state().plots,
defaultPlots: this.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,
});
}
/**
* Returns the default from values from parameters.
*
* @param parameters The service parameters
*/
usageFormValues(parameters: GmmDistanceUsageParameters): FormControls {
return {
...this.defaultFormValues(),
dip: parameters.dip.value as number,
Mw: parameters.Mw.value as number,
vs30: parameters.vs30.value as number,
width: parameters.width.value as number,
z1p0: parameters.z1p0.value as number,
z2p5: parameters.z2p5.value as number,
zTor: parameters.zTor.value as number,
};
}
private handleServiceResponses(
serviceResponses: GmmDistanceResponse[],
): 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();
}
private handleUsageResponse(usageResponse: GmmDistanceUsage): void {
const parameters = usageResponse.response.parameters;
const values = this.usageFormValues(parameters);
this.formGroup.patchValue({
...values,
});
const serviceCallInfo: ServiceCallInfo = {
...this.state().serviceCallInfo,
usage: [this.serviceUrl],
};
this.updateState({
serviceCallInfo,
usageResponse,
});
}
private initialFormSet(): void {
const query = this.route.snapshot.queryParams as Query;
const defaultValues = this.formGroup.getRawValue();
const parameters = this.usage().response.parameters;
const gmmSource = gmmUtils.queryToGmmSource(query.gmm, parameters);
this.formGroup.patchValue({
multiSelectableParam:
query?.multiSelectableParam ?? defaultValues.multiSelectableParam,
});
this.formGroup.patchValue({
dip: nshmpUtils.queryParseNumber(defaultValues.dip, query?.dip),
gmmGroupType: query?.gmmGroupType ?? defaultValues.gmmGroupType,
gmmSource,
imt: query?.imt ?? defaultValues.imt,
Mw: nshmpUtils.queryParseNumber(defaultValues.Mw, query?.Mw),
MwMulti: nshmpUtils
.queryStringToArray(query?.MwMulti)
.map(mw => Number.parseFloat(mw)),
rMax: nshmpUtils.queryParseNumber(defaultValues.rMax, query?.rMax),
rMin: nshmpUtils.queryParseNumber(defaultValues.rMin, query?.rMin),
showEpistemicUncertainty: nshmpUtils.queryParseBoolean(
defaultValues.showEpistemicUncertainty,
query?.showEpistemicUncertainty,
),
vs30: nshmpUtils.queryParseNumber(defaultValues.vs30, query?.vs30),
vs30Multi: nshmpUtils
.queryStringToArray(query?.vs30Multi)
.map(vs30 => Number.parseFloat(vs30)),
width: nshmpUtils.queryParseNumber(defaultValues.width, query?.width),
z1p0: nshmpUtils.queryParseNumber(defaultValues.z1p0, query?.z1p0),
z2p5: nshmpUtils.queryParseNumber(defaultValues.z2p5, query?.z2p5),
zSed: nshmpUtils.queryParseNumber(defaultValues.zSed, query?.zSed),
zTor: nshmpUtils.queryParseNumber(defaultValues.zTor, query?.zTor),
});
if (
this.formGroup.value.multiSelectableParam === MultiSelectableParam.MW ||
this.formGroup.value.multiSelectableParam === MultiSelectableParam.VS30
) {
this.formGroup.patchValue({
gmmSource: gmmSource.length > 0 ? [gmmSource.pop()] : [],
});
}
if (this.formGroup.valid) {
this.callService();
} else {
this.formGroup.markAsDirty();
}
this.formGroup.valueChanges.subscribe(() => this.updateUrl());
}
/**
* Transform Gmm distance service responses to plots.
*
* @param state The application state
*/
private serviceResponseToPlotData(
state: AppState,
form: FormGroupControls<FormControls>,
): Map<string, NshmpPlot> {
if (
state.serviceResponses === null ||
state.serviceResponses?.length === 0
) {
return this.defaultPlots();
}
const plots = new Map<string, NshmpPlot>();
const formValues = form.getRawValue();
const meanPlot = state.plots.get(gmmUtils.PlotType.MEANS);
const hoverTemplate = '%{x} km, %{y} AFE';
const title = `Ground Motion vs. Distance: ${imtToString(
formValues.imt as Imt,
)}`;
meanPlot.settingsForm.patchValue({
layout: {
title: {
text: title,
},
},
});
const responses: gmmUtils.GmmResponse<XySequence, number[]>[] =
state.serviceResponses.map(serviceResponse => ({
input: serviceResponse.request.input,
meanPlotConfig: {
hoverTemplate,
},
response: serviceResponse.response,
sigmaPlotConfig: {
hoverTemplate,
},
}));
const gmmPlots = gmmUtils.gmmResponsesToPlots({
meanPlot,
meanTitle: title,
multiSelectableParam: formValues.multiSelectableParam,
responses,
showEpistemicUncertainty: formValues.showEpistemicUncertainty,
sigmaPlot: meanPlot,
});
plots.set(gmmUtils.PlotType.MEANS, {
...meanPlot,
plotData: gmmPlots.means,
});
return plots;
}
private updateUrl(): void {
const value = this.formGroup.getRawValue();
const query: Query = {
dip: value.dip?.toString(),
gmm: gmmUtils.gmmSourceToQuery(value.gmmSource),
gmmGroupType: value.gmmGroupType,
imt: value.imt,
multiSelectableParam: value.multiSelectableParam,
Mw: value.Mw?.toString(),
MwMulti: value.MwMulti?.map(mw => mw.toString()),
rMax: value.rMax?.toString(),
rMin: value.rMin?.toString(),
showEpistemicUncertainty: value.showEpistemicUncertainty.toString(),
vs30: value.vs30?.toString(),
vs30Multi: value.vs30Multi?.map(vs30 => vs30.toString()),
width: value.width?.toString(),
z1p0: value.z1p0?.toString(),
z2p5: value.z2p5?.toString(),
zSed: value.zSed?.toString(),
zTor: value.zTor?.toString(),
};
this.location.replaceState(
apps().gmm.distance.routerLink,
new HttpParams().appendAll({...query}).toString(),
);
}
}