def setUp(self):
     self.dmap_element = DynamicMap(lambda: Image([]))
     self.dmap_overlay = DynamicMap(lambda: Overlay([Curve([]), Points([])]))
     self.dmap_ndoverlay = DynamicMap(lambda: NdOverlay({0: Curve([]), 1: Curve([])}))
     self.element = Scatter([])
     self.el1, self.el2 = Path([]), HLine(0)
     self.overlay = Overlay([self.el1, self.el2])
     self.ndoverlay = NdOverlay({0: VectorField([]), 1: VectorField([])})
    def test_ndlayout_overlay_ndlayout_partial_reverse(self):
        items = [(0, self.view1), (1, self.view2), (3, self.view2)]
        grid = NdLayout(items, 'X')
        items2 = [(0, self.view2), (2, self.view2), (3, self.view3)]
        grid2 = NdLayout(items2, 'X')

        expected_items = [(0, Overlay([self.view2, self.view1])),
                          (1, Overlay([self.view2])),
                          (2, Overlay([self.view2])),
                          (3, Overlay([self.view3, self.view2]))]
        expected = NdLayout(expected_items, 'X')
        self.assertEqual(grid2 * grid, expected)
    def test_gridspace_overlay_gridspace_partial_reverse(self):
        items = [(0, self.view1), (1, self.view2), (3, self.view2)]
        grid = GridSpace(items, 'X')
        items2 = [(0, self.view2), (2, self.view2), (3, self.view3)]
        grid2 = GridSpace(items2, 'X')

        expected_items = [(0, Overlay([self.view2, self.view1])),
                          (1, Overlay([self.view2])),
                          (2, Overlay([self.view2])),
                          (3, Overlay([self.view3, self.view2]))]
        expected = GridSpace(expected_items, 'X')
        self.assertEqual(grid2 * grid, expected)
Esempio n. 4
0
 def __init__(self, **params):
     self._session = params.get("session")
     self.title = params.get("viz_instance").title
     self.overlays = Overlay()
     self.traces = []
     self.tracesParam = []
     self.id = params.get("viz_instance").id
     self.dictParameters = self.get_param_values()
     self.configTracePanel = pn.Column()
     self.configVizPanel = pn.Column()
     self.debugPanel = pn.Column()
     self.silently = False
     super(BaseVizApp, self).__init__(**params)
Esempio n. 5
0
    def test_overlay_options_complete(self):
        """
        Complete specification style.
        """
        data = [zip(range(10), range(10)), zip(range(5), range(5))]
        o = Overlay([Curve(c) for c in data])({
            'Curve.Curve': {
                'plot': {
                    'show_grid': True
                },
                'style': {
                    'color': 'b'
                }
            }
        })

        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.I,
                                 'plot').kwargs['show_grid'], True)
        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.II,
                                 'plot').kwargs['show_grid'], True)
        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.I,
                                 'style').kwargs['color'], 'b')
        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.II,
                                 'style').kwargs['color'], 'b')
Esempio n. 6
0
    def test_overlay_options_partitioned(self):
        """
        The new style introduced in #73
        """
        data = [zip(range(10), range(10)), zip(range(5), range(5))]
        o = Overlay([Curve(c) for c in data
                     ])(dict(plot={'Curve.Curve': {
                         'show_grid': False
                     }},
                             style={'Curve.Curve': {
                                 'color': 'k'
                             }}))

        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.I,
                                 'plot').kwargs['show_grid'], False)
        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.II,
                                 'plot').kwargs['show_grid'], False)
        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.I,
                                 'style').kwargs['color'], 'k')
        self.assertEqual(
            Store.lookup_options('matplotlib', o.Curve.II,
                                 'style').kwargs['color'], 'k')
 def test_interleaved_overlay(self):
     """
     Test to avoid regression after fix of https://github.com/ioam/holoviews/issues/41
     """
     o = Overlay([
         Curve(np.array([[0, 1]])),
         Scatter([[1, 1]]),
         Curve(np.array([[0, 1]]))
     ])
     OverlayPlot(o)
Esempio n. 8
0
 def test_overlay_from_values_retains_custom_path_with_label(self):
     overlay = Overlay([('Custom', self.el6)])
     paths = Overlay.from_values([overlay, self.el2]).keys()
     self.assertEqual(paths, [('Custom', 'LabelA'), ('Element', 'I')])
