public bindDataToAxis()

in src/app/stores/app_store.ts [1720:2081]


  public bindDataToAxis(options: {
    object: Specification.PlotSegment;
    property?: string;
    appendToProperty?: string;
    dataExpression: DragData.DataExpression;
    type?: AxisDataBindingType;
    numericalMode?: NumericalMode;
    autoDomainMax: boolean;
    autoDomainMin: boolean;
    domainMin: number;
    domainMax: number;
    defineCategories: boolean;
  }) {
    const { object, property, appendToProperty, dataExpression } = options;

    this.normalizeDataExpression(dataExpression);

    let groupExpression = dataExpression.expression;
    let valueType = dataExpression.valueType;
    const propertyValue = object.properties[options.property] as any;
    const type = dataExpression.type
      ? options.type
      : this.getBindingByDataKind(options.dataExpression.metadata.kind);
    const rawColumnExpression = dataExpression.rawColumnExpression;
    if (
      rawColumnExpression &&
      dataExpression.valueType !== DataType.Date &&
      (options.dataExpression.metadata.kind === DataKind.Ordinal ||
        options.dataExpression.metadata.kind === DataKind.Categorical)
    ) {
      groupExpression = rawColumnExpression;
      valueType = DataType.String;
    }

    const objectProperties = object.properties[
      options.property
    ] as ObjectProperties;

    const expression =
      appendToProperty === "dataExpressions" && propertyValue
        ? ((propertyValue as any).expression as string)
        : groupExpression;

    const column = getColumnNameByExpression(expression);

    const orderByCategories: Array<string> = [];
    let dataBinding: Specification.Types.AxisDataBinding = {
      type: options.type || type,
      // Don't change current expression (use current expression), if user appends data expression ()
      expression: expression,
      rawExpression:
        dataExpression.rawColumnExpression != undefined
          ? dataExpression.rawColumnExpression
          : expression,
      valueType: valueType !== undefined ? valueType : null,
      gapRatio:
        propertyValue?.gapRatio === undefined ? 0.1 : propertyValue.gapRatio,
      visible:
        objectProperties?.visible !== undefined
          ? objectProperties?.visible
          : true,
      side: propertyValue?.side || "default",
      style:
        (objectProperties?.style as AxisRenderingStyle) ||
        deepClone(defaultAxisStyle),
      numericalMode:
        options.numericalMode != undefined ? options.numericalMode : null,
      dataKind:
        dataExpression.metadata.kind != undefined
          ? dataExpression.metadata.kind
          : null,
      order:
        dataExpression.metadata.order !== undefined
          ? dataExpression.metadata.order
          : null,
      orderMode:
        dataExpression.metadata.orderMode !== undefined
          ? dataExpression.metadata.orderMode
          : null,
      autoDomainMax:
        options.autoDomainMax != undefined ? options.autoDomainMax : true,
      autoDomainMin:
        options.autoDomainMin != undefined ? options.autoDomainMin : true,
      tickFormat:
        <string>objectProperties?.tickFormat !== undefined
          ? <string>objectProperties?.tickFormat
          : null,
      tickDataExpression:
        <string>objectProperties?.tickDataExpression !== undefined
          ? <string>objectProperties?.tickDataExpression
          : null,
      tickFormatType:
        (objectProperties?.tickFormatType as TickFormatType) ??
        TickFormatType.None,
      domainMin:
        <number>objectProperties?.domainMin !== undefined
          ? <number>objectProperties?.domainMin
          : null,
      domainMax:
        <number>objectProperties?.domainMax !== undefined
          ? <number>objectProperties?.domainMax
          : null,
      dataDomainMin:
        <number>objectProperties?.domainMin !== undefined
          ? <number>objectProperties?.domainMin
          : null,
      dataDomainMax:
        <number>objectProperties?.domainMax !== undefined
          ? <number>objectProperties?.domainMax
          : null,
      enablePrePostGap:
        <boolean>objectProperties?.enablePrePostGap !== undefined
          ? <boolean>objectProperties?.enablePrePostGap
          : null,
      categories:
        <string[]>objectProperties?.categories !== undefined
          ? <string[]>objectProperties?.categories
          : null,
      allCategories:
        <string[]>objectProperties?.allCategories !== undefined
          ? <string[]>objectProperties?.allCategories
          : <string[]>objectProperties?.categories !== undefined
          ? <string[]>objectProperties?.categories
          : null,
      scrollPosition:
        <number>objectProperties?.scrollPosition !== undefined
          ? <number>objectProperties?.scrollPosition
          : 0,
      allowScrolling:
        <boolean>objectProperties?.allowScrolling !== undefined
          ? <boolean>objectProperties?.allowScrolling
          : false,
      windowSize:
        <number>objectProperties?.windowSize !== undefined
          ? <number>objectProperties?.windowSize
          : 10,
      barOffset:
        <number>objectProperties?.barOffset !== undefined
          ? <number>objectProperties?.barOffset
          : 0,
      offset:
        <number>objectProperties?.offset !== undefined
          ? <number>objectProperties?.offset
          : 0,
      onTop:
        <boolean>objectProperties?.onTop !== undefined
          ? <boolean>objectProperties?.onTop
          : false,
      enableSelection:
        <boolean>objectProperties?.enableSelection !== undefined
          ? <boolean>objectProperties?.enableSelection
          : false,

      orderByCategories:
        <string[]>objectProperties?.orderByCategories !== undefined
          ? <string[]>objectProperties?.orderByCategories
          : orderByCategories,
      orderByExpression: <string>objectProperties?.orderByExpression ?? column,
    };

    let expressions = [groupExpression];

    if (appendToProperty) {
      if (object.properties[appendToProperty] == null) {
        object.properties[appendToProperty] = [
          { name: uniqueID(), expression: groupExpression },
        ];
      } else {
        (object.properties[appendToProperty] as any[]).push({
          name: uniqueID(),
          expression: groupExpression,
        });
      }
      expressions = (object.properties[appendToProperty] as any[]).map(
        (x) => x.expression
      );
      if (object.properties[property] == null) {
        object.properties[property] = dataBinding;
      } else {
        dataBinding = object.properties[
          property
        ] as Specification.Types.AxisDataBinding;
      }
    } else {
      object.properties[property] = dataBinding;
    }

    const groupBy: Specification.Types.GroupBy = this.getGroupingExpression(
      object
    );
    let values: ValueType[] = [];
    if (
      appendToProperty == "dataExpressions" &&
      dataBinding.domainMax !== undefined &&
      dataBinding.domainMin !== undefined
    ) {
      // save current range of scale if user adds data
      values = values.concat(dataBinding.domainMax, dataBinding.domainMin);
    }
    for (const expr of expressions) {
      if (expr) {
        const r = this.chartManager.getGroupedExpressionVector(
          dataExpression.table.name,
          groupBy,
          expr
        );
        values = values.concat(r);
      }
    }

    if (dataExpression.metadata) {
      switch (dataExpression.metadata.kind) {
        case Specification.DataKind.Categorical:
        case Specification.DataKind.Ordinal:
          {
            dataBinding.type = AxisDataBindingType.Categorical;
            dataBinding.valueType = dataExpression.valueType;

            const { categories, order } = this.getCategoriesForDataBinding(
              dataExpression.metadata,
              dataExpression.valueType,
              values
            );

            dataBinding.orderByCategories = deepClone(categories);
            dataBinding.order = order != undefined ? order : null;
            dataBinding.allCategories = deepClone(categories);

            if (
              dataBinding.windowSize == null ||
              dataBinding.windowSize > dataBinding.allCategories.length
            ) {
              dataBinding.windowSize =
                dataBinding.allCategories?.length ??
                Math.ceil(categories.length / 10);
            }
            dataBinding.categories = categories;
            if (dataBinding.allowScrolling) {
              const start = Math.floor(
                ((categories.length - dataBinding.windowSize) / 100) *
                  dataBinding.scrollPosition
              );
              dataBinding.categories = categories.slice(
                start,
                start + dataBinding.windowSize
              );
            }
          }

          break;
        case Specification.DataKind.Numerical:
          {
            if (options.numericalMode === NumericalMode.Logarithmic) {
              const scale = new Scale.LogarithmicScale();
              scale.inferParameters(values as number[]);
              if (dataBinding.autoDomainMin) {
                dataBinding.domainMin = scale.domainMin;
              } else {
                dataBinding.domainMin = options.domainMin;
              }
              if (dataBinding.autoDomainMax) {
                dataBinding.domainMax = scale.domainMax;
              } else {
                dataBinding.domainMax = options.domainMax;
              }
              dataBinding.type = AxisDataBindingType.Numerical;
              dataBinding.numericalMode = NumericalMode.Logarithmic;
            } else {
              const scale = new Scale.LinearScale();
              scale.inferParameters(values as number[]);
              if (dataBinding.autoDomainMin) {
                dataBinding.domainMin = scale.domainMin;
              } else {
                dataBinding.domainMin = options.domainMin;
              }
              if (dataBinding.autoDomainMax) {
                dataBinding.domainMax = scale.domainMax;
              } else {
                dataBinding.domainMax = options.domainMax;
              }
              dataBinding.type = AxisDataBindingType.Numerical;
              dataBinding.numericalMode = NumericalMode.Linear;
            }
            if (options.defineCategories) {
              dataBinding.categories = defineCategories(values);
            }

            if (dataBinding.windowSize == null) {
              dataBinding.windowSize =
                (dataBinding.domainMax - dataBinding.domainMin) / 10;
            }
            dataBinding.dataDomainMin = dataBinding.domainMin;
            dataBinding.dataDomainMax = dataBinding.domainMax;
          }
          break;
        case Specification.DataKind.Temporal:
          {
            const scale = new Scale.DateScale();
            scale.inferParameters(values as number[], false);
            if (dataBinding.autoDomainMin) {
              dataBinding.domainMin = scale.domainMin;
            } else {
              dataBinding.domainMin = options.domainMin;
            }
            if (dataBinding.autoDomainMax) {
              dataBinding.domainMax = scale.domainMax;
            } else {
              dataBinding.domainMax = options.domainMax;
            }
            dataBinding.type = AxisDataBindingType.Numerical;
            dataBinding.numericalMode = NumericalMode.Temporal;
            const { categories } = this.getCategoriesForDataBinding(
              dataExpression.metadata,
              dataExpression.valueType,
              values
            );
            dataBinding.allCategories = deepClone(categories);
            dataBinding.categories = categories;
            if (dataBinding.allowScrolling) {
              const start = Math.floor(
                ((categories.length - dataBinding.windowSize) / 100) *
                  dataBinding.scrollPosition
              );
              dataBinding.categories = categories.slice(
                start,
                start + dataBinding.windowSize
              );
            }
          }
          break;
      }
    }

    // Adjust sublayout option if current option is not available
    const props = object.properties as Prototypes.PlotSegments.Region2DProperties;
    if (props.sublayout) {
      if (
        props.sublayout.type == Region2DSublayoutType.DodgeX ||
        props.sublayout.type == Region2DSublayoutType.DodgeY ||
        props.sublayout.type == Region2DSublayoutType.Grid
      ) {
        if (props.xData && props.xData.type == "numerical") {
          props.sublayout.type = Region2DSublayoutType.Overlap;
        }
        if (props.yData && props.yData.type == "numerical") {
          props.sublayout.type = Region2DSublayoutType.Overlap;
        }
      }

      //set default sublayout type for Categorical - Categorical data
      if (
        props.xData &&
        props.xData.type == AxisDataBindingType.Categorical &&
        props.yData &&
        props.yData.type == AxisDataBindingType.Categorical
      ) {
        if (props.sublayout.type == Region2DSublayoutType.Overlap) {
          props.sublayout.type = Region2DSublayoutType.Grid;
        }
      }
    }
  }