diff --git a/src/atlasComponents/sapi/constants.ts b/src/atlasComponents/sapi/constants.ts index 40011a86af1ff7dd9476077fed02c50d76abd094..c9ba1fc1d987c62344d8d0b838b7ceb1c301d4bd 100644 --- a/src/atlasComponents/sapi/constants.ts +++ b/src/atlasComponents/sapi/constants.ts @@ -1,7 +1,8 @@ export const IDS = { TEMPLATES: { BIG_BRAIN: "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", - MNI152: "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2" + MNI152: "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", + COLIN27: "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992" }, PARCELLATION: { JBA29: "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290" diff --git a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..30542d6fc0441e31a41fc7ac5b372208ede998d1 --- /dev/null +++ b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.spec.ts @@ -0,0 +1,114 @@ +import { InterSpaceCoordXformSvc, VALID_TEMPLATE_SPACE_NAMES } from './interSpaceCoordXform.service' +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing' +import { TestBed, fakeAsync, tick } from '@angular/core/testing' + +describe('InterSpaceCoordXformSvc.service.spec.ts', () => { + describe('InterSpaceCoordXformSvc', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + InterSpaceCoordXformSvc + ] + }) + }) + + afterEach(() => { + const ctrl = TestBed.inject(HttpTestingController) + ctrl.verify() + }) + + describe('#transform', () => { + it('should instantiate service properly', () => { + const service = TestBed.inject(InterSpaceCoordXformSvc) + expect(service).toBeTruthy() + expect(service.transform).toBeTruthy() + }) + + it('should transform argument properly', () => { + const service = TestBed.inject(InterSpaceCoordXformSvc) + const httpTestingController = TestBed.inject(HttpTestingController) + + // subscriptions are necessary for http fetch to occur + service.transform( + VALID_TEMPLATE_SPACE_NAMES.MNI152, + VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, + [1,2,3] + ).subscribe((_ev) => { + + }) + const req = httpTestingController.expectOne(service['url']) + expect(req.request.method).toEqual('POST') + expect( + JSON.parse(req.request.body) + ).toEqual({ + 'source_space': VALID_TEMPLATE_SPACE_NAMES.MNI152, + 'target_space': VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, + 'source_points': [ + [1e-6, 2e-6, 3e-6] + ] + }) + req.flush({}) + }) + + + it('should transform response properly', () => { + + const service = TestBed.inject(InterSpaceCoordXformSvc) + const httpTestingController = TestBed.inject(HttpTestingController) + + service.transform( + VALID_TEMPLATE_SPACE_NAMES.MNI152, + VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, + [1,2,3] + ).subscribe(({ status, result }) => { + expect(status).toEqual('completed') + expect(result).toEqual([1e6, 2e6, 3e6]) + }) + const req = httpTestingController.expectOne(service['url']) + req.flush({ + 'target_points':[ + [1, 2, 3] + ] + }) + }) + + it('if server returns >=400, fallback gracefully', done => { + const service = TestBed.inject(InterSpaceCoordXformSvc) + const httpTestingController = TestBed.inject(HttpTestingController) + + service.transform( + VALID_TEMPLATE_SPACE_NAMES.MNI152, + VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, + [1,2,3] + ).subscribe(({ status }) => { + expect(status).toEqual('error') + done() + }) + const req = httpTestingController.expectOne(service['url']) + + req.flush('intercepted', { status: 500, statusText: 'internal server error' }) + }) + + it('if server does not respond after 3s, fallback gracefully', fakeAsync(() => { + + const service = TestBed.inject(InterSpaceCoordXformSvc) + const httpTestingController = TestBed.inject(HttpTestingController) + + service.transform( + VALID_TEMPLATE_SPACE_NAMES.MNI152, + VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, + [1,2,3] + ).subscribe(({ status, statusText }) => { + expect(status).toEqual('error') + expect(statusText).toEqual(`Timeout after 3s`) + }) + const req = httpTestingController.expectOne(service['url']) + tick(4000) + expect(req.cancelled).toBe(true) + })) + }) + }) +}) diff --git a/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a0c0c690be0eb7615922a8068ffc86422af54fb --- /dev/null +++ b/src/atlasComponents/sapi/core/space/interSpaceCoordXform.service.ts @@ -0,0 +1,107 @@ +import { Injectable } from "@angular/core"; +import { HttpClient, HttpHeaders, HttpErrorResponse } from "@angular/common/http"; +import { catchError, timeout, map } from "rxjs/operators"; +import { of, Observable } from "rxjs"; +import { environment } from 'src/environments/environment' +import { IDS } from "src/atlasComponents/sapi/constants" + +type ITemplateCoordXformResp = { + status: 'pending' | 'error' | 'completed' | 'cached' + statusText?: string + result? : [number, number, number] +} + +export const VALID_TEMPLATE_SPACE_NAMES = { + MNI152: 'MNI 152 ICBM 2009c Nonlinear Asymmetric', + COLIN27: 'MNI Colin 27', + BIG_BRAIN: 'Big Brain (Histology)', + INFANT: 'Infant Atlas', +} as const + +export type ValidTemplateSpaceName = typeof VALID_TEMPLATE_SPACE_NAMES[keyof typeof VALID_TEMPLATE_SPACE_NAMES] + +@Injectable({ + providedIn: 'root', +}) +export class InterSpaceCoordXformSvc { + + static TmplIdToValidSpaceName(tmplId: string): ValidTemplateSpaceName{ + switch (tmplId) { + case IDS.TEMPLATES.BIG_BRAIN: return VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN + case IDS.TEMPLATES.MNI152: return VALID_TEMPLATE_SPACE_NAMES.MNI152 + case IDS.TEMPLATES.COLIN27: return VALID_TEMPLATE_SPACE_NAMES.COLIN27 + default: return null + } + } + + private cache = { + _map: new Map<string, [number, number, number]>(), + _getKey(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number]) { + return `${srcTmplName}:${targetTmplName}:${coordinatesInNm.map(v => Math.round(v / 1e3)).join(',')}` + }, + set(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number], result: [number, number, number]) { + const key = this._getKey(srcTmplName, targetTmplName, coordinatesInNm) + return this._map.set(key, result) + }, + get(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number]) { + const key = this._getKey(srcTmplName, targetTmplName, coordinatesInNm) + return this._map.get(key) + } + } + + constructor(private httpClient: HttpClient) {} + + private url = `${environment.SPATIAL_TRANSFORM_BACKEND.replace(/\/$/, '')}/v1/transform-points` + + // jasmine marble cannot test promise properly + // see https://github.com/ngrx/platform/issues/498#issuecomment-337465179 + // in order to properly test with marble, use obs instead of promise + transform(srcTmplName: ValidTemplateSpaceName, targetTmplName: ValidTemplateSpaceName, coordinatesInNm: [number, number, number]): Observable<ITemplateCoordXformResp> { + if (!srcTmplName || !targetTmplName) { + return of({ + status: 'error', + statusText: 'either srcTmplName or targetTmplName is undefined' + } as ITemplateCoordXformResp) + } + const cachedResult = this.cache.get(srcTmplName, targetTmplName, coordinatesInNm) + if (cachedResult) { + return of({ + status: 'cached', + result: cachedResult, + } as ITemplateCoordXformResp) + } + + const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + }) + } + return this.httpClient.post( + this.url, + JSON.stringify({ + 'source_points': [[...coordinatesInNm.map(c => c/1e6)]], + 'source_space': srcTmplName, + 'target_space': targetTmplName + }), + httpOptions + ).pipe( + map(resp => { + const result = resp['target_points'][0].map((r: number)=> r * 1e6) as [number, number, number] + this.cache.set(srcTmplName, targetTmplName, coordinatesInNm, result) + return { + status: 'completed', + result + } as ITemplateCoordXformResp + }), + catchError(err => { + if (err instanceof HttpErrorResponse) { + return of(({ status: 'error', statusText: err.message } as ITemplateCoordXformResp)) + } else { + return of(({ status: 'error', statusText: err.toString() } as ITemplateCoordXformResp)) + } + }), + timeout(3000), + catchError(() => of(({ status: 'error', statusText: `Timeout after 3s` } as ITemplateCoordXformResp))), + ) + } +} \ No newline at end of file diff --git a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts index be8b5bfc581d88baf37a5957cea41c115f2fd659..5636ccdd3e36c4930bab5a923f1b20d4ec25507a 100644 --- a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts +++ b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.component.ts @@ -1,8 +1,8 @@ import { animate, state, style, transition, trigger } from "@angular/animations"; import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, QueryList, ViewChild, ViewChildren } from "@angular/core"; -import { Store } from "@ngrx/store"; +import { select, Store } from "@ngrx/store"; import { combineLatest, forkJoin, merge, Observable, Subject, Subscription } from "rxjs"; -import { distinctUntilChanged, map, mapTo, shareReplay, switchMap, tap } from "rxjs/operators"; +import { distinctUntilChanged, map, mapTo, shareReplay, switchMap, take } from "rxjs/operators"; import { SAPI } from "src/atlasComponents/sapi"; import { atlasSelection } from "src/state"; import { fromRootStore } from "src/state/atlasSelection"; @@ -10,6 +10,7 @@ import { IQuickTourData } from "src/ui/quickTour"; import { ARIA_LABELS, CONST, QUICKTOUR_DESC } from 'common/constants' import { MatMenuTrigger } from "@angular/material/menu"; import { SapiParcellationModel, SapiSpaceModel } from "src/atlasComponents/sapi/type"; +import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service" @Component({ selector: `sxplr-sapiviews-core-atlas-tmplparcselector`, @@ -126,7 +127,11 @@ export class SapiViewsCoreAtlasAtlasTmplParcSelector { } - constructor(private store$: Store, private sapi: SAPI) { + constructor( + private store$: Store, + private sapi: SAPI, + private interSpaceCoordXformSvc: InterSpaceCoordXformSvc, + ) { } ngOnDestroy() { @@ -136,6 +141,34 @@ export class SapiViewsCoreAtlasAtlasTmplParcSelector { toggleSelector() { this.selectorExpanded = !this.selectorExpanded + /** + * on selector open, call transform end point + * this caches the result, and will not bottleneck when the user selects a new space + */ + if (this.selectorExpanded) { + forkJoin({ + availableTemplates: this.availableTemplates$.pipe( + take(1) + ), + selectedTemplate: this.selectedTemplate$.pipe( + take(1) + ), + navigation: this.store$.pipe( + select(atlasSelection.selectors.navigation), + take(1) + ) + }).pipe( + + ).subscribe(({ availableTemplates, selectedTemplate, navigation }) => { + for (const avail of availableTemplates) { + this.interSpaceCoordXformSvc.transform( + InterSpaceCoordXformSvc.TmplIdToValidSpaceName(selectedTemplate["@id"]), + InterSpaceCoordXformSvc.TmplIdToValidSpaceName(avail["@id"]), + navigation.position as [number, number, number] + ).subscribe() + } + }) + } } closeSelector() { diff --git a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts index c5bae1bbdef9c9d0709271e63580043fcc406aee..3f18f737ec57e70b77c688c95f7fdbdc84c9e95b 100644 --- a/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts +++ b/src/atlasComponents/sapiViews/core/atlas/tmplParcSelector/tmplParcSelector.stories.ts @@ -4,6 +4,7 @@ import { provideMockStore } from "@ngrx/store/testing" import { action } from "@storybook/addon-actions" import { Meta, moduleMetadata, Story } from "@storybook/angular" import { SAPI } from "src/atlasComponents/sapi" +import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service" import { spaceId, provideDarkTheme, getHumanAtlas, getMni152, getJba29, getSpace, atlasId, getParc, parcId } from "src/atlasComponents/sapi/stories.base" import { AngularMaterialModule } from "src/sharedModules" import { atlasSelection } from "src/state" @@ -27,6 +28,7 @@ export default { ], providers: [ SAPI, + InterSpaceCoordXformSvc, ...provideDarkTheme, ], declarations: [ diff --git a/src/main.module.ts b/src/main.module.ts index dfdc4113f166398136e161fdf71f4a1952580502..7d26a8ee52400fa2acd8e9cbd0a670ddd06c5670 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -18,7 +18,7 @@ import { UIService } from "./services/uiService.service"; import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR, UtilModule } from "src/util"; import { SpotLightModule } from 'src/spotlight/spot-light.module' import { TryMeComponent } from "./ui/tryme/tryme.component"; -import { TemplateCoordinatesTransformation } from "src/services/templateCoordinatesTransformation.service"; +import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service"; import { WidgetModule } from 'src/widget'; import { PluginModule } from './plugin/plugin.module'; import { LoggingModule } from './logging/logging.module'; @@ -94,7 +94,7 @@ import { CONST } from "common/constants" AuthService, DialogService, UIService, - TemplateCoordinatesTransformation, + InterSpaceCoordXformSvc, ClickInterceptorService, { provide: CANCELLABLE_DIALOG, diff --git a/src/services/templateCoordinatesTransformation.service.spec.ts b/src/services/templateCoordinatesTransformation.service.spec.ts deleted file mode 100644 index 0aa477d103f4dfe8b0894314e4a08bfde4666cfb..0000000000000000000000000000000000000000 --- a/src/services/templateCoordinatesTransformation.service.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { TemplateCoordinatesTransformation } from './templateCoordinatesTransformation.service' -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing' -import { TestBed, fakeAsync, tick } from '@angular/core/testing' - -describe('templateCoordinatesTransformation.service.spec.ts', () => { - describe('TemplateCoordinatesTransformation', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule - ], - providers: [ - TemplateCoordinatesTransformation - ] - }) - }) - - afterEach(() => { - const ctrl = TestBed.inject(HttpTestingController) - ctrl.verify() - }) - - describe('getPointCoordinatesForTemplate', () => { - it('should instantiate service properly', () => { - const service = TestBed.inject(TemplateCoordinatesTransformation) - expect(service).toBeTruthy() - expect(service.getPointCoordinatesForTemplate).toBeTruthy() - }) - - it('should transform argument properly', () => { - const service = TestBed.inject(TemplateCoordinatesTransformation) - const httpTestingController = TestBed.inject(HttpTestingController) - - // subscriptions are necessary for http fetch to occur - service.getPointCoordinatesForTemplate( - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152, - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - [1,2,3] - ).subscribe((_ev) => { - - }) - const req = httpTestingController.expectOne(service.url) - expect(req.request.method).toEqual('POST') - expect( - JSON.parse(req.request.body) - ).toEqual({ - 'source_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152, - 'target_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - 'source_points': [ - [1e-6, 2e-6, 3e-6] - ] - }) - req.flush({}) - }) - - it('transforms mapped space name(s)', () => { - const service = TestBed.inject(TemplateCoordinatesTransformation) - const httpTestingController = TestBed.inject(HttpTestingController) - - const key = Array.from(TemplateCoordinatesTransformation.NameMap.keys())[0] - service.getPointCoordinatesForTemplate( - key, - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - [1,2,3] - ).subscribe((_ev) => { - - }) - const req = httpTestingController.expectOne(service.url) - expect(req.request.method).toEqual('POST') - expect( - JSON.parse(req.request.body) - ).toEqual({ - 'source_space': TemplateCoordinatesTransformation.NameMap.get(key), - 'target_space': TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - 'source_points': [ - [1e-6, 2e-6, 3e-6] - ] - }) - req.flush({}) - }) - - - it('should transform response properly', () => { - - const service = TestBed.inject(TemplateCoordinatesTransformation) - const httpTestingController = TestBed.inject(HttpTestingController) - - service.getPointCoordinatesForTemplate( - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152, - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - [1,2,3] - ).subscribe(({ status, result }) => { - expect(status).toEqual('completed') - expect(result).toEqual([1e6, 2e6, 3e6]) - }) - const req = httpTestingController.expectOne(service.url) - req.flush({ - 'target_points':[ - [1, 2, 3] - ] - }) - }) - - it('if server returns >=400, fallback gracefully', () => { - const service = TestBed.inject(TemplateCoordinatesTransformation) - const httpTestingController = TestBed.inject(HttpTestingController) - - service.getPointCoordinatesForTemplate( - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152, - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - [1,2,3] - ).subscribe(({ status }) => { - expect(status).toEqual('error') - }) - const req = httpTestingController.expectOne(service.url) - - req.flush('intercepted', { status: 500, statusText: 'internal server error' }) - }) - - it('if server does not respond after 3s, fallback gracefully', fakeAsync(() => { - - const service = TestBed.inject(TemplateCoordinatesTransformation) - const httpTestingController = TestBed.inject(HttpTestingController) - - service.getPointCoordinatesForTemplate( - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152, - TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN, - [1,2,3] - ).subscribe(({ status, statusText }) => { - expect(status).toEqual('error') - expect(statusText).toEqual(`Timeout after 3s`) - }) - const req = httpTestingController.expectOne(service.url) - tick(4000) - expect(req.cancelled).toBe(true) - })) - }) - }) -}) diff --git a/src/services/templateCoordinatesTransformation.service.ts b/src/services/templateCoordinatesTransformation.service.ts deleted file mode 100644 index c9fcf3a9d5b191ecf9acc3b68890f06cfceb549c..0000000000000000000000000000000000000000 --- a/src/services/templateCoordinatesTransformation.service.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {Injectable} from "@angular/core"; -import {HttpClient, HttpHeaders, HttpErrorResponse} from "@angular/common/http"; -import { catchError, timeout, map } from "rxjs/operators"; -import { of, Observable } from "rxjs"; -import { environment } from 'src/environments/environment' - -export interface ITemplateCoordXformResp{ - status: 'pending' | 'error' | 'completed' - statusText?: string - result? : [number, number, number] -} - -@Injectable({ - providedIn: 'root', -}) -export class TemplateCoordinatesTransformation { - - static VALID_TEMPLATE_SPACE_NAMES = { - MNI152: 'MNI 152 ICBM 2009c Nonlinear Asymmetric', - COLIN27: 'MNI Colin 27', - BIG_BRAIN: 'Big Brain (Histology)', - INFANT: 'Infant Atlas', - } - - static NameMap = new Map([ - ['MNI152 2009c nonl asym', TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.MNI152], - ["Big Brain", TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES.BIG_BRAIN] - ]) - - constructor(private httpClient: HttpClient) {} - - public url = `${environment.SPATIAL_TRANSFORM_BACKEND.replace(/\/$/, '')}/v1/transform-points` - - // jasmine marble cannot test promise properly - // see https://github.com/ngrx/platform/issues/498#issuecomment-337465179 - // in order to properly test with marble, use obs instead of promise - getPointCoordinatesForTemplate(sourceTemplateName: string, targetTemplateName: string, coordinatesInNm: [number, number, number]): Observable<ITemplateCoordXformResp> { - const httpOptions = { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - } - const srcTmplName = Object.values(TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES).includes(sourceTemplateName) - ? sourceTemplateName - : TemplateCoordinatesTransformation.NameMap.get(sourceTemplateName) - - const targetTmplName = Object.values(TemplateCoordinatesTransformation.VALID_TEMPLATE_SPACE_NAMES).includes(targetTemplateName) - ? targetTemplateName - : TemplateCoordinatesTransformation.NameMap.get(targetTemplateName) - - return this.httpClient.post( - this.url, - JSON.stringify({ - 'source_points': [[...coordinatesInNm.map(c => c/1e6)]], - 'source_space': srcTmplName, - 'target_space': targetTmplName - }), - httpOptions - ).pipe( - map(resp => { - return { - status: 'completed', - result: resp['target_points'][0].map((r: number)=> r * 1e6) - } as ITemplateCoordXformResp - }), - catchError(err => { - if (err instanceof HttpErrorResponse) { - return of(({ status: 'error', statusText: err.message } as ITemplateCoordXformResp)) - } else { - return of(({ status: 'error', statusText: err.toString() } as ITemplateCoordXformResp)) - } - }), - timeout(3000), - catchError(() => of(({ status: 'error', statusText: `Timeout after 3s` } as ITemplateCoordXformResp))), - ) - } -} \ No newline at end of file diff --git a/src/state/atlasSelection/effects.ts b/src/state/atlasSelection/effects.ts index 75c1a0d6b83461fc3705ac7e4130d6fb04c287c6..0ef6018a23025064c199a59e33cfe1c5dc04bc7f 100644 --- a/src/state/atlasSelection/effects.ts +++ b/src/state/atlasSelection/effects.ts @@ -10,8 +10,9 @@ import { fromRootStore } from "./util"; import { AtlasSelectionState } from "./const" import { ParcellationIsBaseLayer } from "src/atlasComponents/sapiViews/core/parcellation/parcellationIsBaseLayer.pipe"; import { OrderParcellationByVersionPipe } from "src/atlasComponents/sapiViews/core/parcellation/parcellationVersion.pipe"; -import { atlasAppearance } from ".."; +import { atlasAppearance, atlasSelection } from ".."; import { ParcellationSupportedInSpacePipe } from "src/atlasComponents/sapiViews/util/parcellationSupportedInSpace.pipe"; +import { InterSpaceCoordXformSvc } from "src/atlasComponents/sapi/core/space/interSpaceCoordXform.service"; type OnTmplParcHookArg = { previous: { @@ -47,6 +48,37 @@ export class Effect { } }) ) + }, + ({ current, previous }) => { + const prevSpcName = InterSpaceCoordXformSvc.TmplIdToValidSpaceName(previous.template["@id"]) + const currSpcName = InterSpaceCoordXformSvc.TmplIdToValidSpaceName(current.template["@id"]) + /** + * if either space name is undefined, return default state for navigation + */ + if (!prevSpcName || !currSpcName) { + return of({ + navigation: atlasSelection.defaultState.navigation + }) + } + return this.store.pipe( + select(atlasSelection.selectors.navigation), + take(1), + switchMap(({ position, ...rest }) => + this.interSpaceCoordXformSvc.transform(prevSpcName, currSpcName, position as [number, number, number]).pipe( + map(value => { + if (value.status === "error") { + return {} + } + return { + navigation: { + ...rest, + position: value.result, + } + } as Partial<AtlasSelectionState> + }) + ) + ) + ) } ] @@ -352,6 +384,7 @@ export class Effect { private action: Actions, private sapiSvc: SAPI, private store: Store, + private interSpaceCoordXformSvc: InterSpaceCoordXformSvc, ){ } diff --git a/src/util/priority.ts b/src/util/priority.ts index 93329679e5f069dda8efe6c7dc98e39a1d760233..ab703f76b14e1a299fa2ae017d81038a43c1c858 100644 --- a/src/util/priority.ts +++ b/src/util/priority.ts @@ -132,8 +132,12 @@ export class PriorityHttpInterceptor implements HttpInterceptor{ } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { - - if (this.disablePriority) { + /** + * Do not use priority for requests other than get. + * Since the way in which serialization occurs is via path and query param... + * body is not used. + */ + if (this.disablePriority || req.method !== 'GET') { return next.handle(req) }