Skip to content Skip to sidebar Skip to footer

Bokeh Circle Does Not Fit Into Square?

I am plotting some geometry using bokeh and came across this. I am plotting a rectangle with equal sides (i.e. a square), and in that square, plotting a circle with diameter = wi

Solution 1:

I would like to report that as of Bokeh 0.12.7, this issue can now be fixed in a simpler manner.

As described in other posts, the main issue is not that the circle is not a circle, but that the square is not a square. This is due to the fact that actual area on which Bokeh draws the figure (the canvas) is usually not a square by default or even when the width and height are set to the same value. Bokeh by default will attempt to draw a figure by using up all the space on the canvas. This creates a mismatch between the data distance and the pixel distance of the plot.

As of 0.12.7, figures can now accept a match_aspect property which when set to True will will match the aspect of the data space to the pixel space of the plot.

In your example, simply adding the match_aspect = True in your figure

p = figure(width=500, height=500, match_aspect=True, 
           title="Circle touches all 4 sides of square")
p.rect(0, 0, 300, 300, line_color='black')
p.circle(x=0, y=0, radius=150, line_color='black', 
         fill_color='grey',  radius_units='data')

will now produce

Circle touches all 4 sides of the square


Solution 2:

UPDATE: Please note new answer by @DuCorey below. As of Bokeh 0.12.7, aspect control is now available, for situations like this.


The issue is actually that the square is not square, and that is because the pixel aspect ratio and the "data" aspect ratio do not match. i.e., the distance per pixel is different in the x direction than it is in the y direction.

There are a few options:

  • You can use various properties to control the dimensions of the central plot area (e.g. plot border width and axis tick label orientation) You can also control you data ranges explicitly. In other words, you can make the aspect ratios match, and then the circle and rect will match

  • You can use absolute pixel units (e.g. size for a circle, and use a large square marker instead of rect) instead of "data" units.

Alternatively, if you want a circle that "deforms" when the aspects do not match, then your best bet is to use an ellipse with an identical width and height, which will work because in this case bokeh has two dimensions to use to measure (instead of the single radius) and can match each to the scale along each dimension independently.

(This is actually the fundamental difference that explains the behaviour: rect has two dimensions to measure independently. circle does not, it only has one, and has to arbitrarily use the x or y dimension to measure distance per pixel)


Solution 3:

ok, based on the suggestions, I tried a few things.

  • Changed the orientation of the y-axis tick labels - still had issue.
  • Changed various stand-offs, even moving in the tick labels inside the plot (with a negative offset). Did not work either.
  • Changed the x_range and r_range in figure() to be equal tuples. Did not work either
  • Changes the plot_height (decreased it), and I could eventually, through rial and error, get the circle to fit in the square with a plot_height that was < plot width.

Lots of great practice controlling attributes of the plot. Time will invested.

However, the last change I tried worked the best. It was one of the first suggestions - change the plot border.

Weirdly, setting p.min_border=40, which on 0.12.6 is the default value, and voila, it appears the chart aspect ratio for a chart where plot_width=plot_height is truly 1 on the screen as well.

p = figure(plot_width=500, plot_height=500)

p.rect(0, 0, 300, 300, line_color=None)

p.circle(x=0, y=0, radius=150, line_color=None, 
         fill_color='lightgrey',  radius_units='data')

p.min_border=40

show(p)

Before and after images showing the effect of adding p.min_border=40. Any value over ~33 appeared to be enough force the plot area to have the same screen x and y dimension - so the square was really a square (and the circle fit inside).

enter image description here

enter image description here


Solution 4:

Not sure, but the bleu rectangle is not your rectangle.

Replace:

p.rect(0, 0, 300, 300, line_color='black')

By:

p.rect(-150, -150, 150, 150, line_color='black')

Solution 5:

The reason for this is that you're creating a circular marker (or circle glyphs) and placing it at position (0, 0), while it seems like you want to create a circle centered at 0.

I think the rect here "happens" to work because it can scale correctly in both dimensions and remain a "rectangle".

Keyword Args:
    radius (UnitsSpecPropertyDescriptor) : The radius values for circle markers (in "data space" units, by default). (default None)
    radius_dimension (BasicPropertyDescriptor) : What dimension to measure circle radii along. (default 'x')
    radius_units (Enum('screen', 'data')) :  (default 'data')

I guess my point is here you've taken a shortcut by trying to use a "glyph" as your plot and specifying the units to be the data units.

If you want to create an actual circle you could do the following:

th = np.linspace(0, 2*np.pi)

r = 150

p = figure(width=500, height=500)

p.rect(0, 0, 300, 300, line_color='black')

p.line(r * np.cos(th), r * np.sin(th), line_color='black')
# p.circle(x=0, y=0, radius=150, line_color='black',
#         fill_color='grey', radius_units='data')

p.axis.minor_tick_out = 0

show(p)

Notice the above is harder to fill (I didn't bother) because I'm guessing you need to define some closed polygon function while I only defined a line that happens to be a closed polygon, in this case a circle.


Post a Comment for "Bokeh Circle Does Not Fit Into Square?"