Long running DynamicMap callables


I am trying to run a bokeh server with a holoviews dynamicmap generated from a callable. This callable generates an overlay of geoviews layers (Contour and Polygons) along with a tile source base map. It currently works, however is very slow so I am trying to find ways to optimise this. The layers generate fairly fast, however the actual rendering is where the majority of the time gets spent. I’m not really sure where this occurs within holoviews/bokeh code though. The dynamicmap gets updated using a RangeXY stream attached to it, when the user moves the map to a new location or otherwise changes the view bounds.

Inside init:

self._current_plot = DynamicMap(self.compose_overlay_plot, streams=self.callback_streams)
self.server = panel.serve(self._current_plot, start=False, show=False)

This is the callable:

    def compose_overlay_plot(self, x_range: Optional[Tuple[float, float]] = (-1.6, -1.2),
                         y_range: Optional[Tuple[float, float]] = (50.8, 51.05)) \
        -> Union[Overlay, Element, DynamicMap]:
    Compose all generated HoloViews layers in self.layers into a single overlay plot.
    Overlaid in a first-on-the-bottom manner.

    If plot bounds has moved outside of data bounds, generate more as required.

    :param tuple x_range: (min, max) longitude range in EPSG:4326 coordinates
    :param tuple y_range: (min, max) latitude range in EPSG:4326 coordinates
    :returns: overlay plot of stored layers
    if not is_null(x_range) and not is_null(y_range):
        # Construct box around requested bounds
        bounds_poly = make_bounds_polygon(x_range, y_range)
        # Ensure bounds are small enough to render without OOM or heat death of universe
        if bounds_poly.area < 0.2:
            # If new bounds are contained within existing bounds do nothing
            # as polygons are already rendered
            # If rasterising layers this must be called each map update to avoid loss of raster resolution
            if self.rasterise or not self._current_bounds.contains(bounds_poly):
                t0 = time()
                self._current_bounds = bounds_poly
                self._progress_callback("Rendering new map...")
                print("Generated all layers in ", time() - t0)
            self._progress_callback('Area too large to render')
    t0 = time()
    plot = Overlay(list(self._generated_layers.values()))

    print("Overlaid plots in ", time() - t0)
    return plot.opts(width=self.plot_size[0], height=self.plot_size[1],
                     tools=self.tools, active_tools=self.active_tools)

This is inside an app that is basically a client-server contained in one PyQt app. The plot gets displayed in a QWebEngineView.

Are there any suggestions on how else to do this?