I am trying to make a custom annotation through a TypeScript extension. My annotation is a combination of a horizontal bar with alternating colors and horizontal line. Everything in screen units. The figure below shows what I am aiming for (this has been created with the patches
and line
glyphs).
In the TypeScript code I make 3 arrays: bar_x
and bar_y
(similar to list of lists) and one array for the alternating colors (the colors are defined within the code, not passed as a property).
This gives an error in the terminal because of the wrong assignment of color (in the code: this.model.fill_color = scale_color
).
How to specify fill_color
ColorSpec property? How can I easily convert an array of colors eg ['blue', 'orange', 'blue', 'orange', 'blue']
to the correct ColorSpec format?
Compilation failed:
bar.ts:38:5 - error TS2322: Type 'string[]' is not assignable to type 'Color | Vector<Color | null> | null'.
Type 'string[]' is not assignable to type '[R: number, G: number, B: number, A?: number | undefined]'.
Target requires 3 element(s) but source may have fewer.
38 this.model.fill_color = scale_color
~~~~~~~~~~~~~~~~~~~~~
Also, how do I handle multiple annotations in the same Class
in TS? Is there not a risk of mixing properties between the bar and line I try to make?
This is my very first attempt into more advanced extensions so please comment if my approach is completely wrong. I am here to learn.
python code
from bokeh.models import Annotation
from bokeh.plotting import figure, output_file, save
from bokeh.layouts import row
output_file("bar.html", title="Annotation extension")
class Bar(Annotation):
''' Render a shaded polygonal region as an annotation.
See :ref:`userguide_annotations_polygon_annotations` for information on
plotting polygon annotations.
'''
__implementation__ = 'bar.ts'
xs = []
[xs.append([-1+i,0+i, 0+i, -1+i]) for i in range(0,5)]
ys = [[0, 0, 5, 5]]*5
color1 = 'blue'
color2 = 'orange'
color = [color1, color2, color1, color2, color1]
print(color)
# range bounds supplied in web mercator coordinates
p = figure(plot_height = 300, x_range=(-2, 6), y_range=(-10, 70))
p.patches(xs = xs, ys = ys, fill_color = color)
p.line(x = [-1, 4], y = [9, 9], line_color = 'black', line_width = 3)
bar = Bar()
p.add_layout(bar)
save(row(p))
bar.ts
import {Annotation, AnnotationView} from "models/annotations/annotation"
import * as mixins from "core/property_mixins"
import * as visuals from "core/visuals"
import * as p from "core/properties"
export class BarView extends AnnotationView {
override model: Bar
override visuals: Bar.Visuals
protected _render(): void {
const {frame} = this.plot_view
const {ctx} = this.layer
// use screen units for the drawing of the bar and line
const x_offset = 15
const y_offset = 15
const dy = 7
const sub_bar = 25
const color1 = 'blue'
const color2 = 'orange'
const scale_color = [color1, color2, color1, color2, color1]
var bar_x = []
var bar_y = []
const y0 = y_offset
const y1 = y_offset + dy
for (var i = 0; i < 5; i++) {
var x0 = Math.round(x_offset + frame.bbox.left + sub_bar * i)
var x1 = Math.round(x_offset + frame.bbox.left + sub_bar * (i+1))
bar_x.push([x0, x1, x1, x0])
bar_y.push([y0, y0, y1, y1])
}
// How do I specify the color array for this.visuals.fill????
this.model.fill_color = scale_color
// draw bar's
for (let i = 0; i < bar_x.length; i++) {
ctx.beginPath()
const sx_i = bar_x[i]
const sy_i = bar_y[i]
for (let j = 0; j < sx_i.length; j++) {
const sx_j = sx_i[j]
const sy_j = sy_i[j]
if (j === 0) {
ctx.moveTo(sx_j, sy_j)
} else {
ctx.lineTo(sx_j, sy_j)
}
}
ctx.closePath()
this.visuals.fill.apply(ctx, i)
}
// draw line
ctx.beginPath()
ctx.lineTo(x_offset + frame.bbox.left, 35)
ctx.lineTo(x_offset + frame.bbox.left+70, 35)
ctx.closePath
// How do I make sure line properties for the line are not the same as the line for the bar?
this.model.fill_color = 'black'
this.visuals.line.apply(ctx)
}
}
export namespace Bar {
export type Attrs = p.AttrsOf<Props>
export type Props = Annotation.Props & Mixins
export type Mixins = mixins.Line & mixins.FillVector
export type Visuals = Annotation.Visuals & {line: visuals.Line, fill: visuals.FillVector}
}
export interface Bar extends Bar.Attrs {}
export class Bar extends Annotation {
override properties: Bar.Props
override __view_type__: BarView
constructor(attrs?: Partial<Bar.Attrs>) {
super(attrs)
}
static {
this.prototype.default_view = BarView
this.mixins<Bar.Mixins>([mixins.Line, mixins.FillVector])
}
}
System info
Python version : 3.9.12 (main, Mar 26 2022, 15:52:10)
IPython version : (not installed)
Tornado version : 6.1
Bokeh version : 2.4.2
BokehJS static path : /.venv/lib/python3.9/site-packages/bokeh/server/static
node.js version : v16.14.2
npm version : 8.5.0
Operating system : macOS-11.6-x86_64-i386-64bit