Esempio n. 9
0
 def test_overlay_constructor2(self):
     t = Overlay([self.el8])
     self.assertEqual(t.keys(), [('ValA', 'LabelB')])
    def compose_overlay_plot(self, x_range: Optional[Sequence[float]] = (-1.6, -1.2),
                             y_range: Optional[Sequence[float]] = (50.8, 51.05)) \
            -> Union[Overlay, Element]:
        """
        Compose all generated HoloViews layers in self.data_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
        """
        from itertools import chain

        try:
            if not self._preload_complete:
                # If layers aren't preloaded yet just return the map tiles
                self._progress_callback('Still preloading layer data...')
                plot = self._base_tiles
            else:
                # Construct box around requested bounds
                bounds_poly = make_bounds_polygon(x_range, y_range)
                raster_shape = self._get_raster_dimensions(
                    bounds_poly, self.raster_resolution_m)
                # Ensure bounds are small enough to render without OOM or heat death of universe
                if bounds_poly.area < 0.2:
                    from time import time

                    t0 = time()
                    self._progress_bar_callback(10)
                    self.generate_layers(bounds_poly, raster_shape)
                    self._progress_bar_callback(50)
                    plot = Overlay([
                        res[0] for res in self._generated_data_layers.values()
                    ])
                    print("Generated all layers in ", time() - t0)
                    if self.annotation_layers:
                        import matplotlib.pyplot as mpl
                        plot = Overlay([
                            res[0]
                            for res in self._generated_data_layers.values()
                        ])
                        raw_datas = [
                            res[2]
                            for res in self._generated_data_layers.values()
                        ]
                        raster_indices = dict(
                            Longitude=np.linspace(x_range[0],
                                                  x_range[1],
                                                  num=raster_shape[0]),
                            Latitude=np.linspace(y_range[0],
                                                 y_range[1],
                                                 num=raster_shape[1]))
                        raster_grid = np.zeros(
                            (raster_shape[1], raster_shape[0]),
                            dtype=np.float64)
                        for res in self._generated_data_layers.values():
                            layer_raster_grid = res[1]
                            if layer_raster_grid is not None:
                                nans = np.isnan(layer_raster_grid)
                                layer_raster_grid[nans] = 0
                                raster_grid += layer_raster_grid
                        raster_grid = np.flipud(raster_grid)
                        raster_indices['Latitude'] = np.flip(
                            raster_indices['Latitude'])

                        self._progress_callback('Annotating Layers...')
                        res = jl.Parallel(
                            n_jobs=1, verbose=1, backend='threading')(
                                jl.delayed(layer.annotate)(raw_datas, (
                                    raster_indices, raster_grid))
                                for layer in self.annotation_layers)

                        annotation_overlay = Overlay(
                            [annot for annot in res if annot is not None])
                        plot = Overlay(
                            [self._base_tiles, plot,
                             annotation_overlay]).collate()
                    else:
                        plot = Overlay([self._base_tiles, plot]).collate()
                    self._progress_bar_callback(90)

                else:
                    self._progress_callback('Area too large to render!')
                    if not self._generated_data_layers:
                        plot = self._base_tiles
                    else:
                        plot = Overlay([
                            self._base_tiles,
                            *list(self._generated_data_layers.values())
                        ])

                layers = []
                for layer in chain(self.data_layers, self.annotation_layers):
                    d = {'key': layer.key}
                    if hasattr(layer, '_colour'):
                        d.update(colour=layer._colour)
                    if hasattr(layer, '_osm_tag'):
                        d.update(dataTag=layer._osm_tag)
                    layers.append(d)

                self._progress_callback("Rendering new map...")
                self._update_callback(
                    list(chain(self.data_layers, self.annotation_layers)))

        except Exception as e:
            # Catch-all to prevent plot blanking out and/or crashing app
            # Just display map tiles in case this was transient
            import traceback
            traceback.print_exc()
            print(e)
            plot = self._base_tiles
        return plot.opts(width=self.plot_size[0],
                         height=self.plot_size[1],
                         tools=self.tools,
                         active_tools=self.active_tools)
Esempio n. 11
0
 def overlaypairs(self, pairs):
     "returns an overlay to display the pairings"
     return Overlay([
         Curve([self._toxy(f1), self._toxy(f2)])
         for i, (f1, f2) in enumerate(pairs)
     ])
