mapillary-js
Version:
WebGL JavaScript library for displaying street level imagery from mapillary.com
1,123 lines (1,058 loc) • 39.5 kB
text/typescript
import {
throwError as observableThrowError,
combineLatest as observableCombineLatest,
Observable,
} from "rxjs";
import {first} from "rxjs/operators";
import * as when from "when";
import {ILatLon} from "../API";
import {EdgeDirection} from "../Edge";
import {
FilterExpression,
Node,
} from "../Graph";
import {
ComponentController,
Container,
IViewerOptions,
Navigator,
Observer,
} from "../Viewer";
import {
Component,
IComponentConfiguration,
} from "../Component";
import {
EventEmitter,
Settings,
Urls,
} from "../Utils";
import {RenderMode} from "../Render";
import {TransitionMode} from "../State";
import { IPointOfView } from "./interfaces/interfaces";
import RenderCamera from "../render/RenderCamera";
import ILatLonAlt from "../geo/interfaces/ILatLonAlt";
/**
* @class Viewer
*
* @classdesc The Viewer object represents the navigable image viewer.
* Create a Viewer by specifying a container, client ID, image key 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.
*
* The viewer works with a few different coordinate systems.
*
* Container pixel coordinates
*
* Pixel coordinates are coordinates on the viewer container. The origin is
* in the top left corner of the container. The axes are
* directed according to the following for a viewer container with a width
* of 640 pixels and height of 480 pixels.
*
* ```
* (0,0) (640, 0)
* +------------------------>
* |
* |
* |
* v +
* (0, 480) (640, 480)
* ```
*
* Basic image coordinates
*
* Basic image coordinates represents points in the original image adjusted for
* orientation. They range from 0 to 1 on both axes. The origin is in the top left
* corner of the image and the axes are directed
* according to the following for all image types.
*
* ```
* (0,0) (1, 0)
* +------------------------>
* |
* |
* |
* v +
* (0, 1) (1, 1)
* ```
*
* For every camera viewing direction it is possible to convert between these
* two coordinate systems for the current node. The image can be panned and
* zoomed independently of the size of the viewer container resulting in
* different conversion results for different viewing directions.
*/
export class Viewer extends EventEmitter {
/**
* Fired when the viewing direction of the camera changes.
*
* @description Related to the computed compass angle
* ({@link Node.computedCA}) from SfM, not the original EXIF compass
* angle.
*
* @event
* @type {number} bearing - Value indicating the current bearing
* measured in degrees clockwise with respect to north.
*/
public static bearingchanged: string = "bearingchanged";
/**
* Fired when a pointing device (usually a mouse) is pressed and released at
* the same point in the viewer.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static click: string = "click";
/**
* Fired when the right button of the mouse is clicked within the viewer.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static contextmenu: string = "contextmenu";
/**
* Fired when a pointing device (usually a mouse) is clicked twice at
* the same point in the viewer.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static dblclick: string = "dblclick";
/**
* Fired when the viewer's vertical field of view changes.
*
* @event
* @type {@link IViewerEvent} event - The event object.
*/
public static fovchanged: string = "fovchanged";
/**
* Fired when the viewer is loading more data.
* @event
* @type {boolean} loading - Boolean indicating whether the viewer is loading.
*/
public static loadingchanged: string = "loadingchanged";
/**
* Fired when a pointing device (usually a mouse) is pressed within the viewer.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static mousedown: string = "mousedown";
/**
* Fired when a pointing device (usually a mouse) is moved within the viewer.
* @description Will not fire when the mouse is actively used, e.g. for drag pan.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static mousemove: string = "mousemove";
/**
* Fired when a pointing device (usually a mouse) leaves the viewer's canvas.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static mouseout: string = "mouseout";
/**
* Fired when a pointing device (usually a mouse) is moved onto the viewer's canvas.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static mouseover: string = "mouseover";
/**
* Fired when a pointing device (usually a mouse) is released within the viewer.
* @event
* @type {@link IViewerMouseEvent} event - Viewer mouse event data.
*/
public static mouseup: string = "mouseup";
/**
* Fired when the viewer motion stops and it is in a fixed
* position with a fixed point of view.
* @event
*/
public static moveend: string = "moveend";
/**
* Fired when the motion from one view to another start,
* either by changing the position (e.g. when changing node) or
* when changing point of view (e.g. by interaction such as pan and zoom).
* @event
*/
public static movestart: string = "movestart";
/**
* Fired when the navigable state of the viewer changes.
*
* @description The navigable state indicates if the viewer supports
* moving, i.e. calling the `moveToKey`, `moveDir` and `moveCloseTo`
* methods. The viewer will not be in a navigable state if the cover
* is activated and the viewer has been supplied a key. When the cover
* is deactivated or activated without being supplied a key it will
* be navigable.
*
* @event
* @type {boolean} navigable - Boolean indicating whether the viewer is navigable.
*/
public static navigablechanged: string = "navigablechanged";
/**
* Fired every time the viewer navigates to a new node.
*
* @event
* @type {@link Node} node - Current node.
*/
public static nodechanged: string = "nodechanged";
/**
* Fired when the viewer's position changes.
*
* @description The viewer's position changes when transitioning
* between nodes.
*
* @event
* @type {@link IViewerEvent} event - The event object.
*/
public static positionchanged: string = "positionchanged";
/**
* Fired when the viewer's point of view changes. The point of view changes
* when the bearing, or tilt changes.
*
* @event
* @type {@link IViewerEvent} event - The event object.
*/
public static povchanged: string = "povchanged";
/**
* Fired every time the sequence edges of the current node changes.
* @event
* @type {@link IEdgeStatus} status - The edge status object.
*/
public static sequenceedgeschanged: string = "sequenceedgeschanged";
/**
* Fired every time the spatial edges of the current node changes.
* @event
* @type {@link IEdgeStatus} status - The edge status object.
*/
public static spatialedgeschanged: string = "spatialedgeschanged";
/**
* 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 throught
* the vast seas of Mapillary.
*/
private _navigator: Navigator;
/**
* Create a new viewer instance.
*
* @description It is possible to initialize the viewer with or
* without a key.
*
* When you want to show a specific image in the viewer from
* the start you should initialize it with a key.
*
* When you do not know the first image key at implementation
* time, e.g. in a map-viewer application you should initialize
* the viewer without a key and call `moveToKey` instead.
*
* When initializing with a key the viewer is bound to that key
* until the node for that key has been successfully loaded.
* Also, a cover with the image of the key will be shown.
* If the data for that key can not be loaded because the key is
* faulty or other errors occur it is not possible to navigate
* to another key because the viewer is not navigable. The viewer
* becomes navigable when the data for the key 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 key (with null or
* undefined) it is not bound to any particular key and it is
* possible to move to any key with `viewer.moveToKey("<my-image-key>")`.
* If the first move to a key fails it is possible to move to another
* key. 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 key is not known at implementation
* time.
*
* @param {string | HTMLElement} container - The HTML element in which
* MapillaryJS will render the viewer, or the element's string `id`. The
* specified element must have no children.
* @param {string} clientId - Required `Mapillary API ClientID`. Can
* be obtained from https://www.mapillary.com/app/settings/developers.
* @param {string} key - Optional `image-key` to start from. The key
* can be any Mapillary image. If a key is provided the viewer is
* bound to that key until it has been fully loaded. If null is provided
* no image is loaded at viewer initialization and the viewer is not
* bound to any particular key. Any image can then be navigated to
* with e.g. `viewer.moveToKey("<my-image-key>")`.
* @param {IViewerOptions} options - Optional configuration object
* specifing Viewer's and the components' initial setup.
* @param {string} token - Optional bearer token for API requests of
* protected resources.
*
* @example
* ```
* var viewer = new Mapillary.Viewer("<element-id>", "<client-id>", "<image-key>");
* ```
*/
constructor (container: string | HTMLElement, clientId: string, key?: string, options?: IViewerOptions, token?: string) {
super();
options = options != null ? options : {};
Settings.setOptions(options);
Urls.setOptions(options.url);
this._navigator = new Navigator(clientId, options, token);
this._container = new Container(container, this._navigator.stateService, options);
this._observer = new Observer(this, this._navigator, this._container);
this._componentController = new ComponentController(this._container, this._navigator, this._observer, key, options.component);
}
/**
* 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 moveToKey}, {@link moveDir}
* and {@link moveCloseTo} methods or changing the authentication state,
* i.e. calling {@link setAuthToken}. The viewer will not be in a navigable
* state if the cover is activated and the viewer has been supplied a key.
* When the cover is deactivated or the viewer is activated without being
* supplied a key 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 {string} name - Name of the component which will become active.
*
* @example
* ```
* viewer.activateComponent("marker");
* ```
*/
public activateComponent(name: string): void {
this._componentController.activate(name);
}
/**
* Activate the cover (deactivates all other components).
*/
public activateCover(): void {
this._componentController.activateCover();
}
/**
* 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 {string} name - Name of component which become inactive.
*
* @example
* ```
* viewer.deactivateComponent("mouse");
* ```
*/
public deactivateComponent(name: string): void {
this._componentController.deactivate(name);
}
/**
* Deactivate the cover (activates all components marked as active).
*/
public deactivateCover(): void {
this._componentController.deactivateCover();
}
/**
* 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 node 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
* ```
* viewer.getBearing().then((b) => { console.log(b); });
* ```
*/
public getBearing(): when.Promise<number> {
return when.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);
});
});
}
/**
* 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 {HTMLElement} The container viewer's <canvas> element.
*/
public getCanvasContainer(): HTMLElement {
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
* ```
* viewer.getCenter().then((c) => { console.log(c); });
* ```
*/
public getCenter(): when.Promise<number[]> {
return when.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
* ```
* var mouseComponent = viewer.getComponent("mouse");
* ```
*/
public getComponent<TComponent extends Component<IComponentConfiguration>>(name: string): 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.element;
}
/**
* 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
* ```
* viewer.getFieldOfView().then((fov) => { console.log(fov); });
* ```
*/
public getFieldOfView(): when.Promise<number> {
return when.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 point of view.
*
* @returns {Promise<IPointOfView>} Promise to the current point of view
* of the viewer camera.
*
* @example
* ```
* viewer.getPointOfView().then((pov) => { console.log(pov); });
* ```
*/
public getPointOfView(): when.Promise<IPointOfView> {
return when.promise<IPointOfView>(
(resolve: (value: IPointOfView) => 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<ILatLon>} Promise to the viewers's current
* position.
*
* @example
* ```
* viewer.getPosition().then((pos) => { console.log(pos); });
* ```
*/
public getPosition(): when.Promise<ILatLon> {
return when.promise<ILatLon>(
(resolve: (value: ILatLon) => void, reject: (reason: Error) => void): void => {
observableCombineLatest(
this._container.renderService.renderCamera$,
this._navigator.stateService.reference$).pipe(
first())
.subscribe(
([render, reference]: [RenderCamera, ILatLonAlt]): void => {
resolve(this._observer.projection.cameraToLatLon(render, reference));
},
(error: Error): void => {
reject(error);
});
});
}
/**
* Get the image's current zoom level.
*
* @returns {Promise<number>} Promise to the viewers's current
* zoom level.
*
* @example
* ```
* viewer.getZoom().then((z) => { console.log(z); });
* ```
*/
public getZoom(): when.Promise<number> {
return when.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);
});
});
}
/**
* Move close to given latitude and longitude.
*
* @description Because the method propagates IO errors, these potential errors
* need to be handled by the method caller (see example).
*
* @param {Number} lat - Latitude, in degrees.
* @param {Number} lon - Longitude, in degrees.
* @returns {Promise<Node>} Promise to the node that was navigated to.
* @throws {Error} If no nodes exist close to provided latitude
* longitude.
* @throws {Error} Propagates any IO errors to the caller.
* @throws {Error} When viewer is not navigable.
* @throws {@link AbortMapillaryError} When a subsequent move request is made
* before the move close to call has completed.
*
* @example
* ```
* viewer.moveCloseTo(0, 0).then(
* (n) => { console.log(n); },
* (e) => { console.error(e); });
* ```
*/
public moveCloseTo(lat: number, lon: number): when.Promise<Node> {
const moveCloseTo$: Observable<Node> = this.isNavigable ?
this._navigator.moveCloseTo$(lat, lon) :
observableThrowError(new Error("Calling moveCloseTo is not supported when viewer is not navigable."));
return when.promise<Node>(
(resolve: (value: Node) => void, reject: (reason: Error) => void): void => {
moveCloseTo$.subscribe(
(node: Node): void => {
resolve(node);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* Navigate in a given direction.
*
* @description This method has to be called through EdgeDirection enumeration as in the example.
*
* @param {EdgeDirection} dir - Direction in which which to move.
* @returns {Promise<Node>} Promise to the node that was navigated to.
* @throws {Error} If the current node does not have the edge direction
* or the edges has not yet been cached.
* @throws {Error} Propagates any IO errors to the caller.
* @throws {Error} When viewer is not navigable.
* @throws {@link AbortMapillaryError} When a subsequent move request is made
* before the move dir call has completed.
*
* @example
* ```
* viewer.moveDir(Mapillary.EdgeDirection.Next).then(
* (n) => { console.log(n); },
* (e) => { console.error(e); });
* ```
*/
public moveDir(dir: EdgeDirection): when.Promise<Node> {
const moveDir$: Observable<Node> = this.isNavigable ?
this._navigator.moveDir$(dir) :
observableThrowError(new Error("Calling moveDir is not supported when viewer is not navigable."));
return when.promise<Node>(
(resolve: (value: Node) => void, reject: (reason: Error) => void): void => {
moveDir$.subscribe(
(node: Node): void => {
resolve(node);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* Navigate to a given image key.
*
* @param {string} key - A valid Mapillary image key.
* @returns {Promise<Node>} Promise to the node that was navigated to.
* @throws {Error} Propagates any IO errors to the caller.
* @throws {Error} When viewer is not navigable.
* @throws {@link AbortMapillaryError} When a subsequent move request is made
* before the move to key call has completed.
*
* @example
* ```
* viewer.moveToKey("<my key>").then(
* (n) => { console.log(n); },
* (e) => { console.error(e); });
* ```
*/
public moveToKey(key: string): when.Promise<Node> {
const moveToKey$: Observable<Node> = this.isNavigable ?
this._navigator.moveToKey$(key) :
observableThrowError(new Error("Calling moveToKey is not supported when viewer is not navigable."));
return when.promise<Node>(
(resolve: (value: Node) => void, reject: (reason: Error) => void): void => {
moveToKey$.subscribe(
(node: Node): void => {
resolve(node);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* Project an ILatLon representing geographicalcoordinates to
* canvas pixel coordinates.
*
* @description The geographical coordinates may not always correspond to pixel
* coordinates, e.g. if the geographical 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 lat-lon
* 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 geographical
* point is zero.
*
* Note that whenever the camera moves, the result of the method will be
* different.
*
* @param {ILatLon} latLon - Geographical coordinates to project.
* @returns {Promise<Array<number>>} Promise to the pixel coordinates corresponding
* to the latLon.
*
* @example
* ```
* viewer.project({ lat: 0, lon: 0 })
* .then((pixelPoint) => {
* if (!pixelPoint) {
* console.log("no correspondence");
* }
*
* console.log(pixelPoint);
* });
* ```
*/
public project(latLon: ILatLon): when.Promise<number[]> {
return when.promise<number[]>(
(resolve: (value: number[]) => void, reject: (reason: Error) => void): void => {
this._observer.project$(latLon)
.subscribe(
(pixelPoint: number[]): void => {
resolve(pixelPoint);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* Project basic image coordinates for the current node 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
* ```
* viewer.projectFromBasic([0.3, 0.7])
* .then((pixelPoint) => { console.log(pixelPoint); });
* ```
*/
public projectFromBasic(basicPoint: number[]): when.Promise<number[]> {
return when.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);
});
});
}
/**
* Detect the viewer's new width and height and resize it.
*
* @description The components will also detect the viewer's
* new size and resize their rendered elements if needed.
*
* @example
* ```
* viewer.resize();
* ```
*/
public resize(): void {
this._container.renderService.resize$.next(null);
}
/**
* Set a bearer token for authenticated API requests of
* protected resources.
*
* @description When the supplied token is null or undefined,
* any previously set bearer token will be cleared and the
* viewer will make unauthenticated requests.
*
* Calling setAuthToken aborts all outstanding move requests.
* The promises of those move requests will be rejected with a
* {@link AbortMapillaryError} the rejections need to be caught.
*
* Calling setAuthToken also resets the complete viewer cache
* so it should not be called repeatedly.
*
* @param {string} [token] token - Bearer token.
* @returns {Promise<void>} Promise that resolves after token
* is set.
*
* @throws {Error} When viewer is not navigable.
*
* @example
* ```
* viewer.setAuthToken("<my token>")
* .then(() => { console.log("token set"); });
* ```
*/
public setAuthToken(token?: string): when.Promise<void> {
const setToken$: Observable<void> = this.isNavigable ?
this._navigator.setToken$(token) :
observableThrowError(new Error("Calling setAuthToken is not supported when viewer is not navigable."));
return when.promise<void>(
(resolve: (value: void) => void, reject: (reason: Error) => void): void => {
setToken$
.subscribe(
(): void => {
resolve(undefined);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* 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
* ```
* viewer.setCenter([0.5, 0.5]);
* ```
*/
public setCenter(center: number[]): void {
this._navigator.stateService.setCenter(center);
}
/**
* Set the filter selecting nodes to use when calculating
* the spatial edges.
*
* @description The following filter types are supported:
*
* Comparison
*
* `["==", key, value]` equality: `node[key] = value`
*
* `["!=", key, value]` inequality: `node[key] ≠ value`
*
* `["<", key, value]` less than: `node[key] < value`
*
* `["<=", key, value]` less than or equal: `node[key] ≤ value`
*
* `[">", key, value]` greater than: `node[key] > value`
*
* `[">=", key, value]` greater than or equal: `node[key] ≥ value`
*
* Set membership
*
* `["in", key, v0, ..., vn]` set inclusion: `node[key] ∈ {v0, ..., vn}`
*
* `["!in", key, v0, ..., vn]` set exclusion: `node[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 Node} property. 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 Node} class
* documentation for a full list of properties that can be used
* in a filter) and common use cases:
*
* ```
* fullPano // Show only full 360 panoramas or not
* organizationKey // Show images from one or several organizations
* sequenceKey // Show images from one or several sequences
* userKey // Show images from one or several users
* capturedAt // Show images from a certain time interval
* ```
*
* @param {FilterExpression} filter - The filter expression.
* @returns {Promise<void>} Promise that resolves after filter is applied.
*
* @example
* ```
* viewer.setFilter(["==", "sequenceKey", "<my sequence key>"]);
*
* // Other examples
* // viewer.setFilter(["==", "organizationKey", "<my organization key>"]);
* // viewer.setFilter(["in", "userKey", "<my user key #1>", "<my user key #2>"]);
* // viewer.setFilter(["==", "fullPano", true]);
* // viewer.setFilter([">=", "capturedAt", <my time stamp>]);
* ```
*/
public setFilter(filter: FilterExpression): when.Promise<void> {
return when.promise<void>(
(resolve: (value: void) => void, reject: (reason: Error) => void): void => {
this._navigator.setFilter$(filter)
.subscribe(
(): void => {
resolve(undefined);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* 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
* ```
* 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 viewer's render mode.
*
* @param {RenderMode} renderMode - Render mode.
*
* @example
* ```
* viewer.setRenderMode(Mapillary.RenderMode.Letterbox);
* ```
*/
public setRenderMode(renderMode: RenderMode): void {
this._container.renderService.renderMode$.next(renderMode);
}
/**
* Set the viewer's transition mode.
*
* @param {TransitionMode} transitionMode - Transition mode.
*
* @example
* ```
* viewer.setTransitionMode(Mapillary.TransitionMode.Instantaneous);
* ```
*/
public setTransitionMode(transitionMode: TransitionMode): void {
this._navigator.stateService.setTransitionMode(transitionMode);
}
/**
* Set the image's current zoom level.
*
* @description Possible zoom level values are on the [0, 3] interval.
* Zero means zooming out to fit the image to the view whereas three
* shows the highest level of detail.
*
* @param {number} The image's current zoom level.
*
* @example
* ```
* viewer.setZoom(2);
* ```
*/
public setZoom(zoom: number): void {
this._navigator.stateService.setZoom(zoom);
}
/**
* Unproject canvas pixel coordinates to an ILatLon representing geographical
* coordinates.
*
* @description The pixel point may not always correspond to geographical
* coordinates. In the case of no correspondence the returned value will
* be `null`.
*
* The unprojection to a latLon will be performed towards the ground plane, i.e.
* the altitude with respect to the ground plane for the returned latLon is zero.
*
* @param {Array<number>} pixelPoint - Pixel coordinates to unproject.
* @returns {Promise<ILatLon>} Promise to the latLon corresponding to the pixel point.
*
* @example
* ```
* viewer.unproject([100, 100])
* .then((latLon) => { console.log(latLon); });
* ```
*/
public unproject(pixelPoint: number[]): when.Promise<ILatLon> {
return when.promise<ILatLon>(
(resolve: (value: ILatLon) => void, reject: (reason: Error) => void): void => {
this._observer.unproject$(pixelPoint)
.subscribe(
(latLon: ILatLon): void => {
resolve(latLon);
},
(error: Error): void => {
reject(error);
});
});
}
/**
* Unproject canvas pixel coordinates to basic image coordinates for the
* current node.
*
* @description The pixel point may not always correspond to basic image
* coordinates. In the case of no correspondence the returned value will
* be `null`.
*
* @param {Array<number>} pixelPoint - Pixel coordinates to unproject.
* @returns {Promise<ILatLon>} Promise to the basic coordinates corresponding
* to the pixel point.
*
* @example
* ```
* viewer.unprojectToBasic([100, 100])
* .then((basicPoint) => { console.log(basicPoint); });
* ```
*/
public unprojectToBasic(pixelPoint: number[]): when.Promise<number[]> {
return when.promise<number[]>(
(resolve: (value: number[]) => void, reject: (reason: Error) => void): void => {
this._observer.unprojectBasic$(pixelPoint)
.subscribe(
(basicPoint: number[]): void => {
resolve(basicPoint);
},
(error: Error): void => {
reject(error);
});
});
}
}