UNPKG

mapillary-js

Version:

WebGL JavaScript library for displaying street level imagery from mapillary.com

711 lines (650 loc) 17.9 kB
import {map} from "rxjs/operators"; import {Observable} from "rxjs"; import { ICoreNode, IFillNode, IGPano, ILatLon, } from "../API"; import {IEdge} from "../Edge"; import { IEdgeStatus, IMesh, ILoadStatus, NodeCache, } from "../Graph"; import {ImageSize} from "../Viewer"; /** * @class Node * * @classdesc Represents a node in the navigation graph. * * Explanation of position and bearing properties: * * When images are uploaded they will have GPS information in the EXIF, this is what * is called `originalLatLon` {@link Node.originalLatLon}. * * When Structure from Motions has been run for a node a `computedLatLon` that * differs from the `originalLatLon` will be created. It is different because * GPS positions are not very exact and SfM aligns the camera positions according * to the 3D reconstruction {@link Node.computedLatLon}. * * At last there exist a `latLon` property which evaluates to * the `computedLatLon` from SfM if it exists but falls back * to the `originalLatLon` from the EXIF GPS otherwise {@link Node.latLon}. * * Everything that is done in in the Viewer is based on the SfM positions, * i.e. `computedLatLon`. That is why the smooth transitions go in the right * direction (nd not in strange directions because of bad GPS). * * E.g. when placing a marker in the Viewer it is relative to the SfM * position i.e. the `computedLatLon`. * * The same concept as above also applies to the compass angle (or bearing) properties * `originalCa`, `computedCa` and `ca`. */ export class Node { private _cache: NodeCache; private _core: ICoreNode; private _fill: IFillNode; /** * Create a new node instance. * * @description Nodes are always created internally by the library. * Nodes can not be added to the library through any API method. * * @param {ICoreNode} coreNode - Raw core node data. * @ignore */ constructor(core: ICoreNode) { this._cache = null; this._core = core; this._fill = null; } /** * Get assets cached. * * @description The assets that need to be cached for this property * to report true are the following: fill properties, image and mesh. * The library ensures that the current node will always have the * assets cached. * * @returns {boolean} Value indicating whether all assets have been * cached. * * @ignore */ public get assetsCached(): boolean { return this._core != null && this._fill != null && this._cache != null && this._cache.image != null && this._cache.mesh != null; } /** * Get alt. * * @description If SfM has not been run the computed altitude is * set to a default value of two meters. * * @returns {number} Altitude, in meters. */ public get alt(): number { return this._fill.calt; } /** * Get ca. * * @description If the SfM computed compass angle exists it will * be returned, otherwise the original EXIF compass angle. * * @returns {number} Compass angle, measured in degrees * clockwise with respect to north. */ public get ca(): number { return this._fill.cca != null ? this._fill.cca : this._fill.ca; } /** * Get cameraProjection. * * @description Will be undefined if SfM has not been run. * * @returns {number} The camera projection of the image. */ public get cameraProjection(): "perspective" | "fisheye" | "equirectangular" { return this._fill.camera_projection_type; } /** * Get capturedAt. * * @returns {number} Timestamp when the image was captured. */ public get capturedAt(): number { return this._fill.captured_at; } /** * Get camera uuid. * * @description Will be undefined if the camera uuid was not * recorded in the image exif information. * * @returns {string} Universally unique id for camera used * when capturing image. */ public get cameraUuid(): string { return this._fill.captured_with_camera_uuid; } /** * Get clusterKey. * * @returns {string} Unique key of the SfM cluster to which * the node belongs. */ public get clusterKey(): string { return this._fill.cluster_key; } /** * Get ck1. * * @description Will not be set if SfM has not been run. * * @returns {number} SfM computed radial distortion parameter * k1. */ public get ck1(): number { return this._fill.ck1; } /** * Get ck2. * * @description Will not be set if SfM has not been run. * * @returns {number} SfM computed radial distortion parameter * k2. */ public get ck2(): number { return this._fill.ck2; } /** * Get computedCA. * * @description Will not be set if SfM has not been run. * * @returns {number} SfM computed compass angle, measured * in degrees clockwise with respect to north. */ public get computedCA(): number { return this._fill.cca; } /** * Get computedLatLon. * * @description Will not be set if SfM has not been run. * * @returns {ILatLon} SfM computed latitude longitude in WGS84 datum, * measured in degrees. */ public get computedLatLon(): ILatLon { return this._core.cl; } /** * Get focal. * * @description Will not be set if SfM has not been run. * * @returns {number} SfM computed focal length. */ public get focal(): number { return this._fill.cfocal; } /** * Get full. * * @description The library ensures that the current node will * always be full. * * @returns {boolean} Value indicating whether the node has all * properties filled. * * @ignore */ public get full(): boolean { return this._fill != null; } /** * Get fullPano. * * @returns {boolean} Value indicating whether the node is a complete * 360 panorama. */ public get fullPano(): boolean { return this._fill.gpano != null && this._fill.gpano.CroppedAreaLeftPixels === 0 && this._fill.gpano.CroppedAreaTopPixels === 0 && this._fill.gpano.CroppedAreaImageWidthPixels === this._fill.gpano.FullPanoWidthPixels && this._fill.gpano.CroppedAreaImageHeightPixels === this._fill.gpano.FullPanoHeightPixels; } /** * Get gpano. * * @description Will not be set for non panoramic images. * * @returns {IGPano} Panorama information for panorama images. */ public get gpano(): IGPano { return this._fill.gpano; } /** * Get height. * * @returns {number} Height of original image, not adjusted * for orientation. */ public get height(): number { return this._fill.height; } /** * Get image. * * @description The image will always be set on the current node. * * @returns {HTMLImageElement} Cached image element of the node. */ public get image(): HTMLImageElement { return this._cache.image; } /** * Get image$. * * @returns {Observable<HTMLImageElement>} Observable emitting * the cached image when it is updated. * * @ignore */ public get image$(): Observable<HTMLImageElement> { return this._cache.image$; } /** * Get key. * * @returns {string} Unique key of the node. */ public get key(): string { return this._core.key; } /** * Get latLon. * * @description If the SfM computed latitude longitude exist * it will be returned, otherwise the original EXIF latitude * longitude. * * @returns {ILatLon} Latitude longitude in WGS84 datum, * measured in degrees. */ public get latLon(): ILatLon { return this._core.cl != null ? this._core.cl : this._core.l; } /** * Get loadStatus. * * @returns {ILoadStatus} Value indicating the load status * of the mesh and image. * * @ignore */ public get loadStatus(): ILoadStatus { return this._cache.loadStatus; } /** * Get merged. * * @returns {boolean} Value indicating whether SfM has been * run on the node and the node has been merged into a * connected component. */ public get merged(): boolean { return this._fill != null && this._fill.merge_version != null && this._fill.merge_version > 0; } /** * Get mergeCC. * * @description Will not be set if SfM has not yet been run on * node. * * @returns {number} SfM connected component key to which * image belongs. */ public get mergeCC(): number { return this._fill.merge_cc; } /** * Get mergeVersion. * * @returns {number} Version for which SfM was run and image was merged. */ public get mergeVersion(): number { return this._fill.merge_version; } /** * Get mesh. * * @description The mesh will always be set on the current node. * * @returns {IMesh} SfM triangulated mesh of reconstructed * atomic 3D points. */ public get mesh(): IMesh { return this._cache.mesh; } /** * Get organizationKey. * * @returns {string} Unique key of the organization to which * the node belongs. If the node does not belong to an * organization the organization key will be undefined. */ public get organizationKey(): string { return this._fill.organization_key; } /** * Get orientation. * * @returns {number} EXIF orientation of original image. */ public get orientation(): number { return this._fill.orientation; } /** * Get originalCA. * * @returns {number} Original EXIF compass angle, measured in * degrees. */ public get originalCA(): number { return this._fill.ca; } /** * Get originalLatLon. * * @returns {ILatLon} Original EXIF latitude longitude in * WGS84 datum, measured in degrees. */ public get originalLatLon(): ILatLon { return this._core.l; } /** * Get pano. * * @returns {boolean} Value indicating whether the node is a panorama. * It could be a cropped or full panorama. */ public get pano(): boolean { return this._fill.gpano != null && this._fill.gpano.FullPanoWidthPixels != null; } /** * Get private. * * @returns {boolean} Value specifying if image is accessible to * organization members only or to everyone. */ public get private(): boolean { return this._fill.private; } /** * Get projectKey. * * @returns {string} Unique key of the project to which * the node belongs. If the node does not belong to a * project the project key will be undefined. * * @deprecated This property will be deprecated in favor * of the organization key and private properties. */ public get projectKey(): string { return this._fill.project != null ? this._fill.project.key : null; } /** * Get rotation. * * @description Will not be set if SfM has not been run. * * @returns {Array<number>} Rotation vector in angle axis representation. */ public get rotation(): number[] { return this._fill.c_rotation; } /** * Get scale. * * @description Will not be set if SfM has not been run. * * @returns {number} Scale of atomic reconstruction. */ public get scale(): number { return this._fill.atomic_scale; } /** * Get sequenceKey. * * @returns {string} Unique key of the sequence to which * the node belongs. */ public get sequenceKey(): string { return this._core.sequence_key; } /** * Get sequenceEdges. * * @returns {IEdgeStatus} Value describing the status of the * sequence edges. * * @ignore */ public get sequenceEdges(): IEdgeStatus { return this._cache.sequenceEdges; } /** * Get sequenceEdges$. * * @description Internal observable, should not be used as an API. * * @returns {Observable<IEdgeStatus>} Observable emitting * values describing the status of the sequence edges. * * @ignore */ public get sequenceEdges$(): Observable<IEdgeStatus> { return this._cache.sequenceEdges$; } /** * Get spatialEdges. * * @returns {IEdgeStatus} Value describing the status of the * spatial edges. * * @ignore */ public get spatialEdges(): IEdgeStatus { return this._cache.spatialEdges; } /** * Get spatialEdges$. * * @description Internal observable, should not be used as an API. * * @returns {Observable<IEdgeStatus>} Observable emitting * values describing the status of the spatial edges. * * @ignore */ public get spatialEdges$(): Observable<IEdgeStatus> { return this._cache.spatialEdges$; } /** * Get userKey. * * @returns {string} Unique key of the user who uploaded * the image. */ public get userKey(): string { return this._fill.user.key; } /** * Get username. * * @returns {string} Username of the user who uploaded * the image. */ public get username(): string { return this._fill.user.username; } /** * Get width. * * @returns {number} Width of original image, not * adjusted for orientation. */ public get width(): number { return this._fill.width; } /** * Cache the image and mesh assets. * * @description The assets are always cached internally by the * library prior to setting a node as the current node. * * @returns {Observable<Node>} Observable emitting this node whenever the * load status has changed and when the mesh or image has been fully loaded. * * @ignore */ public cacheAssets$(): Observable<Node> { return this._cache.cacheAssets$(this.key, this.pano, this.merged).pipe( map( (): Node => { return this; })); } /** * Cache the image asset. * * @description Use for caching a differently sized image than * the one currently held by the node. * * @returns {Observable<Node>} Observable emitting this node whenever the * load status has changed and when the mesh or image has been fully loaded. * * @ignore */ public cacheImage$(imageSize: ImageSize): Observable<Node> { return this._cache.cacheImage$(this.key, imageSize).pipe( map( (): Node => { return this; })); } /** * Cache the sequence edges. * * @description The sequence edges are cached asynchronously * internally by the library. * * @param {Array<IEdge>} edges - Sequence edges to cache. * @ignore */ public cacheSequenceEdges(edges: IEdge[]): void { this._cache.cacheSequenceEdges(edges); } /** * Cache the spatial edges. * * @description The spatial edges are cached asynchronously * internally by the library. * * @param {Array<IEdge>} edges - Spatial edges to cache. * @ignore */ public cacheSpatialEdges(edges: IEdge[]): void { this._cache.cacheSpatialEdges(edges); } /** * Dispose the node. * * @description Disposes all cached assets. * @ignore */ public dispose(): void { if (this._cache != null) { this._cache.dispose(); this._cache = null; } this._core = null; this._fill = null; } /** * Initialize the node cache. * * @description The node cache is initialized internally by * the library. * * @param {NodeCache} cache - The node cache to set as cache. * @ignore */ public initializeCache(cache: NodeCache): void { if (this._cache != null) { throw new Error(`Node cache already initialized (${this.key}).`); } this._cache = cache; } /** * Fill the node with all properties. * * @description The node is filled internally by * the library. * * @param {IFillNode} fill - The fill node struct. * @ignore */ public makeFull(fill: IFillNode): void { if (fill == null) { throw new Error("Fill can not be null."); } this._fill = fill; } /** * Reset the sequence edges. * * @ignore */ public resetSequenceEdges(): void { this._cache.resetSequenceEdges(); } /** * Reset the spatial edges. * * @ignore */ public resetSpatialEdges(): void { this._cache.resetSpatialEdges(); } /** * Clears the image and mesh assets, aborts * any outstanding requests and resets edges. * * @ignore */ public uncache(): void { if (this._cache == null) { return; } this._cache.dispose(); this._cache = null; } } export default Node;