File

services/app.service.ts

Description

Entrypoint to store for dynamic hazard application.

Extends

SharedService

Index

Properties
Methods
Accessors

Constructor

constructor(formBuilder: FormBuilder, spinnerService: SpinnerService, nshmpService: NshmpService, hazardService: HazardService, route: ActivatedRoute, location: LocationService, http: HttpClient, destroyRef: DestroyRef)
Parameters :
Name Type Optional
formBuilder FormBuilder No
spinnerService SpinnerService No
nshmpService NshmpService No
hazardService HazardService No
route ActivatedRoute No
location LocationService No
http HttpClient No
destroyRef DestroyRef No

Methods

Private addRequiredValidator
addRequiredValidator(control: AbstractControl)
Parameters :
Name Type Optional
control AbstractControl No
Returns : void
addValidators
addValidators()
Returns : void
callService
callService()

Call the hazard service.

Returns : void
Private createHazardPlotData
createHazardPlotData(responseData: HazardResponseData, plot: NshmpPlot, form: ControlForm, returnPeriod: number)

Create the hazard plot data.

Parameters :
Name Type Optional Description
responseData HazardResponseData No

The hazard response data

plot NshmpPlot No

Plot

form ControlForm No

Form values

returnPeriod number No

Return period

Returns : PlotlyPlot
createPlots
createPlots()
Returns : void
Private createServiceEndpoint
createServiceEndpoint(serviceUrl: string, values: ControlForm)
Parameters :
Name Type Optional
serviceUrl string No
values ControlForm No
Returns : string
Private createSourcesPlotData
createSourcesPlotData(responseData: HazardResponseData, plot: NshmpPlot, form: ControlForm)
Parameters :
Name Type Optional
responseData HazardResponseData No
plot NshmpPlot No
form ControlForm No
Returns : PlotlyPlot
defaultFormValues
defaultFormValues()

Returns the default form values.

Returns : ControlForm
init
init()

Initialize applicaiton.

Returns : void
Private initialFormSet
initialFormSet()
Returns : void
initialState
initialState()

Hazard app initial state

Returns : AppState
Private localInit
localInit(spinnerRef: MatDialogRef)
Parameters :
Name Type Optional
spinnerRef MatDialogRef<NshmpTemplateSpinnerComponent> No
Returns : void
Private onInit
onInit(availableModels: Parameter[], nshmServices: NshmMetadata[], usageResponses: Map, spinnerRef: MatDialogRef)
Parameters :
Name Type Optional
availableModels Parameter[] No
nshmServices NshmMetadata[] No
usageResponses Map<string | HazardUsageResponse> No
spinnerRef MatDialogRef<NshmpTemplateSpinnerComponent> No
Returns : void
resetControlPanel
resetControlPanel()

Reset the control panel.

Returns : void
resetSettings
resetSettings()

Reset the plot settings.

Returns : void
resetState
resetState()
Returns : void
Private responseSpectrumPlotData
responseSpectrumPlotData(spectra: ResponseSpectra, plot: NshmpPlot)

Return the response spectrum plot data.

Parameters :
Name Type Optional Description
spectra ResponseSpectra No

Response spectra

plot NshmpPlot No

plot

Returns : PlotlyPlot
Private responseToPlots
responseToPlots(state: AppState, formGroup: FormGroupControls<ControlForm>)

Transform the service response the plot data.

Parameters :
Name Type Optional Description
state AppState No

The current state

formGroup FormGroupControls<ControlForm> No
Returns : Map<string, NshmpPlot>
Private returnPeriodPlotData
returnPeriodPlotData(responseData: HazardResponseData, sourceType: string, returnPeriod: number)

Returns the plot data for the return period.

Parameters :
Name Type Optional Description
responseData HazardResponseData No

Hazard response data

sourceType string No

Hazard source type

returnPeriod number No

Retrun period

Returns : Partial<PlotData>
setLocation
setLocation(location: Location)

Set the location form fields.

Parameters :
Name Type Optional Description
location Location No

The location

