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)
Parameters :
Name Type Optional
formBuilder FormBuilder No
spinnerService SpinnerService No
nshmpService NshmpService No
hazardService HazardService No
route ActivatedRoute No
location LocationService 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: StaticResponseData[], plot: NshmpPlot, form: HazardControlForm, returnPeriod: number)

Return hazard plot data from response data.

Parameters :
Name Type Optional Description
responseData StaticResponseData<HazardResponseMetadata>[] No

Service response data

plot NshmpPlot No

Plot

form HazardControlForm No

Form field values

returnPeriod number No

Return period

Returns : PlotlyPlot
createPlots
createPlots()
Returns : void
Private createResponseSpectra
createResponseSpectra(serviceResponse: StaticHazardResponse, formGroup: FormGroupControls)

Calculate the response spectrum for each site class.

Parameters :
Name Type Optional
serviceResponse StaticHazardResponse No
formGroup FormGroupControls<HazardControlForm> No
Returns : ResponseSpectra[]
Private createServiceEndpoint
createServiceEndpoint(serviceUrl: string, values: HazardControlForm)
Parameters :
Name Type Optional
serviceUrl string No
values HazardControlForm No
Returns : string
defaultFormValues
defaultFormValues()

Returns the default form values.

Returns : HazardControlForm
formServiceUrl
formServiceUrl(services: HazardWebServices, model: NshmId, longitude: number, latitude: number)

Form the url to call.

Parameters :
Name Type Optional Description
services HazardWebServices<StaticCurvesServices> No
model NshmId No

The model

longitude number No

The longitude

latitude number No

The latitude

Returns : string
Private getResponseData
getResponseData(state: AppState, formGroup: FormGroupControls)

Returns the response data for a site class.

Parameters :
Name Type Optional Description
state AppState No

The current state

formGroup FormGroupControls<HazardControlForm> No
Returns : StaticResponseData[]
Private hazardCurveMap
hazardCurveMap(responseData: StaticResponseData[], form: HazardControlForm)

Return map of imt to xy sequence.

Parameters :
Name Type Optional Description
responseData StaticResponseData<HazardResponseMetadata>[] No

Service response data

form HazardControlForm No

Form field values

Returns : Map<Imt, XySequence>
init
init()

Initialize applicaiton.

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

Static hazard app initial state

Returns : AppState
resetControlPanel
resetControlPanel()

Reset the control panel.

Returns : void
resetSettings
resetSettings()

Reset the plot settings.

Returns : void
resetState
resetState()
Returns : void
Private responseSpectrumPlotData
responseSpectrumPlotData(responseData: StaticResponseData[], plot: NshmpPlot, form: HazardControlForm, returnPeriodValue: number)

Return the response spectrum plot data.

Parameters :
Name Type Optional Description
responseData StaticResponseData<HazardResponseMetadata>[] No

The response data

plot NshmpPlot No
form HazardControlForm No
returnPeriodValue number No
Returns : PlotlyPlot
Private responseToPlots
responseToPlots(state: AppState, formGroup: FormGroupControls)

Transform the service response to plot data.

Parameters :
Name Type Optional Description
state AppState No

The current state

formGroup FormGroupControls<HazardControlForm> No
Returns : Map<string, NshmpPlot>
Private returnPeriodPlotData
returnPeriodPlotData(plotData: Partial[], returnPeriod: number)

Returns the plot data for the return period.

Parameters :
Name Type Optional Description
plotData Partial<PlotData>[] No

The plot data

returnPeriod number No

The return 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
staticHazardOnSiteClassChange
staticHazardOnSiteClassChange(state: AppState, formGroup: FormGroupControls)

Replot hazard curves when site class changes.

Parameters :
Name Type Optional Description
state AppState No

The current state

formGroup FormGroupControls<HazardControlForm> No
Returns : Map<string, NshmpPlot>
Private staticService
staticService(services: HazardWebServices, model: NshmId)

Returns the static service associated with a model

Parameters :
Name Type Optional Description
services HazardWebServices<StaticCurvesServices> No
model NshmId No

The model

Returns : HazardWebServiceConfig
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<HazardControlForm>( this.defaultFormValues(), )
Private nshmpWsStatic
Default value : environment.webServices.nshmpWsStatic

nshmp-haz-ws web config

