services/app.service.ts
Entrypoint to store for dynamic hazard application.
SharedService
Properties |
|
Methods |
|
Accessors |
constructor(formBuilder: FormBuilder, spinnerService: SpinnerService, nshmpService: NshmpService, hazardService: HazardService, route: ActivatedRoute, location: LocationService)
|
|||||||||||||||||||||
Defined in services/app.service.ts:77
|
|||||||||||||||||||||
Parameters :
|
Private addRequiredValidator | ||||||
addRequiredValidator(control: AbstractControl)
|
||||||
Defined in services/app.service.ts:372
|
||||||
Parameters :
Returns :
void
|
addValidators |
addValidators()
|
Defined in services/app.service.ts:185
|
Returns :
void
|
callService |
callService()
|
Defined in services/app.service.ts:201
|
Call the hazard service.
Returns :
void
|
Private createHazardPlotData | ||||||||||||||||||||
createHazardPlotData(responseData: HazardResponseData, plot: NshmpPlot, form: DynamicHazardControlForm, returnPeriod: number)
|
||||||||||||||||||||
Defined in services/app.service.ts:384
|
||||||||||||||||||||
Create the hazard plot data.
Parameters :
Returns :
PlotlyPlot
|
createPlots |
createPlots()
|
Defined in services/app.service.ts:245
|
Returns :
void
|
Private createServiceEndpoint | |||||||||
createServiceEndpoint(serviceUrl: string, values: DynamicHazardControlForm)
|
|||||||||
Defined in services/app.service.ts:461
|
|||||||||
Parameters :
Returns :
string
|
defaultFormValues |
defaultFormValues()
|
Defined in services/app.service.ts:253
|
Returns the default form values.
Returns :
DynamicHazardControlForm
|
init |
init()
|
Defined in services/app.service.ts:264
|
Initialize applicaiton.
Returns :
void
|
Private initialFormSet |
initialFormSet()
|
Defined in services/app.service.ts:469
|
Returns :
void
|
initialState |
initialState()
|
Defined in services/app.service.ts:297
|
Hazard app initial state
Returns :
AppState
|
resetControlPanel |
resetControlPanel()
|
Defined in services/app.service.ts:319
|
Reset the control panel.
Returns :
void
|
resetSettings |
resetSettings()
|
Defined in services/app.service.ts:327
|
Reset the plot settings.
Returns :
void
|
resetState |
resetState()
|
Defined in services/app.service.ts:334
|
Returns :
void
|
Private responseSpectrumPlotData | ||||||||||||||||
responseSpectrumPlotData(spectra: ResponseSpectra, plot: NshmpPlot, returnPeriodValue: number)
|
||||||||||||||||
Defined in services/app.service.ts:529
|
||||||||||||||||
Return the response spectrum plot data.
Parameters :
Returns :
PlotlyPlot
|
Private responseToPlots | ||||||||||||
responseToPlots(state: AppState, formGroup: FormGroupControls
|
||||||||||||
Defined in services/app.service.ts:639
|
||||||||||||
Transform the service response the plot data.
Parameters :
Returns :
Map<string, NshmpPlot>
|
Private returnPeriodPlotData | ||||||||||||||||
returnPeriodPlotData(responseData: HazardResponseData, sourceType: string, returnPeriod: number)
|
||||||||||||||||
Defined in services/app.service.ts:686
|
||||||||||||||||
Returns the plot data for the return period.
Parameters :
Returns :
Partial<PlotData>
|
setLocation | ||||||||
setLocation(location: Location)
|
||||||||
Defined in services/app.service.ts:351
|
||||||||
Set the location form fields.
Parameters :
Returns :
void
|
updateState | ||||||
updateState(state: Partial<AppState>)
|
||||||
Defined in services/app.service.ts:358
|
||||||
Parameters :
Returns :
void
|
Private updateUrl |
updateUrl()
|
Defined in services/app.service.ts:712
|
Returns :
void
|
Private updateUsageUrl |
updateUsageUrl()
|
Defined in services/app.service.ts:719
|
Returns :
void
|
Readonly formGroup |
Default value : this.formBuilder.group<DynamicHazardControlForm>(
this.defaultFormValues(),
)
|
Defined in services/app.service.ts:72
|
Private nshmpHazWs |
Default value : environment.webServices.nshmpHazWs
|
Defined in services/app.service.ts:68
|
nshmp-haz-ws web config |
Private serviceEndpoint |
Default value : this.nshmpHazWs.services.curveServices.hazard
|
Defined in services/app.service.ts:70
|
Hazard endpoint |
Readonly state |
Default value : signal<AppState>(this.initialState())
|
Defined in services/app.service.ts:77
|
Application state |
availableModels |
getavailableModels()
|
Defined in services/app.service.ts:94
|
Returns the available models.
Returns :
Signal<Parameter[]>
|
hazardPlotData |
gethazardPlotData()
|
Defined in services/app.service.ts:101
|
Returns the hazard plot data.
Returns :
Signal<PlotlyPlot>
|
hazardPlotSettings |
gethazardPlotSettings()
|
Defined in services/app.service.ts:108
|
Returns the hazard plot settings form.
Returns :
Signal<FormGroup<NshmpPlotSettingFormGroup>>
|
hazardPlotState |
gethazardPlotState()
|
Defined in services/app.service.ts:115
|
Returns the hazard plot.
Returns :
Signal<NshmpPlot>
|
nshmService |
getnshmService()
|
Defined in services/app.service.ts:122
|
Returns the metadata of the NSHM observable.
Returns :
Signal<NshmMetadata>
|
plots |
getplots()
|
Defined in services/app.service.ts:130
|
responseSpectra |
getresponseSpectra()
|
Defined in services/app.service.ts:137
|
Returns the response spectra
Returns :
Signal<ResponseSpectra>
|
serviceCallInfo |
getserviceCallInfo()
|
Defined in services/app.service.ts:144
|
Returns the service call info.
Returns :
Signal<ServiceCallInfo>
|
serviceResponse |
getserviceResponse()
|
Defined in services/app.service.ts:151
|
Returns the disagg response.
Returns :
Signal<HazardCalcResponse>
|
spectrumPlotData |
getspectrumPlotData()
|
Defined in services/app.service.ts:158
|
Returns the spectrum plot data.
Returns :
Signal<PlotlyPlot>
|
spectrumPlotSettings |
getspectrumPlotSettings()
|
Defined in services/app.service.ts:165
|
Returns the spectrum plot settings form.
Returns :
Signal<FormGroup<NshmpPlotSettingFormGroup>>
|
spectrumPlotState |
getspectrumPlotState()
|
Defined in services/app.service.ts:172
|
Returns the response spectrum plot.
Returns :
Signal<NshmpPlot>
|
usage |
getusage()
|
Defined in services/app.service.ts:179
|
Return the usage for the selected model.
Returns :
Signal<HazardUsageResponse>
|
import {Location as LocationService} from '@angular/common';
import {HttpParams} from '@angular/common/http';
import {computed, Injectable, Signal, signal} from '@angular/core';
import {
AbstractControl,
FormBuilder,
FormGroup,
Validators,
} from '@angular/forms';
import {ActivatedRoute} from '@angular/router';
import {
DynamicHazardControlForm,
HazardPlots,
HazardService,
hazardUtils,
ResponseSpectra,
} from '@ghsc/nshmp-lib-ng/hazard';
import {
FormGroupControls,
NshmpService,
returnPeriodAltName,
ServiceCallInfo,
SpinnerService,
} from '@ghsc/nshmp-lib-ng/nshmp';
import {
NshmpPlot,
NshmpPlotSettingFormGroup,
plotUtils,
} from '@ghsc/nshmp-lib-ng/plot';
import {
HazardCalcResponse,
HazardRequestMetadata,
HazardResponseData,
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, imtToPeriod} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm';
import {
SourceType,
sourceTypeToCapitalCase,
} 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 {PlotlyPlot} from '@ghsc/nshmp-utils-ts/libs/plotly';
import deepEqual from 'deep-equal';
import {PlotData} from 'plotly.js';
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/operators';
import {DynamicHazardQuery} from '../models/query.model';
import {AppState} from '../models/state.model';
/**
* Entrypoint to store for dynamic hazard application.
*/
@Injectable({
providedIn: 'root',
})
export class AppService
extends SharedService
implements AppServiceModel<AppState, DynamicHazardControlForm>
{
/** nshmp-haz-ws web config */
private nshmpHazWs = environment.webServices.nshmpHazWs;
/** Hazard endpoint */
private serviceEndpoint = this.nshmpHazWs.services.curveServices.hazard;
readonly formGroup = this.formBuilder.group<DynamicHazardControlForm>(
this.defaultFormValues(),
);
/** Application state */
readonly state = signal<AppState>(this.initialState());
constructor(
private formBuilder: FormBuilder,
private spinnerService: SpinnerService,
private nshmpService: NshmpService,
private hazardService: HazardService,
private route: ActivatedRoute,
private location: LocationService,
) {
super();
this.addValidators();
}
/**
* Returns the available models.
*/
get availableModels(): Signal<Parameter[]> {
return computed(() => this.state().availableModels);
}
/**
* Returns the hazard plot data.
*/
get hazardPlotData(): Signal<PlotlyPlot> {
return computed(() => this.hazardPlotState().plotData);
}
/**
* Returns the hazard plot settings form.
*/
get hazardPlotSettings(): Signal<FormGroup<NshmpPlotSettingFormGroup>> {
return computed(() => this.hazardPlotState().settingsForm);
}
/**
* Returns the hazard plot.
*/
get hazardPlotState(): Signal<NshmpPlot> {
return computed(() => this.state().plots.get(HazardPlots.HAZARD));
}
/**
* Returns the metadata of the NSHM observable.
*/
get nshmService(): Signal<NshmMetadata> {
return computed(() =>
this.state().nshmServices.find(
nshmService => nshmService.model === this.formGroup.getRawValue().model,
),
);
}
get plots(): Signal<Map<string, NshmpPlot>> {
return computed(() => this.state().plots);
}
/**
* Returns the response spectra
*/
get responseSpectra(): Signal<ResponseSpectra> {
return computed(() => this.state().responseSpectra);
}
/**
* Returns the service call info.
*/
get serviceCallInfo(): Signal<ServiceCallInfo> {
return computed(() => this.state().serviceCallInfo);
}
/**
* Returns the disagg response.
*/
get serviceResponse(): Signal<HazardCalcResponse> {
return computed(() => this.state().serviceResponse);
}
/**
* Returns the spectrum plot data.
*/
get spectrumPlotData(): Signal<PlotlyPlot> {
return computed(() => this.spectrumPlotState().plotData);
}
/**
* Returns the spectrum plot settings form.
*/
get spectrumPlotSettings(): Signal<FormGroup<NshmpPlotSettingFormGroup>> {
return computed(() => this.spectrumPlotState().settingsForm);
}
/**
* Returns the response spectrum plot.
*/
get spectrumPlotState(): Signal<NshmpPlot> {
return computed(() => this.state().plots.get(HazardPlots.SPECTRUM));
}
/**
* Return the usage for the selected model.
*/
get usage(): Signal<HazardUsageResponse> {
return computed(() =>
this.state().usageResponses?.get(this.formGroup.getRawValue().model),
);
}
addValidators(): void {
const controls = this.formGroup.controls;
this.addRequiredValidator(controls.latitude);
this.addRequiredValidator(controls.longitude);
this.addRequiredValidator(controls.model);
this.addRequiredValidator(controls.vs30);
this.addRequiredValidator(controls.returnPeriod);
controls.latitude.addValidators(this.validateNan());
controls.longitude.addValidators(this.validateNan());
}
/**
* Call the hazard service.
*/
callService(): void {
const spinnerRef = this.spinnerService.show(
`${SpinnerService.MESSAGE_SERVICE}
<br>
(Could take 30+ seconds)
`,
);
const values = this.formGroup.getRawValue();
const service = this.state().nshmServices.find(
nshmService => nshmService.model === values.model,
);
const serviceUrl = `${service.url}${this.serviceEndpoint}`;
const url = this.createServiceEndpoint(serviceUrl, values);
this.nshmpService
.callService$<HazardCalcResponse>(url)
.pipe(
catchError((error: Error) => {
spinnerRef.close();
return this.nshmpService.throwError$(error);
}),
)
.subscribe(serviceResponse => {
spinnerRef.close();
const responseSpectra = hazardUtils.responseSpectra(
serviceResponse.response.hazardCurves,
this.formGroup.getRawValue(),
);
this.updateState({
responseSpectra,
serviceCallInfo: {
...this.state().serviceCallInfo,
serviceCalls: [url],
},
serviceResponse,
});
this.createPlots();
});
}
createPlots(): void {
const plots = this.responseToPlots(this.state(), this.formGroup);
this.updateState({plots});
}
/**
* Returns the default form values.
*/
defaultFormValues(): DynamicHazardControlForm {
return {
...hazardUtils.hazardDefaultFormValues(),
sourceType: sourceTypeToCapitalCase(SourceType.TOTAL),
vs30: 760,
};
}
/**
* Initialize applicaiton.
*/
init(): void {
const spinnerRef = this.spinnerService.show(
SpinnerService.MESSAGE_METADATA,
);
this.hazardService
.dynamicNshms$<HazardRequestMetadata>(
`${this.nshmpHazWs.url}${this.nshmpHazWs.services.nshms}`,
this.serviceEndpoint,
)
.pipe(
catchError((error: Error) => {
spinnerRef.close();
return this.nshmpService.throwError$(error);
}),
)
.subscribe(({models, nshmServices, usageResponses}) => {
spinnerRef.close();
this.updateState({
availableModels: models,
nshmServices,
usageResponses,
});
this.updateUsageUrl();
this.initialFormSet();
});
}
/**
* Hazard app initial state
*/
initialState(): AppState {
const usageResponses: Map<string, HazardUsageResponse> = new Map();
usageResponses.set(NshmId.CONUS_2018, null);
return {
availableModels: [],
nshmServices: [],
plots: hazardUtils.hazardDefaultPlots(),
responseSpectra: null,
serviceCallInfo: {
serviceCalls: [],
serviceName: 'Dynamic Hazard Curves',
usage: [],
},
serviceResponse: null,
usageResponses,
};
}
/**
* Reset the control panel.
*/
resetControlPanel(): void {
this.formGroup.reset(this.defaultFormValues());
this.resetState();
}
/**
* Reset the plot settings.
*/
resetSettings(): void {
super.resetPlotSettings({
currentPlots: this.state().plots,
defaultPlots: this.initialState().plots,
});
}
resetState(): void {
this.updateState({
plots: this.initialState().plots,
serviceCallInfo: {
...this.state().serviceCallInfo,
serviceCalls: [],
},
serviceResponse: null,
});
this.updateUsageUrl();
}
/**
* Set the location form fields.
*
* @param location The location
*/
setLocation(location: Location): void {
this.formGroup.patchValue({
latitude: location.latitude,
longitude: location.longitude,
});
}
updateState(state: Partial<AppState>): void {
const updatedState = {
...this.state(),
...state,
};
if (!deepEqual(updatedState, this.state())) {
this.state.set({
...this.state(),
...state,
});
}
}
private addRequiredValidator(control: AbstractControl): void {
control.addValidators(control => Validators.required(control));
}
/**
* Create the hazard plot data.
*
* @param responseData The hazard response data
* @param plot Plot
* @param form Form values
* @param returnPeriod Return period
*/
private createHazardPlotData(
responseData: HazardResponseData,
plot: NshmpPlot,
form: DynamicHazardControlForm,
returnPeriod: number,
): PlotlyPlot {
const hazardCurves = responseData.hazardCurves.filter(
response => response.imt.value !== Imt.PGV.toString(),
);
const data = hazardCurves.map((response, index) => {
const imt = response.imt.value as Imt;
const sourceTypeData = hazardUtils.getSourceTypeData(
response.data,
form.sourceType,
);
const xy = hazardUtils.updateXySequence(
form,
hazardUtils.cleanXySequence(sourceTypeData.values),
imt,
);
const plotlyData: Partial<PlotData> = {
hovertemplate: '%{x} g, %{y} AFE',
line: {
color: hazardUtils.color(index, hazardCurves.length),
},
mode: 'lines+markers',
name:
imt === Imt.PGA || imt === Imt.PGV
? imt
: `${imtToPeriod(imt)} s ${imt.substring(0, 2)}`,
x: xy.xs,
y: xy.ys,
};
return plotlyData;
});
const title = `Hazard Curves - ${form.sourceType}`;
plot.settingsForm.patchValue({
layout: {
title: {
text: title,
},
},
});
const metadata = responseData.metadata;
const layout = plotUtils.updatePlotLabels({
layout: plot.plotData.layout,
title,
xLabel: metadata.xlabel,
yLabel: metadata.ylabel,
});
const mobileLayout = plotUtils.updatePlotLabels({
layout: plot.plotData.mobileLayout,
title,
xLabel: metadata.xlabel,
yLabel: metadata.ylabel,
});
return {
config: {...plot.plotData.config},
data: [
this.returnPeriodPlotData(responseData, form.sourceType, returnPeriod),
...data,
],
id: 'hazard-curves',
layout,
mobileConfig: {...plot.plotData.mobileConfig},
mobileLayout,
};
}
private createServiceEndpoint(
serviceUrl: string,
values: DynamicHazardControlForm,
): string {
const {longitude, latitude, vs30} = values;
return `${serviceUrl}/${longitude}/${latitude}/${vs30}`;
}
private initialFormSet(): void {
const query = this.route.snapshot.queryParams as DynamicHazardQuery;
const defaultValues = this.defaultFormValues();
const formValues: DynamicHazardControlForm = {
commonReturnPeriods: defaultValues.commonReturnPeriods,
latitude:
query.latitude !== undefined
? Number.parseFloat(query.latitude)
: defaultValues.latitude,
longitude:
query.longitude !== undefined
? Number.parseFloat(query.longitude)
: defaultValues.longitude,
maxDirection:
query.maxDirection !== undefined
? (JSON.parse(query.maxDirection) as boolean)
: defaultValues.maxDirection,
model: query.model !== undefined ? query.model : defaultValues.model,
returnPeriod:
query.returnPeriod !== undefined
? Number.parseInt(query.returnPeriod, 10)
: defaultValues.returnPeriod,
siteClass:
query.siteClass !== undefined
? query.siteClass
: defaultValues.siteClass,
sourceType:
query.sourceType !== undefined
? query.sourceType
: defaultValues.sourceType,
truncate:
query.truncate !== undefined
? (JSON.parse(query.truncate) as boolean)
: defaultValues.truncate,
vs30:
query.vs30 !== undefined
? Number.parseFloat(query.vs30)
: defaultValues.vs30,
};
this.formGroup.patchValue(formValues);
if (this.formGroup.valid) {
this.nshmpService.selectPlotControl();
this.callService();
} else if (this.formGroup.getRawValue() !== defaultValues) {
this.formGroup.markAsDirty();
}
this.formGroup.valueChanges.subscribe(() => this.updateUrl());
}
/**
* Return the response spectrum plot data.
*
* @param spectra Response spectra
* @param plot plot
* @param returnPeriodValue Return period
*/
private responseSpectrumPlotData(
spectra: ResponseSpectra,
plot: NshmpPlot,
returnPeriodValue: number,
): PlotlyPlot {
if (spectra === null || spectra === undefined) {
return;
}
const imts = spectra.imts;
const lines: Partial<PlotData>[] = [];
spectra.responseSpectrum.forEach((spectrum, iSpectra) => {
let lineWidth = 2;
let markerSize = 7;
const color = hazardUtils.color(
iSpectra,
spectra.responseSpectrum.length,
);
if (spectrum.returnPeriod === returnPeriodValue) {
lineWidth = 4;
markerSize = 12;
}
const spectraX: number[] = [];
const spectraY: number[] = [];
const scatters: Partial<PlotData>[] = [];
for (let iSpectrum = 0; iSpectrum < spectrum.values.length; iSpectrum++) {
const imt = imts[iSpectrum];
if (imt === Imt.PGV) {
continue;
} else if (imt === Imt.PGA) {
scatters.push({
hovertemplate: '%{y} g',
legendgroup: imt,
marker: {
color,
size: markerSize,
symbol: 'square',
},
mode: 'markers',
name: imt,
showlegend: false,
x: [imtToPeriod(imt)],
y: [spectrum.values[iSpectrum]],
});
} else {
spectraX.push(imtToPeriod(imt));
spectraY.push(spectrum.values[iSpectrum]);
}
}
lines.push({
hovertemplate: '%{y} g',
line: {
color,
width: lineWidth,
},
marker: {
size: markerSize,
},
mode: 'lines+markers',
name:
returnPeriodAltName[spectrum.returnPeriod] ??
`${spectrum.returnPeriod} yr`,
x: spectraX,
y: spectraY,
});
lines.push(...scatters);
});
const legendEntries: Partial<PlotData>[] = [
{
legendgroup: Imt.PGA,
marker: {
color: 'white',
line: {
width: 1.5,
},
size: 7,
symbol: 'square',
},
mode: 'markers',
name: 'PGA',
x: [-1],
y: [-1],
},
];
lines.push(...legendEntries);
return {
config: {...plot.plotData.config},
data: lines,
id: 'response-spectrum',
layout: {...plot.plotData.layout},
mobileConfig: {...plot.plotData.mobileConfig},
mobileLayout: {...plot.plotData.mobileLayout},
};
}
/**
* Transform the service response the plot data.
*
* @param state The current state
*/
private responseToPlots(
state: AppState,
formGroup: FormGroupControls<DynamicHazardControlForm>,
): Map<string, NshmpPlot> {
if (state.serviceResponse === null || state.serviceResponse === undefined) {
return state.plots;
}
const returnPeriod = formGroup.controls.returnPeriod.value;
const responseData = state.serviceResponse.response;
const plots = new Map<string, NshmpPlot>();
const hazardPlot = state.plots.get(HazardPlots.HAZARD);
const spectrumPlot = state.plots.get(HazardPlots.SPECTRUM);
const hazardCurves = this.createHazardPlotData(
responseData,
hazardPlot,
formGroup.getRawValue(),
returnPeriod,
);
const spectrum = this.responseSpectrumPlotData(
state.responseSpectra,
spectrumPlot,
returnPeriod,
);
plots.set(HazardPlots.HAZARD, {
...hazardPlot,
plotData: hazardCurves,
});
plots.set(HazardPlots.SPECTRUM, {
...spectrumPlot,
plotData: spectrum,
});
return plots;
}
/**
* Returns the plot data for the return period.
*
* @param responseData Hazard response data
* @param sourceType Hazard source type
* @param returnPeriod Retrun period
*/
private returnPeriodPlotData(
responseData: HazardResponseData,
sourceType: string,
returnPeriod: number,
): Partial<PlotData> {
const data = responseData.hazardCurves
.filter(hazard => hazard.imt.value !== Imt.PGV.toString())
.map(
hazard => hazardUtils.getSourceTypeData(hazard.data, sourceType).values,
);
const xMin = Math.min(...data.map(xy => Math.min(...xy.xs)));
const xMax = Math.max(...data.map(xy => Math.max(...xy.xs)));
return {
hoverinfo: 'none',
line: {
color: 'black',
},
mode: 'lines',
name: `${returnPeriod} yr.`,
x: [xMin, xMax],
y: [1 / returnPeriod, 1 / returnPeriod],
};
}
private updateUrl(): void {
this.location.replaceState(
apps().hazard.dynamic.routerLink,
new HttpParams().appendAll(this.formGroup.getRawValue()).toString(),
);
}
private updateUsageUrl() {
const nshmService = this.state().nshmServices.find(
nshm => nshm.model === this.formGroup.getRawValue().model,
);
this.updateState({
serviceCallInfo: {
...this.state().serviceCallInfo,
usage: [nshmService.url],
},
});
}
}