protected _activate()

in src/component/spatial/SpatialComponent.ts [147:583]


    protected _activate(): void {
        this._navigator.cacheService.configure({ cellDepth: 3 });

        const subs = this._subscriptions;

        subs.push(this._navigator.stateService.reference$
            .pipe(
                pairwise())
            .subscribe(
                ([prevReference, reference]: [LngLatAlt, LngLatAlt]): void => {
                    this._scene.resetReference(reference, prevReference);
                }
            ));

        subs.push(this._navigator.graphService.filter$
            .subscribe(imageFilter => { this._scene.setFilter(imageFilter); }));

        const bearing$ = this._container.renderService.bearing$.pipe(
            map(
                (bearing: number): number => {
                    const interval = 6;
                    const discrete = interval * Math.floor(bearing / interval);
                    return discrete;
                }),
            distinctUntilChanged(),
            publishReplay(1),
            refCount());

        const cellId$ = this._navigator.stateService.currentImage$
            .pipe(
                map(
                    (image: Image): string => {
                        return this._navigator.api.data.geometry
                            .lngLatToCellId(image.originalLngLat);
                    }),
                distinctUntilChanged(),
                publishReplay(1),
                refCount());

        const cellGridDepth$ = this._configuration$
            .pipe(
                map(
                    (c: SpatialConfiguration): number => {
                        return this._spatial.clamp(c.cellGridDepth, 1, 3);
                    }),
                distinctUntilChanged(),
                publishReplay(1),
                refCount());

        const sequencePlay$ = observableCombineLatest(
            this._navigator.playService.playing$,
            this._navigator.playService.speed$).pipe(
                map(
                    ([playing, speed]: [boolean, number]): boolean => {
                        return playing && speed > PlayService.sequenceSpeed;
                    }),
                distinctUntilChanged(),
                publishReplay(1),
                refCount());

        const isOverview$ = this._navigator.stateService.state$.pipe(
            map(
                (state: State): boolean => {
                    return isOverviewState(state);
                }),
            distinctUntilChanged(),
            publishReplay(1),
            refCount());

        subs.push(isOverview$.subscribe(
            (isOverview: boolean): void => {
                this._scene.setNavigationState(isOverview);
            }));

        const cell$ = observableCombineLatest(
            isOverview$,
            sequencePlay$,
            bearing$,
            cellGridDepth$,
            this._navigator.stateService.currentImage$)
            .pipe(
                distinctUntilChanged((
                    [o1, s1, b1, d1, i1]: AdjancentParams,
                    [o2, s2, b2, d2, i2]: AdjancentParams)
                    : boolean => {
                    if (o1 !== o2) {
                        return false;
                    }
                    const isd = i1.id === i2.id && s1 === s2 && d1 === d2;
                    if (o1) {
                        return isd;
                    }
                    return isd && b1 === b2;
                }),
                concatMap(
                    ([isOverview, sequencePlay, bearing, depth, image]
                        : AdjancentParams)
                        : Observable<string[]> => {
                        if (isOverview) {
                            const geometry = this._navigator.api.data.geometry;
                            const cellId = geometry
                                .lngLatToCellId(image.originalLngLat);
                            const cells = sequencePlay ?
                                [cellId] :
                                connectedComponent(cellId, depth, geometry);
                            return observableOf(cells);
                        }

                        const fov = sequencePlay ? 30 : 90;
                        return observableOf(
                            this._cellsInFov(
                                image,
                                bearing,
                                fov));
                    }),
                switchMap(
                    (cellIds: string[]): Observable<Cell> => {
                        return observableFrom(cellIds).pipe(
                            mergeMap(
                                (cellId: string): Observable<Cell> => {
                                    const t$ = this._cache.hasCell(cellId) ?
                                        observableOf(
                                            this._cache.getCell(cellId)) :
                                        this._cache.cacheCell$(cellId);

                                    return t$.pipe(
                                        map((images: Image[]) => ({ id: cellId, images })));
                                },
                                6));
                    }));

        subs.push(cell$.pipe(
            withLatestFrom(this._navigator.stateService.reference$))
            .subscribe(
                ([cell, reference]: [Cell, LngLatAlt]): void => {
                    if (this._scene.hasCell(cell.id)) {
                        return;
                    }

                    this._scene.addCell(
                        this._cellToTopocentric(cell.id, reference),
                        cell.id);
                }));

        subs.push(cell$.pipe(
            withLatestFrom(this._navigator.stateService.reference$))
            .subscribe(
                ([cell, reference]: [Cell, LngLatAlt]): void => {
                    this._addSceneImages(cell, reference);
                }));

        subs.push(cell$.pipe(
            concatMap(
                (cell: Cell): Observable<[string, ClusterContract]> => {
                    const cellId = cell.id;
                    let reconstructions$: Observable<ClusterContract>;
                    if (this._cache.hasClusters(cellId)) {
                        reconstructions$ = observableFrom(this._cache.getClusters(cellId));
                    } else if (this._cache.isCachingClusters(cellId)) {
                        reconstructions$ = this._cache.cacheClusters$(cellId).pipe(
                            last(null, {}),
                            switchMap(
                                (): Observable<ClusterContract> => {
                                    return observableFrom(this._cache.getClusters(cellId));
                                }));
                    } else if (this._cache.hasCell(cellId)) {
                        reconstructions$ = this._cache.cacheClusters$(cellId);
                    } else {
                        reconstructions$ = observableEmpty();
                    }

                    return observableCombineLatest(observableOf(cellId), reconstructions$);
                }),
            withLatestFrom(this._navigator.stateService.reference$))
            .subscribe(
                ([[cellId, reconstruction], reference]: [[string, ClusterContract], LngLatAlt]): void => {
                    if (this._scene
                        .hasCluster(
                            reconstruction.id,
                            cellId)) {
                        return;
                    }

                    this._scene.addCluster(
                        reconstruction,
                        this._computeTranslation(
                            reconstruction,
                            reference),
                        cellId);
                }));

        subs.push(this._configuration$.pipe(
            map(
                (c: SpatialConfiguration): SpatialConfiguration => {
                    c.cameraSize = this._spatial.clamp(c.cameraSize, 0.01, 1);
                    c.pointSize = this._spatial.clamp(c.pointSize, 0.01, 1);
                    const pointVisualizationMode =
                        c.pointsVisible ?
                            c.pointVisualizationMode ?? PointVisualizationMode.Original :
                            PointVisualizationMode.Hidden;
                    return {
                        cameraSize: c.cameraSize,
                        cameraVisualizationMode: c.cameraVisualizationMode,
                        cellsVisible: c.cellsVisible,
                        originalPositionMode: c.originalPositionMode,
                        pointSize: c.pointSize,
                        pointVisualizationMode,
                    };
                }),
            distinctUntilChanged(
                (c1: SpatialConfiguration, c2: SpatialConfiguration)
                    : boolean => {
                    return c1.cameraSize === c2.cameraSize &&
                        c1.cameraVisualizationMode === c2.cameraVisualizationMode &&
                        c1.cellsVisible === c2.cellsVisible &&
                        c1.originalPositionMode === c2.originalPositionMode &&
                        c1.pointSize === c2.pointSize &&
                        c1.pointVisualizationMode === c2.pointVisualizationMode;
                }))
            .subscribe(
                (c: SpatialConfiguration): void => {
                    this._scene.setCameraSize(c.cameraSize);
                    const cvm = c.cameraVisualizationMode;
                    this._scene.setCameraVisualizationMode(cvm);
                    this._scene.setCellVisibility(c.cellsVisible);
                    this._scene.setPointSize(c.pointSize);
                    const pvm = c.pointVisualizationMode;
                    this._scene.setPointVisualizationMode(pvm);
                    const opm = c.originalPositionMode;
                    this._scene.setPositionMode(opm);
                }));

        subs.push(observableCombineLatest(cellId$, cellGridDepth$)
            .subscribe(
                ([cellId, depth]: [string, number]): void => {
                    const keepCells = connectedComponent(
                        cellId,
                        depth,
                        this._navigator.api.data.geometry);
                    this._scene.uncache(keepCells);
                    this._cache.uncache(keepCells);
                }));

        subs.push(this._navigator.playService.playing$.pipe(
            switchMap(
                (playing: boolean): Observable<MouseEvent> => {
                    return playing ?
                        observableEmpty() :
                        this._container.mouseService.dblClick$;
                }),
            withLatestFrom(this._container.renderService.renderCamera$),
            switchMap(
                ([event, render]: [MouseEvent, RenderCamera]): Observable<Image> => {
                    const element = this._container.container;
                    const [canvasX, canvasY] = this._viewportCoords
                        .canvasPosition(event, element);
                    const viewport = this._viewportCoords.canvasToViewport(
                        canvasX,
                        canvasY,
                        element);

                    const id = this._scene.intersection
                        .intersectObjects(viewport, render.perspective);

                    return !!id ?
                        this._navigator.moveTo$(id).pipe(
                            catchError(
                                (): Observable<Image> => {
                                    return observableEmpty();
                                })) :
                        observableEmpty();
                }))
            .subscribe());

        const intersectChange$ = observableCombineLatest(
            this._configuration$,
            this._navigator.stateService.state$).pipe(
                map(
                    ([c, state]: [SpatialConfiguration, State])
                        : IntersectConfiguration => {
                        c.cameraSize = this._spatial.clamp(
                            c.cameraSize,
                            0.01,
                            1);
                        return {
                            size: c.cameraSize,
                            visible: isModeVisible(c.cameraVisualizationMode),
                            state,
                        };
                    }),
                distinctUntilChanged(
                    (c1: IntersectConfiguration, c2: IntersectConfiguration): boolean => {
                        return c1.size === c2.size &&
                            c1.visible === c2.visible &&
                            c1.state === c2.state;
                    }));

        const mouseMove$ = this._container.mouseService.mouseMove$.pipe(
            publishReplay(1),
            refCount());

        subs.push(mouseMove$.subscribe());

        const mouseHover$ = observableMerge(
            this._container.mouseService.mouseEnter$,
            this._container.mouseService.mouseLeave$,
            this._container.mouseService.windowBlur$);

        subs.push(observableCombineLatest(
            this._navigator.playService.playing$,
            mouseHover$,
            isOverview$,
            this._navigator.graphService.filter$)
            .pipe(
                switchMap(
                    ([playing, mouseHover]:
                        [boolean, IntersectEvent, boolean, FilterFunction])
                        : Observable<[IntersectEvent, RenderCamera, IntersectConfiguration]> => {
                        return !playing && mouseHover.type === "pointerenter" ?
                            observableCombineLatest(
                                observableConcat(
                                    mouseMove$.pipe(take(1)),
                                    this._container.mouseService.mouseMove$),
                                this._container.renderService.renderCamera$,
                                intersectChange$) :
                            observableCombineLatest(
                                observableOf(mouseHover),
                                observableOf(null),
                                observableOf(null));
                    }))
            .subscribe(
                ([event, render]
                    : [IntersectEvent, RenderCamera, IntersectConfiguration]): void => {
                    if (event.type !== "pointermove") {
                        this._scene.setHoveredImage(null);
                        return;
                    }

                    const element = this._container.container;
                    const [canvasX, canvasY] = this._viewportCoords.canvasPosition(<MouseEvent>event, element);
                    const viewport = this._viewportCoords.canvasToViewport(
                        canvasX,
                        canvasY,
                        element);

                    const key = this._scene.intersection
                        .intersectObjects(viewport, render.perspective);

                    this._scene.setHoveredImage(key);
                }));

        subs.push(this._navigator.stateService.currentId$
            .subscribe(
                (id: string): void => {
                    this._scene.setSelectedImage(id);
                }));

        subs.push(this._navigator.stateService.currentState$
            .pipe(
                map((frame: AnimationFrame): GLRenderHash => {
                    const scene = this._scene;

                    return {
                        name: this._name,
                        renderer: {
                            frameId: frame.id,
                            needsRender: scene.needsRender,
                            render: scene.render.bind(scene),
                            pass: RenderPass.Opaque,
                        },
                    };
                }))
            .subscribe(this._container.glRenderer.render$));

        const updatedCell$ = this._navigator.graphService.dataAdded$
            .pipe(
                filter(
                    (cellId: string) => {
                        return this._cache.hasCell(cellId);
                    }),
                mergeMap(
                    (cellId: string): Observable<[Cell, LngLatAlt]> => {
                        return this._cache.updateCell$(cellId).pipe(
                            map((images: Image[]) => ({ id: cellId, images })),
                            withLatestFrom(
                                this._navigator.stateService.reference$
                            )
                        );
                    }),
                publish<[Cell, LngLatAlt]>(),
                refCount());

        subs.push(updatedCell$
            .subscribe(
                ([cell, reference]: [Cell, LngLatAlt]): void => {
                    this._addSceneImages(cell, reference);
                }));

        subs.push(updatedCell$
            .pipe(
                concatMap(
                    ([cell]: [Cell, LngLatAlt]): Observable<[string, ClusterContract]> => {
                        const cellId = cell.id;
                        const cache = this._cache;
                        let reconstructions$: Observable<ClusterContract>;
                        if (cache.hasClusters(cellId)) {
                            reconstructions$ =
                                cache.updateClusters$(cellId);
                        } else if (cache.isCachingClusters(cellId)) {
                            reconstructions$ = this._cache.cacheClusters$(cellId).pipe(
                                last(null, {}),
                                switchMap(
                                    (): Observable<ClusterContract> => {
                                        return observableFrom(
                                            cache.updateClusters$(cellId));
                                    }));
                        } else {
                            reconstructions$ = observableEmpty();
                        }

                        return observableCombineLatest(
                            observableOf(cellId),
                            reconstructions$);
                    }),
                withLatestFrom(this._navigator.stateService.reference$))
            .subscribe(
                ([[cellId, reconstruction], reference]: [[string, ClusterContract], LngLatAlt]): void => {
                    if (this._scene.hasCluster(reconstruction.id, cellId)) {
                        return;
                    }

                    this._scene.addCluster(
                        reconstruction,
                        this._computeTranslation(reconstruction, reference),
                        cellId);
                }));
    }