Private serviceEndpoint
Default value : this.nshmpWsStatic.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>
nshmService
getnshmService()

Returns the metadata of the NSHM observable.

Returns : Signal<StaticNshmMetadata>
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<StaticHazardResponse>
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<StaticHazardUsage>
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 {
  HazardAppQuery,
  HazardControlForm,
  HazardPlots,
  HazardService,
  hazardUtils,
  ResponseSpectra,
  ResponseSpectrum,
} from '@ghsc/nshmp-lib-ng/hazard';
import {
  FormGroupControls,
  NshmpService,
  RETURN_PERIOD_VALUES,
  returnPeriodAltName,
  ServiceCallInfo,
  SpinnerService,
} from '@ghsc/nshmp-lib-ng/nshmp';
import {
  NshmpPlot,
  NshmpPlotSettingFormGroup,
  plotUtils,
} from '@ghsc/nshmp-lib-ng/plot';
import {Maths} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/calc';
import {XySequence} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/data';
import {Location} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/geo';
import {Imt, imtToPeriod} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm';
import {NshmId} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/nshm';
import {
  HazardResponseMetadata,
  StaticHazardResponse,
  StaticHazardUsage,
  StaticResponseData,
} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-static/hazard-service';
import {StaticNshmMetadata} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-static/nshm-service';
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 {
  HazardWebServiceConfig,
  HazardWebServices,
  StaticCurvesServices,
} from 'projects/nshmp-apps/src/shared/models/web-services.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 {AppState} from '../models/state.model';

/**
 * Entrypoint to store for dynamic hazard application.
 */
