mapillary-js
Version:
WebGL JavaScript library for displaying street level imagery from mapillary.com
349 lines (299 loc) • 11.6 kB
text/typescript
import {catchError, map} from "rxjs/operators";
import * as falcor from "falcor";
import {Observable, Subscriber} from "rxjs";
import {
ICoreNode,
IFillNode,
IFullNode,
ISequence,
ModelCreator,
} from "../API";
interface IImageByKey<T> {
imageByKey: { [key: string]: T };
}
interface IImageCloseTo<T> {
imageCloseTo: { [key: string]: T };
}
interface IImagesByH<T> {
imagesByH: { [key: string]: { [index: string]: T } };
}
interface ISequenceByKey<T> {
sequenceByKey: { [sequenceKey: string]: T };
}
type APIPath =
"imageByKey" |
"imageCloseTo" |
"imagesByH" |
"imageViewAdd" |
"sequenceByKey" |
"sequenceViewAdd";
/**
* @class APIv3
*
* @classdesc Provides methods for access of API v3.
*/
export class APIv3 {
private _clientId: string;
private _model: falcor.Model;
private _modelCreator: ModelCreator;
private _pageCount: number;
private _pathImageByKey: APIPath;
private _pathImageCloseTo: APIPath;
private _pathImagesByH: APIPath;
private _pathImageViewAdd: APIPath;
private _pathSequenceByKey: APIPath;
private _pathSequenceViewAdd: APIPath;
private _propertiesCore: string[];
private _propertiesFill: string[];
private _propertiesKey: string[];
private _propertiesSequence: string[];
private _propertiesSpatial: string[];
private _propertiesUser: string[];
/**
* Create a new api v3 instance.
*
* @param {number} clientId - Client id for API requests.
* @param {number} [token] - Optional bearer token for API requests of
* protected resources.
* @param {ModelCreator} [creator] - Optional model creator instance.
*/
constructor(clientId: string, token?: string, creator?: ModelCreator) {
this._clientId = clientId;
this._modelCreator = creator != null ? creator : new ModelCreator();
this._model = this._modelCreator.createModel(clientId, token);
this._pageCount = 999;
this._pathImageByKey = "imageByKey";
this._pathImageCloseTo = "imageCloseTo";
this._pathImagesByH = "imagesByH";
this._pathImageViewAdd = "imageViewAdd";
this._pathSequenceByKey = "sequenceByKey";
this._pathSequenceViewAdd = "sequenceViewAdd";
this._propertiesCore = [
"cl",
"l",
"sequence_key",
];
this._propertiesFill = [
"captured_at",
"captured_with_camera_uuid",
"user",
"organization_key",
"private",
"project",
];
this._propertiesKey = [
"key",
];
this._propertiesSequence = [
"keys",
];
this._propertiesSpatial = [
"atomic_scale",
"cluster_key",
"c_rotation",
"ca",
"calt",
"camera_projection_type",
"cca",
"cfocal",
"ck1",
"ck2",
"gpano",
"height",
"merge_cc",
"merge_version",
"orientation",
"width",
];
this._propertiesUser = [
"username",
];
}
public get clientId(): string {
return this._clientId;
}
public imageByKeyFill$(keys: string[]): Observable<{ [key: string]: IFillNode }> {
return this._catchInvalidateGet$(
this._wrapModelResponse$<falcor.JSONEnvelope<IImageByKey<IFillNode>>>(this._model.get([
this._pathImageByKey,
keys,
this._propertiesKey
.concat(this._propertiesFill)
.concat(this._propertiesSpatial),
this._propertiesKey
.concat(this._propertiesUser)])).pipe(
map(
(value: falcor.JSONEnvelope<IImageByKey<IFillNode>>): { [key: string]: IFillNode } => {
if (!value) {
throw new Error(`Images (${keys.join(", ")}) could not be found.`);
}
return value.json.imageByKey;
})),
this._pathImageByKey,
keys);
}
public imageByKeyFull$(keys: string[]): Observable<{ [key: string]: IFullNode }> {
return this._catchInvalidateGet$(
this._wrapModelResponse$<falcor.JSONEnvelope<IImageByKey<IFullNode>>>(this._model.get([
this._pathImageByKey,
keys,
this._propertiesKey
.concat(this._propertiesCore)
.concat(this._propertiesFill)
.concat(this._propertiesSpatial),
this._propertiesKey
.concat(this._propertiesUser)])).pipe(
map(
(value: falcor.JSONEnvelope<IImageByKey<IFullNode>>): { [key: string]: IFullNode } => {
if (!value) {
throw new Error(`Images (${keys.join(", ")}) could not be found.`);
}
return value.json.imageByKey;
})),
this._pathImageByKey,
keys);
}
public imageCloseTo$(lat: number, lon: number): Observable<IFullNode> {
let lonLat: string = `${lon}:${lat}`;
return this._catchInvalidateGet$(
this._wrapModelResponse$<falcor.JSONEnvelope<IImageCloseTo<IFullNode>>>(this._model.get([
this._pathImageCloseTo,
[lonLat],
this._propertiesKey
.concat(this._propertiesCore)
.concat(this._propertiesFill)
.concat(this._propertiesSpatial),
this._propertiesKey
.concat(this._propertiesUser)])).pipe(
map(
(value: falcor.JSONEnvelope<IImageCloseTo<IFullNode>>): IFullNode => {
return value != null ? value.json.imageCloseTo[lonLat] : null;
})),
this._pathImageCloseTo,
[lonLat]);
}
public imagesByH$(hs: string[]): Observable<{ [h: string]: { [index: string]: ICoreNode } }> {
return this._catchInvalidateGet$(
this._wrapModelResponse$<falcor.JSONEnvelope<IImagesByH<ICoreNode>>>(this._model.get([
this._pathImagesByH,
hs,
{ from: 0, to: this._pageCount },
this._propertiesKey
.concat(this._propertiesCore)])).pipe(
map(
(value: falcor.JSONEnvelope<IImagesByH<ICoreNode>>): { [h: string]: { [index: string]: ICoreNode } } => {
if (!value) {
value = { json: { imagesByH: {} } };
for (let h of hs) {
value.json.imagesByH[h] = {};
for (let i: number = 0; i <= this._pageCount; i++) {
value.json.imagesByH[h][i] = null;
}
}
}
return value.json.imagesByH;
})),
this._pathImagesByH,
hs);
}
public imageViewAdd$(keys: string[]): Observable<void> {
return this._catchInvalidateCall$(
this._wrapCallModelResponse$(
this._model.call(
[this._pathImageViewAdd],
[keys])),
this._pathImageViewAdd,
keys);
}
public invalidateImageByKey(keys: string[]): void {
this._invalidateGet(this._pathImageByKey, keys);
}
public invalidateImagesByH(hs: string[]): void {
this._invalidateGet(this._pathImagesByH, hs);
}
public invalidateSequenceByKey(sKeys: string[]): void {
this._invalidateGet(this._pathSequenceByKey, sKeys);
}
public setToken(token?: string): void {
this._model.invalidate([]);
this._model = null;
this._model = this._modelCreator.createModel(this._clientId, token);
}
public sequenceByKey$(sequenceKeys: string[]): Observable<{ [sequenceKey: string]: ISequence }> {
return this._catchInvalidateGet$(
this._wrapModelResponse$<falcor.JSONEnvelope<ISequenceByKey<ISequence>>>(this._model.get([
this._pathSequenceByKey,
sequenceKeys,
this._propertiesKey
.concat(this._propertiesSequence)])).pipe(
map(
(value: falcor.JSONEnvelope<ISequenceByKey<ISequence>>): { [sequenceKey: string]: ISequence } => {
if (!value) {
value = { json: { sequenceByKey: {} } };
}
for (const sequenceKey of sequenceKeys) {
if (!(sequenceKey in value.json.sequenceByKey)) {
console.warn(`Sequence data missing (${sequenceKey})`);
value.json.sequenceByKey[sequenceKey] = { key: sequenceKey, keys: [] };
}
}
return value.json.sequenceByKey;
})),
this._pathSequenceByKey,
sequenceKeys);
}
public sequenceViewAdd$(sequenceKeys: string[]): Observable<void> {
return this._catchInvalidateCall$(
this._wrapCallModelResponse$(
this._model.call(
[this._pathSequenceViewAdd],
[sequenceKeys])),
this._pathSequenceViewAdd,
sequenceKeys);
}
private _catchInvalidateGet$<TResult>(observable: Observable<TResult>, path: APIPath, paths: string[]): Observable<TResult> {
return observable.pipe(
catchError(
(error: Error): Observable<TResult> => {
this._invalidateGet(path, paths);
throw error;
}));
}
private _catchInvalidateCall$<TResult>(observable: Observable<TResult>, path: APIPath, paths: string[]): Observable<TResult> {
return observable.pipe(
catchError(
(error: Error): Observable<TResult> => {
this._invalidateCall(path, paths);
throw error;
}));
}
private _invalidateGet(path: APIPath, paths: string[]): void {
this._model.invalidate([path, paths]);
}
private _invalidateCall(path: APIPath, paths: string[]): void {
this._model.invalidate([path], [paths]);
}
private _wrapModelResponse$<T>(modelResponse: falcor.ModelResponse<T>): Observable<T> {
return Observable
.create(
(subscriber: Subscriber<T>): void => {
modelResponse
.then(
(value: T): void => {
subscriber.next(value);
subscriber.complete();
},
(error: Error): void => {
subscriber.error(error);
});
});
}
private _wrapCallModelResponse$<T>(modelResponse: falcor.ModelResponse<falcor.JSONEnvelope<T>>): Observable<T> {
return this._wrapModelResponse$(modelResponse).pipe(
map<falcor.JSONEnvelope<T>, T>(
(value: falcor.JSONEnvelope<T>): T => {
return;
}));
}
}
export default APIv3;