UNPKG

mapillary-js

Version:

A WebGL interactive street imagery library

299 lines (265 loc) 9.9 kB
import { LineBasicMaterial, Object3D, Scene, } from "three"; import { CameraFrameBase, CameraFrameParameters } from "./CameraFrameBase"; import { PositionLine, PositionLineParameters } from "./PositionLine"; import { SpatialIntersection } from "./SpatialIntersection"; import { Image } from "../../../graph/Image"; import { Transform } from "../../../geo/Transform"; import { OriginalPositionMode } from "../enums/OriginalPositionMode"; import { FilterFunction } from "../../../graph/FilterCreator"; import { CameraVisualizationMode } from "../enums/CameraVisualizationMode"; import { isSpherical } from "../../../geo/Geo"; import { SphericalCameraFrame } from "./SphericalCameraFrame"; import { PerspectiveCameraFrame } from "./PerspectiveCameraFrame"; import { LngLatAlt } from "../../../api/interfaces/LngLatAlt"; import { resetEnu } from "../SpatialCommon"; type ColorIdCamerasMap = Map<string, CameraFrameBase[]>; type ImageIdMap = { ccId: string; clusterId: string; sequenceId: string; }; type ImageProps = { image: Image; idMap: ImageIdMap; }; type ImageVisualizationProps = { id: string; color: string; transform: Transform; originalPosition: number[]; scale: number; maxSize: number; visible: boolean; positionMode: OriginalPositionMode; }; export class SpatialCell { public readonly cameras: Object3D; public readonly keys: string[]; public clusterVisibles: { [key: string]: boolean; }; private readonly _positions: Object3D; private readonly _positionLines: { [key: string]: PositionLine; }; private readonly _cameraFrames: { [key: string]: CameraFrameBase; }; private readonly _clusters: ColorIdCamerasMap; private readonly _connectedComponents: ColorIdCamerasMap; private readonly _sequences: ColorIdCamerasMap; private readonly _props: { [id: string]: { image: Image, ids: ImageIdMap, }; }; private _frameMaterial: LineBasicMaterial; private _positionMaterial: LineBasicMaterial; constructor( public readonly id: string, private _scene: Scene, private _intersection: SpatialIntersection) { this.cameras = new Object3D(); this.keys = []; this._positionLines = {}; this._positions = new Object3D(); this._cameraFrames = {}; this._clusters = new Map(); this._connectedComponents = new Map(); this._sequences = new Map(); this._props = {}; this.clusterVisibles = {}; this._frameMaterial = new LineBasicMaterial({ fog: false, vertexColors: true, }); this._positionMaterial = new LineBasicMaterial({ fog: false, color: 0xff0000, }); this._scene.add( this.cameras, this._positions); } public addImage(props: ImageProps): void { const image = props.image; const id = image.id; if (this.hasImage(id)) { throw new Error(`Image exists ${id}`); } const ccId = props.idMap.ccId; if (!(this._connectedComponents.has(ccId))) { this._connectedComponents.set(ccId, []); } const cId = props.idMap.clusterId; if (!this._clusters.has(cId)) { this._clusters.set(cId, []); } const sId = props.idMap.sequenceId; if (!this._sequences.has(sId)) { this._sequences.set(sId, []); } this._props[id] = { image: image, ids: { ccId, clusterId: cId, sequenceId: sId }, }; this.keys.push(id); } public applyCameraColor(imageId: string, color: string): void { this._cameraFrames[imageId].setColor(color); } public applyCameraSize(size: number): void { for (const camera of this.cameras.children) { (<CameraFrameBase>camera).resize(size); } } public applyFilter(filter: FilterFunction): void { const clusterVisibles = this.clusterVisibles; for (const clusterId in clusterVisibles) { if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; } clusterVisibles[clusterId] = false; } const cameraFrames = this._cameraFrames; const positionLines = this._positionLines; const interactiveLayer = this._intersection.interactiveLayer; for (const props of Object.values(this._props)) { const image = props.image; const visible = filter(image); const key = image.id; positionLines[key].visible = visible; const camera = cameraFrames[key]; this._setCameraVisibility(camera, visible, interactiveLayer); clusterVisibles[props.ids.clusterId] ||= visible; } } public applyPositionMode(mode: OriginalPositionMode): void { this._positions.visible = mode !== OriginalPositionMode.Hidden; for (const position of this._positions.children) { (<PositionLine>position).setMode(mode); } } public dispose(): void { this._disposeCameras(); this._disposePositions(); this._scene = null; this._intersection = null; } public getCamerasByMode( mode: CameraVisualizationMode): ColorIdCamerasMap { if (mode === CameraVisualizationMode.Cluster) { return this._clusters; } else if (mode === CameraVisualizationMode.ConnectedComponent) { return this._connectedComponents; } else if (mode === CameraVisualizationMode.Sequence) { return this._sequences; } const cvm = CameraVisualizationMode; const defaultId = cvm[cvm.Homogeneous]; const cameras = <ColorIdCamerasMap>new Map(); cameras.set(defaultId, <CameraFrameBase[]>this.cameras.children); return cameras; } public getColorId(imageId: string, mode: CameraVisualizationMode): string { const props = this._props[imageId]; const cvm = CameraVisualizationMode; switch (mode) { case cvm.Cluster: return props.ids.clusterId; case cvm.ConnectedComponent: return props.ids.ccId; case cvm.Sequence: return props.ids.sequenceId; default: return cvm[cvm.Homogeneous]; } } public hasImage(key: string): boolean { return this.keys.indexOf(key) !== -1; } public resetReference( reference: LngLatAlt, prevReference: LngLatAlt) : void { const frames = this._cameraFrames; for (const frameId in frames) { if (!frames.hasOwnProperty(frameId)) { continue; } const frame = frames[frameId]; frame.position.fromArray(resetEnu( reference, frame.position.toArray(), prevReference)); } const lines = this._positionLines; for (const lineId in lines) { if (!lines.hasOwnProperty(lineId)) { continue; } const line = lines[lineId]; line.position.fromArray(resetEnu( reference, line.position.toArray(), prevReference)); } } public visualize(props: ImageVisualizationProps): void { const id = props.id; const visible = props.visible; const transform = props.transform; const cameraParameters: CameraFrameParameters = { color: props.color, material: this._frameMaterial, scale: props.scale, size: props.maxSize, transform, }; const camera = isSpherical(transform.cameraType) ? new SphericalCameraFrame(cameraParameters) : new PerspectiveCameraFrame(cameraParameters); const interactiveLayer = this._intersection.interactiveLayer; this._setCameraVisibility(camera, visible, interactiveLayer); this.cameras.add(camera); this._cameraFrames[id] = camera; const intersection = this._intersection; intersection.add(camera, id); const ids = this._props[id].ids; this.clusterVisibles[ids.clusterId] ||= visible; this._connectedComponents.get(ids.ccId).push(camera); this._clusters.get(ids.clusterId).push(camera); this._sequences.get(ids.sequenceId).push(camera); const positionParameters: PositionLineParameters = { material: this._positionMaterial, mode: props.positionMode, originalOrigin: props.originalPosition, transform, }; const position = new PositionLine(positionParameters); position.visible = visible; this._positions.add(position); this._positionLines[id] = position; } private _disposeCameras(): void { const intersection = this._intersection; const cameras = this.cameras; for (const camera of cameras.children.slice()) { (<CameraFrameBase>camera).dispose(); intersection.remove(camera); cameras.remove(camera); } this._scene.remove(this.cameras); } private _disposePositions(): void { const positions = this._positions; for (const position of positions.children.slice()) { (<PositionLine>position).dispose(); positions.remove(position); } this._scene.remove(this._positions); } private _setCameraVisibility( camera: Object3D, visible: boolean, layer: number): void { camera.visible = visible; if (visible) { camera.layers.enable(layer); } else { camera.layers.disable(layer); } } };