Esempio n. 12
0
 def test_overlay_group(self):
     t1 = (self.el1 * self.el2)
     t2 = Overlay(list(t1.relabel(group='NewValue')))
     self.assertEqual(t2.keys(), [('NewValue', 'I'), ('NewValue', 'II')])
Esempio n. 13
0
 def test_overlay_from_values1(self):
     t = Overlay.from_values(self.el1)
     self.assertEqual(t.keys(), [('Element', 'I')])
Esempio n. 14
0
 def test_overlay_from_values2(self):
     t = Overlay.from_values(self.el8)
     self.assertEqual(t.keys(), [('ValA', 'LabelB')])
Esempio n. 15
0
 def test_overlay_from_values_retains_custom_path_with_label(self):
     overlay = Overlay([('Custom', self.el6)])
     paths = Overlay.from_values([overlay, self.el2]).keys()
     self.assertEqual(paths, [('Custom', 'LabelA'), ('Element', 'I')])
Esempio n. 16
0
 def test_overlay_constructor1(self):
     t = Overlay([self.el1])
     self.assertEqual(t.keys(), [('Element', 'I')])
Esempio n. 17
0
 def test_overlay_from_values1(self):
     t = Overlay.from_values(self.el1)
     self.assertEqual(t.keys(),  [('Element', 'I')])
Esempio n. 18
0
 def test_overlay_keys_2(self):
     t = Overlay([self.el1, self.el2])
     self.assertEqual(t.keys(),
                      [('Element', 'I'), ('Element', 'II')])
Esempio n. 19
0
 def test_overlay_constructor_retains_custom_path(self):
     overlay = Overlay([('Custom', self.el1)])
     paths = Overlay([overlay, self.el2]).keys()
     self.assertEqual(paths, [('Custom', 'I'), ('Element', 'I')])
