UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

171 lines 6.9 kB
import { Axis, AxisView } from "./axis"; import { CategoricalTicker } from "../tickers/categorical_ticker"; import { CategoricalTickFormatter } from "../formatters/categorical_tick_formatter"; import * as mixins from "../../core/property_mixins"; import { LabelOrientation } from "../../core/enums"; import { GraphicsBoxes, TextBox } from "../../core/graphics"; import { isString } from "../../core/util/types"; export class CategoricalAxisView extends AxisView { static __name__ = "CategoricalAxisView"; _hit_value(sx, sy) { const [range] = this.ranges; const { start, end, span } = range; switch (this.dimension) { case 0: { const { x0, width } = this.bbox; return range.factor(span * (sx - x0) / width + start); } case 1: { const { y0, height } = this.bbox; return range.factor(end - span * (sy - y0) / height); } } } _paint(ctx) { const { tick_coords, extents } = this; super._paint(ctx); this._draw_group_separators(ctx, extents, tick_coords); } _draw_group_separators(ctx, _extents, _tick_coords) { const [range] = this.ranges; const [start, end] = this.computed_bounds; const { factors } = range; const { tops } = range.mapper; if (tops == null || tops.length < 2 || !this.visuals.separator_line.doit) { return; } const dim = this.dimension; const alt = 1 - dim; const coords = [[], []]; let ind = 0; for (let i = 0; i < tops.length - 1; i++) { let first, last; for (let j = ind; j < factors.length; j++) { if (factors[j][0] == tops[i + 1]) { [first, last] = [factors[j - 1], factors[j]]; ind = j; break; } } const pt = (range.synthetic(first) + range.synthetic(last)) / 2; if (pt > start && pt < end) { coords[dim].push(pt); coords[alt].push(this.loc); } } const tex = this.extents.tick_label; this._draw_ticks(ctx, coords, -3, tex - 6, this.visuals.separator_line); } _draw_major_labels(ctx, extents, _tick_coords) { const info = this._get_factor_info(); let standoff = extents.tick + this.model.major_label_standoff; for (let i = 0; i < info.length; i++) { const [labels, coords, orient, visuals] = info[i]; this._draw_oriented_labels(ctx, labels, coords, orient, standoff, visuals); standoff += extents.tick_labels[i]; } } _tick_label_extents() { const info = this._get_factor_info(); const extents = []; for (const [labels, , orient, visuals] of info) { const extent = this._oriented_labels_extent(labels, orient, this.model.major_label_standoff, visuals); extents.push(extent); } return extents; } _get_factor_info() { const [range] = this.ranges; const [start, end] = this.computed_bounds; const loc = this.loc; const ticks = this.model.ticker.get_ticks(start, end, range, loc); const coords = this.tick_coords; const info = []; const map = (labels) => { return new GraphicsBoxes(labels.map((label) => isString(label) ? new TextBox({ text: label }) : label)); }; const format = (ticks) => { return map(this.model.formatter.doFormat(ticks, this)); }; switch (range.mapper.levels) { case 1: { const major = ticks.major; const labels = format(major); info.push([labels, coords.major, this.model.major_label_orientation, this.visuals.major_label_text]); break; } case 2: { const major = ticks.major.map((x) => x[1]); const labels = format(major); info.push([labels, coords.major, this.model.major_label_orientation, this.visuals.major_label_text]); info.push([map(ticks.tops), coords.tops, this.model.group_label_orientation, this.visuals.group_text]); break; } case 3: { const major = ticks.major.map((x) => x[2]); const labels = format(major); const mid_labels = ticks.mids.map((x) => x[1]); info.push([labels, coords.major, this.model.major_label_orientation, this.visuals.major_label_text]); info.push([map(mid_labels), coords.mids, this.model.subgroup_label_orientation, this.visuals.subgroup_text]); info.push([map(ticks.tops), coords.tops, this.model.group_label_orientation, this.visuals.group_text]); break; } } return info; } get tick_coords() { const i = this.dimension; const j = 1 - i; const [range] = this.ranges; const [start, end] = this.computed_bounds; const ticks = this.model.ticker.get_ticks(start, end, range, this.loc); const coords = { major: [[], []], mids: [[], []], tops: [[], []], minor: [[], []], }; coords.major[i] = ticks.major; coords.major[j] = ticks.major.map(() => this.loc); const { levels } = range.mapper; if (levels == 3) { coords.mids[i] = ticks.mids; coords.mids[j] = ticks.mids.map(() => this.loc); } if (levels > 1) { coords.tops[i] = ticks.tops; coords.tops[j] = ticks.tops.map(() => this.loc); } return coords; } } export class CategoricalAxis extends Axis { static __name__ = "CategoricalAxis"; constructor(attrs) { super(attrs); } static { this.prototype.default_view = CategoricalAxisView; this.mixins([ ["separator_", mixins.Line], ["group_", mixins.Text], ["subgroup_", mixins.Text], ]); this.define(({ Float, Or }) => ({ group_label_orientation: [Or(LabelOrientation, Float), "parallel"], subgroup_label_orientation: [Or(LabelOrientation, Float), "parallel"], })); this.override({ ticker: () => new CategoricalTicker(), formatter: () => new CategoricalTickFormatter(), separator_line_color: "lightgrey", separator_line_width: 2, group_text_font_style: "bold", group_text_font_size: "11px", group_text_color: "grey", subgroup_text_font_style: "bold", subgroup_text_font_size: "11px", }); } } //# sourceMappingURL=categorical_axis.js.map