Pixel dimensions of paper coordinates

Trying to get, e.g., two annotations to lay one next to/above/below the other on a figure using xref=‘paper’, but (0,0) and (1,1) in the paper domain correspond to the plottable area, not the entire figure. Since layout.height and layout.width set the pixel size of the figure, I need to figure out how to back out the actual pixel dimensions of the paper coordinates. I tried subtracting out the margins, but that’s still not quite right (see code and image below).

Anyone know how I can get the exact pixel dimensions of the “paper” domain?

annotation

ann_width=80
ann_height=20
left_px = 50
right_px = 50
top_px = 50
bottom_px = 50
W_px = 800
H_px = 600

ann_1 = go.layout.Annotation(
    text='TEXT_A',
    x=0,
    y=0,
    xref='paper',
    yref='paper',
    bordercolor='black',
    borderwidth=1,
    height=ann_height,
    width=ann_width,
    showarrow=False
)

ann_2 = go.layout.Annotation(
    text='TEXT_B',
    x=ann_width/(W_px-left_px-right_px),
    y=0,
    xref='paper',
    yref='paper',
    bordercolor='black',
    borderwidth=1,
    height=ann_height,
    width=ann_width,
    showarrow=False
)

fig_layout = go.Layout(
    width=W_px,
    height=H_px,
    margin=dict(
        l=left_px,
        r=right_px,
        t=top_px,
        b=bottom_px,
    ),
    annotations=[ann_1,ann_2]
)

fig = go.Figure(layout=fig_layout)
fig.show()

HI @gwurman welcome to the forums.

You are pretty close, but I think you have to include the border width of the annotations. Not sure if this is 100% correct though.

import plotly.graph_objects as go

ann_width=80
ann_height=20
left_px = 50
right_px = 50
top_px = 50
bottom_px = 50
W_px = 1800
H_px = 800
total = W_px-left_px-right_px
border_ann_1 = 1
border_ann_2 = 1

ann_1 = go.layout.Annotation(
    text='TEXT_A',
    x=0,
    y=0,
    xref='paper',
    yref='paper',
    bordercolor='black',
    borderwidth=border_ann_1,
    height=ann_height,
    width=ann_width,
    showarrow=False
)

ann_2 = go.layout.Annotation(
    text='TEXT_B',
    x=(ann_width+2*(border_ann_1+border_ann_2))/total,
    y=0,
    xref='paper',
    yref='paper',
    bordercolor='black',
    borderwidth=border_ann_2,
    height=ann_height,
    width=ann_width,
    showarrow=False
)

fig_layout = go.Layout(
    width=W_px,
    height=H_px,
    margin=dict(
        l=left_px,
        r=right_px,
        t=top_px,
        b=bottom_px,
    ),
    annotations=[ann_1,ann_2]
)

fig = go.Figure(layout=fig_layout)
fig.show()

Thanks, that was exactly what I was missing. However, this is almost right. With a little playing around, I was able to figure out that the augmented annotation width should be:

aug_ann_width = ann_width + 2 * (border_width + 1)

This is assuming the annotations all have the same border width, which mine do. For a 1-pixel border the solutions are the same.

Great, glad you figured it out. Would you mind posting your full working MRE? I imagine this could be quite helpful.

Hah! Be careful what you ask for! MRE is below. In the process of testing it I found a bug (try ncols>=4 or nrows>=8), which I will post as a new thread after I explore it a bit more.

import plotly.graph_objects as go

ann_width = 80
ann_height = 20
border_px = 2
aug_ann_height = ann_height + 2 * (border_px + 1)
aug_ann_width = ann_width + 2 * (border_px + 1)

left_px = 50
right_px = 50
top_px = 50
bottom_px = 50
W_px = 800
H_px = 600
reduced_W = W_px - left_px - right_px
reduced_H = H_px - top_px - bottom_px

nrows = 2
ncols = 2

annot_list = []
for row in range(nrows):
    for col in range(ncols):
        annot = go.layout.Annotation(
            text = f'ANN_{len(annot_list)}',
            x = col * (aug_ann_width / reduced_W),
            y = row * (aug_ann_height / reduced_H),
            xref = 'paper',
            yref = 'paper',
            bordercolor = 'black',
            borderwidth = border_px,
            height = ann_height,
            width = ann_width,
            showarrow=False
        )

        annot_list.append(annot)


fig_layout = go.Layout(
    width = W_px,
    height = H_px,
    margin = dict(
        l = left_px,
        r = right_px,
        t = top_px,
        b = bottom_px,
    ),
    annotations = annot_list
)

fig = go.Figure(layout=fig_layout)
fig.show()
1 Like

Upon further investigation, it is not a bug, but rather an artifact of how annotations are aligned in paper coordinates.

Annotations with x on [0,1/3) will be aligned on the left edge of the annotation. With x on (2/3,1] they will be aligned on the right edge, and on [1/3,2/3] they will be aligned on center. Similar behavior in the y coordinate.

Caveat coder :grin:

1 Like