@Injectable({
  providedIn: 'root',
})
export class AppService
  extends SharedService
  implements AppServiceModel<AppState, HazardControlForm>
{
  /** nshmp-haz-ws web config */
  private nshmpWsStatic = environment.webServices.nshmpWsStatic;
  /** Hazard endpoint */
  private serviceEndpoint = this.nshmpWsStatic.services.curveServices.hazard;

  readonly formGroup = this.formBuilder.group<HazardControlForm>(
    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<StaticNshmMetadata> {
    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<StaticHazardResponse> {
    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<StaticHazardUsage> {
    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.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);
    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$<StaticHazardResponse>(url)
      .pipe(
        catchError((error: Error) => {
          spinnerRef.close();
          return this.nshmpService.throwError$(error);
        }),
      )
      .subscribe(serviceResponse => {
        spinnerRef.close();

        this.updateState({
          responseSpectra: this.createResponseSpectra(
            serviceResponse,
            this.formGroup,
          ),
          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(): HazardControlForm {
    return {
      ...hazardUtils.hazardDefaultFormValues(),
    };
  }

  /**
   * Form the url to call.
   *
   * @param model The model
   * @param longitude The longitude
   * @param latitude The latitude
   */
  formServiceUrl(
    services: HazardWebServices<StaticCurvesServices>,
    model: NshmId,
    longitude: number,
    latitude: number,
  ): string {
    const service = this.staticService(services, model);
    return `${service.url}${services.services.hazard}/${longitude}/${latitude}`;
  }

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

    this.hazardService
      .staticNshms$(
        `${this.nshmpWsStatic.url}${this.nshmpWsStatic.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();
      });
  }

  /**
   * Static hazard app initial state
   */
  initialState(): AppState {
    const usageResponses: Map<string, StaticHazardUsage> = 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,
    });
  }

  /**
   * Replot hazard curves when site class changes.
   *
   * @param state The current state
   */
  staticHazardOnSiteClassChange(
    state: AppState,
    formGroup: FormGroupControls<HazardControlForm>,
  ): Map<string, NshmpPlot> {
    if (state.serviceResponse) {
      return this.responseToPlots(state, formGroup);
    } else {
      return state.plots;
    }
  }

  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));
  }

  /**
   * Return hazard plot data from response data.
   *
   * @param responseData Service response data
   * @param plot Plot
   * @param form Form field values
   * @param returnPeriod Return period
   */
  private createHazardPlotData(
    responseData: StaticResponseData<HazardResponseMetadata>[],
    plot: NshmpPlot,
    form: HazardControlForm,
    returnPeriod: number,
  ): PlotlyPlot {
    const staticResponseData = responseData.filter(
      response => response.metadata.imt.value !== Imt.PGV.toString(),
    );

    let data = staticResponseData.map((response, index) => {
      const imt = response.metadata.imt.value as Imt;

      const label = imt === Imt.PGA ? imt : `${imtToPeriod(imt)} s`;

      const xy = hazardUtils.updateXySequence(
        form,
        hazardUtils.cleanXySequence(response.data),
        imt,
      );
      const plotlyData: Partial<PlotData> = {
        hovertemplate: '%{x} g, %{y} AFE',
        line: {
          color: hazardUtils.color(index, staticResponseData.length),
        },
        mode: 'lines+markers',
        name: label,
        x: xy.xs,
        y: xy.ys,
      };

      return plotlyData;
    });

    const metadata = [...responseData].shift().metadata;
    const layout = plotUtils.updatePlotLabels({
      layout: plot.plotData.layout,
      xLabel: metadata.xLabel,
      yLabel: metadata.yLabel,
    });

    const mobileLayout = plotUtils.updatePlotLabels({
      layout: plot.plotData.mobileLayout,
      xLabel: metadata.xLabel,
      yLabel: metadata.yLabel,
    });

    data = [this.returnPeriodPlotData(data, returnPeriod), ...data];

    return {
      config: {
        ...plot.plotData.config,
      },
      data,
      id: 'hazard-curves',
      layout,
      mobileConfig: {...plot.plotData.mobileConfig},
      mobileLayout: {
        ...mobileLayout,
      },
    };
  }

  /**
   * Calculate the response spectrum for each site class.
   *
   * @param state The current state
   */
  private createResponseSpectra(
    serviceResponse: StaticHazardResponse,
    formGroup: FormGroupControls<HazardControlForm>,
  ): ResponseSpectra[] {
    if (serviceResponse === null) {
      return [];
    }

    const returnPeriodValue = formGroup.getRawValue().returnPeriod;
    const hasReturnPeriod = RETURN_PERIOD_VALUES.includes(returnPeriodValue);
    const returnPeriods = [...RETURN_PERIOD_VALUES];

    if (!hasReturnPeriod) {
      returnPeriods.unshift(returnPeriodValue);
    }

    return serviceResponse.response.map(serviceResponse => {
      const hazardCurves: Map<Imt, XySequence> = this.hazardCurveMap(
        serviceResponse,
        formGroup.getRawValue(),
      );

      const spectra: ResponseSpectrum[] = returnPeriods.map(
        (returnPeriod: number) => {
          const spectrum = Maths.responseSpectrum(hazardCurves, returnPeriod);

          return {
            returnPeriod,
            values: spectrum.ys,
          };
        },
      );

      return {
        imts: [...hazardCurves.keys()],
        responseSpectrum: spectra,
        siteClass: serviceResponse[0].metadata.siteClass,
      };
    });
  }

  private createServiceEndpoint(
    serviceUrl: string,
    values: HazardControlForm,
  ): string {
    const {longitude, latitude} = values;
    return `${serviceUrl}/${longitude}/${latitude}`;
  }

  /**
   * Returns the response data for a site class.
   *
   * @param state The current state
   */
  private getResponseData(
    state: AppState,
    formGroup: FormGroupControls<HazardControlForm>,
  ): StaticResponseData<HazardResponseMetadata>[] {
    const siteClass = formGroup.getRawValue().siteClass;

    return state.serviceResponse
      ? state.serviceResponse.response.find(response => {
          return response.find(data => data.metadata.siteClass === siteClass);
        })
      : null;
  }

  /**
   * Return map of imt to xy sequence.
   *
   * @param responseData Service response data
   * @param form Form field values
   */
  private hazardCurveMap(
    responseData: StaticResponseData<HazardResponseMetadata>[],
    form: HazardControlForm,
  ): Map<Imt, XySequence> {
    const hazardCurves: Map<Imt, XySequence> = new Map();
    responseData.forEach(response => {
      const imt = response.metadata.imt.value as Imt;
      const xy = hazardUtils.updateXySequence(
        form,
        hazardUtils.cleanXySequence(response.data),
        imt,
      );
      hazardCurves.set(imt, xy);
    });

    return hazardCurves;
  }

  private initialFormSet(): void {
    this.formGroup.valueChanges.subscribe(() => this.updateUrl());

    const query = this.route.snapshot.queryParams as HazardAppQuery;
    const defaultValues = this.defaultFormValues();

    const formValues: HazardControlForm = {
      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,
      truncate:
        query.truncate !== undefined
          ? (JSON.parse(query.truncate) as boolean)
          : defaultValues.truncate,
    };

    this.formGroup.patchValue(formValues);

    if (this.formGroup.valid) {
      this.nshmpService.selectPlotControl();
      this.callService();
    } else if (this.formGroup.getRawValue() !== defaultValues) {
      this.formGroup.markAsDirty();
    }
  }

  /**
   * Return the response spectrum plot data.
   *
   * @param responseData The response data
   */
  private responseSpectrumPlotData(
    responseData: StaticResponseData<HazardResponseMetadata>[],
    plot: NshmpPlot,
    form: HazardControlForm,
    returnPeriodValue: number,
  ): PlotlyPlot {
    const hazardCurves = this.hazardCurveMap(responseData, form);
    const imts: Imt[] = [];

    responseData.forEach(response => {
      imts.push(response.metadata.imt.value as Imt);
    });

    const lines: Partial<PlotData>[] = [];

    const hasReturnPeriod = RETURN_PERIOD_VALUES.includes(returnPeriodValue);
    const returnPeriods = [...RETURN_PERIOD_VALUES];

    if (!hasReturnPeriod) {
      returnPeriods.unshift(returnPeriodValue);
    }

    returnPeriods.forEach((returnPeriod, iReturnPeriod) => {
      const spectrum = Maths.responseSpectrum(hazardCurves, returnPeriod);
      let lineWidth = 2;
      let markerSize = 7;
      const color = hazardUtils.color(iReturnPeriod, returnPeriods.length);

      if (returnPeriod === returnPeriodValue) {
        lineWidth = 4;
        markerSize = 12;
      }

      const spectraX: number[] = [];
      const spectraY: number[] = [];
      const scatters: Partial<PlotData>[] = [];

      for (let iSpectrum = 0; iSpectrum < spectrum.xs.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: [Math.min(...spectrum.xs.filter(x => x !== imtToPeriod(imt)))],
            y: [spectrum.ys[iSpectrum]],
          });
        } else {
          spectraX.push(spectrum.xs[iSpectrum]);
          spectraY.push(spectrum.ys[iSpectrum]);
        }
      }

      lines.push({
        hovertemplate: '%{y} g',
        line: {
          color,
          width: lineWidth,
        },
        marker: {
          size: markerSize,
        },
        mode: 'lines+markers',
        name: returnPeriodAltName[returnPeriod] ?? `${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 to plot data.
   *
   * @param state The current state
   */
  private responseToPlots(
    state: AppState,
    formGroup: FormGroupControls<HazardControlForm>,
  ): Map<string, NshmpPlot> {
    if (state.serviceResponse === null || state.serviceResponse === undefined) {
      return state.plots;
    }

    const returnPeriod = formGroup.getRawValue().returnPeriod;
    const responseData = this.getResponseData(state, formGroup);
    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(
      responseData,
      spectrumPlot,
      formGroup.getRawValue(),
      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 plotData The plot data
   * @param returnPeriod The return period
   */
  private returnPeriodPlotData(
    plotData: Partial<PlotData>[],
    returnPeriod: number,
  ): Partial<PlotData> {
    const xMin = Math.min(
      ...plotData.map(xy => Math.min(...(xy.x as number[]))),
    );
    const xMax = Math.max(
      ...plotData.map(xy => Math.max(...(xy.x as number[]))),
    );

    return {
      hoverinfo: 'none',
      line: {
        color: 'black',
      },
      mode: 'lines',
      name: `${returnPeriod} yr.`,
      x: [xMin, xMax],
      y: [1 / returnPeriod, 1 / returnPeriod],
    };
  }

  /**
   * Returns the static service associated with a model
   *
   * @param service The static hazard services
   * @param model The model
   */
  private staticService(
    services: HazardWebServices<StaticCurvesServices>,
    model: NshmId,
  ): HazardWebServiceConfig {
    return services.models.find(service => model === service.model);
  }

  private updateUrl(): void {
    this.location.replaceState(
      apps().hazard.static.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],
      },
    });
  }
}

results matching ""

    No results matching ""