Esempio n. 20
0
class BaseVizApp(param.Parameterized):
    rafraichir = param.Action(lambda self: self.refreshViz())

    nb_loaded = param.String(default='')
    viewable = param.Boolean(True)
    loaded = param.Boolean(default=False)
    change_state = param.Boolean(True)
    traceSelector = param.ObjectSelector()

    def __init__(self, **params):
        self._session = params.get("session")
        self.title = params.get("viz_instance").title
        self.overlays = Overlay()
        self.traces = []
        self.tracesParam = []
        self.id = params.get("viz_instance").id
        self.dictParameters = self.get_param_values()
        self.configTracePanel = pn.Column()
        self.configVizPanel = pn.Column()
        self.debugPanel = pn.Column()
        self.silently = False
        super(BaseVizApp, self).__init__(**params)

    @param.depends(
        "viewable", "loaded", "change_state"
    )  # ajouter aussi dans les classes heritantes de la baseVizApp
    def view(self):

        if self.viewable and not self.loaded:
            # lorque la Viz app est chargé coté client on lance les chargements assynchrone des données FULL
            print("Chargement assynchrone des données ")
            self.tracesLoader(assyncLoading=True)

        if self.viewable != True:
            return

        elif self.viewable:
            print("""
            --------------------
            Rendu du composant {}
            --------------------
            """.format(self))

            return self.getView()

    ###### Gestion des Panels

    def getDebugPanel(self):
        self.debugPanel = pn.Column(self.param)
        return self.debugPanel

    def getConfigTracePanel(self, **params):

        return self.configTracePanel

    def getConfigVizPanel(self):
        return self.configVizPanel

    ###### Gestion des Rafraichissements

    def refreshViz(self):
        for trace in self.traces:
            self.doRefreshByTrace(trace)

    def doRefreshByTrace(self, trace):
        traceP = self.getTraceParamByTrace(trace)
        overlay = self.configureOverlay(trace)
        self.refreshOverlay(overlay, traceP=traceP)
        self.changeState()

    def doRefreshByTraceParam(self, traceP):

        trace = self.getTraceByTraceParam(traceP)

        overlay = self.configureOverlay(trace)
        self.refreshOverlay(overlay, traceP=traceP)
        self.changeState()

    def changeState(self):
        if self.change_state == True:
            self.change_state = False
        else:
            self.change_state = True

    ###### Gestion des TRACES

    def getTraceParamByTrace(self, trace):
        for tuple in self.tracesParam:
            if tuple[0] == trace:
                return tuple[1]

    def getTraceByTraceParam(self, traceParam):
        for tuple in self.tracesParam:
            if tuple[1] == traceParam:
                return tuple[0]

    def getAllTraceParam(self):
        return [tuple[1] for tuple in self.tracesParam]

    def addTrace(self, trace):
        if not trace in self.traces:
            self.traces.append(trace)
            traceP = TraceParam(trace=trace, data=trace.dataFrame, viz=self)
            self.tracesParam.append((trace, traceP))

    def tracesLoader(self, assyncLoading=True):
        for trace in self.traces:
            self.dataLoader(trace, assyncLoading)
            self.nb_loaded = str(trace.dataFrame.shape[0])
            if not assyncLoading:
                self.setLoaded()
                trace.setReady()

    def dataLoader(self, trace, assyncLoading=True):
        allDataAlreadyGetFromCache = None
        traceP = self.getTraceParamByTrace(trace)

        # on regarde d'abord si les full data sont en cache
        trace.configureConnector(FULL)

        if trace.isDataInCache:
            trace.lookCachedData()
            allDataAlreadyGetFromCache = True
        else:
            print("Les données Full ne sont pas en cache")
            # on regarde maintenant si le sample est en cache
            trace.configureConnector(SAMPLE)
            if trace.isDataInCache:
                trace.lookCachedData()
            else:
                print("Les données SAMPLE ne sont pas en cache")
                # sinon on charge les données sample
                trace.loadData()
                trace.finishConnection()

        if trace.getTotalNbEntity == trace.dataFrame.shape[0]:
            # on a toute les données disponible même avec le sample
            allDataAlreadyGetFromCache = True
            self.nb_loaded = str(trace.dataFrame.shape[0])

        if allDataAlreadyGetFromCache:
            self.setLoaded()
            trace.setReady()
            self.doRefreshByTrace(trace)
            return
        else:
            overlay = self.configureOverlay(trace=trace)
            self.addOverlay(overlay, traceParam=traceP)

        if assyncLoading == True:
            self.initProgressBarForAllVizWithSameTrace(trace=trace)
            self.assyncLoading(trace)
        else:
            print(trace.dataFrame.head())

    def assyncLoading(self, trace):
        trace.configureConnector(FULL)
        # on va chercher toutes les données en assync

        # tache assynchrone de loading de l'ensemble des entités
        loop = IOLoop.current()
        traceParam = self.getTraceParamByTrace(trace)
        trace.connectorInterface.connector.nbEntityLoaded = 0
        for i in range(0, traceParam.progress.num_tasks):
            future = executor.submit(blocking_function,
                                     self,
                                     trace=trace,
                                     step=i * LIMIT_PARAM)
            loop.add_future(future, self.updateProgressBar)

        loop.add_future(future, self.update)

    def updateProgressBar(self, future):
        trace = future.result()[1]
        traceParam = self.getTraceParamByTrace(trace)

        with pn.io.unlocked():
            traceParam.progress.completed += 1
            if traceParam.progress.completed == traceParam.progress.num_tasks:
                traceParam.progress.reset()
                self.setLoaded()
                trace.setReady()

    def update(self, future):
        trace = future.result()[1]
        viz = future.result()[0]

        #self.refreshAllVizWithSameTrace(trace=trace)

        with pn.io.unlocked():
            self = viz
            # update des reference pour le cache
            trace.connectorInterface.full_data = trace.dataFrame
            self.doRefreshByTrace(trace)

    def setLoaded(self):
        if self.loaded == False:
            self.loaded = True
        self.loaded = True

    ###### gestion des OVERLAYS

    def configureOverlay(self, trace, viz=None):
        # on ajoute une traceParam à la viz pour pouvoir la manipuler par la suite
        if not viz == None:
            self = viz
        traceP = self.getTraceParamByTrace(trace)
        if not traceP:
            traceP = TraceParam(trace=trace, data=trace.dataFrame, viz=self)
            self.tracesParam.append((trace, traceP))
        else:
            # on met à jour le dataframe de la traceP
            traceP.data = traceP.getUpdatedData(trace.dataFrame)

        overlay = self.createOverlay(traceParam=traceP, label=trace.name)
        self.nb_loaded = str(trace.dataFrame.shape[0])
        return overlay

    def finderOverlayByGroup(self, tree, groupName):

        children = tree.get('children')
        for child in children:
            node = tree.get(child)
            if node.group == groupName:
                return node
            else:
                if hasattr(node, 'children'):
                    self.finderOverlayByGroup(node, groupName)
                else:
                    return None

    def delGroupOverlay(self, groupName):

        if groupName in self.overlays.children:
            self.overlays.__delitem__(groupName)

    def addOverlay(self, newOverlay, traceParam):
        if not newOverlay:
            return

        if self.overlays:

            if traceParam.groupeName:
                self.delGroupOverlay(traceParam.groupeName)
                self.overlays = self.overlays * newOverlay

            else:
                self.overlays = self.overlays * newOverlay

        else:
            self.overlays = hv.Overlay([newOverlay])

        traceParam.setOverlay(newOverlay)

        return self.overlays

    def clearAllOverlays(self):
        self.overlays = hv.Overlay()

    def getAllOverlay(self):
        new_attrtree = AttrTree()
        for path, item in self.overlays.data.items():
            if path[0] != 'Tiles':
                new_attrtree.set_path(path, item)
        return new_attrtree

    def refreshOverlay(self, overlay, traceP):
        self.addOverlay(overlay, traceParam=traceP)
        print("After refreshing overlay with options : ",
              self.overlays.opts.info())

    def findAllPathStringOverlayWithout(self, idToNotKeep):
        pathList = []
        for keys, overlay in self.overlays.data.items():
            if overlay.id != idToNotKeep.id:
                pathList.append('.'.join(keys))
        return pathList

    def findPathStringOverlayByTraceId(self, overlayId):
        for keys, overlay in self.overlays.data.items():
            if overlay.id == overlayId:
                pathString = '.'.join(keys)
                return pathString

    def setOption(self, option):
        options = hv.Options(option)
        return self.opts(options)

    #### Fonctions sur de multiples Instances Viz

    def refreshAllVizWithSameTrace(self, trace=None):
        vizListe = VizConstructor.getVizByTraceIdAndSessionId(
            traceId=trace.id, sessionId=self._session)
        for viz in vizListe:
            viz.doRefreshByTrace(trace)

    def createAllVizWithSameTrace(self, trace=None):
        vizListe = VizConstructor.getVizByTraceIdAndSessionId(
            traceId=trace.id, sessionId=self._session)
        for viz in vizListe:
            overlay = self.configureOverlay(trace=trace, viz=viz)
            traceP = self.getTraceParamByTrace(trace)
            viz.addOverlay(overlay, traceParam=traceP)

        if len(vizListe) > 1:
            pass
            #DataLink(vizListe[0].overlays.Polygons, vizListe[1].overlays.Polygons)

    def initProgressBarForAllVizWithSameTrace(self, trace=None):
        traceParam = self.getTraceParamByTrace(trace)
        traceParam.progress.num_tasks = trace.getNbRequest

    #### Divers

    def formatter(value):
        return str(value)

    ############## @abstractmethod

    # abstractMethod
    def createOverlay(self, **kwargs):
        data = kwargs["traceParam"].data
        return data.hvplot()

    def getView(self):
        pass

    def getPanel(self):
        pass
