export default function transformProps()

in superset-frontend/plugins/plugin-chart-echarts/src/Radar/transformProps.ts [72:296]


export default function transformProps(
  chartProps: EchartsRadarChartProps,
): RadarChartTransformedProps {
  const {
    formData,
    height,
    hooks,
    filterState,
    queriesData,
    width,
    theme,
    inContextMenu,
    emitCrossFilters,
  } = chartProps;
  const refs: Refs = {};
  const { data = [] } = queriesData[0];
  const coltypeMapping = getColtypesMapping(queriesData[0]);

  const {
    colorScheme,
    groupby,
    labelType,
    labelPosition,
    legendOrientation,
    legendType,
    legendMargin,
    metrics = [],
    numberFormat,
    dateFormat,
    showLabels,
    showLegend,
    isCircle,
    columnConfig,
    sliceId,
  }: EchartsRadarFormData = {
    ...DEFAULT_LEGEND_FORM_DATA,
    ...DEFAULT_RADAR_FORM_DATA,
    ...formData,
  };
  const { setDataMask = () => {}, onContextMenu } = hooks;
  const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
  const numberFormatter = getNumberFormatter(numberFormat);
  const formatter = (params: CallbackDataParams) =>
    formatLabel({
      params,
      numberFormatter,
      labelType,
    });

  const metricLabels = metrics.map(getMetricLabel);
  const groupbyLabels = groupby.map(getColumnLabel);

  const metricLabelAndMaxValueMap = new Map<string, number>();
  const metricLabelAndMinValueMap = new Map<string, number>();
  const columnsLabelMap = new Map<string, string[]>();
  const transformedData: RadarSeriesDataItemOption[] = [];
  data.forEach(datum => {
    const joinedName = extractGroupbyLabel({
      datum,
      groupby: groupbyLabels,
      coltypeMapping,
      timeFormatter: getTimeFormatter(dateFormat),
    });
    // map(joined_name: [columnLabel_1, columnLabel_2, ...])
    columnsLabelMap.set(
      joinedName,
      groupbyLabels.map(col => datum[col] as string),
    );

    // put max value of series into metricLabelAndMaxValueMap
    // eslint-disable-next-line no-restricted-syntax
    for (const [metricLabel, value] of Object.entries(datum)) {
      if (metricLabelAndMaxValueMap.has(metricLabel)) {
        metricLabelAndMaxValueMap.set(
          metricLabel,
          Math.max(
            value as number,
            ensureIsInt(
              metricLabelAndMaxValueMap.get(metricLabel),
              Number.MIN_SAFE_INTEGER,
            ),
          ),
        );
      } else {
        metricLabelAndMaxValueMap.set(metricLabel, value as number);
      }

      if (metricLabelAndMinValueMap.has(metricLabel)) {
        metricLabelAndMinValueMap.set(
          metricLabel,
          Math.min(
            value as number,
            ensureIsInt(
              metricLabelAndMinValueMap.get(metricLabel),
              Number.MAX_SAFE_INTEGER,
            ),
          ),
        );
      } else {
        metricLabelAndMinValueMap.set(metricLabel, value as number);
      }
    }

    const isFiltered =
      filterState.selectedValues &&
      !filterState.selectedValues.includes(joinedName);

    // generate transformedData
    transformedData.push({
      value: metricLabels.map(metricLabel => datum[metricLabel]),
      name: joinedName,
      itemStyle: {
        color: colorFn(joinedName, sliceId),
        opacity: isFiltered
          ? OpacityEnum.Transparent
          : OpacityEnum.NonTransparent,
      },
      lineStyle: {
        opacity: isFiltered
          ? OpacityEnum.SemiTransparent
          : OpacityEnum.NonTransparent,
      },
      label: {
        show: showLabels,
        position: labelPosition,
        formatter,
      },
    } as RadarSeriesDataItemOption);
  });

  const selectedValues = (filterState.selectedValues || []).reduce(
    (acc: Record<string, number>, selectedValue: string) => {
      const index = transformedData.findIndex(
        ({ name }) => name === selectedValue,
      );
      return {
        ...acc,
        [index]: selectedValue,
      };
    },
    {},
  );

  const indicator = metricLabels.map(metricLabel => {
    const maxValueInControl = columnConfig?.[metricLabel]?.radarMetricMaxValue;
    const minValueInControl = columnConfig?.[metricLabel]?.radarMetricMinValue;

    // Ensure that 0 is at the center of the polar coordinates
    const metricValueAsMax =
      metricLabelAndMaxValueMap.get(metricLabel) === 0
        ? Number.MAX_SAFE_INTEGER
        : metricLabelAndMaxValueMap.get(metricLabel);
    const max =
      maxValueInControl === null ? metricValueAsMax : maxValueInControl;

    let min: number;
    // If the min value doesn't exist, set it to 0 (default),
    // if it is null, set it to the min value of the data,
    // otherwise, use the value from the control
    if (minValueInControl === undefined) {
      min = 0;
    } else if (minValueInControl === null) {
      min = metricLabelAndMinValueMap.get(metricLabel) || 0;
    } else {
      min = minValueInControl;
    }

    return {
      name: metricLabel,
      max,
      min,
    };
  });

  const series: RadarSeriesOption[] = [
    {
      type: 'radar',
      ...getChartPadding(showLegend, legendOrientation, legendMargin),
      animation: false,
      emphasis: {
        label: {
          show: true,
          fontWeight: 'bold',
          backgroundColor: theme.colors.grayscale.light5,
        },
      },
      data: transformedData,
    },
  ];

  const echartOptions: EChartsCoreOption = {
    grid: {
      ...defaultGrid,
    },
    tooltip: {
      ...getDefaultTooltip(refs),
      show: !inContextMenu,
      trigger: 'item',
    },
    legend: {
      ...getLegendProps(legendType, legendOrientation, showLegend, theme),
      data: Array.from(columnsLabelMap.keys()),
    },
    series,
    radar: {
      shape: isCircle ? 'circle' : 'polygon',
      indicator,
    },
  };

  return {
    formData,
    width,
    height,
    echartOptions,
    emitCrossFilters,
    setDataMask,
    labelMap: Object.fromEntries(columnsLabelMap),
    groupby,
    selectedValues,
    onContextMenu,
    refs,
    coltypeMapping,
  };
}