UNPKG

mapillary-js

Version:

A WebGL interactive street imagery library

295 lines (260 loc) 9.38 kB
import { MapillaryError } from '../../error/MapillaryError'; import { decompress, fetchArrayBuffer, readMeshPbf, xhrFetch, XMLHttpRequestHeader, } from '../Common'; import { ClusterContract } from '../contracts/ClusterContract'; import { CoreImagesContract } from '../contracts/CoreImagesContract'; import { EntContract } from '../contracts/EntContract'; import { ImagesContract } from '../contracts/ImagesContract'; import { ImageTilesContract } from '../contracts/ImageTilesContract'; import { ImageTilesRequestContract } from '../contracts/ImageTilesRequestContract'; import { MeshContract } from '../contracts/MeshContract'; import { SequenceContract } from '../contracts/SequenceContract'; import { SpatialImagesContract } from '../contracts/SpatialImagesContract'; import { DataProviderBase } from '../DataProviderBase'; import { IDEnt } from '../ents/IDEnt'; import { ImageEnt } from '../ents/ImageEnt'; import { ImageTileEnt } from '../ents/ImageTileEnt'; import { SpatialImageEnt } from '../ents/SpatialImageEnt'; import { IGeometryProvider } from '../interfaces/IGeometryProvider'; import { S2GeometryProvider } from '../S2GeometryProvider'; import { GraphClusterContract, GraphContract, GraphError, } from './GraphContracts'; import { GraphConverter } from './GraphConverter'; import { GraphDataProviderOptions } from './GraphDataProviderOptions'; import { GraphCoreImageEnt, GraphImageEnt, GraphSpatialImageEnt, } from './GraphEnts'; import { GraphQueryCreator } from './GraphQueryCreator'; export class GraphDataProvider extends DataProviderBase { private readonly _method: "GET"; private readonly _endpoint: string; private readonly _convert: GraphConverter; private readonly _query: GraphQueryCreator; private _accessToken: string | undefined; constructor( options?: GraphDataProviderOptions, geometry?: IGeometryProvider, converter?: GraphConverter, queryCreator?: GraphQueryCreator) { super(geometry ?? new S2GeometryProvider()); this._convert = converter ?? new GraphConverter(); this._query = queryCreator ?? new GraphQueryCreator(); this._method = 'GET'; const opts = options ?? {}; this._endpoint = opts.endpoint ?? "https://graph.mapillary.com"; this._accessToken = opts.accessToken; } public getCluster( url: string, abort?: Promise<void>) : Promise<ClusterContract> { return fetchArrayBuffer(url, abort) .then( (buffer: ArrayBuffer) => { const reconstructions = <GraphClusterContract[]> decompress(buffer); if (reconstructions.length < 1) { throw new Error('Cluster reconstruction empty'); } return this._convert .clusterReconstruction(reconstructions[0]); }); } public getCoreImages( cellId: string) : Promise<CoreImagesContract> { const fields = [ ...this._query.idFields, ...this._query.coreFields, ]; const query = this._query.imagesS2(cellId, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract<GraphCoreImageEnt[]>( query, url) .then(r => { const result: CoreImagesContract = { cell_id: cellId, images: [], }; const items = r.data; for (const item of items) { const coreImage = this._convert.coreImage(item); result.images.push(coreImage); } return result; }); } public getImageBuffer( url: string, abort?: Promise<void>) : Promise<ArrayBuffer> { return fetchArrayBuffer(url, abort); } public getImages( imageIds: string[]) : Promise<ImagesContract> { const fields = [ ...this._query.idFields, ...this._query.coreFields, ...this._query.spatialFields, ]; const query = this._query.images(imageIds, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract<GraphImageEnt[]>( query, url) .then(r => { const result: ImagesContract = []; const items = r.data; for (const item of items) { const coreImage = this._convert.coreImage(item); const spatialImage = this._convert.spatialImage(item); const image = Object.assign({}, spatialImage, coreImage); const contract: EntContract<ImageEnt> = { node: image, node_id: item.id, }; result.push(contract); } return result; }); } public getImageTiles( request: ImageTilesRequestContract) : Promise<ImageTilesContract> { const fields = [ ...this._query.imageTileFields, ]; const query = this._query.imageTiles(request.z, fields); const url = new URL( this._query.imageTilesPath(request.imageId), this._endpoint).href; return this ._fetchGraphContract<ImageTileEnt[]>( query, url) .then(r => { const result: ImageTilesContract = { node: r.data, node_id: request.imageId, }; return result; }); } public getMesh( url: string, abort?: Promise<void>) : Promise<MeshContract> { return fetchArrayBuffer(url, abort) .then( (buffer: ArrayBuffer) => { return readMeshPbf(buffer); }); } public getSequence( sequenceId: string) : Promise<SequenceContract> { const query = this._query.sequence(sequenceId); const url = new URL(this._query.sequencePath, this._endpoint).href; return this ._fetchGraphContract<IDEnt[]>( query, url) .then(r => { const result: SequenceContract = { id: sequenceId, image_ids: r.data.map(item => item.id), }; return result; }); } public getSpatialImages( imageIds: string[]) : Promise<SpatialImagesContract> { const fields = [ ...this._query.idFields, ...this._query.coreFields, ...this._query.spatialFields, ]; const query = this._query.images(imageIds, fields); const url = new URL(this._query.imagesPath, this._endpoint).href; return this ._fetchGraphContract<GraphSpatialImageEnt[]>( query, url) .then(r => { const result: SpatialImagesContract = []; const items = r.data; for (const item of items) { const spatialImage = this._convert.spatialImage(item); const contract: EntContract<SpatialImageEnt> = { node: spatialImage, node_id: item.id, }; result.push(contract); } return result; }); } public setAccessToken(accessToken: string): void { this._accessToken = accessToken; } private _createHeaders(): XMLHttpRequestHeader[] { const headers: XMLHttpRequestHeader[] = [ { name: 'Accept', value: 'application/json' }, { name: 'Content-Type', value: 'application/x-www-form-urlencoded', }, ]; if (this._accessToken) { headers.push({ name: 'Authorization', value: `OAuth ${this._accessToken}`, }); } return headers; } private _fetchGraphContract<T>( body: string, url: string): Promise<GraphContract<T>> { const method = this._method; const headers = this._createHeaders(); const query = `${url}?${body}`; return xhrFetch<GraphContract<T>>( query, method, "json", headers, null, null) .catch( (error: GraphError) => { const message = this._makeErrorMessage(error); throw new MapillaryError(message); } ); } private _makeErrorMessage(graphError: GraphError): string { const error = graphError.error; const message = error ? `${error.code} (${error.type}, ${error.fbtrace_id}): ${error.message}` : "Failed to fetch data"; return message; } }