Esempio n. 21
0
 def test_overlay_id_inheritance(self):
     overlay = Overlay([], id=1)
     self.assertEqual(overlay.clone().id, 1)
     self.assertEqual(overlay.clone()._plot_id, overlay._plot_id)
     self.assertNotEqual(overlay.clone([])._plot_id, overlay._plot_id)
Esempio n. 22
0
 def test_overlay_values_2(self):
     t = Overlay([self.el1, self.el2])
     self.assertEqual(t.values(), [self.el1, self.el2])
Esempio n. 23
0
 def test_overlay_with_holomap(self):
     overlay = Overlay([('Custom', self.el6)])
     composite = overlay * HoloMap({0: Element(None, group='HoloMap')})
     self.assertEqual(composite.last.keys(), [('Custom', 'LabelA'), ('HoloMap', 'I')])
Esempio n. 24
0
 def test_overlay_from_values2(self):
     t = Overlay.from_values(self.el8)
     self.assertEqual(t.keys(),  [('ValA', 'LabelB')])
Esempio n. 25
0
 def test_overlay_keys_2(self):
     t = Overlay([self.el1, self.el2])
     self.assertEqual(t.keys(), [('Element', 'I'), ('Element', 'II')])
Esempio n. 26
0
 def test_overlay_values_2(self):
     t = Overlay([self.el1, self.el2])
     self.assertEqual(t.values(), [self.el1, self.el2])
