mirror of
https://github.com/mapillary/mapillary-js.git
synced 2026-01-18 13:56:53 +00:00
feat(api): get static resources through data provider
Use data provider in node caching.
This commit is contained in:
parent
0469c966ab
commit
2b4617bd7a
@ -1,7 +1,4 @@
|
||||
import {empty as observableEmpty, Observable} from "rxjs";
|
||||
|
||||
import {catchError, retry} from "rxjs/operators";
|
||||
|
||||
import * as pako from "pako";
|
||||
import * as falcor from "falcor";
|
||||
|
||||
import {
|
||||
@ -12,6 +9,8 @@ import {
|
||||
ModelCreator,
|
||||
} from "../../src/API";
|
||||
import DataProvider from "../../src/api/DataProvider";
|
||||
import { MapillaryError } from "../../src/Error";
|
||||
import IClusterReconstruction from "../../src/component/spatialdata/interfaces/IClusterReconstruction";
|
||||
|
||||
describe("DataProvider.ctor", () => {
|
||||
it("should create a data provider", () => {
|
||||
@ -43,7 +42,7 @@ describe("DataProvider.getFillImages", () => {
|
||||
|
||||
provider.getFillImages([key])
|
||||
.then(
|
||||
(result: { [key: string]: IFillNode}): void => {
|
||||
(result: { [key: string]: IFillNode }): void => {
|
||||
expect(result).toBeDefined();
|
||||
|
||||
expect(modelSpy.calls.count()).toBe(1);
|
||||
@ -186,7 +185,7 @@ describe("DataProvider.getFullImages", () => {
|
||||
|
||||
provider.getFullImages([key])
|
||||
.then(
|
||||
(result: { [key: string]: IFillNode}): void => {
|
||||
(result: { [key: string]: IFillNode }): void => {
|
||||
expect(result).toBeDefined();
|
||||
|
||||
expect(spy.calls.count()).toBe(1);
|
||||
@ -252,7 +251,7 @@ describe("DataProvider.getFullImages", () => {
|
||||
|
||||
provider.getFullImages([key])
|
||||
.then(
|
||||
(result: { [key: string]: IFillNode}): void => { return; },
|
||||
(result: { [key: string]: IFillNode }): void => { return; },
|
||||
(error: Error): void => {
|
||||
expect(invalidateSpy.calls.count()).toBe(1);
|
||||
expect(invalidateSpy.calls.first().args.length).toBe(1);
|
||||
@ -575,3 +574,175 @@ describe("DataProvider.setToken", () => {
|
||||
expect(creatorSpy.calls.mostRecent().args[1]).toBe("token");
|
||||
});
|
||||
});
|
||||
|
||||
class XMLHTTPRequestMock {
|
||||
public response: {};
|
||||
public responseType: string;
|
||||
public status: number;
|
||||
public timeout: number;
|
||||
|
||||
public onload: (e: Event) => any;
|
||||
public onerror: (e: Event) => any;
|
||||
public ontimeout: (e: Event) => any;
|
||||
public onabort: (e: Event) => any;
|
||||
|
||||
public abort(): void { this.onabort(new Event("abort")); }
|
||||
public open(...args: any[]): void { return; }
|
||||
public send(...args: any[]): void { return; }
|
||||
};
|
||||
|
||||
describe("DataProvider.getImage", () => {
|
||||
it("should return array buffer on successful load", (done: Function) => {
|
||||
const requestMock: XMLHTTPRequestMock = new XMLHTTPRequestMock();
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
|
||||
const abort: Promise<void> = new Promise((_, __): void => { /*noop*/ });
|
||||
const provider: DataProvider = new DataProvider(
|
||||
"clientId", undefined, undefined);
|
||||
|
||||
const response: ArrayBuffer = new ArrayBuffer(1024);
|
||||
|
||||
provider.getImage("key", 320, abort)
|
||||
.then(
|
||||
(buffer: ArrayBuffer): void => {
|
||||
expect(buffer instanceof ArrayBuffer).toBeTrue();
|
||||
expect(buffer).toEqual(response);
|
||||
done();
|
||||
});
|
||||
|
||||
requestMock.status = 200;
|
||||
requestMock.response = response;
|
||||
requestMock.onload(undefined);
|
||||
});
|
||||
|
||||
it("should reject on abort", (done: Function) => {
|
||||
const requestMock: XMLHTTPRequestMock = new XMLHTTPRequestMock();
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
|
||||
let aborter: Function;
|
||||
const abort: Promise<void> = new Promise(
|
||||
(_, reject): void => {
|
||||
aborter = reject;
|
||||
});
|
||||
|
||||
const provider: DataProvider = new DataProvider(
|
||||
"clientId", undefined, undefined);
|
||||
|
||||
provider.getImage("key", 320, abort)
|
||||
.then(
|
||||
undefined,
|
||||
(reason: Error): void => {
|
||||
expect(reason instanceof MapillaryError).toBeTrue();
|
||||
expect(reason.message).toContain("abort");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
aborter();
|
||||
});
|
||||
|
||||
it("should reject on unsuccessful load", (done: Function) => {
|
||||
const requestMock: XMLHTTPRequestMock = new XMLHTTPRequestMock();
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
|
||||
const abort: Promise<void> = new Promise((_, __): void => { /*noop*/ });
|
||||
const provider: DataProvider = new DataProvider(
|
||||
"clientId", undefined, undefined);
|
||||
|
||||
const response: ArrayBuffer = new ArrayBuffer(1024);
|
||||
|
||||
provider.getImage("key", 320, abort)
|
||||
.then(
|
||||
undefined,
|
||||
(reason: Error): void => {
|
||||
expect(reason instanceof MapillaryError).toBeTrue();
|
||||
expect(reason.message).toContain("status");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
requestMock.status = 404;
|
||||
requestMock.response = response;
|
||||
requestMock.onload(undefined);
|
||||
});
|
||||
|
||||
it("should reject for empty response on load", (done: Function) => {
|
||||
const requestMock: XMLHTTPRequestMock = new XMLHTTPRequestMock();
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
|
||||
const abort: Promise<void> = new Promise((_, __): void => { /*noop*/ });
|
||||
const provider: DataProvider = new DataProvider(
|
||||
"clientId", undefined, undefined);
|
||||
|
||||
const response: ArrayBuffer = new ArrayBuffer(1024);
|
||||
|
||||
provider.getImage("key", 320, abort)
|
||||
.then(
|
||||
undefined,
|
||||
(reason: Error): void => {
|
||||
expect(reason instanceof MapillaryError).toBeTrue();
|
||||
expect(reason.message).toContain("empty");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
requestMock.status = 200;
|
||||
requestMock.response = undefined;
|
||||
requestMock.onload(undefined);
|
||||
});
|
||||
|
||||
it("should reject on error", (done: Function) => {
|
||||
const requestMock: XMLHTTPRequestMock = new XMLHTTPRequestMock();
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
|
||||
const abort: Promise<void> = new Promise((_, __): void => { /*noop*/ });
|
||||
const provider: DataProvider = new DataProvider(
|
||||
"clientId", undefined, undefined);
|
||||
|
||||
const response: ArrayBuffer = new ArrayBuffer(1024);
|
||||
|
||||
provider.getImage("key", 320, abort)
|
||||
.then(
|
||||
undefined,
|
||||
(reason: Error): void => {
|
||||
expect(reason instanceof MapillaryError).toBeTrue();
|
||||
expect(reason.message).toContain("error");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
requestMock.onerror(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DataProvider.getClusterReconstruction", () => {
|
||||
it("should return cluster reconstruction on successful load", (done: Function) => {
|
||||
const requestMock: XMLHTTPRequestMock = new XMLHTTPRequestMock();
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
|
||||
const provider: DataProvider = new DataProvider(
|
||||
"clientId", undefined, undefined);
|
||||
|
||||
provider.getClusterReconstruction("clusterKey")
|
||||
.then(
|
||||
(r: IClusterReconstruction): void => {
|
||||
expect(r.points).toEqual({});
|
||||
expect(r.reference_lla.altitude).toBe(1);
|
||||
expect(r.reference_lla.latitude).toBe(2);
|
||||
expect(r.reference_lla.longitude).toBe(3);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
const response: string = pako.deflate(
|
||||
JSON.stringify([{
|
||||
points: {},
|
||||
reference_lla: { altitude: 1, latitude: 2, longitude: 3 },
|
||||
}]),
|
||||
{ to: "string" });
|
||||
|
||||
requestMock.status = 200;
|
||||
requestMock.response = response;
|
||||
requestMock.onload(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@ -60,7 +60,7 @@ describe("DirectionComponent.activate", () => {
|
||||
directionComponent.activate();
|
||||
|
||||
const node: Node = new NodeHelper().createNode();
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
node.cacheSpatialEdges([]);
|
||||
(<Subject<Node>>navigatorMock.stateService.currentNode$).next(node);
|
||||
|
||||
@ -88,7 +88,7 @@ describe("DirectionComponent.activate", () => {
|
||||
(<jasmine.Spy>navigatorMock.graphService.cacheSequence$).and.returnValue(observableOf<Sequence>(sequence));
|
||||
|
||||
const node: Node = new NodeHelper().createNode();
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
node.cacheSpatialEdges([]);
|
||||
(<Subject<Node>>navigatorMock.stateService.currentNode$).next(node);
|
||||
|
||||
@ -117,7 +117,7 @@ describe("DirectionComponent.activate", () => {
|
||||
(<jasmine.Spy>navigatorMock.graphService.cacheSequence$).and.returnValue(cacheSequence$);
|
||||
|
||||
const node: Node = new NodeHelper().createNode();
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
node.cacheSpatialEdges([]);
|
||||
(<Subject<Node>>navigatorMock.stateService.currentNode$).next(node);
|
||||
|
||||
@ -149,7 +149,7 @@ describe("DirectionComponent.activate", () => {
|
||||
(<jasmine.Spy>navigatorMock.graphService.cacheSequence$).and.returnValue(observableThrowError(new Error("Failed to cache seq.")));
|
||||
|
||||
const node: Node = new NodeHelper().createNode();
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
node.cacheSpatialEdges([]);
|
||||
(<Subject<Node>>navigatorMock.stateService.currentNode$).next(node);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {NodeHelper} from "../helper/NodeHelper.spec";
|
||||
import {ICoreNode, IFillNode} from "../../src/API";
|
||||
import {IMesh, Node, NodeCache} from "../../src/Graph";
|
||||
import {Node, NodeCache} from "../../src/Graph";
|
||||
import IMesh from "../../src/api/interfaces/IMesh";
|
||||
|
||||
describe("Node", () => {
|
||||
let helper: NodeHelper;
|
||||
@ -68,7 +69,7 @@ describe("Node.dispose", () => {
|
||||
it("should dipose cache", () => {
|
||||
let coreNode: ICoreNode = helper.createCoreNode();
|
||||
let node: Node = new Node(coreNode);
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let disposeSpy: jasmine.Spy = spyOn(nodeCache, "dispose");
|
||||
disposeSpy.and.stub();
|
||||
@ -98,7 +99,7 @@ describe("Node.uncache", () => {
|
||||
it("should dispose node cache", () => {
|
||||
let coreNode: ICoreNode = helper.createCoreNode();
|
||||
let node: Node = new Node(coreNode);
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let disposeSpy: jasmine.Spy = spyOn(nodeCache, "dispose");
|
||||
disposeSpy.and.stub();
|
||||
@ -113,7 +114,7 @@ describe("Node.uncache", () => {
|
||||
it("should be able to initialize cache again after uncache", () => {
|
||||
let coreNode: ICoreNode = helper.createCoreNode();
|
||||
let node: Node = new Node(coreNode);
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let disposeSpy: jasmine.Spy = spyOn(nodeCache, "dispose");
|
||||
disposeSpy.and.stub();
|
||||
@ -337,6 +338,8 @@ describe("Node.assetsCached", () => {
|
||||
protected _overridingImage: HTMLImageElement;
|
||||
protected _overridingMesh: IMesh;
|
||||
|
||||
constructor() { super(undefined); }
|
||||
|
||||
public get image(): HTMLImageElement {
|
||||
return this._overridingImage;
|
||||
}
|
||||
|
||||
@ -1,36 +1,38 @@
|
||||
import {first, skip} from "rxjs/operators";
|
||||
import {EdgeDirection, IEdge} from "../../src/Edge";
|
||||
import { first, skip } from "rxjs/operators";
|
||||
import { EdgeDirection, IEdge } from "../../src/Edge";
|
||||
import {
|
||||
IEdgeStatus,
|
||||
NodeCache,
|
||||
} from "../../src/Graph";
|
||||
import { ImageSize } from "../../src/Viewer";
|
||||
import { MockCreator } from "../helper/MockCreator.spec";
|
||||
import { IDataProvider } from "../../src/API";
|
||||
import DataProvider from "../../src/api/DataProvider";
|
||||
|
||||
describe("NodeCache.ctor", () => {
|
||||
it("should create a node cache", () => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
expect(nodeCache).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("NodeCache.mesh", () => {
|
||||
it("should be null initially", () => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
expect(nodeCache.mesh).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("NodeCache.image", () => {
|
||||
it("should be null initially", () => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
expect(nodeCache.image).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("NodeCache.sequenceEdges$", () => {
|
||||
it("should emit uncached empty edge status initially", (done: Function) => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
nodeCache.sequenceEdges$.pipe(
|
||||
first())
|
||||
@ -44,7 +46,7 @@ describe("NodeCache.sequenceEdges$", () => {
|
||||
});
|
||||
|
||||
it("should emit cached non empty edge status when sequence edges cached", (done: Function) => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let sequenceEdge: IEdge = {
|
||||
data: {
|
||||
@ -76,7 +78,7 @@ describe("NodeCache.sequenceEdges$", () => {
|
||||
|
||||
describe("NodeCache.resetSequenceEdges", () => {
|
||||
it("should reset the sequence edges", () => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let sequenceEdge: IEdge = {
|
||||
data: {
|
||||
@ -102,7 +104,7 @@ describe("NodeCache.resetSequenceEdges", () => {
|
||||
|
||||
describe("NodeCache.spatialEdges$", () => {
|
||||
it("should emit uncached empty edge status initially", (done: Function) => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
nodeCache.spatialEdges$.pipe(
|
||||
first())
|
||||
@ -116,7 +118,7 @@ describe("NodeCache.spatialEdges$", () => {
|
||||
});
|
||||
|
||||
it("should emit cached non empty edge status when spatial edges cached", (done: Function) => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let spatialEdge: IEdge = {
|
||||
data: {
|
||||
@ -148,7 +150,7 @@ describe("NodeCache.spatialEdges$", () => {
|
||||
|
||||
describe("NodeCache.resetSpatialEdges", () => {
|
||||
it("should reset the spatial edges", () => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let spatialEdge: IEdge = {
|
||||
data: {
|
||||
@ -174,7 +176,7 @@ describe("NodeCache.resetSpatialEdges", () => {
|
||||
|
||||
describe("NodeCache.dispose", () => {
|
||||
it("should clear all properties", () => {
|
||||
let nodeCache: NodeCache = new NodeCache();
|
||||
let nodeCache: NodeCache = new NodeCache(undefined);
|
||||
|
||||
let sequencEdge: IEdge = {
|
||||
data: {
|
||||
@ -211,11 +213,14 @@ describe("NodeCache.dispose", () => {
|
||||
|
||||
describe("NodeCache.cacheImage$", () => {
|
||||
it("should return the node cache with a cached image", (done: Function) => {
|
||||
const requestMock: XMLHttpRequest = new XMLHttpRequest();
|
||||
spyOn(requestMock, "send").and.stub();
|
||||
spyOn(requestMock, "open").and.stub();
|
||||
const promise: any = {
|
||||
then: (resolve: (result: any) => void, reject: (error: Error) => void): void => {
|
||||
resolve(undefined);
|
||||
},
|
||||
};
|
||||
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
const dataProvider: IDataProvider = new DataProvider("cid");
|
||||
spyOn(dataProvider, "getImage").and.returnValue(promise);
|
||||
|
||||
const imageMock: HTMLImageElement = new Image();
|
||||
spyOn(window, <keyof Window>"Image").and.returnValue(imageMock);
|
||||
@ -225,7 +230,7 @@ describe("NodeCache.cacheImage$", () => {
|
||||
spyOn(window, "Blob").and.returnValue(<Blob>{});
|
||||
spyOn(window.URL, "createObjectURL").and.returnValue("url");
|
||||
|
||||
const nodeCache: NodeCache = new NodeCache();
|
||||
const nodeCache: NodeCache = new NodeCache(dataProvider);
|
||||
|
||||
expect(nodeCache.image).toBeNull();
|
||||
|
||||
@ -238,18 +243,18 @@ describe("NodeCache.cacheImage$", () => {
|
||||
done();
|
||||
});
|
||||
|
||||
new MockCreator().mockProperty(requestMock, "status", 200);
|
||||
requestMock.dispatchEvent(new ProgressEvent("load", { total: 1, loaded: 1}));
|
||||
|
||||
imageMock.dispatchEvent(new CustomEvent("load"));
|
||||
});
|
||||
|
||||
it("should cache an image", () => {
|
||||
const requestMock: XMLHttpRequest = new XMLHttpRequest();
|
||||
spyOn(requestMock, "send").and.stub();
|
||||
spyOn(requestMock, "open").and.stub();
|
||||
const promise: any = {
|
||||
then: (resolve: (result: any) => void, reject: (error: Error) => void): void => {
|
||||
resolve(undefined);
|
||||
},
|
||||
};
|
||||
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
const dataProvider: IDataProvider = new DataProvider("cid");
|
||||
spyOn(dataProvider, "getImage").and.returnValue(promise);
|
||||
|
||||
const imageMock: HTMLImageElement = new Image();
|
||||
spyOn(window, <keyof Window>"Image").and.returnValue(imageMock);
|
||||
@ -259,15 +264,12 @@ describe("NodeCache.cacheImage$", () => {
|
||||
spyOn(window, "Blob").and.returnValue(<Blob>{});
|
||||
spyOn(window.URL, "createObjectURL").and.returnValue("url");
|
||||
|
||||
const nodeCache: NodeCache = new NodeCache();
|
||||
const nodeCache: NodeCache = new NodeCache(dataProvider);
|
||||
|
||||
expect(nodeCache.image).toBeNull();
|
||||
|
||||
nodeCache.cacheImage$("key", ImageSize.Size640).subscribe();
|
||||
|
||||
new MockCreator().mockProperty(requestMock, "status", 200);
|
||||
requestMock.dispatchEvent(new ProgressEvent("load", { total: 1, loaded: 1}));
|
||||
|
||||
imageMock.dispatchEvent(new CustomEvent("load"));
|
||||
|
||||
expect(nodeCache.image).not.toBeNull();
|
||||
@ -275,11 +277,14 @@ describe("NodeCache.cacheImage$", () => {
|
||||
});
|
||||
|
||||
it("should emit the cached image", (done: Function) => {
|
||||
const requestMock: XMLHttpRequest = new XMLHttpRequest();
|
||||
spyOn(requestMock, "send").and.stub();
|
||||
spyOn(requestMock, "open").and.stub();
|
||||
const promise: any = {
|
||||
then: (resolve: (result: any) => void, reject: (error: Error) => void): void => {
|
||||
resolve(undefined);
|
||||
},
|
||||
};
|
||||
|
||||
spyOn(window, <keyof Window>"XMLHttpRequest").and.returnValue(requestMock);
|
||||
const dataProvider: IDataProvider = new DataProvider("cid");
|
||||
spyOn(dataProvider, "getImage").and.returnValue(promise);
|
||||
|
||||
const imageMock: HTMLImageElement = new Image();
|
||||
spyOn(window, <keyof Window>"Image").and.returnValue(imageMock);
|
||||
@ -289,7 +294,7 @@ describe("NodeCache.cacheImage$", () => {
|
||||
spyOn(window, "Blob").and.returnValue(<Blob>{});
|
||||
spyOn(window.URL, "createObjectURL").and.returnValue("url");
|
||||
|
||||
const nodeCache: NodeCache = new NodeCache();
|
||||
const nodeCache: NodeCache = new NodeCache(dataProvider);
|
||||
|
||||
expect(nodeCache.image).toBeNull();
|
||||
|
||||
@ -304,10 +309,6 @@ describe("NodeCache.cacheImage$", () => {
|
||||
});
|
||||
|
||||
nodeCache.cacheImage$("key", ImageSize.Size640).subscribe();
|
||||
|
||||
new MockCreator().mockProperty(requestMock, "status", 200);
|
||||
requestMock.dispatchEvent(new ProgressEvent("load", { total: 1, loaded: 1}));
|
||||
|
||||
imageMock.dispatchEvent(new CustomEvent("load"));
|
||||
});
|
||||
});
|
||||
|
||||
@ -285,7 +285,7 @@ describe("PlayService.play", () => {
|
||||
playService.play();
|
||||
|
||||
const frame: IFrame = new FrameHelper().createFrame();
|
||||
frame.state.currentNode.initializeCache(new NodeCache());
|
||||
frame.state.currentNode.initializeCache(new NodeCache(undefined));
|
||||
(<Subject<IFrame>>stateService.currentState$).next(frame);
|
||||
|
||||
frame.state.currentNode.cacheSequenceEdges([]);
|
||||
@ -350,7 +350,7 @@ describe("PlayService.play", () => {
|
||||
playService.play();
|
||||
|
||||
const frame: IFrame = new FrameHelper().createFrame();
|
||||
frame.state.currentNode.initializeCache(new NodeCache());
|
||||
frame.state.currentNode.initializeCache(new NodeCache(undefined));
|
||||
(<Subject<IFrame>>stateService.currentState$).next(frame);
|
||||
|
||||
frame.state.currentNode.cacheSequenceEdges([]);
|
||||
@ -370,7 +370,7 @@ describe("PlayService.play", () => {
|
||||
|
||||
const frame: IFrame = new FrameHelper().createFrame();
|
||||
const node: Node = frame.state.currentNode;
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
const sequenceEdgesSubject: Subject<IEdgeStatus> = new Subject<IEdgeStatus>();
|
||||
new MockCreator().mockProperty(node, "sequenceEdges$", sequenceEdgesSubject);
|
||||
|
||||
@ -400,7 +400,7 @@ describe("PlayService.play", () => {
|
||||
|
||||
const frame: IFrame = new FrameHelper().createFrame();
|
||||
const node: Node = frame.state.currentNode;
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
const sequenceEdgesSubject: Subject<IEdgeStatus> = new Subject<IEdgeStatus>();
|
||||
|
||||
const prevFullNode: IFullNode = new NodeHelper().createFullNode();
|
||||
@ -658,7 +658,7 @@ describe("PlayService.play", () => {
|
||||
|
||||
const frame: IFrame = new FrameHelper().createFrame();
|
||||
const node: Node = frame.state.currentNode;
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
const sequenceEdgesSubject: Subject<IEdgeStatus> = new Subject<IEdgeStatus>();
|
||||
new MockCreator().mockProperty(node, "sequenceEdges$", sequenceEdgesSubject);
|
||||
|
||||
@ -710,7 +710,7 @@ describe("PlayService.play", () => {
|
||||
|
||||
const frame: IFrame = new FrameHelper().createFrame();
|
||||
const node: Node = frame.state.currentNode;
|
||||
node.initializeCache(new NodeCache());
|
||||
node.initializeCache(new NodeCache(undefined));
|
||||
const sequenceEdgesSubject: Subject<IEdgeStatus> = new Subject<IEdgeStatus>();
|
||||
new MockCreator().mockProperty(node, "sequenceEdges$", sequenceEdgesSubject);
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ export {GraphCalculator} from "./graph/GraphCalculator";
|
||||
export {GraphMode} from "./graph/GraphMode";
|
||||
export {GraphService} from "./graph/GraphService";
|
||||
export {ImageLoadingService} from "./graph/ImageLoadingService";
|
||||
export {MeshReader} from "./graph/MeshReader";
|
||||
export {Node} from "./graph/Node";
|
||||
export {NodeCache} from "./graph/NodeCache";
|
||||
export {Sequence} from "./graph/Sequence";
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
import * as falcor from "falcor";
|
||||
import * as pako from "pako";
|
||||
|
||||
import {
|
||||
ICoreNode,
|
||||
IDataProvider,
|
||||
IFillNode,
|
||||
IFullNode,
|
||||
ISequence,
|
||||
ModelCreator,
|
||||
} from "../API";
|
||||
|
||||
import MapillaryError from "../error/MapillaryError";
|
||||
import IMesh from "./interfaces/IMesh";
|
||||
import MeshReader from "./MeshReader";
|
||||
import Urls from "../utils/Urls";
|
||||
import IClusterReconstruction from "../component/spatialdata/interfaces/IClusterReconstruction";
|
||||
import IDataProvider from "./interfaces/IDataProvider";
|
||||
import ModelCreator from "./ModelCreator";
|
||||
import ICoreNode from "./interfaces/ICoreNode";
|
||||
import IFillNode from "./interfaces/IFillNode";
|
||||
import IFullNode from "./interfaces/IFullNode";
|
||||
import ISequence from "./interfaces/ISequence";
|
||||
|
||||
interface IImageByKey<T> {
|
||||
imageByKey: { [key: string]: T };
|
||||
@ -125,29 +130,53 @@ export class DataProvider implements IDataProvider {
|
||||
Promise<{ [geohash: string]: { [imageKey: string]: ICoreNode } }> {
|
||||
return Promise.resolve(<PromiseLike<falcor.JSONEnvelope<IImagesByH<ICoreNode>>>>this._model
|
||||
.get([
|
||||
this._pathImagesByH,
|
||||
geohashes,
|
||||
{ from: 0, to: this._pageCount },
|
||||
this._propertiesKey
|
||||
.concat(this._propertiesCore)]))
|
||||
this._pathImagesByH,
|
||||
geohashes,
|
||||
{ from: 0, to: this._pageCount },
|
||||
this._propertiesKey
|
||||
.concat(this._propertiesCore)]))
|
||||
.then(
|
||||
(value: falcor.JSONEnvelope<IImagesByH<ICoreNode>>): { [h: string]: { [index: string]: ICoreNode } } => {
|
||||
if (!value) {
|
||||
value = { json: { imagesByH: {} } };
|
||||
for (let h of geohashes) {
|
||||
value.json.imagesByH[h] = {};
|
||||
for (let i: number = 0; i <= this._pageCount; i++) {
|
||||
value.json.imagesByH[h][i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
(value: falcor.JSONEnvelope<IImagesByH<ICoreNode>>): { [h: string]: { [index: string]: ICoreNode } } => {
|
||||
if (!value) {
|
||||
value = { json: { imagesByH: {} } };
|
||||
for (const h of geohashes) {
|
||||
value.json.imagesByH[h] = {};
|
||||
for (let i: number = 0; i <= this._pageCount; i++) {
|
||||
value.json.imagesByH[h][i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value.json.imagesByH;
|
||||
},
|
||||
(error: Error) => {
|
||||
this._invalidateGet(this._pathImagesByH, geohashes);
|
||||
throw error;
|
||||
});
|
||||
return value.json.imagesByH;
|
||||
},
|
||||
(error: Error) => {
|
||||
this._invalidateGet(this._pathImagesByH, geohashes);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
public getClusterReconstruction(clusterKey: string, abort?: Promise<void>): Promise<IClusterReconstruction> {
|
||||
return this._getArrayBuffer(Urls.clusterReconstruction(clusterKey), abort)
|
||||
.then(
|
||||
(buffer: ArrayBuffer): IClusterReconstruction => {
|
||||
const inflated: string =
|
||||
pako.inflate(<pako.Data>buffer, { to: "string" });
|
||||
|
||||
const reconstructions: IClusterReconstruction[] =
|
||||
JSON.parse(inflated);
|
||||
|
||||
if (reconstructions.length < 1) {
|
||||
throw new MapillaryError("");
|
||||
}
|
||||
|
||||
const reconstruction: IClusterReconstruction = reconstructions[0];
|
||||
reconstruction.key = clusterKey;
|
||||
|
||||
return reconstruction;
|
||||
},
|
||||
(reason: Error): IClusterReconstruction => {
|
||||
throw reason;
|
||||
});
|
||||
}
|
||||
|
||||
public getFillImages(keys: string[]): Promise<{ [key: string]: IFillNode }> {
|
||||
@ -161,8 +190,7 @@ export class DataProvider implements IDataProvider {
|
||||
this._propertiesKey
|
||||
.concat(this._propertiesUser)]))
|
||||
.then(
|
||||
(value: falcor.JSONEnvelope<IImageByKey<IFillNode>>):
|
||||
{ [key: string]: IFillNode } => {
|
||||
(value: falcor.JSONEnvelope<IImageByKey<IFillNode>>): { [key: string]: IFillNode } => {
|
||||
if (!value) {
|
||||
this._invalidateGet(this._pathImageByKey, keys);
|
||||
throw new Error(`Images (${keys.join(", ")}) could not be found.`);
|
||||
@ -188,8 +216,7 @@ export class DataProvider implements IDataProvider {
|
||||
this._propertiesKey
|
||||
.concat(this._propertiesUser)]))
|
||||
.then(
|
||||
(value: falcor.JSONEnvelope<IImageByKey<IFullNode>>):
|
||||
{ [key: string]: IFullNode } => {
|
||||
(value: falcor.JSONEnvelope<IImageByKey<IFullNode>>): { [key: string]: IFullNode } => {
|
||||
if (!value) {
|
||||
this._invalidateGet(this._pathImageByKey, keys);
|
||||
throw new Error(`Images (${keys.join(", ")}) could not be found.`);
|
||||
@ -203,10 +230,36 @@ export class DataProvider implements IDataProvider {
|
||||
});
|
||||
}
|
||||
|
||||
public setToken(token?: string): void {
|
||||
this._model.invalidate([]);
|
||||
this._model = null;
|
||||
this._model = this._modelCreator.createModel(this._clientId, token);
|
||||
public getImage(imageKey: string, size: number, abort?: Promise<void>): Promise<ArrayBuffer> {
|
||||
return this._getArrayBuffer(Urls.thumbnail(imageKey, size, Urls.origin), abort);
|
||||
}
|
||||
|
||||
public getImageTile(
|
||||
imageKey: string,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
scaledW: number,
|
||||
scaledH: number,
|
||||
abort?: Promise<void>): Promise<ArrayBuffer> {
|
||||
const coords: string = `${x},${y},${w},${h}`
|
||||
const size: string = `${scaledW},${scaledH}`;
|
||||
|
||||
return this._getArrayBuffer(
|
||||
Urls.imageTile(imageKey, coords, size),
|
||||
abort);
|
||||
}
|
||||
|
||||
public getMesh(imageKey: string, abort?: Promise<void>): Promise<IMesh> {
|
||||
return this._getArrayBuffer(Urls.protoMesh(imageKey), abort)
|
||||
.then(
|
||||
(buffer: ArrayBuffer): IMesh => {
|
||||
return MeshReader.read(new Buffer(buffer));
|
||||
},
|
||||
(reason: Error): IMesh => {
|
||||
throw reason;
|
||||
});
|
||||
}
|
||||
|
||||
public getSequences(sequenceKeys: string[]):
|
||||
@ -239,6 +292,53 @@ export class DataProvider implements IDataProvider {
|
||||
});
|
||||
}
|
||||
|
||||
public setToken(token?: string): void {
|
||||
this._model.invalidate([]);
|
||||
this._model = null;
|
||||
this._model = this._modelCreator.createModel(this._clientId, token);
|
||||
}
|
||||
|
||||
protected _getArrayBuffer(url: string, abort?: Promise<void>): Promise<ArrayBuffer> {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
|
||||
const promise: Promise<ArrayBuffer> = new Promise(
|
||||
(resolve, reject) => {
|
||||
xhr.open("GET", url, true);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.timeout = 15000;
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status !== 200) {
|
||||
reject(new MapillaryError(`Response status error: ${url}`));
|
||||
}
|
||||
|
||||
if (!xhr.response) {
|
||||
reject(new MapillaryError(`Response empty: ${url}`));
|
||||
}
|
||||
|
||||
resolve(xhr.response);
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
reject(new MapillaryError(`Request error: ${url}`));
|
||||
};
|
||||
|
||||
xhr.ontimeout = (e: Event) => {
|
||||
reject(new MapillaryError(`Request timeout: ${url}`));
|
||||
};
|
||||
|
||||
xhr.onabort = (e: Event) => {
|
||||
reject(new MapillaryError(`Request aborted: ${url}`));
|
||||
};
|
||||
|
||||
xhr.send(null);
|
||||
});
|
||||
|
||||
if (!!abort) { abort.catch((): void => { xhr.abort(); }); }
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _invalidateGet(path: APIPath, paths: string[]): void {
|
||||
this._model.invalidate([path, paths]);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import * as Pbf from "pbf";
|
||||
|
||||
import {IMesh} from "../Graph";
|
||||
import IMesh from "./interfaces/IMesh";
|
||||
|
||||
export class MeshReader {
|
||||
public static read(buffer: Buffer): IMesh {
|
||||
@ -17,3 +17,5 @@ export class MeshReader {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MeshReader;
|
||||
@ -2,6 +2,8 @@ import IFillNode from "./IFillNode";
|
||||
import IFullNode from "./IFullNode";
|
||||
import ICoreNode from "./ICoreNode";
|
||||
import ISequence from "./ISequence";
|
||||
import IClusterReconstruction from "../../component/spatialdata/interfaces/IClusterReconstruction";
|
||||
import IMesh from "./IMesh";
|
||||
|
||||
/**
|
||||
* Interface that describes the data provider functionality.
|
||||
@ -11,10 +13,24 @@ import ISequence from "./ISequence";
|
||||
export interface IDataProvider {
|
||||
getCoreImages(geohashes: string[]):
|
||||
Promise<{ [geohash: string]: { [imageKey: string]: ICoreNode } }>;
|
||||
getClusterReconstruction(clusterKey: string, abort?: Promise<void>):
|
||||
Promise<IClusterReconstruction>;
|
||||
getFillImages(imageKeys: string[]):
|
||||
Promise<{ [imageKey: string]: IFillNode }>;
|
||||
getFullImages(imageKeys: string[]):
|
||||
Promise<{ [imageKey: string]: IFullNode }>;
|
||||
getImage(imageKey: string, size: number, abort?: Promise<void>):
|
||||
Promise<ArrayBuffer>;
|
||||
getImageTile(
|
||||
imageKey: string,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
h: number,
|
||||
scaledW: number,
|
||||
scaledH: number,
|
||||
abort?: Promise<void>): Promise<ArrayBuffer>;
|
||||
getMesh(imageKey: string, abort?: Promise<void>): Promise<IMesh>;
|
||||
getSequences(sequenceKeys: string[]):
|
||||
Promise<{ [sequenceKey: string]: ISequence }>;
|
||||
setToken(token?: string): void;
|
||||
|
||||
@ -27,6 +27,7 @@ import {
|
||||
} from "../Graph";
|
||||
import { GeoRBush } from "../Geo";
|
||||
import API from "../api/API";
|
||||
import { IDataProvider } from "../api/interfaces/interfaces";
|
||||
|
||||
type NodeIndexItem = {
|
||||
lat: number;
|
||||
@ -894,10 +895,11 @@ export class Graph {
|
||||
throw new GraphMapillaryError(`Node already in cache (${key}).`);
|
||||
}
|
||||
|
||||
let node: Node = this.getNode(key);
|
||||
node.initializeCache(new NodeCache());
|
||||
const node: Node = this.getNode(key);
|
||||
const provider: IDataProvider = this._api.dataProvider;
|
||||
node.initializeCache(new NodeCache(provider));
|
||||
|
||||
let accessed: number = new Date().getTime();
|
||||
const accessed: number = new Date().getTime();
|
||||
this._cachedNodes[key] = { accessed: accessed, node: node };
|
||||
|
||||
this._updateCachedTileAccess(key, accessed);
|
||||
|
||||
@ -10,11 +10,11 @@ import {
|
||||
import {IEdge} from "../Edge";
|
||||
import {
|
||||
IEdgeStatus,
|
||||
IMesh,
|
||||
ILoadStatus,
|
||||
NodeCache,
|
||||
} from "../Graph";
|
||||
import {ImageSize} from "../Viewer";
|
||||
import IMesh from "../api/interfaces/IMesh";
|
||||
|
||||
/**
|
||||
* @class Node
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
import {of as observableOf, combineLatest as observableCombineLatest, Subject, Observable, Subscriber, Subscription} from "rxjs";
|
||||
import { of as observableOf, combineLatest as observableCombineLatest, Subject, Observable, Subscriber, Subscription } from "rxjs";
|
||||
|
||||
import {map, tap, startWith, publishReplay, refCount, finalize, first} from "rxjs/operators";
|
||||
import { map, tap, startWith, publishReplay, refCount, finalize, first, throttleTime } from "rxjs/operators";
|
||||
|
||||
import {IEdge} from "../Edge";
|
||||
import { IEdge } from "../Edge";
|
||||
import {
|
||||
IEdgeStatus,
|
||||
IMesh,
|
||||
ILoadStatus,
|
||||
ILoadStatusObject,
|
||||
MeshReader,
|
||||
} from "../Graph";
|
||||
import {
|
||||
Settings,
|
||||
Urls,
|
||||
} from "../Utils";
|
||||
import {ImageSize} from "../Viewer";
|
||||
import { ImageSize } from "../Viewer";
|
||||
import IMesh from "../api/interfaces/IMesh";
|
||||
import MeshReader from "../api/MeshReader";
|
||||
import DataProvider from "../api/DataProvider";
|
||||
import { IDataProvider } from "../api/interfaces/interfaces";
|
||||
|
||||
/**
|
||||
* @class NodeCache
|
||||
@ -24,14 +26,16 @@ import {ImageSize} from "../Viewer";
|
||||
export class NodeCache {
|
||||
private _disposed: boolean;
|
||||
|
||||
private _provider: IDataProvider;
|
||||
|
||||
private _image: HTMLImageElement;
|
||||
private _loadStatus: ILoadStatus;
|
||||
private _mesh: IMesh;
|
||||
private _sequenceEdges: IEdgeStatus;
|
||||
private _spatialEdges: IEdgeStatus;
|
||||
|
||||
private _imageRequest: XMLHttpRequest;
|
||||
private _meshRequest: XMLHttpRequest;
|
||||
private _imageAborter: Function;
|
||||
private _meshAborter: Function;
|
||||
|
||||
private _imageChanged$: Subject<HTMLImageElement>;
|
||||
private _image$: Observable<HTMLImageElement>;
|
||||
@ -49,9 +53,11 @@ export class NodeCache {
|
||||
/**
|
||||
* Create a new node cache instance.
|
||||
*/
|
||||
constructor() {
|
||||
constructor(provider: IDataProvider) {
|
||||
this._disposed = false;
|
||||
|
||||
this._provider = provider;
|
||||
|
||||
this._image = null;
|
||||
this._loadStatus = { loaded: 0, total: 0 };
|
||||
this._mesh = null;
|
||||
@ -190,33 +196,33 @@ export class NodeCache {
|
||||
Settings.baseImageSize;
|
||||
|
||||
this._cachingAssets$ = observableCombineLatest(
|
||||
this._cacheImage$(key, imageSize),
|
||||
this._cacheMesh$(key, merged)).pipe(
|
||||
map(
|
||||
([imageStatus, meshStatus]: [ILoadStatusObject<HTMLImageElement>, ILoadStatusObject<IMesh>]): NodeCache => {
|
||||
this._loadStatus.loaded = 0;
|
||||
this._loadStatus.total = 0;
|
||||
this._cacheImage$(key, imageSize),
|
||||
this._cacheMesh$(key, merged)).pipe(
|
||||
map(
|
||||
([imageStatus, meshStatus]: [ILoadStatusObject<HTMLImageElement>, ILoadStatusObject<IMesh>]): NodeCache => {
|
||||
this._loadStatus.loaded = 0;
|
||||
this._loadStatus.total = 0;
|
||||
|
||||
if (meshStatus) {
|
||||
this._mesh = meshStatus.object;
|
||||
this._loadStatus.loaded += meshStatus.loaded.loaded;
|
||||
this._loadStatus.total += meshStatus.loaded.total;
|
||||
}
|
||||
if (meshStatus) {
|
||||
this._mesh = meshStatus.object;
|
||||
this._loadStatus.loaded += meshStatus.loaded.loaded;
|
||||
this._loadStatus.total += meshStatus.loaded.total;
|
||||
}
|
||||
|
||||
if (imageStatus) {
|
||||
this._image = imageStatus.object;
|
||||
this._loadStatus.loaded += imageStatus.loaded.loaded;
|
||||
this._loadStatus.total += imageStatus.loaded.total;
|
||||
}
|
||||
if (imageStatus) {
|
||||
this._image = imageStatus.object;
|
||||
this._loadStatus.loaded += imageStatus.loaded.loaded;
|
||||
this._loadStatus.total += imageStatus.loaded.total;
|
||||
}
|
||||
|
||||
return this;
|
||||
}),
|
||||
finalize(
|
||||
(): void => {
|
||||
this._cachingAssets$ = null;
|
||||
}),
|
||||
publishReplay(1),
|
||||
refCount());
|
||||
return this;
|
||||
}),
|
||||
finalize(
|
||||
(): void => {
|
||||
this._cachingAssets$ = null;
|
||||
}),
|
||||
publishReplay(1),
|
||||
refCount());
|
||||
|
||||
this._cachingAssets$.pipe(
|
||||
first(
|
||||
@ -319,13 +325,16 @@ export class NodeCache {
|
||||
|
||||
this._disposed = true;
|
||||
|
||||
if (this._imageRequest != null) {
|
||||
this._imageRequest.abort();
|
||||
if (this._imageAborter != null) {
|
||||
this._imageAborter();
|
||||
this._imageAborter = null;
|
||||
}
|
||||
|
||||
if (this._meshRequest != null) {
|
||||
this._meshRequest.abort();
|
||||
if (this._meshAborter != null) {
|
||||
this._meshAborter();
|
||||
this._meshAborter = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,77 +365,47 @@ export class NodeCache {
|
||||
private _cacheImage$(key: string, imageSize: ImageSize): Observable<ILoadStatusObject<HTMLImageElement>> {
|
||||
return Observable.create(
|
||||
(subscriber: Subscriber<ILoadStatusObject<HTMLImageElement>>): void => {
|
||||
let xmlHTTP: XMLHttpRequest = new XMLHttpRequest();
|
||||
xmlHTTP.open("GET", Urls.thumbnail(key, imageSize, Urls.origin), true);
|
||||
xmlHTTP.responseType = "arraybuffer";
|
||||
xmlHTTP.timeout = 15000;
|
||||
const abort: Promise<void> = new Promise(
|
||||
(_, reject): void => {
|
||||
this._imageAborter = reject;
|
||||
});
|
||||
|
||||
xmlHTTP.onload = (pe: ProgressEvent) => {
|
||||
if (xmlHTTP.status !== 200) {
|
||||
this._imageRequest = null;
|
||||
this._provider.getImage(key, imageSize, abort)
|
||||
.then(
|
||||
(buffer: ArrayBuffer): void => {
|
||||
this._imageAborter = null;
|
||||
|
||||
subscriber.error(
|
||||
new Error(`Failed to fetch image (${key}). Status: ${xmlHTTP.status}, ${xmlHTTP.statusText}`));
|
||||
const image: HTMLImageElement = new Image();
|
||||
image.crossOrigin = "Anonymous";
|
||||
|
||||
return;
|
||||
}
|
||||
image.onload = (e: Event) => {
|
||||
if (this._disposed) {
|
||||
window.URL.revokeObjectURL(image.src);
|
||||
subscriber.error(new Error(`Image load was aborted (${key})`));
|
||||
|
||||
let image: HTMLImageElement = new Image();
|
||||
image.crossOrigin = "Anonymous";
|
||||
return;
|
||||
}
|
||||
|
||||
image.onload = (e: Event) => {
|
||||
this._imageRequest = null;
|
||||
subscriber.next({
|
||||
loaded: { loaded: 1, total: 1 },
|
||||
object: image
|
||||
});
|
||||
subscriber.complete();
|
||||
};
|
||||
|
||||
if (this._disposed) {
|
||||
window.URL.revokeObjectURL(image.src);
|
||||
subscriber.error(new Error(`Image load was aborted (${key})`));
|
||||
image.onerror = () => {
|
||||
this._imageAborter = null;
|
||||
|
||||
return;
|
||||
}
|
||||
subscriber.error(new Error(`Failed to load image (${key})`));
|
||||
};
|
||||
|
||||
subscriber.next({ loaded: { loaded: pe.loaded, total: pe.total }, object: image });
|
||||
subscriber.complete();
|
||||
};
|
||||
|
||||
image.onerror = (error: ErrorEvent) => {
|
||||
this._imageRequest = null;
|
||||
|
||||
subscriber.error(new Error(`Failed to load image (${key})`));
|
||||
};
|
||||
|
||||
let blob: Blob = new Blob([xmlHTTP.response]);
|
||||
image.src = window.URL.createObjectURL(blob);
|
||||
};
|
||||
|
||||
xmlHTTP.onprogress = (pe: ProgressEvent) => {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
subscriber.next({loaded: { loaded: pe.loaded, total: pe.total }, object: null });
|
||||
};
|
||||
|
||||
xmlHTTP.onerror = (error: Event) => {
|
||||
this._imageRequest = null;
|
||||
|
||||
subscriber.error(new Error(`Failed to fetch image (${key})`));
|
||||
};
|
||||
|
||||
xmlHTTP.ontimeout = (e: Event) => {
|
||||
this._imageRequest = null;
|
||||
|
||||
subscriber.error(new Error(`Image request timed out (${key})`));
|
||||
};
|
||||
|
||||
xmlHTTP.onabort = (event: Event) => {
|
||||
this._imageRequest = null;
|
||||
|
||||
subscriber.error(new Error(`Image request was aborted (${key})`));
|
||||
};
|
||||
|
||||
this._imageRequest = xmlHTTP;
|
||||
|
||||
xmlHTTP.send(null);
|
||||
const blob: Blob = new Blob([buffer]);
|
||||
image.src = window.URL.createObjectURL(blob);
|
||||
},
|
||||
(error: Error): void => {
|
||||
this._imageAborter = null;
|
||||
subscriber.error(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -448,61 +427,30 @@ export class NodeCache {
|
||||
return;
|
||||
}
|
||||
|
||||
let xmlHTTP: XMLHttpRequest = new XMLHttpRequest();
|
||||
xmlHTTP.open("GET", Urls.protoMesh(key), true);
|
||||
xmlHTTP.responseType = "arraybuffer";
|
||||
xmlHTTP.timeout = 15000;
|
||||
const abort: Promise<void> = new Promise(
|
||||
(_, reject): void => {
|
||||
this._meshAborter = reject;
|
||||
});
|
||||
|
||||
xmlHTTP.onload = (pe: ProgressEvent) => {
|
||||
this._meshRequest = null;
|
||||
this._provider.getMesh(key, abort)
|
||||
.then(
|
||||
(mesh: IMesh): void => {
|
||||
this._meshAborter = null;
|
||||
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mesh: IMesh = xmlHTTP.status === 200 ?
|
||||
MeshReader.read(new Buffer(xmlHTTP.response)) :
|
||||
{ faces: [], vertices: [] };
|
||||
|
||||
subscriber.next({ loaded: { loaded: pe.loaded, total: pe.total }, object: mesh });
|
||||
subscriber.complete();
|
||||
};
|
||||
|
||||
xmlHTTP.onprogress = (pe: ProgressEvent) => {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
subscriber.next({ loaded: { loaded: pe.loaded, total: pe.total }, object: null });
|
||||
};
|
||||
|
||||
xmlHTTP.onerror = (e: Event) => {
|
||||
this._meshRequest = null;
|
||||
|
||||
console.error(`Failed to cache mesh (${key})`);
|
||||
|
||||
subscriber.next(this._createEmptyMeshLoadStatus());
|
||||
subscriber.complete();
|
||||
};
|
||||
|
||||
xmlHTTP.ontimeout = (e: Event) => {
|
||||
this._meshRequest = null;
|
||||
|
||||
console.error(`Mesh request timed out (${key})`);
|
||||
|
||||
subscriber.next(this._createEmptyMeshLoadStatus());
|
||||
subscriber.complete();
|
||||
};
|
||||
|
||||
xmlHTTP.onabort = (e: Event) => {
|
||||
this._meshRequest = null;
|
||||
|
||||
subscriber.error(new Error(`Mesh request was aborted (${key})`));
|
||||
};
|
||||
|
||||
this._meshRequest = xmlHTTP;
|
||||
|
||||
xmlHTTP.send(null);
|
||||
subscriber.next({
|
||||
loaded: { loaded: 1, total: 1 },
|
||||
object: mesh,
|
||||
});
|
||||
subscriber.complete();
|
||||
},
|
||||
(error: Error): void => {
|
||||
this._meshAborter = null;
|
||||
subscriber.error(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -2,4 +2,3 @@ export {IEdgeStatus} from "./IEdgeStatus";
|
||||
export {IGraphConfiguration} from "./IGraphConfiguration";
|
||||
export {ILoadStatus} from "./ILoadStatus";
|
||||
export {ILoadStatusObject} from "./ILoadStatusObject";
|
||||
export {IMesh} from "./IMesh";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {IUrlOptions} from "../Viewer";
|
||||
import { IUrlOptions } from "../Viewer";
|
||||
|
||||
export class Urls {
|
||||
private static _apiHost: string = "a.mapillary.com";
|
||||
@ -42,6 +42,10 @@ export class Urls {
|
||||
return `${Urls._scheme}://${Urls._apiHost}/v3/model.json?client_id=${clientId}`;
|
||||
}
|
||||
|
||||
public static imageTile(imageKey: string, coords: string, size: string): string {
|
||||
return `${Urls.tileScheme}://${Urls.tileDomain}/${imageKey}/${coords}/${size}/0/default.jpg`;
|
||||
}
|
||||
|
||||
public static protoMesh(key: string): string {
|
||||
return `${Urls._scheme}://${Urls._meshHost}/v2/mesh/${key}`;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user