UNPKG

mapillary-js

Version:

A WebGL interactive street imagery library

1,538 lines (1,481 loc) 56.6 kB
import { combineLatest as observableCombineLatest, throwError as observableThrowError, Observable, } from "rxjs"; import { first, } from "rxjs/operators"; import { LngLat } from "../api/interfaces/LngLat"; import { Component } from "../component/Component"; import { ComponentConfiguration } from "../component/interfaces/ComponentConfiguration"; import { LngLatAlt } from "../api/interfaces/LngLatAlt"; import { FilterExpression } from "../graph/FilterExpression"; import { Image } from "../graph/Image"; import { NavigationDirection } from "../graph/edge/NavigationDirection"; import { RenderCamera } from "../render/RenderCamera"; import { RenderMode } from "../render/RenderMode"; import { TransitionMode } from "../state/TransitionMode"; import { EventEmitter } from "../util/EventEmitter"; import { ICustomRenderer } from "./interfaces/ICustomRenderer"; import { PointOfView } from "./interfaces/PointOfView"; import { ViewerOptions } from "./options/ViewerOptions"; import { ComponentController } from "./ComponentController"; import { Container } from "./Container"; import { Navigator } from "./Navigator"; import { Observer } from "./Observer"; import { CustomRenderer } from "./CustomRenderer"; import { IViewer } from "./interfaces/IViewer"; import { ViewerBearingEvent } from "./events/ViewerBearingEvent"; import { ViewerEventType } from "./events/ViewerEventType"; import { ViewerDataLoadingEvent } from "./events/ViewerDataLoadingEvent"; import { ViewerMouseEvent } from "./events/ViewerMouseEvent"; import { ViewerNavigableEvent } from "./events/ViewerNavigableEvent"; import { ViewerNavigationEdgeEvent } from "./events/ViewerNavigationEdgeEvent"; import { ViewerImageEvent } from "./events/ViewerImageEvent"; import { ViewerStateEvent } from "./events/ViewerStateEvent"; import { ComponentName } from "../component/ComponentName"; import { FallbackComponentName } from "../component/fallback/FallbackComponentName"; import { CameraControls } from "./enums/CameraControls"; import { State } from "../state/State"; import { ICustomCameraControls } from "./interfaces/ICustomCameraControls"; import { CustomCameraControls } from "./CustomCameraControls"; import { ViewerLoadEvent } from "./events/ViewerLoadEvent"; import { cameraControlsToState } from "./Modes"; import { ViewerReferenceEvent } from "./events/ViewerReferenceEvent"; import { IDataProvider } from "../external/api"; /** * @class Viewer * * @classdesc The Viewer object represents the navigable image viewer. * Create a Viewer by specifying a container, client ID, image ID and * other options. The viewer exposes methods and events for programmatic * interaction. * * In the case of asynchronous methods, MapillaryJS returns promises to * the results. Notifications are always emitted through JavaScript events. */ export class Viewer extends EventEmitter implements IViewer { /** * Private component controller object which manages component states. */ private _componentController: ComponentController; /** * Private container object which maintains the DOM Element, * renderers and relevant services. */ private _container: Container; /** * Private observer object which observes the viewer state and * fires events on behalf of the viewer. */ private _observer: Observer; /** * Private navigator object which controls navigation. */ private _navigator: Navigator; /** * Private custom camera controls object which handles * custom control subscriptions. */ private _customCameraControls: CustomCameraControls; /** * Private custom renderer object which controls WebGL custom * rendering subscriptions. */ private _customRenderer: CustomRenderer; /** * Create a new viewer instance. * * @description The `Viewer` object represents the street imagery * viewer on your web page. It exposes methods and properties that * you can use to programatically change the view, and fires * events as users interact with it. * * It is possible to initialize the viewer with or * without a ID. * * When you want to show a specific image in the viewer from * the start you should initialize it with a ID. * * When you do not know the first image ID at implementation * time, e.g. in a map-viewer application you should initialize * the viewer without a ID and call `moveTo` instead. * * When initializing with an ID the viewer is bound to that ID * until the image for that ID has been successfully loaded. * Also, a cover with the image of the ID will be shown. * If the data for that ID can not be loaded because the ID is * faulty or other errors occur it is not possible to navigate * to another ID because the viewer is not navigable. The viewer * becomes navigable when the data for the ID has been loaded and * the image is shown in the viewer. This way of initializing * the viewer is mostly for embedding in blog posts and similar * where one wants to show a specific image initially. * * If the viewer is initialized without a ID (with null or * undefined) it is not bound to any particular ID and it is * possible to move to any ID with `viewer.moveTo("<my-image-id>")`. * If the first move to a ID fails it is possible to move to another * ID. The viewer will show a black background until a move * succeeds. This way of intitializing is suited for a map-viewer * application when the initial ID is not known at implementation * time. * * @param {ViewerOptions} options - Optional configuration object * specifying Viewer's and the components' initial setup. * * @example * ```js * var viewer = new Viewer({ * accessToken: "<my-access-token>", * container: "<my-container-id>", * }); * ``` */ constructor(options: ViewerOptions) { super(); this._navigator = new Navigator(options); this._container = new Container( options, this._navigator.stateService); this._observer = new Observer( this, this._navigator, this._container); this._componentController = new ComponentController( this._container, this._navigator, this._observer, options.imageId, options.component); this._customRenderer = new CustomRenderer( this._container, this._navigator); this._customCameraControls = new CustomCameraControls( this._container, this._navigator); } /** * Returns the data provider used by the viewer to fetch * all contracts, ents, and buffers. * * @description The viewer's data provider can be set * upon initialization through the {@link ViewerOptions.dataProvider} * property. * * @returns {IDataProvider} The viewer's data provider. */ public get dataProvider(): IDataProvider { return this._navigator.api.data; } /** * Return a boolean indicating if the viewer is in a navigable state. * * @description The navigable state indicates if the viewer supports * moving, i.e. calling the {@link moveTo} and {@link moveDir} * methods or changing the authentication state, * i.e. calling {@link setAccessToken}. The viewer will not be in a navigable * state if the cover is activated and the viewer has been supplied a ID. * When the cover is deactivated or the viewer is activated without being * supplied a ID it will be navigable. * * @returns {boolean} Boolean indicating whether the viewer is navigable. */ public get isNavigable(): boolean { return this._componentController.navigable; } /** * Activate the combined panning functionality. * * @description The combined panning functionality is active by default. */ public activateCombinedPanning(): void { this._navigator.panService.enable(); } /** * Activate a component. * * @param {ComponentName | FallbackComponentName} name - Name of * the component which will become active. * * @example * ```js * viewer.activateComponent("marker"); * ``` */ public activateComponent( name: ComponentName | FallbackComponentName): void { this._componentController.activate(name); } /** * Activate the cover (deactivates all other components). */ public activateCover(): void { this._componentController.activateCover(); } /** * Add a custom renderer to the viewer's rendering pipeline. * * @description During a render pass, custom renderers * are called in the order they were added. * * @param renderer - The custom renderer implementation. */ public addCustomRenderer(renderer: ICustomRenderer): void { this._customRenderer.add(renderer, this); } /** * Attach custom camera controls to control the viewer's * camera pose and projection. * * @description Custom camera controls allow the API user * to move the viewer's camera freely and define the camera * projection. These camera properties are used * to render the viewer 3D scene directly into the * viewer's GL context. * * Only a single custom camera control instance can be * attached to the viewer. A new custom camera control * instance can be attached after detaching a previous * one. * * Set the viewer's camera controls to * {@link CameraControls.Custom} to activate attached * camera controls. If {@link CameraControls.Custom} * has already been set when a custom camera control * instance is attached, it will be activated immediately. * * Set the viewer's camera controls to any other * {@link CameraControls} mode to deactivate the * custom camera controls. * * @param controls - The custom camera controls implementation. * * @throws {MapillaryError} When camera controls attached * are already attached to the viewer. */ public attachCustomCameraControls(controls: ICustomCameraControls): void { this._customCameraControls.attach(controls, this); } /** * Deactivate the combined panning functionality. * * @description Deactivating the combined panning functionality * could be needed in scenarios involving sequence only navigation. */ public deactivateCombinedPanning(): void { this._navigator.panService.disable(); } /** * Deactivate a component. * * @param {ComponentName | FallbackComponentName} name - Name * of component which become inactive. * * @example * ```js * viewer.deactivateComponent("pointer"); * ``` */ public deactivateComponent( name: ComponentName | FallbackComponentName): void { this._componentController.deactivate(name); } /** * Deactivate the cover (activates all components marked as active). */ public deactivateCover(): void { this._componentController.deactivateCover(); } /** * Detach a previously attached custom camera control * instance from the viewer. * * @description If no custom camera control instance * has previously been attached, calling this method * has no effect. * * Already attached custom camera controls need to * be detached before attaching another custom camera * control instance. */ public detachCustomCameraControls(): Promise<ICustomCameraControls> { return this._customCameraControls.detach(this); } public fire( type: ViewerBearingEvent["type"], event: ViewerBearingEvent) : void; public fire( type: ViewerDataLoadingEvent["type"], event: ViewerDataLoadingEvent) : void; public fire( type: ViewerNavigableEvent["type"], event: ViewerNavigableEvent) : void; public fire( type: ViewerImageEvent["type"], event: ViewerImageEvent) : void; public fire( type: ViewerNavigationEdgeEvent["type"], event: ViewerNavigationEdgeEvent) : void; public fire( type: ViewerReferenceEvent["type"], event: ViewerReferenceEvent) : void; public fire( type: ViewerStateEvent["type"], event: ViewerStateEvent) : void; public fire( type: ViewerMouseEvent["type"], event: ViewerMouseEvent) : void; public fire<T>( type: ViewerEventType, event: T) : void { super.fire(type, event); } /** * Get the bearing of the current viewer camera. * * @description The bearing depends on how the camera * is currently rotated and does not correspond * to the compass angle of the current image if the view * has been panned. * * Bearing is measured in degrees clockwise with respect to * north. * * @returns {Promise<number>} Promise to the bearing * of the current viewer camera. * * @example * ```js * viewer.getBearing().then(b => { console.log(b); }); * ``` */ public getBearing(): Promise<number> { return new Promise<number>( (resolve: (value: number) => void, reject: (reason: Error) => void): void => { this._container.renderService.bearing$.pipe( first()) .subscribe( (bearing: number): void => { resolve(bearing); }, (error: Error): void => { reject(error); }); }); } /** * Get the viewer's camera control mode. * * @description The camera control mode determines * how the camera is controlled when the viewer * receives pointer and keyboard input. * * @returns {CameraControls} controls - Camera control mode. * * @example * ```js * viewer.getCameraControls().then(c => { console.log(c); }); * ``` */ public getCameraControls(): Promise<CameraControls> { return new Promise<number>(( resolve: (value: CameraControls) => void, reject: (reason: Error) => void) : void => { this._navigator.stateService.state$.pipe( first()) .subscribe( (state: State): void => { switch (state) { case State.Custom: resolve(CameraControls.Custom); break; case State.Earth: resolve(CameraControls.Earth); break; default: resolve(CameraControls.Street); break; } }, (error: Error): void => { reject(error); }); }); } /** * Returns the viewer's canvas element. * * @description This is the element onto which the viewer renders * the WebGL content. * * @returns {HTMLCanvasElement} The viewer's canvas element, or * null or not initialized. */ public getCanvas(): HTMLCanvasElement { return <HTMLCanvasElement>this._container.canvas; } /** * Returns the HTML element containing the viewer's canvas element. * * @description This is the element to which event bindings for viewer * interactivity (such as panning and zooming) are attached. * * @returns {HTMLDivElement} The container for the viewer's * canvas element. */ public getCanvasContainer(): HTMLDivElement { return this._container.canvasContainer; } /** * Get the basic coordinates of the current image that is * at the center of the viewport. * * @description Basic coordinates are 2D coordinates on the [0, 1] interval * and have the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * @returns {Promise<number[]>} Promise to the basic coordinates * of the current image at the center for the viewport. * * @example * ```js * viewer.getCenter().then(c => { console.log(c); }); * ``` */ public getCenter(): Promise<number[]> { return new Promise<number[]>( (resolve: (value: number[]) => void, reject: (reason: Error) => void): void => { this._navigator.stateService.getCenter() .subscribe( (center: number[]): void => { resolve(center); }, (error: Error): void => { reject(error); }); }); } /** * Get a component. * * @param {string} name - Name of component. * @returns {Component} The requested component. * * @example * ```js * var pointerComponent = viewer.getComponent("pointer"); * ``` */ public getComponent<TComponent extends Component<ComponentConfiguration>>( name: ComponentName | FallbackComponentName): TComponent { return this._componentController.get<TComponent>(name); } /** * Returns the viewer's containing HTML element. * * @returns {HTMLElement} The viewer's container. */ public getContainer(): HTMLElement { return this._container.container; } /** * Get the viewer's current vertical field of view. * * @description The vertical field of view rendered on the viewer canvas * measured in degrees. * * @returns {Promise<number>} Promise to the current field of view * of the viewer camera. * * @example * ```js * viewer.getFieldOfView().then(fov => { console.log(fov); }); * ``` */ public getFieldOfView(): Promise<number> { return new Promise<number>( (resolve: (value: number) => void, reject: (reason: Error) => void): void => { this._container.renderService.renderCamera$.pipe( first()) .subscribe( (rc: RenderCamera): void => { resolve(rc.perspective.fov); }, (error: Error): void => { reject(error); }); }); } /** * Get the viewer's current image. * * @returns {Promise<Image>} Promise to the current image. * * @example * ```js * viewer.getImage().then(image => { console.log(image.id); }); * ``` */ public getImage(): Promise<Image> { return new Promise<Image>( (resolve: (image: Image) => void, reject: (reason: Error) => void): void => { this._navigator.stateService.currentImage$.pipe( first()) .subscribe( (image) => { resolve(image); }, (error) => { reject(error); }); } ); } /** * Get the viewer's current point of view. * * @returns {Promise<PointOfView>} Promise to the current point of view * of the viewer camera. * * @example * ```js * viewer.getPointOfView().then(pov => { console.log(pov); }); * ``` */ public getPointOfView(): Promise<PointOfView> { return new Promise<PointOfView>( (resolve: (value: PointOfView) => void, reject: (reason: Error) => void): void => { observableCombineLatest( this._container.renderService.renderCamera$, this._container.renderService.bearing$).pipe( first()) .subscribe( ([rc, bearing]: [RenderCamera, number]): void => { resolve({ bearing: bearing, tilt: rc.getTilt(), }); }, (error: Error): void => { reject(error); }); }); } /** * Get the viewer's current position * * @returns {Promise<LngLat>} Promise to the viewers's current * position. * * @example * ```js * viewer.getPosition().then(pos => { console.log(pos); }); * ``` */ public getPosition(): Promise<LngLat> { return new Promise<LngLat>( (resolve: (value: LngLat) => void, reject: (reason: Error) => void): void => { observableCombineLatest( this._container.renderService.renderCamera$, this._navigator.stateService.reference$).pipe( first()) .subscribe( ([render, reference]: [RenderCamera, LngLatAlt]): void => { resolve(this._observer.projection.cameraToLngLat(render, reference)); }, (error: Error): void => { reject(error); }); }); } /** * Get the viewer's current reference position. * * @description The reference position specifies the origin in * the viewer's topocentric coordinate system. * * @returns {Promise<LngLatAlt>} Promise to the reference position. * * @example * ```js * viewer.getReference().then(reference => { console.log(reference); }); * ``` */ public getReference(): Promise<LngLatAlt> { return new Promise<LngLatAlt>( (resolve: (reference: LngLatAlt) => void, reject: (reason: Error) => void): void => { this._navigator.stateService.reference$.pipe( first()) .subscribe( (reference) => { resolve(reference); }, (error) => { reject(error); }); } ); } /** * Get the image's current zoom level. * * @returns {Promise<number>} Promise to the viewers's current * zoom level. * * @example * ```js * viewer.getZoom().then(z => { console.log(z); }); * ``` */ public getZoom(): Promise<number> { return new Promise<number>( (resolve: (value: number) => void, reject: (reason: Error) => void): void => { this._navigator.stateService.getZoom() .subscribe( (zoom: number): void => { resolve(zoom); }, (error: Error): void => { reject(error); }); }); } /** * Check if a controls instance is the camera controls that are * currently attached to the viewer. * * @param {ICustomCameraControls} controls - Camera controls instance. * @returns {boolean} Value indicating whether the controls instance * is currently attached. */ public hasCustomCameraControls(controls: ICustomCameraControls): boolean { return this._customCameraControls.has(controls); } /** * Check if a custom renderer has been added to the viewer's * rendering pipeline. * * @param {string} id - Unique ID of the custom renderer. * @returns {boolean} Value indicating whether the customer * renderer has been added. */ public hasCustomRenderer(rendererId: string): boolean { return this._customRenderer.has(rendererId); } /** * Navigate in a given direction. * * @param {NavigationDirection} direction - Direction in which which to move. * @returns {Promise<Image>} Promise to the image that was navigated to. * @throws If the current image does not have the edge direction * or the edges has not yet been cached. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent move request * is made before the move dir call has completed. * * @example * ```js * viewer.moveDir(NavigationDirection.Next).then( * image => { console.log(image); }, * error => { console.error(error); }); * ``` */ public moveDir(direction: NavigationDirection): Promise<Image> { const moveDir$: Observable<Image> = this.isNavigable ? this._navigator.moveDir$(direction) : observableThrowError(new Error("Calling moveDir is not supported when viewer is not navigable.")); return new Promise<Image>( (resolve: (value: Image) => void, reject: (reason: Error) => void): void => { moveDir$.subscribe( (image: Image): void => { resolve(image); }, (error: Error): void => { reject(error); }); }); } /** * Navigate to a given image ID. * * @param {string} imageId - Id of the image to move to. * @returns {Promise<Image>} Promise to the image that was navigated to. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent * move request is made before the move to ID call has completed. * * @example * ```js * viewer.moveTo("<my-image-id>").then( * image => { console.log(image); }, * error => { console.error(error); }); * ``` */ public moveTo(imageId: string): Promise<Image> { const moveTo$: Observable<Image> = this.isNavigable ? this._navigator.moveTo$(imageId) : observableThrowError(new Error("Calling moveTo is not supported when viewer is not navigable.")); return new Promise<Image>( (resolve: (value: Image) => void, reject: (reason: Error) => void): void => { moveTo$.subscribe( (image: Image): void => { resolve(image); }, (error: Error): void => { reject(error); }); }); } public off( type: ViewerBearingEvent["type"], handler: (event: ViewerBearingEvent) => void) : void; public off( type: ViewerDataLoadingEvent["type"], handler: (event: ViewerDataLoadingEvent) => void) : void; public off( type: ViewerNavigableEvent["type"], handler: (event: ViewerNavigableEvent) => void) : void; public off( type: ViewerImageEvent["type"], handler: (event: ViewerImageEvent) => void) : void; public off( type: ViewerNavigationEdgeEvent["type"], handler: (event: ViewerNavigationEdgeEvent) => void) : void; public off( type: ViewerReferenceEvent["type"], handler: (event: ViewerReferenceEvent) => void) : void; public off( type: ViewerStateEvent["type"], handler: (event: ViewerStateEvent) => void) : void; public off( type: ViewerMouseEvent["type"], handler: (event: ViewerMouseEvent) => void) : void; public off<T>( type: ViewerEventType, handler: (event: T) => void) : void { super.off(type, handler); } /** * Fired when the viewing direction of the camera changes. * * @event bearing * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("bearing", function() { * console.log("A bearing event has occurred."); * }); * ``` */ public on( type: "bearing", handler: (event: ViewerBearingEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) is * pressed and released at the same point in the viewer. * * @event click * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("click", function() { * console.log("A click event has occurred."); * }); * ``` */ public on( type: "click", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when the right button of the mouse is clicked * within the viewer. * * @event contextmenu * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("contextmenu", function() { * console.log("A contextmenu event has occurred."); * }); * ``` */ public on( type: "contextmenu", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when the viewer is loading data. * * @event loading * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("dataloading", function() { * console.log("A loading event has occurred."); * }); * ``` */ public on( type: "dataloading", handler: (event: ViewerDataLoadingEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) is clicked twice at * the same point in the viewer. * * @event dblclick * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("dblclick", function() { * console.log("A dblclick event has occurred."); * }); * ``` */ public on( type: "dblclick", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when the viewer's vertical field of view changes. * * @event fov * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("fov", function() { * console.log("A fov event has occurred."); * }); * ``` */ public on( type: "fov", handler: (event: ViewerStateEvent) => void) : void; /** * Fired immediately after all necessary resources * have been downloaded and the first visually complete * rendering of the viewer has occurred. * * This event is only fired for viewer configurations where * the WebGL context is created, i.e. not when using the * fallback functionality only. * * @event load * @example * @example * ```js * // Set an event listener * viewer.on('load', function(event) { * console.log('A load event has occured'); * }); * ``` */ public on( type: "load", handler: (event: ViewerLoadEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) is pressed * within the viewer. * * @event mousedown * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("mousedown", function() { * console.log("A mousedown event has occurred."); * }); * ``` */ public on( type: "mousedown", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) * is moved within the viewer. * * @event mousemove * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("mousemove", function() { * console.log("A mousemove event has occurred."); * }); * ``` */ public on( type: "mousemove", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) * leaves the viewer's canvas. * * @event mouseout * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("mouseout", function() { * console.log("A mouseout event has occurred."); * }); * ``` */ public on( type: "mouseout", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) * is moved onto the viewer's canvas. * * @event mouseover * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("mouseover", function() { * console.log("A mouseover event has occurred."); * }); * ``` */ public on( type: "mouseover", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when a pointing device (usually a mouse) * is released within the viewer. * * @event mouseup * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("mouseup", function() { * console.log("A mouseup event has occurred."); * }); * ``` */ public on( type: "mouseup", handler: (event: ViewerMouseEvent) => void) : void; /** * Fired when the viewer motion stops and it is in a fixed * position with a fixed point of view. * * @event moveend * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("moveend", function() { * console.log("A moveend event has occurred."); * }); * ``` */ public on( type: "moveend", handler: (event: ViewerStateEvent) => void) : void; /** * Fired when the motion from one view to another start, * either by changing the position (e.g. when changing image) * or when changing point of view * (e.g. by interaction such as pan and zoom). * * @event movestart * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("movestart", function() { * console.log("A movestart event has occurred."); * }); * ``` */ public on( type: "movestart", handler: (event: ViewerStateEvent) => void) : void; /** * Fired when the navigable state of the viewer changes. * * @event navigable * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("navigable", function() { * console.log("A navigable event has occurred."); * }); * ``` */ public on( type: "navigable", handler: (event: ViewerNavigableEvent) => void) : void; /** * Fired every time the viewer navigates to a new image. * * @event image * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("image", function() { * console.log("A image event has occurred."); * }); * ``` */ public on( type: "image", handler: (event: ViewerImageEvent) => void) : void; /** * Fired when the viewer's position changes. * * @description The viewer's position changes when transitioning * between images. * * @event position * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("position", function() { * console.log("A position event has occurred."); * }); * ``` */ public on( type: "position", handler: (event: ViewerStateEvent) => void) : void; /** * Fired when the viewer's point of view changes. The * point of view changes when the bearing, or tilt changes. * * @event pov * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("pov", function() { * console.log("A pov event has occurred."); * }); * ``` */ public on( type: "pov", handler: (event: ViewerStateEvent) => void) : void; /** * Fired when the viewer's reference position changes. * * The reference position specifies the origin in * the viewer's topocentric coordinate system. * * @event reference * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("reference", function(reference) { * console.log("A reference event has occurred."); * }); * ``` */ public on( type: "reference", handler: (event: ViewerReferenceEvent) => void) : void; /** * Fired when the viewer is removed. After this event is emitted * you must not call any methods on the viewer. * * @event remove * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("remove", function() { * console.log("A remove event has occurred."); * }); * ``` */ public on( type: "remove", handler: (event: ViewerStateEvent) => void) : void; /** * Fired every time the sequence edges of the current image changes. * * @event sequenceedges * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("sequenceedges", function() { * console.log("A sequenceedges event has occurred."); * }); * ``` */ public on( type: "sequenceedges", handler: (event: ViewerNavigationEdgeEvent) => void) : void; /** * Fired every time the spatial edges of the current image changes. * * @event spatialedges * @example * ```js * // Initialize the viewer * var viewer = new Viewer({ // viewer options }); * // Set an event listener * viewer.on("spatialedges", function() { * console.log("A spatialedges event has occurred."); * }); * ``` */ public on( type: "spatialedges", handler: (event: ViewerNavigationEdgeEvent) => void) : void; public on<T>( type: ViewerEventType, handler: (event: T) => void) : void { super.on(type, handler); } /** * Project geodetic coordinates to canvas pixel coordinates. * * @description The geodetic coordinates may not always correspond to pixel * coordinates, e.g. if the geodetic coordinates have a position behind the * viewer camera. In the case of no correspondence the returned value will * be `null`. * * If the distance from the viewer camera position to the provided * longitude-latitude is more than 1000 meters `null` will be returned. * * The projection is performed from the ground plane, i.e. * the altitude with respect to the ground plane for the geodetic * point is zero. * * Note that whenever the camera moves, the result of the method will be * different. * * @param {LngLat} lngLat - Geographical coordinates to project. * @returns {Promise<Array<number>>} Promise to the pixel coordinates corresponding * to the lngLat. * * @example * ```js * viewer.project({ lat: 0, lng: 0 }) * .then(pixelPoint => { * if (!pixelPoint) { * console.log("no correspondence"); * } * * console.log(pixelPoint); * }); * ``` */ public project(lngLat: LngLat): Promise<number[]> { return new Promise<number[]>( (resolve: (value: number[]) => void, reject: (reason: Error) => void): void => { this._observer.project$(lngLat) .subscribe( (pixelPoint: number[]): void => { resolve(pixelPoint); }, (error: Error): void => { reject(error); }); }); } /** * Project basic image coordinates for the current image to canvas pixel * coordinates. * * @description The basic image coordinates may not always correspond to a * pixel point that lies in the visible area of the viewer container. In the * case of no correspondence the returned value can be `null`. * * * @param {Array<number>} basicPoint - Basic images coordinates to project. * @returns {Promise<Array<number>>} Promise to the pixel coordinates corresponding * to the basic image point. * * @example * ```js * viewer.projectFromBasic([0.3, 0.7]) * .then(pixelPoint => { console.log(pixelPoint); }); * ``` */ public projectFromBasic(basicPoint: number[]): Promise<number[]> { return new Promise<number[]>( (resolve: (value: number[]) => void, reject: (reason: Error) => void): void => { this._observer.projectBasic$(basicPoint) .subscribe( (pixelPoint: number[]): void => { resolve(pixelPoint); }, (error: Error): void => { reject(error); }); }); } /** * Clean up and release all internal resources associated with * this viewer. * * @description This includes DOM elements, event bindings, and * WebGL resources. * * Use this method when you are done using the viewer and wish to * ensure that it no longer consumes browser resources. Afterwards, * you must not call any other methods on the viewer. * * @fires remove * * @example * ```js * viewer.remove(); * ``` */ public remove(): void { this._customRenderer.dispose(this); this._customCameraControls.dispose(this); this._observer.dispose(); this._componentController.remove(); this._navigator.dispose(); this._container.remove(); const type: ViewerEventType = "remove"; const event: ViewerStateEvent = { target: this, type, }; this.fire(type, event); } /** * Remove a custom renderer from the viewer's rendering pipeline. * * @param id - Unique ID of the custom renderer. */ public removeCustomRenderer(rendererId: string): void { this._customRenderer.remove(rendererId, this); } /** * Detect the viewer's new width and height and resize it * manually. * * @description The components will also detect the viewer's * new size and resize their rendered elements if needed. * * When the {@link ViewerOptions.trackResize} option is * set to true, the viewer will automatically resize * when the browser window is resized. If any other * custom behavior is preferred, the option should be set * to false and the {@link Viewer.resize} method should * be called on demand. * * @example * ```js * viewer.resize(); * ``` */ public resize(): void { this._container.renderService.resize$.next(); } /** * Set the viewer's camera control mode. * * @description The camera control mode determines * how the camera is controlled when the viewer * receives pointer and keyboard input. * * Changing the camera control mode is not possible * when the slider component is active and attempts * to do so will be ignored. * * @param {CameraControls} controls - Camera control mode. * * @example * ```js * viewer.setCameraControls(CameraControls.Street); * ``` */ public setCameraControls(controls: CameraControls): void { const state = cameraControlsToState(controls); if (state === State.Traversing) { this._navigator.stateService.traverse(); } else if (state === State.Earth) { this._navigator.stateService.earth(); } else if (state === State.Custom) { this._navigator.stateService.custom(); } else { console.warn( `Unsupported camera control transition (${controls})`); } } /** * Set the basic coordinates of the current image to be in the * center of the viewport. * * @description Basic coordinates are 2D coordinates on the [0, 1] interval * and has the origin point, (0, 0), at the top left corner and the * maximum value, (1, 1), at the bottom right corner of the original * image. * * @param {number[]} The basic coordinates of the current * image to be at the center for the viewport. * * @example * ```js * viewer.setCenter([0.5, 0.5]); * ``` */ public setCenter(center: number[]): void { this._navigator.stateService.setCenter(center); } /** * Set the viewer's current vertical field of view. * * @description Sets the vertical field of view rendered * on the viewer canvas measured in degrees. The value * will be clamped to be able to set a valid zoom level * based on the projection model of the current image and * the viewer's current render mode. * * @param {number} fov - Vertical field of view in degrees. * * @example * ```js * viewer.setFieldOfView(45); * ``` */ public setFieldOfView(fov: number): void { this._container.renderService.renderCamera$.pipe( first()) .subscribe( (rc: RenderCamera): void => { const zoom: number = rc.fovToZoom(fov); this._navigator.stateService.setZoom(zoom); }); } /** * Set the filter selecting images to use when calculating * the spatial edges. * * @description The following filter types are supported: * * Comparison * * `["==", key, value]` equality: `image[key] = value` * * `["!=", key, value]` inequality: `image[key] ≠ value` * * `["<", key, value]` less than: `image[key] < value` * * `["<=", key, value]` less than or equal: `image[key] ≤ value` * * `[">", key, value]` greater than: `image[key] > value` * * `[">=", key, value]` greater than or equal: `image[key] ≥ value` * * Set membership * * `["in", key, v0, ..., vn]` set inclusion: `image[key] ∈ {v0, ..., vn}` * * `["!in", key, v0, ..., vn]` set exclusion: `image[key] ∉ {v0, ..., vn}` * * Combining * * `["all", f0, ..., fn]` logical `AND`: `f0 ∧ ... ∧ fn` * * A key must be a string that identifies a property name of a * simple {@link Image} property, i.e. a key of the {@link FilterKey} * type. A value must be a string, number, or * boolean. Strictly-typed comparisons are used. The values * `f0, ..., fn` of the combining filter must be filter expressions. * * Clear the filter by setting it to null or empty array. * * Commonly used filter properties (see the {@link Image} class * documentation for a full list of properties that can be used * in a filter) are shown the the example code. * * @param {FilterExpression} [filter] - The filter expression. * Applied filter is cleared if omitted. * @returns {Promise<void>} Promise that resolves after filter is applied. * * @example * ```js * // Examples * viewer.setFilter(["==", "cameraType", "spherical"]); * viewer.setFilter([">=", "capturedAt", <my-time-stamp>]); * viewer.setFilter(["in", "sequenceId", "<sequence-id-1>", "<sequence-id-2>"]); * ``` */ public setFilt