Returns : void
updateState
updateState(state: Partial<AppState>)
Parameters :
Name Type Optional
state Partial<AppState> No
Returns : void
Private updateUrl
updateUrl()
Returns : void
Private updateUsageUrl
updateUsageUrl()
Returns : void

Properties

Readonly formGroup
Default value : this.formBuilder.group<ControlForm>(this.defaultFormValues())
Private localhostUrl
Type : string
Default value : 'http://127.0.0.1:8080'

Localhost url, cors issue with localhost:8008 must use ip

Private nshmpHazWs
Default value : environment.webServices.nshmpHazWs

nshmp-haz-ws web config

Private serviceEndpoint
Default value : this.nshmpHazWs.services.curveServices.hazard

Hazard endpoint

Readonly state
Default value : signal<AppState>(this.initialState())

Application state

Accessors

availableModels
getavailableModels()

Returns the available models.

Returns : Signal<Parameter[]>
hazardPlotData
gethazardPlotData()

Returns the hazard plot data.

Returns : Signal<PlotlyPlot>
hazardPlotSettings
gethazardPlotSettings()

Returns the hazard plot settings form.

Returns : Signal<FormGroup<NshmpPlotSettingFormGroup>>
hazardPlotState
gethazardPlotState()

Returns the hazard plot.

Returns : Signal<NshmpPlot>
sourcesPlotData
getsourcesPlotData()

Returns the hazard plot data.

Returns : Signal<PlotlyPlot>
sourcesPlotSettings
getsourcesPlotSettings()

Returns the hazard plot settings form.

Returns : Signal<FormGroup<NshmpPlotSettingFormGroup>>
sourcesPlotState
getsourcesPlotState()

Returns the hazard plot.

Returns : Signal<NshmpPlot>
nshmService
getnshmService()

Returns the metadata of the NSHM observable.

Returns : Signal<NshmMetadata>
plots
getplots()
responseSpectra
getresponseSpectra()

Returns the response spectra

Returns : Signal<ResponseSpectra>
serviceCallInfo
getserviceCallInfo()

Returns the service call info.

Returns : Signal<ServiceCallInfo>
serviceResponse
getserviceResponse()

Returns the disagg response.

Returns : Signal<HazardCalcResponse>
spectrumPlotData
getspectrumPlotData()

Returns the spectrum plot data.

Returns : Signal<PlotlyPlot>
spectrumPlotSettings
getspectrumPlotSettings()

Returns the spectrum plot settings form.

Returns : Signal<FormGroup<NshmpPlotSettingFormGroup>>
spectrumPlotState
getspectrumPlotState()

Returns the response spectrum plot.

Returns : Signal<NshmpPlot>
usage
getusage()

Return the usage for the selected model.

Returns : Signal<HazardUsageResponse>
import {Location as LocationService} from '@angular/common';
import {HttpClient, HttpParams} from '@angular/common/http';
import {computed, DestroyRef, Injectable, Signal, signal} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {AbstractControl, FormBuilder, FormGroup, Validators} from '@angular/forms';
import {MatDialogRef} from '@angular/material/dialog';
import {ActivatedRoute} from '@angular/router';
import {
  DynamicHazardControlForm,
  HazardPlots,
  HazardService,
  hazardUtils,
  ResponseSpectra,
} from '@ghsc/nshmp-lib-ng/hazard';
import {
  FormGroupControls,
  NshmpService,
  returnPeriodAltName,
  ServiceCallInfo,
} from '@ghsc/nshmp-lib-ng/nshmp';
import {NshmpPlot, NshmpPlotSettingFormGroup, plotUtils} from '@ghsc/nshmp-lib-ng/plot';
import {NshmpTemplateSpinnerComponent, SpinnerService} from '@ghsc/nshmp-template';
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, imtToString} 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';

export interface ControlForm extends DynamicHazardControlForm {
  imt: Imt;
}

enum SourcePlot {
  SOURCES = 'SOURCES',
}

const Plots = {...HazardPlots, ...SourcePlot};
type Plots = typeof Plots;

/**
 * Entrypoint to store for dynamic hazard application.
 */
