protected _activate()

in src/component/bearing/BearingComponent.ts [88:294]


    protected _activate(): void {
        const subs = this._subscriptions;

        const cameraBearingFov$ =
            this._container.renderService.renderCamera$.pipe(
                map(
                    (rc: RenderCamera): [number, number] => {
                        let vFov: number = this._spatial.degToRad(rc.perspective.fov);
                        let hFov: number = rc.perspective.aspect === Number.POSITIVE_INFINITY ?
                            Math.PI :
                            Math.atan(rc.perspective.aspect * Math.tan(0.5 * vFov)) * 2;

                        return [this._spatial.azimuthalToBearing(rc.rotation.phi), hFov];
                    }),
                distinctUntilChanged(
                    (a1: [number, number], a2: [number, number]): boolean => {
                        return Math.abs(a2[0] - a1[0]) < this._distinctThreshold &&
                            Math.abs(a2[1] - a1[1]) < this._distinctThreshold;
                    }));

        const imageFov$ = observableCombineLatest(
            this._navigator.stateService.currentState$.pipe(
                distinctUntilChanged(
                    undefined,
                    (frame: AnimationFrame): string => {
                        return frame.state.currentImage.id;
                    })),
            this._navigator.panService.panImages$).pipe(
                map(
                    ([frame, panImages]:
                        [
                            AnimationFrame,
                            [
                                Image,
                                Transform,
                                number][],
                        ])
                        : ImageFov => {
                        const image: Image = frame.state.currentImage;
                        const transform: Transform = frame.state.currentTransform;

                        if (isSpherical(image.cameraType)) {
                            return [Math.PI, Math.PI];
                        }

                        const currentProjectedPoints =
                            this._computeProjectedPoints(transform);
                        const hFov = this._spatial
                            .degToRad(
                                this._computeHorizontalFov(
                                    currentProjectedPoints));

                        let hFovLeft: number = hFov / 2;
                        let hFovRight: number = hFov / 2;

                        for (const [n, , f] of panImages) {
                            const diff: number = this._spatial.wrap(n.compassAngle - image.compassAngle, -180, 180);
                            if (diff < 0) {
                                hFovLeft = this._spatial.degToRad(Math.abs(diff)) + f / 2;
                            } else {
                                hFovRight = this._spatial.degToRad(Math.abs(diff)) + f / 2;
                            }
                        }

                        return [hFovLeft, hFovRight];
                    }),
                distinctUntilChanged(
                    (
                        [hFovLeft1, hFovRight1]: ImageFov,
                        [hFovLeft2, hFovRight2]: ImageFov): boolean => {

                        return Math.abs(hFovLeft2 - hFovLeft1) < this._distinctThreshold &&
                            Math.abs(hFovRight2 - hFovRight1) < this._distinctThreshold;
                    }));

        const offset$ = observableCombineLatest(
            this._navigator.stateService.currentState$.pipe(
                distinctUntilChanged(
                    undefined,
                    (frame: AnimationFrame): string => {
                        return frame.state.currentImage.id;
                    })),
            this._container.renderService.bearing$).pipe(
                map(
                    ([frame, bearing]: [AnimationFrame, number]): number => {
                        const offset: number = this._spatial.degToRad(frame.state.currentImage.compassAngle - bearing);

                        return offset;
                    }));

        const imageFovOperation$ = new Subject<ImageFovOperation>();

        const smoothImageFov$ = imageFovOperation$.pipe(
            scan(
                (state: ImageFovState, operation: ImageFovOperation): ImageFovState => {
                    return operation(state);
                },
                { alpha: 0, curr: [0, 0, 0], prev: [0, 0, 0] }),
            map(
                (state: ImageFovState): ImageFov => {
                    const alpha = MathUtils.smootherstep(state.alpha, 0, 1);
                    const curr = state.curr;
                    const prev = state.prev;

                    return [
                        this._interpolate(prev[0], curr[0], alpha),
                        this._interpolate(prev[1], curr[1], alpha),
                    ];
                }));

        subs.push(imageFov$.pipe(
            map(
                (nbf: ImageFov): ImageFovOperation => {
                    return (state: ImageFovState): ImageFovState => {
                        const a = MathUtils.smootherstep(state.alpha, 0, 1);
                        const c = state.curr;
                        const p = state.prev;

                        const prev: ImageFov = [
                            this._interpolate(p[0], c[0], a),
                            this._interpolate(p[1], c[1], a),
                        ];

                        const curr: ImageFov = <ImageFov>nbf.slice();

                        return {
                            alpha: 0,
                            curr: curr,
                            prev: prev,
                        };
                    };
                }))
            .subscribe(imageFovOperation$));

        subs.push(imageFov$.pipe(
            switchMap(
                (): Observable<number> => {
                    return this._container.renderService.renderCameraFrame$.pipe(
                        skip(1),
                        scan<RenderCamera, number>(
                            (alpha: number): number => {
                                return alpha + this._animationSpeed;
                            },
                            0),
                        takeWhile(
                            (alpha: number): boolean => {
                                return alpha <= 1 + this._animationSpeed;
                            }),
                        map(
                            (alpha: number): number => {
                                return Math.min(alpha, 1);
                            }));
                }),
            map(
                (alpha: number): ImageFovOperation => {
                    return (nbfState: ImageFovState): ImageFovState => {
                        return {
                            alpha: alpha,
                            curr: <ImageFov>nbfState.curr.slice(),
                            prev: <ImageFov>nbfState.prev.slice(),
                        };
                    };
                }))
            .subscribe(imageFovOperation$));

        const imageBearingFov$ = observableCombineLatest(
            offset$,
            smoothImageFov$).pipe(
                map(
                    ([offset, fov]: [number, ImageFov]): ImageBearingFov => {
                        return [offset, fov[0], fov[1]];
                    }));

        subs.push(observableCombineLatest(
            cameraBearingFov$,
            imageBearingFov$,
            this._configuration$,
            this._container.renderService.size$).pipe(
                map(
                    ([[cb, cf], [no, nfl, nfr], configuration, size]:
                        [[number, number], [number, number, number], BearingConfiguration, ViewportSize]): VirtualNodeHash => {

                        const background: vd.VNode = this._createBackground(cb);
                        const fovIndicator: vd.VNode = this._createFovIndicator(nfl, nfr, no);
                        const north: vd.VNode = this._createNorth(cb);
                        const cameraSector: vd.VNode = this._createCircleSectorCompass(
                            this._createCircleSector(Math.max(Math.PI / 20, cf), "#FFF"));

                        const compact: string = configuration.size === ComponentSize.Small ||
                            configuration.size === ComponentSize.Automatic && size.width < 640 ?
                            ".mapillary-bearing-compact" : "";

                        return {
                            name: this._name,
                            vNode: vd.h(
                                "div.mapillary-bearing-indicator-container" + compact,
                                { oncontextmenu: (event: MouseEvent): void => { event.preventDefault(); } },
                                [
                                    background,
                                    fovIndicator,
                                    north,
                                    cameraSector,
                                ]),
                        };
                    }))
            .subscribe(this._container.domRenderer.render$));
    }