mirror of
https://github.com/mapillary/mapillary-js.git
synced 2025-12-08 17:35:55 +00:00
When larger tile sizes are used the reference should not be reset if it is within the original grid.
454 lines
17 KiB
TypeScript
454 lines
17 KiB
TypeScript
import { bootstrap } from "../Bootstrap";
|
|
bootstrap();
|
|
|
|
import { BehaviorSubject, of as observableOf, Subject } from "rxjs";
|
|
|
|
import { ImageHelper } from "../helper/ImageHelper";
|
|
|
|
import { Image } from "../../src/graph/Image";
|
|
import { APIWrapper } from "../../src/api/APIWrapper";
|
|
import { Graph } from "../../src/graph/Graph";
|
|
import { GraphMode } from "../../src/graph/GraphMode";
|
|
import { GraphService } from "../../src/graph/GraphService";
|
|
import { AnimationFrame } from "../../src/state/interfaces/AnimationFrame";
|
|
import { State } from "../../src/state/State";
|
|
import { StateService } from "../../src/state/StateService";
|
|
import { CacheService } from "../../src/viewer/CacheService";
|
|
import { DataProvider } from "../helper/ProviderHelper";
|
|
import { createDefaultState } from "../helper/StateHelper";
|
|
import { StateServiceMockCreator } from "../helper/StateServiceMockCreator";
|
|
import { S2GeometryProvider } from "../../src/api/S2GeometryProvider";
|
|
import { LngLatAlt } from "../../src/api/interfaces/LngLatAlt";
|
|
|
|
describe("CacheService.ctor", () => {
|
|
it("should be defined when constructed", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graphService = new GraphService(new Graph(api));
|
|
const stateService: StateService = new StateService(State.Traversing, new S2GeometryProvider());
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
expect(cacheService).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe("CacheService.configure", () => {
|
|
it("should configure without errors", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graphService = new GraphService(new Graph(api));
|
|
const stateService: StateService = new StateService(State.Traversing, new S2GeometryProvider());
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
expect(() => { cacheService.configure(); }).not.toThrow();
|
|
expect(() => { cacheService.configure({ cellDepth: 5 }); })
|
|
.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe("CacheService.started", () => {
|
|
it("should not be started", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graphService = new GraphService(new Graph(api));
|
|
const stateService: StateService = new StateService(State.Traversing, new S2GeometryProvider());
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
expect(cacheService.started).toBe(false);
|
|
});
|
|
|
|
it("should be started after calling start", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graphService = new GraphService(new Graph(api));
|
|
const stateService: StateService = new StateService(State.Traversing, new S2GeometryProvider());
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
expect(cacheService.started).toBe(true);
|
|
});
|
|
|
|
it("should not be started after calling stop", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graphService = new GraphService(new Graph(api));
|
|
const stateService: StateService = new StateService(State.Traversing, new S2GeometryProvider());
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
cacheService.stop();
|
|
|
|
expect(cacheService.started).toBe(false);
|
|
});
|
|
});
|
|
|
|
class TestStateService extends StateService {
|
|
private _overridingCurrentState$: Subject<AnimationFrame>;
|
|
|
|
constructor(currentState$: Subject<AnimationFrame>) {
|
|
super(State.Traversing, new S2GeometryProvider());
|
|
|
|
this._overridingCurrentState$ = currentState$;
|
|
}
|
|
|
|
public get currentState$(): Subject<AnimationFrame> {
|
|
return this._overridingCurrentState$;
|
|
}
|
|
}
|
|
|
|
describe("CacheService.start", () => {
|
|
let helper: ImageHelper;
|
|
|
|
beforeEach(() => {
|
|
helper = new ImageHelper();
|
|
});
|
|
|
|
it("should call graph service uncache method", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
graphService.setGraphMode(GraphMode.Spatial);
|
|
|
|
spyOn(graph.api.data.geometry, "lngLatToCellId")
|
|
.and.returnValue("cell-id");
|
|
spyOn(graph.api.data.geometry, "getAdjacent")
|
|
.and.returnValue(["cell-id", "adjacend-id"]);
|
|
|
|
const currentStateSubject$ = new Subject<AnimationFrame>();
|
|
const stateService = new TestStateService(currentStateSubject$);
|
|
|
|
const uncacheSpy = spyOn(graphService, "uncache$");
|
|
const uncacheSubject = new Subject<Graph>();
|
|
uncacheSpy.and.returnValue(uncacheSubject);
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
const image1 = new Image(coreImage1);
|
|
|
|
const coreImage2 = helper.createCoreImageEnt();
|
|
coreImage2.id = "image2";
|
|
const image2 = new Image(coreImage2);
|
|
|
|
const state = createDefaultState();
|
|
state.trajectory = [image1, image2];
|
|
state.currentImage = image1;
|
|
|
|
currentStateSubject$.next({ fps: 60, id: 0, state: state });
|
|
currentStateSubject$.complete();
|
|
|
|
uncacheSubject.complete();
|
|
|
|
expect(uncacheSpy.calls.count()).toBe(1);
|
|
expect(uncacheSpy.calls.first().args.length).toBe(3);
|
|
expect(uncacheSpy.calls.first().args[0].length).toBe(2);
|
|
expect(uncacheSpy.calls.first().args[0][0]).toBe(coreImage1.id);
|
|
expect(uncacheSpy.calls.first().args[0][1]).toBe(coreImage2.id);
|
|
expect(uncacheSpy.calls.first().args[1].length).toBe(2);
|
|
expect(uncacheSpy.calls.first().args[2]).toBeUndefined();
|
|
});
|
|
|
|
it("should call graph service uncache method with sequence key of last trajectory image", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
graphService.setGraphMode(GraphMode.Sequence);
|
|
|
|
spyOn(graph.api.data.geometry, "lngLatToCellId")
|
|
.and.returnValue("cell-id");
|
|
spyOn(graph.api.data.geometry, "getAdjacent")
|
|
.and.returnValue(["cell-id", "adjacent-id"]);
|
|
|
|
const currentStateSubject$ = new Subject<AnimationFrame>();
|
|
const stateService = new TestStateService(currentStateSubject$);
|
|
|
|
const uncacheSpy = spyOn(graphService, "uncache$");
|
|
const uncacheSubject = new Subject<Graph>();
|
|
uncacheSpy.and.returnValue(uncacheSubject);
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
const image1 = new Image(coreImage1);
|
|
|
|
const coreImage2 = helper.createCoreImageEnt();
|
|
coreImage2.id = "image2";
|
|
coreImage2.sequence.id = "sequence2";
|
|
const image2 = new Image(coreImage2);
|
|
|
|
const state = createDefaultState();
|
|
state.trajectory = [image1, image2];
|
|
state.currentImage = image1;
|
|
|
|
currentStateSubject$.next({ fps: 60, id: 0, state: state });
|
|
currentStateSubject$.complete();
|
|
|
|
uncacheSubject.complete();
|
|
|
|
expect(uncacheSpy.calls.count()).toBe(1);
|
|
expect(uncacheSpy.calls.first().args.length).toBe(3);
|
|
expect(uncacheSpy.calls.first().args[0].length).toBe(2);
|
|
expect(uncacheSpy.calls.first().args[0][0]).toBe(coreImage1.id);
|
|
expect(uncacheSpy.calls.first().args[0][1]).toBe(coreImage2.id);
|
|
expect(uncacheSpy.calls.first().args[1].length).toBe(2);
|
|
expect(uncacheSpy.calls.first().args[2]).toBe(coreImage2.sequence.id);
|
|
});
|
|
|
|
it("should cache current image if switching to sequence graph mode", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
|
|
spyOn(graphService, "uncache$").and.returnValue(observableOf<void>(null));
|
|
|
|
graphService.setGraphMode(GraphMode.Spatial);
|
|
|
|
const currentStateSubject$ = new Subject<AnimationFrame>();
|
|
const stateService = new TestStateService(currentStateSubject$);
|
|
|
|
const cacheImageSpy = spyOn(graphService, "cacheImage$");
|
|
const cacheImageSubject = new Subject<Graph>();
|
|
cacheImageSpy.and.returnValue(cacheImageSubject);
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
const image1 = new Image(coreImage1);
|
|
|
|
const coreImage2 = helper.createCoreImageEnt();
|
|
coreImage2.id = "image2";
|
|
const image2 = new Image(coreImage2);
|
|
|
|
const state = createDefaultState();
|
|
state.trajectory = [image1, image2];
|
|
state.currentImage = image1;
|
|
|
|
currentStateSubject$.next({ fps: 60, id: 0, state: state });
|
|
|
|
graphService.setGraphMode(GraphMode.Sequence);
|
|
|
|
expect(cacheImageSpy.calls.count()).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args.length).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args[0]).toBe(coreImage1.id);
|
|
|
|
cacheService.stop();
|
|
});
|
|
|
|
it("should cache current image on data added event", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
|
|
spyOn(graphService, "uncache$").and.returnValue(observableOf<void>(null));
|
|
|
|
const stateService = new StateServiceMockCreator().create();
|
|
|
|
const cacheImageSpy = spyOn(graphService, "cacheImage$");
|
|
const cacheImageSubject = new Subject<Graph>();
|
|
cacheImageSpy.and.returnValue(cacheImageSubject);
|
|
spyOn(graphService, "hasImage$").and.returnValue(new BehaviorSubject(true));
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
|
|
(<Subject<string>>stateService.currentId$).next('image-id');
|
|
(<Subject<string>>graphService.dataAdded$).next('cell-id');
|
|
|
|
expect(cacheImageSpy.calls.count()).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args.length).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args[0]).toBe('image-id');
|
|
|
|
cacheService.stop();
|
|
});
|
|
|
|
it("should cache current image on data deleted event", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
|
|
spyOn(graphService, "uncache$").and.returnValue(observableOf<void>(null));
|
|
|
|
const stateService = new StateServiceMockCreator().create();
|
|
|
|
const cacheImageSpy = spyOn(graphService, "cacheImage$");
|
|
const cacheImageSubject = new Subject<Graph>();
|
|
cacheImageSpy.and.returnValue(cacheImageSubject);
|
|
spyOn(graphService, "hasImage$").and.returnValue(new BehaviorSubject(true));
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
|
|
(<Subject<string>>stateService.currentId$).next('image-id');
|
|
(<Subject<string[]>>graphService.dataDeleted$).next(['cluster-id']);
|
|
|
|
expect(cacheImageSpy.calls.count()).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args.length).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args[0]).toBe('image-id');
|
|
|
|
cacheService.stop();
|
|
});
|
|
|
|
it("should cache all trajectory images ahead if switching to spatial graph mode", () => {
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
|
|
spyOn(graphService, "uncache$").and.returnValue(observableOf<void>(null));
|
|
|
|
graphService.setGraphMode(GraphMode.Sequence);
|
|
|
|
const currentStateSubject$ = new Subject<AnimationFrame>();
|
|
const stateService = new TestStateService(currentStateSubject$);
|
|
|
|
const cacheImageSpy = spyOn(graphService, "cacheImage$");
|
|
const cacheImageSubject = new Subject<Graph>();
|
|
cacheImageSpy.and.returnValue(cacheImageSubject);
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
const image1 = new Image(coreImage1);
|
|
|
|
const coreImage2 = helper.createCoreImageEnt();
|
|
coreImage2.id = "image2";
|
|
const image2 = new Image(coreImage2);
|
|
|
|
const coreImage3 = helper.createCoreImageEnt();
|
|
coreImage3.id = "image3";
|
|
const image3 = new Image(coreImage3);
|
|
|
|
const state = createDefaultState();
|
|
state.trajectory = [image1, image2, image3];
|
|
state.currentImage = image2;
|
|
state.currentIndex = 1;
|
|
|
|
currentStateSubject$.next({ fps: 60, id: 0, state: state });
|
|
|
|
graphService.setGraphMode(GraphMode.Spatial);
|
|
|
|
expect(cacheImageSpy.calls.count()).toBe(2);
|
|
expect(cacheImageSpy.calls.first().args.length).toBe(1);
|
|
expect(cacheImageSpy.calls.first().args[0]).toBe(coreImage2.id);
|
|
expect(cacheImageSpy.calls.argsFor(1).length).toBe(1);
|
|
expect(cacheImageSpy.calls.argsFor(1)[0]).toBe(coreImage3.id);
|
|
|
|
cacheService.stop();
|
|
});
|
|
|
|
it("should keep the subscription open if caching a image fails", () => {
|
|
spyOn(console, "error").and.stub();
|
|
|
|
const api = new APIWrapper(new DataProvider());
|
|
const graph = new Graph(api);
|
|
const graphService = new GraphService(graph);
|
|
|
|
spyOn(graphService, "uncache$").and.returnValue(observableOf<void>(null));
|
|
|
|
const currentStateSubject$ = new Subject<AnimationFrame>();
|
|
const stateService = new TestStateService(currentStateSubject$);
|
|
|
|
const cacheImageSpy = spyOn(graphService, "cacheImage$");
|
|
|
|
const cacheService = new CacheService(graphService, stateService, api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
const image1 = new Image(coreImage1);
|
|
|
|
const state = createDefaultState();
|
|
state.trajectory = [image1];
|
|
state.currentImage = image1;
|
|
state.currentIndex = 0;
|
|
|
|
currentStateSubject$.next({ fps: 60, id: 0, state: state });
|
|
|
|
const cacheImageSubject1 = new Subject<Graph>();
|
|
cacheImageSpy.and.returnValue(cacheImageSubject1);
|
|
|
|
graphService.setGraphMode(GraphMode.Sequence);
|
|
|
|
cacheImageSubject1.error(new Error());
|
|
|
|
const cacheImageSubject2 = new Subject<Graph>();
|
|
cacheImageSpy.and.returnValue(cacheImageSubject2);
|
|
|
|
graphService.setGraphMode(GraphMode.Spatial);
|
|
|
|
expect(cacheImageSpy.calls.count()).toBe(2);
|
|
|
|
cacheService.stop();
|
|
});
|
|
|
|
it("should uncache graph service on reference change", () => {
|
|
const graph = new Graph(new APIWrapper(new DataProvider()));
|
|
const graphService = new GraphService(graph);
|
|
graphService.setGraphMode(GraphMode.Spatial);
|
|
spyOn(graph.api.data.geometry, "lngLatToCellId")
|
|
.and.returnValue("cell-id");
|
|
spyOn(graph.api.data.geometry, "getAdjacent")
|
|
.and.returnValue(["cell-id", "adjacend-id"]);
|
|
|
|
const stateService = new StateServiceMockCreator().create();
|
|
|
|
const uncacheSpy = spyOn(graphService, "uncache$");
|
|
const uncacheSubject = new Subject<Graph>();
|
|
uncacheSpy.and.returnValue(uncacheSubject);
|
|
|
|
const cacheService = new CacheService(graphService, stateService, graph.api);
|
|
|
|
cacheService.start();
|
|
|
|
const coreImage1 = helper.createCoreImageEnt();
|
|
coreImage1.id = "image1";
|
|
const image1 = new Image(coreImage1);
|
|
|
|
const coreImage2 = helper.createCoreImageEnt();
|
|
coreImage2.id = "image2";
|
|
const image2 = new Image(coreImage2);
|
|
|
|
const state = createDefaultState();
|
|
state.trajectory = [image1, image2];
|
|
state.currentImage = image1;
|
|
|
|
(<Subject<AnimationFrame>>stateService.currentState$).next({ fps: 60, id: 0, state: state });
|
|
|
|
expect(uncacheSpy.calls.count()).toBe(1);
|
|
|
|
(<Subject<LngLatAlt>>stateService.reference$).next({ lng: 0, lat: 1, alt: 3 });
|
|
(<Subject<LngLatAlt>>stateService.reference$).complete();
|
|
|
|
uncacheSubject.complete();
|
|
|
|
expect(uncacheSpy.calls.count()).toBe(2);
|
|
expect(uncacheSpy.calls.mostRecent().args.length).toBe(3);
|
|
expect(uncacheSpy.calls.mostRecent().args[0].length).toBe(2);
|
|
expect(uncacheSpy.calls.mostRecent().args[0][0]).toBe(coreImage1.id);
|
|
expect(uncacheSpy.calls.mostRecent().args[0][1]).toBe(coreImage2.id);
|
|
expect(uncacheSpy.calls.mostRecent().args[1].length).toBe(2);
|
|
expect(uncacheSpy.calls.mostRecent().args[2]).toBeUndefined();
|
|
});
|
|
});
|