Repository URL to install this package:
|
Version:
2.4.3 ▾
|
import * as palettes from "./palettes";
import { is_dark, color2rgba } from "../core/util/color";
import { zip, unzip, sum, cumsum, copy, transpose } from "../core/util/array";
import { isArray } from "../core/util/types";
import { sprintf } from "../core/util/templating";
import { Plot, ColumnDataSource, GlyphRenderer, HoverTool, AnnularWedge, Quad, Text, DataRange1d, FactorRange, LinearAxis, CategoricalAxis, LinearScale, CategoricalScale, BasicTickFormatter, NumeralTickFormatter, } from "./models";
function resolve_palette(palette = "Spectral11") {
return isArray(palette) ? palette : palettes[palette];
}
export function pie(data, opts = {}) {
const labels = [];
const values = [];
for (let i = 0; i < Math.min(data.labels.length, data.values.length); i++) {
if (data.values[i] > 0) {
labels.push(data.labels[i]);
values.push(data.values[i]);
}
}
const start_angle = opts.start_angle != null ? opts.start_angle : 0;
const end_angle = opts.end_angle != null ? opts.end_angle : (start_angle + 2 * Math.PI);
const angle_span = Math.abs(end_angle - start_angle);
const to_radians = (x) => angle_span * x;
const total_value = sum(values);
const normalized_values = values.map((v) => v / total_value);
const cumulative_values = cumsum(normalized_values);
const end_angles = cumulative_values.map((v) => start_angle + to_radians(v));
const start_angles = [start_angle].concat(end_angles.slice(0, -1));
const half_angles = zip(start_angles, end_angles).map(([start, end]) => (start + end) / 2);
let cx;
let cy;
if (opts.center == null) {
cx = 0;
cy = 0;
}
else if (isArray(opts.center)) {
cx = opts.center[0];
cy = opts.center[1];
}
else {
cx = opts.center.x;
cy = opts.center.y;
}
const inner_radius = opts.inner_radius != null ? opts.inner_radius : 0;
const outer_radius = opts.outer_radius != null ? opts.outer_radius : 1;
const palette = resolve_palette(opts.palette);
const colors = [];
for (let i = 0; i < normalized_values.length; i++)
colors.push(palette[i % palette.length]);
const text_colors = colors.map((c) => is_dark(color2rgba(c)) ? "white" : "black");
function to_cartesian(r, alpha) {
return [r * Math.cos(alpha), r * Math.sin(alpha)];
}
const half_radius = (inner_radius + outer_radius) / 2;
let [text_cx, text_cy] = unzip(half_angles.map((half_angle) => to_cartesian(half_radius, half_angle)));
text_cx = text_cx.map((x) => x + cx);
text_cy = text_cy.map((y) => y + cy);
const text_angles = half_angles.map((a) => {
if (a >= Math.PI / 2 && a <= 3 * Math.PI / 2)
return a + Math.PI;
else
return a;
});
const source = new ColumnDataSource({
data: {
labels,
values,
percentages: normalized_values.map((v) => sprintf("%.2f%%", v * 100)),
start_angles,
end_angles,
text_angles,
colors,
text_colors,
text_cx,
text_cy,
},
});
const g1 = new AnnularWedge({
x: cx, y: cy,
inner_radius, outer_radius,
start_angle: { field: "start_angles" }, end_angle: { field: "end_angles" },
line_color: null, line_width: 1, fill_color: { field: "colors" },
});
const h1 = new AnnularWedge({
x: cx, y: cy,
inner_radius, outer_radius,
start_angle: { field: "start_angles" }, end_angle: { field: "end_angles" },
line_color: null, line_width: 1, fill_color: { field: "colors" }, fill_alpha: 0.8,
});
const r1 = new GlyphRenderer({
data_source: source,
glyph: g1,
hover_glyph: h1,
});
const g2 = new Text({
x: { field: "text_cx" }, y: { field: "text_cy" },
text: { field: opts.slice_labels ?? "labels" },
angle: { field: "text_angles" },
text_align: "center", text_baseline: "middle",
text_color: { field: "text_colors" }, text_font_size: "12px",
});
const r2 = new GlyphRenderer({
data_source: source,
glyph: g2,
});
const xdr = new DataRange1d({ renderers: [r1], range_padding: 0.2 });
const ydr = new DataRange1d({ renderers: [r1], range_padding: 0.2 });
const plot = new Plot({ x_range: xdr, y_range: ydr });
plot.add_renderers(r1, r2);
const tooltip = "<div>@labels</div><div><b>@values</b> (@percentages)</div>";
const hover = new HoverTool({ renderers: [r1], tooltips: tooltip });
plot.add_tools(hover);
return plot;
}
export function bar(data, opts = {}) {
const column_names = data[0];
const row_data = data.slice(1);
const col_data = transpose(row_data);
const labels = col_data[0].map((v) => v.toString());
const columns = col_data.slice(1);
let yaxis = new CategoricalAxis();
let ydr = new FactorRange({ factors: labels });
let yscale = new CategoricalScale();
let xformatter;
if (opts.axis_number_format != null)
xformatter = new NumeralTickFormatter({ format: opts.axis_number_format });
else
xformatter = new BasicTickFormatter();
let xaxis = new LinearAxis({ formatter: xformatter });
let xdr = new DataRange1d({ start: 0 });
let xscale = new LinearScale();
const palette = resolve_palette(opts.palette);
const stacked = opts.stacked != null ? opts.stacked : false;
const orientation = opts.orientation != null ? opts.orientation : "horizontal";
const renderers = [];
if (stacked) {
const left = [];
const right = [];
for (let i = 0; i < columns.length; i++) {
const bottom = [];
const top = [];
for (let j = 0; j < labels.length; j++) {
const label = labels[j];
if (i == 0) {
left.push(0);
right.push(columns[i][j]);
}
else {
left[j] += columns[i - 1][j];
right[j] += columns[i][j];
}
bottom.push([label, -0.5]);
top.push([label, 0.5]);
}
const source = new ColumnDataSource({
data: {
left: copy(left),
right: copy(right),
top,
bottom,
labels,
values: columns[i],
columns: columns[i].map((_) => column_names[i + 1]),
},
});
const g1 = new Quad({
left: { field: "left" }, bottom: { field: "bottom" },
right: { field: "right" }, top: { field: "top" },
line_color: null, fill_color: palette[i % palette.length],
});
const r1 = new GlyphRenderer({ data_source: source, glyph: g1 });
renderers.push(r1);
}
}
else {
const dy = 1 / columns.length;
for (let i = 0; i < columns.length; i++) {
const left = [];
const right = [];
const bottom = [];
const top = [];
for (let j = 0; j < labels.length; j++) {
const label = labels[j];
left.push(0);
right.push(columns[i][j]);
bottom.push([label, i * dy - 0.5]);
top.push([label, (i + 1) * dy - 0.5]);
}
const source = new ColumnDataSource({
data: {
left,
right,
top,
bottom,
labels,
values: columns[i],
columns: columns[i].map((_) => column_names[i + 1]),
},
});
const g1 = new Quad({
left: { field: "left" }, bottom: { field: "bottom" },
right: { field: "right" }, top: { field: "top" },
line_color: null, fill_color: palette[i % palette.length],
});
const r1 = new GlyphRenderer({ data_source: source, glyph: g1 });
renderers.push(r1);
}
}
if (orientation == "vertical") {
[xdr, ydr] = [ydr, xdr];
[xaxis, yaxis] = [yaxis, xaxis];
[xscale, yscale] = [yscale, xscale];
for (const r of renderers) {
const data = r.data_source.data;
[data.left, data.bottom] = [data.bottom, data.left];
[data.right, data.top] = [data.top, data.right];
}
}
const plot = new Plot({ x_range: xdr, y_range: ydr, x_scale: xscale, y_scale: yscale });
plot.add_renderers(...renderers);
plot.add_layout(yaxis, "left");
plot.add_layout(xaxis, "below");
const tooltip = "<div>@labels</div><div>@columns: <b>@values</b></div>";
let anchor;
let attachment;
if (orientation == "horizontal") {
anchor = "center_right";
attachment = "horizontal";
}
else {
anchor = "top_center";
attachment = "vertical";
}
const hover = new HoverTool({
renderers,
tooltips: tooltip,
point_policy: "snap_to_data",
anchor,
attachment,
});
plot.add_tools(hover);
return plot;
}
//# sourceMappingURL=charts.js.map