Esempio n. 27
0
    def compose_overlay_plot(self, x_range: Optional[Sequence[float]] = (-1.6, -1.2),
                             y_range: Optional[Sequence[float]] = (50.8, 51.05)) \
            -> Union[Overlay, Element]:
        """
        Compose all generated HoloViews layers in self.data_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
        """
        try:
            if not self._preload_complete:
                # If layers aren't preloaded yet just return the map tiles
                self._progress_callback('Still preloading layer data...')
                plot = self._base_tiles
            else:
                # Construct box around requested bounds
                bounds_poly = make_bounds_polygon(x_range, y_range)
                raster_shape = self._get_raster_dimensions(
                    bounds_poly, self.raster_resolution_m)
                # Ensure bounds are small enough to render without OOM or heat death of universe
                if (raster_shape[0] * raster_shape[1]) < 7e5:
                    from time import time

                    t0 = time()
                    self._progress_bar_callback(10)
                    # TODO: This will give multiple data layers, these need to be able to fed into their relevent pathfinding layers
                    for annlayer in self.annotation_layers:
                        new_layer = FatalityRiskLayer(
                            'Fatality Risk', ac=annlayer.aircraft['name'])
                        self.add_layer(new_layer)
                    self.remove_duplicate_layers()
                    self._progress_bar_callback(20)
                    self.generate_layers(bounds_poly, raster_shape)
                    self._progress_bar_callback(50)
                    plt_lyr = list(self._generated_data_layers)[0]
                    plot = Overlay([self._generated_data_layers[plt_lyr][0]])
                    print("Generated all layers in ", time() - t0)
                    if self.annotation_layers:
                        plot = Overlay(
                            [self._generated_data_layers[plt_lyr][0]])
                        res = []
                        prog_bar = 50
                        for dlayer in self.data_layers:
                            raster_indices = dict(
                                Longitude=np.linspace(x_range[0],
                                                      x_range[1],
                                                      num=raster_shape[0]),
                                Latitude=np.linspace(y_range[0],
                                                     y_range[1],
                                                     num=raster_shape[1]))
                            raw_data = [
                                self._generated_data_layers[dlayer.key][2]
                            ]
                            raster_grid = np.sum([
                                remove_raster_nans(
                                    self._generated_data_layers[dlayer.key][1])
                            ],
                                                 axis=0)
                            raster_grid = np.flipud(raster_grid)
                            raster_indices['Latitude'] = np.flip(
                                raster_indices['Latitude'])

                            for alayer in self.annotation_layers:
                                if alayer.aircraft == dlayer.ac_dict:
                                    self._progress_bar_callback(prog_bar)
                                    prog_bar += 40 / len(
                                        self.annotation_layers)
                                    self._progress_callback(
                                        f'Finding a path for {alayer.aircraft["name"]}'
                                    )
                                    res.append(
                                        alayer.annotate(
                                            raw_data,
                                            (raster_indices, raster_grid)))

                        self._progress_callback('Plotting paths')
                        self._progress_bar_callback(90)
                        # res = jl.Parallel(n_jobs=1, verbose=1, backend='threading')(
                        #     jl.delayed(layer.annotate)(raw_datas, (raster_indices, raster_grid)) for layer in
                        #     self.annotation_layers )
                        plot = Overlay([
                            self._base_tiles, plot,
                            *[annot for annot in res if annot is not None]
                        ]).collate()
                    else:
                        plot = Overlay([self._base_tiles, plot]).collate()
                    self._progress_bar_callback(90)

                else:
                    self._progress_callback('Area too large to render!')
                    if not self._generated_data_layers:
                        plot = self._base_tiles
                    else:
                        plot = Overlay([
                            self._base_tiles,
                            *list(self._generated_data_layers.values())
                        ])

            self._update_layer_list()
            self._progress_callback("Rendering new map...")

        except Exception as e:
            # Catch-all to prevent plot blanking out and/or crashing app
            # Just display map tiles in case this was transient
            import traceback
            traceback.print_exc()
            self._progress_callback(
                f'Plotting failed with the following error: {e}. Please attempt to re-generate the plot'
            )
            print(e)
            plot = self._base_tiles

        return plot.opts(width=self.plot_size[0],
                         height=self.plot_size[1],
                         tools=self.tools,
                         active_tools=self.active_tools)