@bokeh/bokehjs
Version:
Interactive, novel data visualization
171 lines • 6.9 kB
JavaScript
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