Fill Outside a Shape

Is there a way to fill OUTSIDE a shape (or set of shapes). i.e. if the space is not contained in a shape then fill that space. I am generating maps of geographies and allowing them to be used to select physical mail delivery areas. When printing a specific area (say one of 30) then I can set all the others to filled (with say 0.5 alpha) so the area of import pops if it is surrounded by other (known) areas but if it has unmapped space on one boundary then I cannot set that to have a fill/alpha (because it is not filling a space).

From a logic perspective: instead of select all the pixels within this polygon I want to select all the pixels NOT within the polygon.

Alternatively I could pfaff around and make a super polygon on the display area (or earth perhaps) and put holes in it where I already have polygons (intersection).

I’m not entirely sure I understand your situation correctly, I’m especially unsure what kind of glyphs you are using on your plot as that could be limiting your options.

If you select your figures with the selection tools you should be able to get a list of them and make every figure not in the list opaque.

If that’s not an option you can do what I did (I worked with image-glyphs so selection was a no-no) which is pretty much the same as the alternative you mentioned:
Using a multi-polygon and creating holes for the selected area.

My selection-area is always rectangular, since I’m using the EditBoxTool so the code would be different for you. If you use a SelectionTool you could do the same by checking the bounds from the SelectionGeometry-Event

This is what I used, it’s ripped out of context and can’t be used as-is but it should be a decent start. If something is not clear (but you think the example could help) I’m happy to assist

Example
self.img_shadow = self.plot.multi_polygons(color=['grey'], alpha=0.4)

self.roi_renderer = self.plot.rect('x', 'y', 'width', 'height',
                                 source=ColumnDataSource(data=dict(x=[], y=[], width=[], height=[])),
                                 fill_alpha=0.0)

self.roi_renderer.data_source.on_change("data", lambda attr, old, new: __adjust_roi_shadow_overlay(new))

BoxEditTool(num_objects=1, renderers=[self.roi_renderer])  # add this to your plot

def __adjust_roi_shadow_overlay(self, roi_bounds):
    data = dict(self.img_shadow.data_source.data)
    # reset polygon bounds
    data['xs'] = [[[]]]
    data['ys'] = [[[]]]

    # if roi was not deleted (has a height)
    if roi_bounds['height']:
        sx = self.img.data_source.data['dw']
        sy = self.img.data_source.data['dh']
        img_poly_xs = [0, sx, sx, 0]
        img_poly_ys = [0, 0, sy, sy]

        x_start = roi_bounds['x'][0] - 0.5 * roi_bounds['width'][0]
        x_end = roi_bounds['x'][0] + 0.5 * roi_bounds['width'][0]
        y_start = roi_bounds['y'][0] - 0.5 * roi_bounds['height'][0]
        y_end = roi_bounds['y'][0] + 0.5 * roi_bounds['height'][0]
        hole_xs = [x_start, x_end, x_end, x_start]
        hole_ys = [y_start, y_start, y_end, y_end]
        data['xs'][0][0].extend((img_poly_xs, hole_xs))
        data['ys'][0][0].extend((img_poly_ys, hole_ys))
        self.img_shadow.visible = True

    else:  # roi deleted
        # necessary hack to not reset fill_color
        data['xs'][0][0].append([0])
        data['ys'][0][0].append([0])
        self.img_shadow.visible = False

    self.img_shadow.data_source.data = dict(data)

There is definitely not any “inverted” box or polygon annotation built-in to Bokeh. As far as I can recall, you are the first person to ask about it. Please feel free to submit a GitHub Issue to suggest it for future feature development.