diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index 34d3bd40c67b8b44743451738d577634983ff75d..6c4061386ea4178d3cca27e0b9c2892bf1ae98ac 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -40,7 +40,8 @@ export const NEHUBA_CLICK_OVERRIDE: InjectionToken<(next: () => void) => void> = import { MIN_REQ_EXPLAINER } from 'src/util/constants' import { SlServiceService } from "src/spotlight/sl-service.service"; import { PureContantService } from "src/util"; -import { viewerStateSetSelectedRegions, viewerStateGetOverlayingAdditionalParcellations, viewerStateRemoveAdditionalLayer } from "src/services/state/viewerState.store.helper"; +import { viewerStateSetSelectedRegions, viewerStateRemoveAdditionalLayer, viewerStateSelectParcellation } from "src/services/state/viewerState.store.helper"; +import { viewerStateGetOverlayingAdditionalParcellations, viewerStateParcVersionSelector } from "src/services/state/viewerState/selectors"; /** * TODO @@ -109,6 +110,10 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { select(viewerStateGetOverlayingAdditionalParcellations), ) + public selectedLayerVersions$ = this.store.pipe( + select(viewerStateParcVersionSelector), + ) + constructor( private store: Store<IavRootStoreInterface>, private widgetServices: WidgetServices, @@ -361,6 +366,18 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { }) } + public selectParcellation(parc: any) { + this.store.dispatch( + viewerStateSelectParcellation({ + selectParcellation: parc + }) + ) + } + + public bindFn(fn, arg){ + return () => fn(arg) + } + public clearAdditionalLayer(layer: { ['@id']: string }){ this.store.dispatch( viewerStateRemoveAdditionalLayer({ diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index d69e527a6e4ba6ade595b8cff53f101898017779..ebc678e829a5b26c13620c57278b6a142a590519 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -88,13 +88,14 @@ </atlas-layer-selector> <mat-chip-list class="mb-2"> <!-- additional layer --> - <ng-container *ngIf="!alSelector.selectorExpanded"> + + <ng-container> <ng-container *ngTemplateOutlet="currParcellationTmpl; context: { addParc: (selectedAdditionalLayers$ | async), parc: selectedParcellation }"> </ng-container> </ng-container> <!-- any selected region(s) --> - <ng-container *ngIf="!uiNehubaContainer.navSideDrawerMainSwitch.switchState || !uiNehubaContainer.navSideDrawerMinorSwitch.switchState"> + <ng-container> <ng-container *ngTemplateOutlet="selectedRegionTmpl"> </ng-container> </ng-container> @@ -107,79 +108,98 @@ </mat-chip-list> <!-- current layer tmpl --> + <ng-template #currParcellationTmpl let-parc="parc" let-addParc="addParc"> - <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl"> - <ng-container *ngFor="let p of addParc"> + + <div [matMenuTriggerFor]="layerVersionMenu" #layerVersionMenuTrigger="matMenuTrigger"> + + <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl"> + <ng-container *ngFor="let p of addParc"> + <ng-container *ngTemplateOutlet="chipTmpl; context: { + parcel: p, + selected: true, + dismissable: true, + onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger) + }"> + </ng-container> + </ng-container> + </ng-template> + <ng-template #defaultParcTmpl> <ng-container *ngTemplateOutlet="chipTmpl; context: { - parcel: p, - selected: true, - dismissable: true + parcel: parc, + selected: false, + dismissable: false, + onclick: layerVersionMenuTrigger.toggleMenu.bind(layerVersionMenuTrigger) }"> </ng-container> - </ng-container> - </ng-template> - <ng-template #defaultParcTmpl> - <ng-container *ngTemplateOutlet="chipTmpl; context: { - parcel: parc, - selected: false, - dismissable: false - }"> - </ng-container> - </ng-template> - - <!-- render parc templ --> - <ng-template #chipTmpl - let-parcel="parcel" - let-selected="selected" - let-dismissable="dismissable"> - <mat-chip class="pe-all" - (click)="alSelector.selectorExpanded = true" - [selected]="selected"> - - {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel?.name }} - - <!-- info icon --> - <ng-template [ngIf]="parcel?.originDatasets?.length > 0" - [ngIfElse]="infoIconBasic"> + </ng-template> + </div> + </ng-template> - <mat-icon - *ngFor="let ds of parcel.originDatasets" - fontSet="fas" - fontIcon="fa-info-circle" - iav-stop="click" - iav-dataset-show-dataset-dialog - [iav-dataset-show-dataset-dialog-kgid]="ds['kgId']" - [iav-dataset-show-dataset-dialog-kgschema]="ds['kgSchema']" - [iav-dataset-show-dataset-dialog-name]="parcel?.properties?.name" - [iav-dataset-show-dataset-dialog-description]="parcel?.properties?.description"> - </mat-icon> + <!-- render parc templ --> + <ng-template #chipTmpl + let-parcel="parcel" + let-selected="selected" + let-dismissable="dismissable" + let-onclick="onclick"> + <mat-chip class="pe-all" (click)="onclick && onclick()" [selected]="selected"> + + {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel?.name }} - </ng-template> + <!-- info icon --> + <ng-template [ngIf]="parcel?.originDatasets?.length > 0" [ngIfElse]="infoIconBasic"> - <ng-template #infoIconBasic> - <mat-icon *ngIf="parcel?.properties?.name && parcel?.properties?.description" - fontSet="fas" - fontIcon="fa-info-circle" - iav-stop="click" - iav-dataset-show-dataset-dialog - [iav-dataset-show-dataset-dialog-name]="parcel.properties.name" - [iav-dataset-show-dataset-dialog-description]="parcel.properties.description"> + <mat-icon + *ngFor="let ds of parcel.originDatasets" + fontSet="fas" + fontIcon="fa-info-circle" + iav-stop="click" + iav-dataset-show-dataset-dialog + [iav-dataset-show-dataset-dialog-kgid]="ds['kgId']" + [iav-dataset-show-dataset-dialog-kgschema]="ds['kgSchema']" + [iav-dataset-show-dataset-dialog-name]="parcel?.properties?.name" + [iav-dataset-show-dataset-dialog-description]="parcel?.properties?.description"> + </mat-icon> - </mat-icon> - </ng-template> + </ng-template> - <!-- dismiss icon --> - <mat-icon - *ngIf="dismissable" - (click)="clearAdditionalLayer(parcel)" + <ng-template #infoIconBasic> + <mat-icon *ngIf="parcel?.properties?.name && parcel?.properties?.description" fontSet="fas" + fontIcon="fa-info-circle" iav-stop="click" - fontIcon="fa-times"> + iav-dataset-show-dataset-dialog + [iav-dataset-show-dataset-dialog-name]="parcel.properties.name" + [iav-dataset-show-dataset-dialog-description]="parcel.properties.description"> + </mat-icon> - </mat-chip> - </ng-template> + </ng-template> + + <!-- dismiss icon --> + <mat-icon + *ngIf="dismissable" + (click)="clearAdditionalLayer(parcel)" + fontSet="fas" + iav-stop="click" + fontIcon="fa-times"> + </mat-icon> + </mat-chip> </ng-template> + <!-- layer version selector --> + <mat-menu #layerVersionMenu> + <ng-container *ngFor="let parcVer of selectedLayerVersions$ | async"> + <ng-container *ngTemplateOutlet="chipTmpl; context: { + parcel: parcVer, + selected: selectedParcellation && selectedParcellation['@id'] === parcVer['@id'], + dismissable: false, + onclick: bindFn(selectParcellation.bind(this), parcVer) + }"> + </ng-container> + <div class="mt-1"></div> + </ng-container> + </mat-menu> + <ng-template #selectedRegionTmpl> <ng-container *ngFor="let r of (selectedRegions$ | async)"> diff --git a/src/res/ext/atlas/atlas_allenMouse.json b/src/res/ext/atlas/atlas_allenMouse.json index 07f1b18ef53efb92db32cef4349f7e935af10b5c..8ceb38b173c1db0c0de0bfbbd4bf9da92b59afa4 100644 --- a/src/res/ext/atlas/atlas_allenMouse.json +++ b/src/res/ext/atlas/atlas_allenMouse.json @@ -19,13 +19,25 @@ "availableIn": [{ "@id": "minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9", "name": "Allen Mouse Common Coordinate Framework v3" - }] + }], + "@version": { + "@next": null, + "@this": "minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83", + "name": "v3 2017", + "@previous": "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f" + } },{ "name": "Allen Mouse Common Coordinate Framework v3 2015", "@id": "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f", "availableIn": [{ "@id": "minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9", "name": "Allen Mouse Common Coordinate Framework v3" - }] + }], + "@version": { + "@next": "minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83", + "@this": "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f", + "name": "v3 2015", + "@previous": null + } }] } \ No newline at end of file diff --git a/src/res/ext/atlas/atlas_waxholmRat.json b/src/res/ext/atlas/atlas_waxholmRat.json index 16a1819c18f42e4629be95fca21182c3449cbcf1..294f5b585f071ccc4c3c4323add62ebc1f5a0e54 100644 --- a/src/res/ext/atlas/atlas_waxholmRat.json +++ b/src/res/ext/atlas/atlas_waxholmRat.json @@ -22,20 +22,38 @@ "availableIn": [{ "@id": "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8", "name": "Waxholm Space rat brain MRI/DTI" - }] + }], + "@version": { + "@next": null, + "@this": "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe", + "name": "v3", + "@previous": "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d" + } },{ "name": "Waxholm Space rat brain atlas v2", "@id": "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d", "availableIn": [{ "@id": "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8", "name": "Waxholm Space rat brain MRI/DTI" - }] + }], + "@version": { + "@next": "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe", + "@this": "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d", + "name": "v2", + "@previous": "minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba" + } },{ "name": "Waxholm Space rat brain atlas v1", "@id": "minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba", "availableIn": [{ "@id": "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8", "name": "Waxholm Space rat brain MRI/DTI" - }] + }], + "@version": { + "@next": "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d", + "@this": "minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba", + "name": "v1.01", + "@previous": null + } }] } diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index b9172be04ea57d70d12104f91e9870fe50534d38..eaf14e842e1a044578d8d2904d0ff617bef2cf64 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -45,17 +45,16 @@ export { } import { - viewerStateAllParcellationsSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, - viewerStateSelectedParcellationSelector + viewerStateSelectedParcellationSelector, + viewerStateGetSelectedAtlas, } from './viewerState/selectors' export { - viewerStateAllParcellationsSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, - viewerStateSelectedParcellationSelector + viewerStateSelectedParcellationSelector, } interface IViewerStateHelperStore{ @@ -124,32 +123,6 @@ export const viewerStateHelperReducer = createReducer( export const viewerStateHelperStoreName = 'viewerStateHelper' -export const viewerStateGetOverlayingAdditionalParcellations = createSelector( - state => state[viewerStateHelperStoreName], - state => state['viewerState'], - (viewerHelperState, viewerState ) => { - const { selectedAtlasId, fetchedAtlases } = viewerHelperState - const { parcellationSelected } = viewerState - - const selectedAtlas = selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) - const atlasLayer = selectedAtlas?.parcellations.find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id'])) - const isBaseLayer = atlasLayer && atlasLayer.baseLayer - return (!!atlasLayer && !isBaseLayer) ? [{ - ...(parcellationSelected || {} ), - ...atlasLayer - }] : [] - } -) - -export const viewerStateGetSelectedAtlas = createSelector( - state => state[viewerStateHelperStoreName], - helperState => { - if (!helperState) return null - const { selectedAtlasId, fetchedAtlases } = helperState - return selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) - } -) - export function viewerStateFleshOutDetail(reducer: ActionReducer<any>): ActionReducer<any> { return (state, action) => { if (action.type === viewerStateSelectAtlas.type) { diff --git a/src/services/state/viewerState/selectors.spec.ts b/src/services/state/viewerState/selectors.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9faae82c648c8943a6071552267404664c66d50b --- /dev/null +++ b/src/services/state/viewerState/selectors.spec.ts @@ -0,0 +1,133 @@ +import { + viewerStateGetOverlayingAdditionalParcellations, + viewerStateAtlasParcellationSelector, + viewerStateAtlasLatestParcellationSelector, + viewerStateParcVersionSelector, +} from './selectors' + +const waxholmAtlasJson = require('!json-loader!src/res/ext/atlas/atlas_waxholmRat.json') +const humanAtlasJson = require('!json-loader!src/res/ext/atlas/atlas_multiLevelHuman.json') +const allenAtlasJson = require('!json-loader!src/res/ext/atlas/atlas_allenMouse.json') + +const waxholmTemplates = require('!json-loader!src/res/ext/waxholmRatV2_0.json') +const allenTemplates = require('!json-loader!src/res/ext/allenMouse.json') +const colinTemplates = require('!json-loader!src/res/ext/colin.json') +const mniTemplates = require('!json-loader!src/res/ext/MNI152.json') +const bbTemplates = require('!json-loader!src/res/ext/bigbrain.json') + +const fetchedAtlases = [ + waxholmAtlasJson, + humanAtlasJson, + allenAtlasJson, +] + +const fetchedTemplates = [ + waxholmTemplates, + allenTemplates, + colinTemplates, + mniTemplates, + bbTemplates, +] + +describe('viewerState/selector.ts', () => { + describe('> viewerStateGetOverlayingAdditionalParcellations', () => { + it('> if atlas has no basic layer, should return empty array', () => { + const waxholmParcs = viewerStateGetOverlayingAdditionalParcellations.projector({ + fetchedAtlases, + selectedAtlasId: waxholmAtlasJson['@id'] + }, { + parcellationSelected: waxholmAtlasJson.parcellations[0] + }) + + expect(waxholmParcs).toEqual([]) + + const allenParcs = viewerStateGetOverlayingAdditionalParcellations.projector({ + fetchedAtlases, + selectedAtlasId: allenAtlasJson['@id'] + }, { + parcellationSelected: allenAtlasJson.parcellations[0] + }) + expect(allenParcs).toEqual([]) + }) + + + it('> if atlas has basic layer, should return non empty array, if non basic layer is selected', () => { + const multihumanParcs = viewerStateGetOverlayingAdditionalParcellations.projector({ + fetchedAtlases, + selectedAtlasId: humanAtlasJson['@id'] + }, { + parcellationSelected: humanAtlasJson.parcellations[1] + }) + expect(multihumanParcs.length).toEqual(1) + expect(multihumanParcs[0]['@id']).toEqual(humanAtlasJson.parcellations[1]['@id']) + }) + + it('> if atlas has basic layer, but has basic layer selected, should return empty array', () => { + const multihumanParcs = viewerStateGetOverlayingAdditionalParcellations.projector({ + fetchedAtlases, + selectedAtlasId: humanAtlasJson['@id'] + }, { + parcellationSelected: humanAtlasJson.parcellations[0] + }) + expect(multihumanParcs.length).toEqual(0) + }) + }) + + describe('> viewerStateAtlasParcellationSelector', () => { + const check = (atlasJson, templates) => { + + const parcs = viewerStateAtlasParcellationSelector.projector({ + fetchedAtlases, + selectedAtlasId: atlasJson['@id'] + }, { + fetchedTemplates + }) + const templateParcs = [] + for (const tmpl of templates) { + templateParcs.push(...tmpl.parcellations) + } + for (const parc of parcs) { + const firstHalf = templateParcs.find(p => p['@id'] === parc['@id']) + const secondHalf = atlasJson.parcellations.find(p => p['@id'] === parc['@id']) + expect(firstHalf).toBeTruthy() + expect(secondHalf).toBeTruthy() + //TODO compare strict equality of firsthalf+secondhalf with parc + } + } + it('> should work', () => { + check(waxholmAtlasJson, [waxholmTemplates]) + check(allenAtlasJson, [allenTemplates]) + check(humanAtlasJson, [bbTemplates, mniTemplates, colinTemplates]) + }) + }) + + describe('> viewerStateAtlasLatestParcellationSelector', () => { + it('> for waxholm and allen, sould only show 1 parc', () => { + const waxholmParcs = viewerStateAtlasLatestParcellationSelector.projector(waxholmAtlasJson.parcellations) + expect(waxholmParcs.length).toEqual(1) + const allenparcs = viewerStateAtlasLatestParcellationSelector.projector(allenAtlasJson.parcellations) + expect(allenparcs.length).toEqual(1) + }) + }) + + describe('> viewerStateParcVersionSelector', () => { + it('> for waxholm, should show 3 parc ordered correctly', () => { + const parcs = viewerStateParcVersionSelector.projector(waxholmAtlasJson.parcellations, { + parcellationSelected: waxholmAtlasJson.parcellations[0] + }) + expect(parcs.length).toEqual(3) + + expect(parcs[0]['@version']['@next']).toBeFalsy() + expect(parcs[parcs.length-1]['@version']['@previous']).toBeFalsy() + }) + it('> for allen, should show 2 parc ordered correctly', () => { + const parcs = viewerStateParcVersionSelector.projector(allenAtlasJson.parcellations, { + parcellationSelected: allenAtlasJson.parcellations[0] + }) + expect(parcs.length).toEqual(2) + + expect(parcs[0]['@version']['@next']).toBeFalsy() + expect(parcs[parcs.length-1]['@version']['@previous']).toBeFalsy() + }) + }) +}) \ No newline at end of file diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts index 8b541af1f2ecb9a8f2ca043499ca9bb4af1cb2f7..8c6bd83804c95354905e3e07d0d3516edde66404 100644 --- a/src/services/state/viewerState/selectors.ts +++ b/src/services/state/viewerState/selectors.ts @@ -1,26 +1,21 @@ import { createSelector } from "@ngrx/store" +import { viewerStateHelperStoreName } from "../viewerState.store.helper" export const viewerStateSelectedRegionsSelector = createSelector( state => state['viewerState'], viewerState => viewerState['regionsSelected'] ) -export const viewerStateAllParcellationsSelector = createSelector( - state => state['viewerState'], - viewerState => { - return (viewerState['fetchedTemplates'] as any[] || []) - .reduce((acc, curr) => { - const parcelations = (curr['parcellations'] || []).map(p => { - return { - ...p, - useTheme: curr['useTheme'] - } - }) - - return acc.concat( parcelations ) - }, []) - } -) +const flattenFetchedTemplatesIntoParcellationsReducer = (acc, curr) => { + const parcelations = (curr['parcellations'] || []).map(p => { + return { + ...p, + useTheme: curr['useTheme'] + } + }) + + return acc.concat( parcelations ) +} export const viewerStateSelectedTemplateSelector = createSelector( state => state['viewerState'], @@ -31,3 +26,97 @@ export const viewerStateSelectedParcellationSelector = createSelector( state => state['viewerState'], viewerState => viewerState['parcellationSelected'] ) + +export const viewerStateGetOverlayingAdditionalParcellations = createSelector( + state => state[viewerStateHelperStoreName], + state => state['viewerState'], + (viewerHelperState, viewerState ) => { + const { selectedAtlasId, fetchedAtlases } = viewerHelperState + const { parcellationSelected } = viewerState + const selectedAtlas = selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) + const hasBaseLayer = selectedAtlas?.parcellations.find(p => p.baseLayer) + if (!hasBaseLayer) return [] + const atlasLayer = selectedAtlas?.parcellations.find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id'])) + const isBaseLayer = atlasLayer && atlasLayer.baseLayer + return (!!atlasLayer && !isBaseLayer) ? [{ + ...(parcellationSelected || {} ), + ...atlasLayer + }] : [] + } +) + + +export const viewerStateGetSelectedAtlas = createSelector( + state => state[viewerStateHelperStoreName], + helperState => { + if (!helperState) return null + const { selectedAtlasId, fetchedAtlases } = helperState + return selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) + } +) + +export const viewerStateAtlasParcellationSelector = createSelector( + state => state[viewerStateHelperStoreName], + state => state['viewerState'], + (viewerHelperState, viewerState) => { + const { selectedAtlasId, fetchedAtlases } = viewerHelperState + const { fetchedTemplates } = viewerState + + const allParcellations = fetchedTemplates.reduce(flattenFetchedTemplatesIntoParcellationsReducer, []) + + const selectedAtlas = selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) + const atlasLayers = selectedAtlas?.parcellations + .map(p => { + const otherHalfOfParc = allParcellations.find(parc => parc['@id'] === p['@id']) || {} + return { + ...p, + ...otherHalfOfParc, + } + }) + return atlasLayers + } +) + +export const viewerStateAtlasLatestParcellationSelector = createSelector( + viewerStateAtlasParcellationSelector, + parcs => parcs.filter( p => !p['@version'] || !p['@version']['@next']) +) + +export const viewerStateParcVersionSelector = createSelector( + viewerStateAtlasParcellationSelector, + state => state['viewerState'], + (allAtlasParcellations, viewerState) => { + if (!viewerState || !viewerState.parcellationSelected) return [] + const returnParc = [] + const foundParc = allAtlasParcellations.find(p => p['@id'] === viewerState.parcellationSelected['@id']) + if (!foundParc) return [] + returnParc.push(foundParc) + const traverseParc = parc => { + if (!parc) return [] + if (!parc['@version']) return [] + if (parc['@version']['@next']) { + const nextParc = allAtlasParcellations.find(p => p['@id'] === parc['@version']['@next']) + if (nextParc) { + const nextParcAlreadyIncluded = returnParc.find(p => p['@id'] === nextParc['@id']) + if (!nextParcAlreadyIncluded) { + returnParc.unshift(nextParc) + traverseParc(nextParc) + } + } + } + + if (parc['@version']['@previous']) { + const previousParc = allAtlasParcellations.find(p => p['@id'] === parc['@version']['@previous']) + if (previousParc) { + const previousParcAlreadyIncluded = returnParc.find(p => p['@id'] === previousParc['@id']) + if (!previousParcAlreadyIncluded) { + returnParc.push(previousParc) + traverseParc(previousParc) + } + } + } + } + traverseParc(foundParc) + return returnParc + } +) diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts index 2f2919305554d22325b6379025316acda39fa6cb..ff9f7a5bd781fb74be040ef9869b7c33d18dd8a8 100644 --- a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts +++ b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts @@ -3,8 +3,9 @@ import { select, Store } from "@ngrx/store"; import { safeFilter } from "src/services/stateStore.service"; import { distinctUntilChanged, map, withLatestFrom, shareReplay, groupBy, mergeMap, toArray, switchMap, scan, tap, filter } from "rxjs/operators"; import { Observable, Subscription, from, zip, of, combineLatest } from "rxjs"; -import { viewerStateGetSelectedAtlas, viewerStateSelectTemplateWithId, viewerStateAllParcellationsSelector, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper"; +import { viewerStateSelectTemplateWithId, viewerStateToggleLayer } from "src/services/state/viewerState.store.helper"; import { MatMenuTrigger } from "@angular/material/menu"; +import { viewerStateGetSelectedAtlas, viewerStateAtlasParcellationSelector, viewerStateAtlasLatestParcellationSelector } from "src/services/state/viewerState/selectors"; import {CLEAR_CONNECTIVITY_REGION, SET_CONNECTIVITY_VISIBLE} from "src/services/state/viewerState.store"; @Component({ @@ -98,28 +99,24 @@ export class AtlasLayerSelector implements OnInit { )) ) - this.nonGroupedLayers$ = combineLatest( - this.store$.pipe( - select(viewerStateAllParcellationsSelector) + const atlasLayersLatest$ = this.store$.pipe( + select(viewerStateAtlasLatestParcellationSelector), + shareReplay(1), + ) + + this.nonGroupedLayers$ = atlasLayersLatest$.pipe( + map(allParcellations => + allParcellations + .filter(p => !p['groupName']) + .filter(p => !p['baseLayer']) ), - layersGroupBy$ - ).pipe( - map(([ allParcellations, arr]) => { - const nonGrouped = arr.find(([ _key ]) => !_key) - return ((nonGrouped && nonGrouped[1]) || []).map(layer => { - const fullLayerInfo = allParcellations.find(p => p['@id'] === layer['@id']) || {} - return { - ...fullLayerInfo, - ...layer, - darktheme: (fullLayerInfo || {}).useTheme === 'dark' - } - }) - }), ) this.groupedLayers$ = combineLatest( - this.store$.pipe( - select(viewerStateAllParcellationsSelector) + atlasLayersLatest$.pipe( + map(allParcellations => + allParcellations.filter(p => !p['baseLayer']) + ), ), layersGroupBy$ ).pipe( diff --git a/src/ui/parcellationRegion/region.base.ts b/src/ui/parcellationRegion/region.base.ts index 17b3833ca2e0e4e990a18be9a26e1080e74e20c4..88fe881e36fed5c666456d97bab616b73cc4f3d0 100644 --- a/src/ui/parcellationRegion/region.base.ts +++ b/src/ui/parcellationRegion/region.base.ts @@ -5,7 +5,8 @@ import { distinctUntilChanged, switchMap, filter, map, tap } from "rxjs/operator import { Observable, BehaviorSubject, combineLatest } from "rxjs"; import { ARIA_LABELS } from 'common/constants' import { flattenRegions, getIdFromFullId, rgbToHsl } from 'common/util' -import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect, viewerStateGetSelectedAtlas } from "src/services/state/viewerState.store.helper"; +import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect } from "src/services/state/viewerState.store.helper"; +import { viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors"; export class RegionBase { diff --git a/webpack.aot.js b/webpack.aot.js index 02573be1a0cc31ed917f89c9896b5954e1a1fe30..f11f39b77e09aed04a563bfc245221c1c4b14491 100644 --- a/webpack.aot.js +++ b/webpack.aot.js @@ -32,7 +32,7 @@ module.exports = merge(staticAssets, { { test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, loader: '@ngtools/webpack', - exclude : /third_party|plugin_example/ + exclude : /third_party|plugin_example|spec\.ts$/ }, { test : /\.(html|css)$/,