in src/visualComponent/axes/helpers/axisHelper.ts [549:686]
export function createScale(options: ICreateAxisOptions): ICreateScaleResult {
const {
pixelSpan,
metaDataColumn,
isScalar,
isVertical,
forcedTickCount,
shouldClamp,
maxTickCount,
density,
} = options;
let { dataDomain } = options;
const outerPadding: number = options.outerPadding || DefaultOuterPadding;
const minOrdinalRectThickness: number = options.minOrdinalRectThickness || MinOrdinalRectThickness;
const dataType: powerbi.ValueTypeDescriptor = getCategoryValueType(metaDataColumn as powerbi.DataViewMetadataColumn, isScalar);
let maxTicks: number = isVertical
? getRecommendedNumberOfTicksForYAxis(pixelSpan)
: getRecommendedNumberOfTicksForXAxis(pixelSpan, minOrdinalRectThickness);
if (maxTickCount &&
maxTicks > maxTickCount) {
maxTicks = maxTickCount;
}
let scalarDomain: number[] = dataDomain
? dataDomain.slice()
: null;
let bestTickCount: number = maxTicks;
let scale: ScaleLinear<number, number> | ScalePoint<string>;
let usingDefaultDomain: boolean = false;
if (dataDomain == null
|| (dataDomain.length === 2 && dataDomain[0] == null && dataDomain[1] == null)
|| (dataDomain.length !== 2 && isScalar)) {
usingDefaultDomain = true;
if (dataType.dateTime || !isOrdinal(dataType)) {
dataDomain = emptyDomain;
}
else { // ordinal
dataDomain = [];
}
if (isOrdinal(dataType)) {
scale = createPointScale(pixelSpan, dataDomain);
}
else {
scale = createNumericalScale(
pixelSpan,
dataDomain,
outerPadding,
bestTickCount,
);
}
}
else {
if (isScalar && dataDomain.length > 0) {
bestTickCount = forcedTickCount !== undefined
? (maxTicks !== 0 ? forcedTickCount : 0)
: getBestNumberOfTicks(
dataDomain[0],
dataDomain[dataDomain.length - 1],
[metaDataColumn as powerbi.DataViewMetadataColumn],
maxTicks,
dataType.dateTime);
const normalizedRange = normalizeLinearDomain({
max: dataDomain[dataDomain.length - 1],
min: dataDomain[0],
});
scalarDomain = [
normalizedRange.min,
normalizedRange.max,
];
}
if (isScalar && dataType.numeric && !dataType.dateTime) {
// Note: Don't pass bestTickCount to createNumericalScale, because it overrides boundaries of the domain.
scale = createNumericalScale(
pixelSpan,
scalarDomain,
outerPadding,
null,
shouldClamp,
);
bestTickCount = maxTicks === 0
? 0
: getAmountOfTicksByDensity(Math.floor((pixelSpan - outerPadding) / minOrdinalRectThickness), density);
}
else if (isScalar && dataType.dateTime) {
// Use of a linear scale, instead of a D3.time.scale, is intentional since we want
// to control the formatting of the time values, since d3"s implementation isn"t
// in accordance to our design.
// scalarDomain: should already be in long-int time (via category.values[0].getTime())
scale = createLinearScale(pixelSpan, scalarDomain, outerPadding, null, shouldClamp); // DO NOT PASS TICKCOUNT
bestTickCount = maxTicks === 0 ? 0
: getAmountOfTicksByDensity((Math.max(
MinAmountOfTicksForDates,
(pixelSpan - outerPadding) / minOrdinalRectThickness)), density);
bestTickCount = bestTickCount < MinAmountOfTicksForDates
? MinAmountOfTicksForDates
: bestTickCount;
}
else if (dataType.text || dataType.dateTime || dataType.numeric || dataType.bool) {
scale = createPointScale(pixelSpan, scalarDomain);
bestTickCount = maxTicks === 0
? 0
: getAmountOfTicksByDensity((Math.min(
scalarDomain.length,
(pixelSpan - outerPadding) / minOrdinalRectThickness)), density);
}
}
// vertical ordinal axis (e.g. categorical bar chart) does not need to reverse
if (isVertical && isScalar) {
(scale as ScaleLinear<number, number>).range(scale.range().reverse()); // TODO: check it
}
normalizeInfinityInScale(scale as ScaleLinear<number, number>);
return {
bestTickCount,
scale: scale as ScaleLinear<number, number>,
usingDefaultDomain,
};
}