@Injectable({
  providedIn: 'root',
})
export class AppService extends SharedService implements AppServiceModel<AppState, ControlForm> {
  /** nshmp-haz-ws web config */
  private nshmpHazWs = environment.webServices.nshmpHazWs;
  /** Hazard endpoint */
  private serviceEndpoint = this.nshmpHazWs.services.curveServices.hazard;
  /** Localhost url, cors issue with localhost:8008 must use ip */
  private localhostUrl = 'http://127.0.0.1:8080';

  readonly formGroup = this.formBuilder.group<ControlForm>(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,
    private http: HttpClient,
    private destroyRef: DestroyRef,
  ) {
    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(Plots.HAZARD));
  }

  /**
   * Returns the hazard plot data.
   */
  get sourcesPlotData(): Signal<PlotlyPlot> {
    return computed(() => this.sourcesPlotState().plotData);
  }

  /**
   * Returns the hazard plot settings form.
   */
  get sourcesPlotSettings(): Signal<FormGroup<NshmpPlotSettingFormGroup>> {
    return computed(() => this.sourcesPlotState().settingsForm);
  }

  /**
   * Returns the hazard plot.
   */
  get sourcesPlotState(): Signal<NshmpPlot> {
    return computed(() => this.state().plots.get(Plots.SOURCES));
  }

  /**
   * 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(Plots.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(takeUntilDestroyed(this.destroyRef))
      .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(): ControlForm {
    return {
      ...hazardUtils.hazardDefaultFormValues(),
      imt: null,
      sourceType: sourceTypeToCapitalCase(SourceType.TOTAL),
      vs30: 760,
    };
  }

  /**
   * Initialize applicaiton.
   */
  init(): void {
    const spinnerRef = this.spinnerService.show(SpinnerService.MESSAGE_METADATA);

    if (this.nshmpHazWs.url.startsWith(this.localhostUrl)) {
      this.localInit(spinnerRef);
    } else {
      this.hazardService
        .dynamicNshms$<HazardRequestMetadata>(
          `${this.nshmpHazWs.url}${this.nshmpHazWs.services.nshms}`,
          this.serviceEndpoint,
        )
        .pipe(takeUntilDestroyed(this.destroyRef))
        .pipe(
          catchError((error: Error) => {
            spinnerRef.close();
            return this.nshmpService.throwError$(error);
          }),
        )
        .subscribe(({models, nshmServices, usageResponses}) => {
          this.onInit(models, nshmServices, usageResponses, spinnerRef);
        });
    }
  }

  /**
   * Hazard app initial state
   */
  initialState(): AppState {
    const usageResponses: Map<string, HazardUsageResponse> = new Map();
    usageResponses.set(NshmId.CONUS_2018, null);

    const plots = hazardUtils.hazardDefaultPlots();
    const sourcesPlot = plotUtils.defaultPlot({
      id: 'sources',
      title: 'Sources',
      xLabel: 'Ground Motion (g)',
      yLabel: 'Annual Frequency of Exceedance',
    });
    plots.set(Plots.SOURCES, {
      label: 'Sources',
      plotData: sourcesPlot,
      settingsForm: plotUtils.plotSettingsToFormGroup({
        config: sourcesPlot.config,
        layout: plotUtils.plotlyLayoutToSettings(sourcesPlot.layout),
      }),
    });

    return {
      availableModels: [],
      nshmServices: [],
      plots,
      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));
  }

  private createSourcesPlotData(
    responseData: HazardResponseData,
    plot: NshmpPlot,
    form: ControlForm,
  ): PlotlyPlot {
    const {imt} = form;

    if (imt === null || imt === undefined) {
      return plot.plotData;
    }

    const hazardCurve = responseData.hazardCurves.find(curve => curve.imt.value === imt.toString());

    if (hazardCurve === undefined) {
      return plot.plotData;
    }

    const plotlyData = hazardCurve.data.map((data, index) => {
      const xy = hazardUtils.updateXySequence(form, hazardUtils.cleanXySequence(data.values), imt);

      const plotlyData: Partial<PlotData> = {
        hovertemplate: '%{x} g, %{y} AFE',
        line: {
          color: hazardUtils.color(index, hazardCurve.data.length),
        },
        mode: 'lines+markers',
        name: data.component,
        uid: data.component,
        x: xy.xs,
        y: xy.ys,
      };

      return plotlyData;
    });

    const title = `${imtToString(imt)} Sources`;

    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: plotlyData,
      id: 'sources-by-imt',
      layout,
      mobileConfig: {...plot.plotData.mobileConfig},
      mobileLayout,
    };
  }

  /**
   * 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: ControlForm,
    returnPeriod: number,
  ): PlotlyPlot {
    if (form.sourceType === null) {
      return plot.plotData;
    }

    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)}`,
        uid: imt.toString(),
        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: ControlForm): 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: ControlForm = {
      commonReturnPeriods: defaultValues.commonReturnPeriods,
      imt: [...this.usage().response.model.imts].shift()?.value as Imt,
      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
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(() => this.updateUrl());
  }

  private localInit(spinnerRef: MatDialogRef<NshmpTemplateSpinnerComponent>): void {
    const nshmId = this.defaultFormValues().model;

    const nshmService: NshmMetadata = {
      label: 'Local',
      model: nshmId,
      project: 'local',
      tag: 'local',
      test: null,
      url: this.localhostUrl,
      year: 0,
    };

    const usageResponses = new Map<string, HazardUsageResponse>();

    this.http
      .get<HazardUsageResponse>(`${nshmService.url}${this.serviceEndpoint}`)
      .pipe(takeUntilDestroyed(this.destroyRef))
      .pipe(
        catchError((error: Error) => {
          spinnerRef.close();
          return this.nshmpService.throwError$(error);
        }),
      )
      .subscribe(usage => {
        usageResponses.set(nshmId, usage);

        const model: Parameter = {
          display: `Local: ${usage.response.model.name}`,
          value: nshmId,
        };

        this.onInit([model], [nshmService], usageResponses, spinnerRef);
      });
  }

  private onInit(
    availableModels: Parameter[],
    nshmServices: NshmMetadata[],
    usageResponses: Map<string, HazardUsageResponse>,
    spinnerRef: MatDialogRef<NshmpTemplateSpinnerComponent>,
  ): void {
    spinnerRef.close();

    this.updateState({
      availableModels,
      nshmServices,
      usageResponses,
    });

    this.updateUsageUrl();
    this.initialFormSet();
  }

  /**
   * Return the response spectrum plot data.
   *
   * @param spectra Response spectra
   * @param plot plot
   * @param returnPeriodValue Return period
   */
  private responseSpectrumPlotData(spectra: ResponseSpectra, plot: NshmpPlot): PlotlyPlot {
    if (spectra === null || spectra === undefined) {
      return;
    }

    const imts = spectra.imts;
    const lines: Partial<PlotData>[] = [];

    spectra.responseSpectrum.forEach((spectrum, iSpectra) => {
      const lineWidth = 2;
      const markerSize = 7;

      const color = hazardUtils.color(iSpectra, spectra.responseSpectrum.length);

      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,
            uid: spectrum.returnPeriod.toString(),
            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`,
        uid: spectrum.returnPeriod.toString(),
        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<ControlForm>,
  ): 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(Plots.HAZARD);
    const spectrumPlot = state.plots.get(Plots.SPECTRUM);
    const sourcesPlot = state.plots.get(Plots.SOURCES);

    const hazardCurves = this.createHazardPlotData(
      responseData,
      hazardPlot,
      formGroup.getRawValue(),
      returnPeriod,
    );

    plots.set(Plots.HAZARD, {
      ...hazardPlot,
      plotData: hazardCurves,
    });

    const sources = this.createSourcesPlotData(
      responseData,
      sourcesPlot,
      this.formGroup.getRawValue(),
    );

    plots.set(Plots.SOURCES, {
      ...sourcesPlot,
      plotData: sources,
    });

    const spectrum = this.responseSpectrumPlotData(state.responseSpectra, spectrumPlot);

    plots.set(Plots.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,
    );

    if (nshmService !== undefined) {
      this.updateState({
        serviceCallInfo: {
          ...this.state().serviceCallInfo,
          usage: [nshmService.url],
        },
      });
    }
  }
}

results matching ""

    No results matching ""