From c75cb14800d5bf5b7d4871c3b6842afd294dc4a6 Mon Sep 17 00:00:00 2001 From: Daviti Gogshelidze <daviti1@mail.com> Date: Mon, 27 Feb 2023 19:38:46 +0100 Subject: [PATCH] Make connectivity work on API v3 --- src/environments/environment.common.ts | 2 +- .../connectivityBrowser.component.spec.ts | 26 +- .../connectivityBrowser.component.ts | 296 ++++++++---------- .../connectivityBrowser.stories.ts | 2 +- .../connectivityBrowser.style.scss | 0 .../connectivityBrowser.template.html | 104 ++++++ .../connectivity/excludeConnectivity.pipe.ts | 15 + .../features}/connectivity/index.ts | 0 .../features}/connectivity/module.ts | 12 +- src/features/entry/entry.component.html | 32 +- src/features/entry/entry.component.ts | 3 + src/features/module.ts | 2 + .../connectivityBrowser.template.html | 131 -------- .../connectivity/connectivityDoi.pipe.ts | 15 - .../connectivity/hasConnectivity.directive.ts | 106 ------- tmpFeatures/module.ts | 3 - 16 files changed, 300 insertions(+), 449 deletions(-) rename {tmpFeatures => src/features}/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts (86%) rename {tmpFeatures => src/features}/connectivity/connectivityBrowser/connectivityBrowser.component.ts (53%) rename {tmpFeatures => src/features}/connectivity/connectivityBrowser/connectivityBrowser.stories.ts (97%) rename {tmpFeatures => src/features}/connectivity/connectivityBrowser/connectivityBrowser.style.scss (100%) create mode 100644 src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html create mode 100644 src/features/connectivity/excludeConnectivity.pipe.ts rename {tmpFeatures => src/features}/connectivity/index.ts (100%) rename {tmpFeatures => src/features}/connectivity/module.ts (63%) delete mode 100644 tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.template.html delete mode 100644 tmpFeatures/connectivity/connectivityDoi.pipe.ts delete mode 100644 tmpFeatures/connectivity/hasConnectivity.directive.ts diff --git a/src/environments/environment.common.ts b/src/environments/environment.common.ts index a8511509d..75284adf0 100644 --- a/src/environments/environment.common.ts +++ b/src/environments/environment.common.ts @@ -2,7 +2,7 @@ export const environment = { GIT_HASH: 'unknown hash', VERSION: 'unknown version', - PRODUCTION: true, + PRODUCTION: false, BACKEND_URL: null, SIIBRA_API_ENDPOINTS: 'https://siibra-api-latest.apps-dev.hbp.eu/v3_0', //'https://siibra-api-stable.apps.hbp.eu/v2_0,https://siibra-api-stable.apps.jsc.hbp.eu/v2_0,https://siibra-api-stable-ns.apps.hbp.eu/v2_0', SPATIAL_TRANSFORM_BACKEND: 'https://hbp-spatial-backend.apps.hbp.eu', diff --git a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts similarity index 86% rename from tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts rename to src/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts index 084a7ed86..8f67e9bc8 100644 --- a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts +++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts @@ -7,8 +7,8 @@ import {MockStore, provideMockStore} from "@ngrx/store/testing"; import {Observable, of} from "rxjs"; import {SAPI} from "src/atlasComponents/sapi"; import {AngularMaterialModule} from "src/sharedModules"; -import { SapiAtlasModel, SapiModalityModel, SapiParcellationFeatureModel, SapiParcellationModel } from "src/atlasComponents/sapi/type"; import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; +import { SxplrAtlas, SxplrParcellation } from "src/atlasComponents/sapi/sxplrTypes"; /** * injecting databrowser module is bad idea @@ -41,7 +41,7 @@ describe('ConnectivityComponent', () => { let httpTestingController: HttpTestingController; let req - const types: SapiModalityModel[] = [{ + const types: any[] = [{ name: 'StreamlineCounts', types: ['siibra/features/connectivity/streamlineCounts'] },{ @@ -52,19 +52,19 @@ describe('ConnectivityComponent', () => { types: ['siibra/features/connectivity/functional'] }] - let datasetList: SapiParcellationFeatureModel[] = [ + let datasetList: SxplrParcellation[] = [ { - '@id': 'id1', + id: 'id1', name: 'id1', cohort: 'HCP', subject: '100', '@type': 'siibra/features/connectivity/streamlineCounts', - } as SapiParcellationFeatureModel, { - '@id': 'id2', + } as any, { + id: 'id2', name: 'id2', cohort: '1000BRAINS', subject: 'average', - } as SapiParcellationFeatureModel + } as any ] beforeEach(async () => { @@ -113,11 +113,11 @@ describe('ConnectivityComponent', () => { const parcellation = 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290' const endp = await SAPI.BsEndpoint$.toPromise() - component.atlas = { '@id': atlas } as SapiAtlasModel - component.parcellation = { '@id': parcellation } as SapiParcellationModel + component.atlas = { id: atlas } as SxplrAtlas + component.parcellation = { id: parcellation } as any component.types = types - const url = `${endp}/atlases/${encodeURIComponent(atlas)}/parcellations/${encodeURIComponent(parcellation)}/features?type=${component.selectedTypeId}&size=${100}&page=${1}` + const url = `${endp}/atlases/${encodeURIComponent(atlas)}/parcellations/${encodeURIComponent(parcellation)}/features?type=${component.selectedType.id}&size=${100}&page=${1}` req = httpTestingController.expectOne(`${url}`); @@ -137,9 +137,9 @@ describe('ConnectivityComponent', () => { expect(datasetList).toEqual(component.fetchedItems) }) - it('> Cohorts are set correctly', () => { - expect(datasetList.map(d => d.cohort)).toEqual(component.cohorts) - }) + // it('> Cohorts are set correctly', () => { + // expect(datasetList.map(d => d.cohort)).toEqual(component.cohorts) + // }) }) }); diff --git a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts similarity index 53% rename from tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.component.ts rename to src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts index 579e34570..5b90b1c2c 100644 --- a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.component.ts +++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts @@ -2,31 +2,28 @@ import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild, Input, Chang import {select, Store} from "@ngrx/store"; import {fromEvent, Subscription, BehaviorSubject, Observable} from "rxjs"; import {catchError, take} from "rxjs/operators"; -import { - SAPI, - SapiAtlasModel, - SapiParcellationModel, - SapiRegionModel -} from "src/atlasComponents/sapi"; -import { atlasAppearance, atlasSelection } from "src/state"; -import {PARSE_TYPEDARRAY} from "src/atlasComponents/sapi/sapi.service"; -import {SapiModalityModel, SapiParcellationFeatureMatrixModel, SapiParcellationFeatureModel} from "src/atlasComponents/sapi/type"; + +import { atlasAppearance } from "src/state"; +import {SAPI} from "src/atlasComponents/sapi/sapi.service"; import { of } from "rxjs"; import {CustomLayer} from "src/state/atlasAppearance"; import { HttpClient } from "@angular/common/http"; +import { SxplrAtlas, SxplrParcellation, SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes"; +import { actions, selectors } from "src/state/atlasSelection"; @Component({ - selector: 'sxplr-sapiviews-features-connectivity-browser', + selector: 'sxplr-features-connectivity-browser', templateUrl: './connectivityBrowser.template.html', styleUrls: ['./connectivityBrowser.style.scss'] }) export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { - @Input('sxplr-sapiviews-features-connectivity-browser-atlas') - atlas: SapiAtlasModel - @Input('sxplr-sapiviews-features-connectivity-browser-parcellation') - parcellation: SapiParcellationModel + @Input('sxplr-features-connectivity-browser-atlas') + atlas: SxplrAtlas + + @Input('sxplr-features-connectivity-browser-parcellation') + parcellation: SxplrParcellation /** * accordion expansion should only toggle the clearviewqueue state @@ -34,8 +31,6 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { * setcolormaps$ is set by the presence/absence of clearviewqueue[CONNECTIVITY_NAME_PLATE] */ private _isFirstUpdate = true - - @Input() set accordionExpanded(flag: boolean) { /** @@ -46,8 +41,10 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { return } + if (this.types.length && !this.selectedType) this.selectType(this._types[0]) + if (flag) { - if (this.selectedSubjectDatasetIndex >= 0 && this.allRegions.length) { + if (this.selectedSubjectIndex >= 0 && this.allRegions.length) { this.setCustomLayer() } else { this.setCustomLayerOnLoad = true @@ -57,45 +54,56 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { } } + + private _region: SxplrRegion + @Input() + set region(region) { + this._region = region + this.regionName = region.name + } + + get region() { + return this._region + } + + + private _types: any[] = [] + @Input() + set types(val) { + this._types = val.map(t => ({...t, shortName: t.name.split('.').pop()})) + } + get types() { + return this._types + } - public selectedType: string - public selectedTypeId: string + + public selectedType: any public selectedCohort: string - public cohortSubjects: string[] - public selectedSubjectIndex: number - public selectedSubjectsDatasets: string[] - public selectedSubjectDatasetIndex: number - public fetchedItems: SapiParcellationFeatureModel[] = [] + + public cohortDatasets: any[] + + public selectedSubjectIndex = null + public selectedCohortDatasetIndex: any + public selectedCohortSubjects: any + public fetchedItems: any[] = [] public cohorts: string[] public selectedView: 'subject' | 'average' | null public averageDisabled: boolean = true public subjectsDisabled: boolean = true - @Input() - set region(val) { - const newRegionName = val && val.name - - if (val.status - && !val.name.includes('left hemisphere') - && !val.name.includes('right hemisphere')) { - this.regionHemisphere = val.status - } - - this.regionName = newRegionName - } - public regionName: string - public regionHemisphere: string = null - public selectedDataset: SapiParcellationFeatureModel + + public selectedDataset: any public connectionsString: string public pureConnections: { [key: string]: number } public connectedAreas: BehaviorSubject<ConnectedArea[]> = new BehaviorSubject([]) public noConnectivityForRegion: boolean private subscriptions: Subscription[] = [] - public allRegions: SapiRegionModel[] = [] + public allRegions: SxplrRegion[] = [] private regionIndexInMatrix = -1 public defaultColorMap: Map<string, Map<number, { red: number, green: number, blue: number }>> public matrixString: string + public fetchingPreData: boolean public fetching: boolean public connectivityLayerId = 'connectivity-colormap-id' private setCustomLayerOnLoad = false @@ -104,31 +112,25 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { public logDisabled: boolean = true public logChecked: boolean = true - private _types: SapiModalityModel[] = [] - @Input() - set types(val) { - this._types = val - if (val && val.length) this.selectType(val[0].name) - } - get types() { - return this._types - } + private endpoint: string - @ViewChild('connectivityComponent', {read: ElementRef}) public connectivityComponentElement: ElementRef<any> + + @ViewChild('connectivityComponent') public connectivityComponentElement: ElementRef<any> @ViewChild('fullConnectivityGrid') public fullConnectivityGridElement: ElementRef<any> constructor( private store$: Store, - private sapi: SAPI, private http: HttpClient, private changeDetectionRef: ChangeDetectorRef, - ) {} + ) { + SAPI.BsEndpoint$.pipe(take(1)).subscribe(en => this.endpoint = `${en}/feature/RegionalConnectivity`) + } public ngAfterViewInit(): void { this.subscriptions.push( this.store$.pipe( - select(atlasSelection.selectors.selectedParcAllRegions) + select(selectors.selectedParcAllRegions) ).subscribe(flattenedRegions => { this.defaultColorMap = null this.allRegions = flattenedRegions @@ -140,7 +142,7 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { ) this.subscriptions.push( - fromEvent(this.connectivityComponentElement?.nativeElement, 'customToolEvent', {capture: true}) + fromEvent(this.connectivityComponentElement.nativeElement, 'customToolEvent', {capture: true}) .subscribe((e: CustomEvent) => { if (e.detail.name === 'export csv') { // ToDo Fix in future to use component @@ -148,7 +150,7 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { (a as any).downloadCSV() } }), - fromEvent(this.connectivityComponentElement?.nativeElement, 'connectedRegionClicked', {capture: true}) + fromEvent(this.connectivityComponentElement.nativeElement, 'connectedRegionClicked', {capture: true}) .subscribe((e: CustomEvent) => { this.navigateToRegion(this.getRegionWithName(e.detail.name)) }), @@ -159,7 +161,7 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { if (this.customLayerEnabled) { this.removeCustomLayer() } - const map = new Map<SapiRegionModel, number[]>() + const map = new Map<SxplrRegion, number[]>() const areas = this.connectedAreas.value for (const region of this.allRegions) { const area = areas.find(a => a.name === region.name) @@ -194,152 +196,118 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { this.selectedCohort = null this.cohorts = [] this.selectedCohort = null - this.cohortSubjects = [] + this.selectedCohortDatasetIndex = null + this.selectedCohortSubjects = null + this.selectedSubjectIndex = null - this.selectedSubjectsDatasets = null - this.selectedSubjectDatasetIndex = null } - selectType(typeName) { + selectType(type) { this.clearCohortSelection() - this.selectedType = typeName - this.selectedTypeId = this.types.find(t => t.name === typeName).types[0] - + this.selectedType = type this.removeCustomLayer() - this.getModality() } - getModality(size: number = 100, page: number = 1) { - this.fetching = true - this.fetchModality(size, page).subscribe((res: any) => { + getModality() { + this.fetchingPreData = true + this.fetchModality().subscribe((res: any) => { this.fetchedItems.push(...res.items) - - if (res.total > size*page) { - this.getModality(100, page+1) - } else { - this.cohorts = [...new Set(this.fetchedItems.map(item => item.cohort))] - this.fetching = false - this.changeDetectionRef.detectChanges() - this.selectCohort(this.cohorts[0]) - } + + this.cohorts = [...new Set(this.fetchedItems.map(item => item.cohort))] + this.fetchingPreData = false + this.changeDetectionRef.detectChanges() + this.selectCohort(this.cohorts[0]) + }) } - public fetchModality = (size: number, page: number): Observable<any> => { - let endp - SAPI.BsEndpoint$.pipe(take(1)).subscribe(en => endp = en) - return this.http.get(`${endp}/atlases/${encodeURIComponent(this.atlas['@id'])}/parcellations/${encodeURIComponent(this.parcellation['@id'])}/features?type=${this.selectedTypeId}&size=${size}&page=${page}`,) - .pipe(take(1)) + public fetchModality = (): Observable<any> => { + const url = `${this.endpoint}?parcellation_id=${encodeURIComponent(this.parcellation.id)}&type=${encodeURIComponent(this.selectedType.shortName)}` + return this.http.get(url) } selectCohort(cohort: string) { this.selectedCohort = cohort - this.averageDisabled = !this.fetchedItems.find(s => s.cohort === this.selectedCohort && s.subject === 'average') - this.subjectsDisabled = !this.fetchedItems.find(s => s.cohort === this.selectedCohort && s.subject !== 'average') + this.averageDisabled = !this.fetchedItems.find(s => s.cohort === this.selectedCohort && !s.subjects.length) + this.subjectsDisabled = !this.fetchedItems.find(s => s.cohort === this.selectedCohort && s.subjects.length > 0) this.selectedView = !this.averageDisabled? 'average' : 'subject' - this.cohortSubjects = [...new Set( - this.fetchedItems - .filter(i => this.selectedView === 'average'? i.subject === 'average' : i.subject !== 'average') - .map(item => item.subject) - )] - this.subjectSliderChanged(0) + + this.cohortDatasets = this.fetchedItems.filter(i => this.selectedCohort === i.cohort) + + this.selectedCohortDatasetChanged(0) } - subjectSliderChanged(index: number) { - this.selectedSubjectIndex = index - this.selectedSubjectsDatasets = this.fetchedItems - .filter(fi => fi.cohort === this.selectedCohort && fi.subject === this.cohortSubjects[this.selectedSubjectIndex]) - .map(i => i['@id']) + selectedCohortDatasetChanged(index) { + this.selectedCohortDatasetIndex = index + this.selectedCohortSubjects = this.cohortDatasets[index].subjects - this.selectedSubjectDatasetIndex = 0 - this.loadSubjectConnectivity() - } + this.selectedDataset = this.cohortDatasets[index].datasets[0] - subjectDatasetSliderChanged(index) { - this.selectedSubjectDatasetIndex = index - this.loadSubjectConnectivity() - } + + const keepSubject = this.selectedSubjectIndex >= 0 && this.cohortDatasets[this.selectedCohortDatasetIndex].subjects + .includes(this.selectedCohortSubjects[this.selectedSubjectIndex]) - loadSubjectConnectivity() { - this.fetching = true - this.fetchConnectivity(this.selectedSubjectsDatasets[this.selectedSubjectDatasetIndex]) + this.subjectSliderChanged(keepSubject? this.selectedSubjectIndex : 0) } - // ToDo this temporary fix is for the bug existing on siibra api https://github.com/FZJ-INM1-BDA/siibra-api/issues/100 - private fixDatasetFormat = (ds) => ds.name.includes('{')? ({ - ...ds, - ...JSON.parse(ds.name.substring(ds.name.indexOf('{')).replace(/'/g, '"')) - }) : ds - - fetchConnectivity(datasetId=null) { - const parcellation = this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]) - if (parcellation) { - parcellation.getFeatureInstance(datasetId || this.selectedDataset['@id']) - .pipe(catchError(() => { - this.fetching = false - return of(null) - })) - .subscribe(ds => { - this.selectedDataset = this.fixDatasetFormat(ds) - this.setMatrixData(ds) - this.fetching = false - }) - } + subjectSliderChanged(index: number) { + this.selectedSubjectIndex = index + this.fetchConnectivity() } - // ToDo need to be fixed on configuration side - fixHemisphereNaming(area: string) { - if (area.includes(' - left hemisphere')) { - return area.replace('- left hemisphere', 'left') - } else if (area.includes(' - right hemisphere')) { - return area.replace('- right hemisphere', 'right') - } else { - return area - } - } + fetchConnectivity() { + const subject = this.selectedCohortSubjects[this.selectedSubjectIndex] + const dataset = this.cohortDatasets[this.selectedCohortDatasetIndex] - setMatrixData(data) { - const matrixData = data as SapiParcellationFeatureMatrixModel + this.fetching = true + const url = `${this.endpoint}/${dataset.id}?parcellation_id=${this.parcellation.id}&subject=${subject}&type=${this.selectedType.shortName}` - this.regionIndexInMatrix = (matrixData.columns as Array<string>).findIndex(md => { - return this.fixHemisphereNaming(md) === this.regionName + this.http.get(url).pipe(catchError(() => { + this.fetching = false + return of(null) + })).subscribe(ds => { + this.fetching = false + this.setMatrixData(ds.matrices[subject]) }) + } + + setMatrixData(data) { + this.regionIndexInMatrix = data.columns.findIndex(re => this.region.id === re['@id']) if (this.regionIndexInMatrix < 0) { - this.fetching = false this.noConnectivityForRegion = true this.changeDetectionRef.detectChanges() return } else if (this.noConnectivityForRegion) { this.noConnectivityForRegion = false } - this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(matrixData.matrix, PARSE_TYPEDARRAY.RAW_ARRAY) - .then(matrix => { - const regionProfile = matrix.rawArray[this.regionIndexInMatrix] - - const maxStrength = Math.max(...regionProfile) - this.logChecked = maxStrength > 1 - this.logDisabled = maxStrength <= 1 - - const areas = regionProfile.reduce((p, c, i) => { - return { - ...p, - [this.fixHemisphereNaming(matrixData.columns[i])]: c - } - }, {}) - this.pureConnections = areas - this.connectionsString = JSON.stringify(areas) - this.connectedAreas.next(this.formatConnections(areas)) - this.setCustomLayer() + const regionProfile = data.data[this.regionIndexInMatrix] - this.matrixString = JSON.stringify(matrixData.columns.map((mc, i) => ([mc, ...matrix.rawArray[i]]))) - this.changeDetectionRef.detectChanges() + const maxStrength = Math.max(...regionProfile) - }) + this.logChecked = maxStrength > 1 + this.logDisabled = maxStrength <= 1 + const areas = regionProfile.reduce((p, c, i) => { + return { + ...p, + [data.columns[i].name]: c + } + }, {}) + + this.pureConnections = areas + this.connectionsString = JSON.stringify(areas) + this.connectedAreas.next(this.formatConnections(areas)) + + this.setCustomLayer() + this.matrixString = JSON.stringify(data.columns.map((mc, i) => ([mc.name, ...data.data[i]]))) + this.changeDetectionRef.detectChanges() + + + return data } @@ -351,13 +319,13 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { } //ToDo bestViewPoint is null for the most cases - navigateToRegion(region: SapiRegionModel) { - const regionCentroid = this.region.hasAnnotation?.bestViewPoint?.coordinates + navigateToRegion(region: SxplrRegion) { + const regionCentroid = region.centroid if (regionCentroid) this.store$.dispatch( - atlasSelection.actions.navigateTo({ + actions.navigateTo({ navigation: { - position: regionCentroid.map(v => v.value*1e6), + position: regionCentroid.loc.map(v => v*1e6), }, animation: true }) diff --git a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.stories.ts b/src/features/connectivity/connectivityBrowser/connectivityBrowser.stories.ts similarity index 97% rename from tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.stories.ts rename to src/features/connectivity/connectivityBrowser/connectivityBrowser.stories.ts index 870492b96..2ce655aa0 100644 --- a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.stories.ts +++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.stories.ts @@ -8,10 +8,10 @@ import { SAPI, SapiAtlasModel, SapiParcellationModel } from "src/atlasComponents import { getJba29Features, getHumanAtlas, getJba29 } from "src/atlasComponents/sapi/stories.base" import {SapiParcellationFeatureMatrixModel, SapiParcellationFeatureModel} from "src/atlasComponents/sapi/type" import { AngularMaterialModule } from "src/sharedModules" -import {ConnectivityBrowserComponent} from "src/atlasComponents/sapiViews/features/connectivity"; import {PARSE_TYPEDARRAY} from "src/atlasComponents/sapi/sapi.service"; import {catchError, take} from "rxjs/operators" import {of} from "rxjs"; +import { ConnectivityBrowserComponent } from "./connectivityBrowser.component" @Component({ selector: 'autoradiograph-wrapper-cmp', diff --git a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.style.scss b/src/features/connectivity/connectivityBrowser/connectivityBrowser.style.scss similarity index 100% rename from tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.style.scss rename to src/features/connectivity/connectivityBrowser/connectivityBrowser.style.scss diff --git a/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html new file mode 100644 index 000000000..56847f3c9 --- /dev/null +++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html @@ -0,0 +1,104 @@ +<div class="w-100 h-100 d-block d-flex flex-column sxplr-pb-2"> + <div> + <div *ngIf="types && types.length" + class="flex-grow-0 flex-shrink-0 d-flex flex-row flex-nowrap d-flex flex-column"> + <mat-form-field class="flex-grow-1 flex-shrink-1 w-100"> + <mat-label> + Modality + </mat-label> + <mat-select [value]="selectedType" (selectionChange)="selectType($event.value)"> + <mat-option *ngFor="let type of types" [value]="type"> + {{ type.shortName }} + </mat-option> + </mat-select> + </mat-form-field> + + <mat-form-field *ngIf="!fetchingPreData && selectedType" class="flex-grow-1 flex-shrink-1 w-100"> + <mat-label> + Cohort + </mat-label> + <mat-select [value]="selectedCohort" (selectionChange)="selectCohort($event.value)"> + <mat-option *ngFor="let cohort of cohorts" [value]="cohort"> + {{ cohort }} + </mat-option> + </mat-select> + </mat-form-field> + </div> + + <mat-radio-group *ngIf="selectedCohort" [(ngModel)]="selectedView"> + <mat-radio-button value="average" class="m-2" [disabled]="averageDisabled" color="primary"> + Average + </mat-radio-button> + <mat-radio-button value="subject" class="m-2" [disabled]="subjectsDisabled" color="primary"> + Subject + </mat-radio-button> + </mat-radio-group> + + <div *ngIf="cohortDatasets && cohortDatasets.length > 1" + class="flex-grow-0 flex-shrink-0 d-flex flex-nowrap align-items-center"> + <div class="flex-grow-1 flex-shrink-1 w-100"> + <mat-label> + Dataset + </mat-label> + <mat-slider [min]="0" [max]="cohortDatasets.length - 1" (change)="selectedCohortDatasetChanged($event.value)" + [value]="selectedCohortDatasetIndex" thumbLabel step="1" class="w-100"> + </mat-slider> + </div> + </div> + + <div *ngIf="selectedCohortDatasetIndex >= 0 && selectedCohortSubjects" + class="flex-grow-0 flex-shrink-0 d-flex flex-nowrap align-items-center"> + <div class="flex-grow-1 flex-shrink-1 w-100"> + <mat-label> + Subject + </mat-label> + <mat-slider [min]="0" [max]="selectedCohortSubjects.length - 1" + (change)="subjectSliderChanged($event.value)" [value]="selectedSubjectIndex" + thumbLabel step="1" class="w-100"> + </mat-slider> + </div> + </div> + </div> + + <div class="d-flex justify-content-center"> + <mat-spinner *ngIf="fetching"></mat-spinner> + </div> + + <div *ngIf="regionName && !fetching" + [style.visibility]="selectedCohort && (selectedSubjectIndex >= 0 || !averageDisabled)? 'visible' : 'hidden'" + class="d-flex align-items-center"> + <mat-checkbox class="mr-2" [checked]="logChecked" (change)="changeLog($event.checked)" + [disabled]="logDisabled || noConnectivityForRegion">Log 10</mat-checkbox> + <button mat-button [matMenuTriggerFor]="exportMenu" [disabled]="!connectedAreas.value"> + <i class="fas fa-download mr-2"></i> + <span>Export</span> + </button> + <button *ngIf="selectedDataset" iav-stop="mousedown click" class="icons" mat-icon-button sxplr-dialog + [sxplr-dialog-size]="null" [sxplr-dialog-data]="{ + title: selectedDataset?.name, + descMd: selectedDataset?.description + '' + (selectedDataset?.authors && selectedDataset?.authors.join()), + actions: [selectedDataset.ebrains_page] + }"> + <i class="fas fa-info"></i> + </button> + </div> + + <hbp-connectivity-matrix-row #connectivityComponent + [style.visibility]="selectedCohort && (selectedSubjectIndex >= 0 || !averageDisabled)? 'visible' : 'hidden'" + *ngIf="regionName && !fetching && !noConnectivityForRegion" + [region]="regionName" + [connections]="connectionsString" + show-export="true" hide-export-view="true" theme="dark"> + </hbp-connectivity-matrix-row> + <div *ngIf="noConnectivityForRegion">No connectivity for the region.</div> + + <full-connectivity-grid #fullConnectivityGrid [matrix]="matrixString" [datasetName]="selectedDataset?.name" + [datasetDescription]="selectedDataset?.description" only-export="true"> + </full-connectivity-grid> + + <mat-menu #exportMenu="matMenu"> + <button mat-menu-item [disabled]="noConnectivityForRegion" + (click)="exportConnectivityProfile()">Regional</button> + <button mat-menu-item (click)="exportFullConnectivity()">Dataset</button> + </mat-menu> +</div> \ No newline at end of file diff --git a/src/features/connectivity/excludeConnectivity.pipe.ts b/src/features/connectivity/excludeConnectivity.pipe.ts new file mode 100644 index 000000000..829679fa1 --- /dev/null +++ b/src/features/connectivity/excludeConnectivity.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from "@angular/core" +import { Input } from "postcss" + +@Pipe({ + name: 'isConnectivity', + pure: true +}) +export class ExcludeConnectivityPipe implements PipeTransform { + + public transform(datasets: any[], isConnectivity: boolean): any[] { + return datasets? isConnectivity? [datasets.find(d => d.key === 'connectivity')] + : datasets.filter(d => d.key !== 'connectivity') + : null + } +} diff --git a/tmpFeatures/connectivity/index.ts b/src/features/connectivity/index.ts similarity index 100% rename from tmpFeatures/connectivity/index.ts rename to src/features/connectivity/index.ts diff --git a/tmpFeatures/connectivity/module.ts b/src/features/connectivity/module.ts similarity index 63% rename from tmpFeatures/connectivity/module.ts rename to src/features/connectivity/module.ts index d796c5f22..96f346ac8 100644 --- a/tmpFeatures/connectivity/module.ts +++ b/src/features/connectivity/module.ts @@ -1,12 +1,13 @@ import { CommonModule } from "@angular/common"; import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from "@angular/core"; import { SAPI } from "src/atlasComponents/sapi"; -import {ConnectivityBrowserComponent} from "src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component"; -import {HasConnectivity} from "src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive"; + import {AngularMaterialModule} from "src/sharedModules"; import {FormsModule} from "@angular/forms"; import { DialogModule } from "src/ui/dialogInfo"; -import { ConnectivityDoiPipe } from "./connectivityDoi.pipe"; + +import { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component"; +import { ExcludeConnectivityPipe } from "./excludeConnectivity.pipe"; @NgModule({ imports: [ @@ -17,12 +18,11 @@ import { ConnectivityDoiPipe } from "./connectivityDoi.pipe"; ], declarations: [ ConnectivityBrowserComponent, - HasConnectivity, - ConnectivityDoiPipe + ExcludeConnectivityPipe ], exports: [ ConnectivityBrowserComponent, - HasConnectivity + ExcludeConnectivityPipe ], providers: [ SAPI, diff --git a/src/features/entry/entry.component.html b/src/features/entry/entry.component.html index bd413fc42..a6355b145 100644 --- a/src/features/entry/entry.component.html +++ b/src/features/entry/entry.component.html @@ -1,6 +1,6 @@ <mat-card> <mat-accordion> - <mat-expansion-panel *ngFor="let keyvalue of (cateogryCollections$ | async | keyvalue)" + <mat-expansion-panel *ngFor="let keyvalue of (cateogryCollections$ | async | keyvalue | isConnectivity : false)" sxplrCategoryAcc #categoryAcc="categoryAcc" [ngClass]="{ @@ -23,23 +23,18 @@ <div class="c3-outer"> <div class="c3-inner"> - <mat-card class="c3 mat-elevation-z4" *ngFor="let feature of keyvalue.value" [ngClass]="{ 'sxplr-d-none': (list.state$ | async) === 'noresult' }"> <mat-card-header> - <mat-card-title> <span class="category-title sxplr-white-space-nowrap"> {{ feature.name | featureNamePipe }} </span> </mat-card-title> - </mat-card-header> - - <mat-card-content> <sxplr-feature-list [template]="template" @@ -51,15 +46,34 @@ #list="featureList" > </sxplr-feature-list> - <spinner-cmp *ngIf="(list.state$ | async) === 'busy'"></spinner-cmp> - </mat-card-content> </mat-card> </div> </div> </mat-expansion-panel> - </mat-accordion> + + <mat-expansion-panel sxplr-sapiviews-features-connectivity-check + #connectivityAccordion + *ngIf="(cateogryCollections$ | async | keyvalue | isConnectivity : true) as connectivity"> + <mat-expansion-panel-header> + <mat-panel-title> + {{ connectivity[0].key }} + </mat-panel-title> + </mat-expansion-panel-header> + + <sxplr-features-connectivity-browser class="pe-all flex-shrink-1" + [region]="region" + [sxplr-features-connectivity-browser-atlas]="atlas | async" + [sxplr-features-connectivity-browser-parcellation]="parcellation" + [accordionExpanded]="connectivityAccordion.expanded" + [types]="connectivity[0].value"> + </sxplr-features-connectivity-browser> + + </mat-expansion-panel> + + + </mat-accordion> </mat-card> diff --git a/src/features/entry/entry.component.ts b/src/features/entry/entry.component.ts index e2bb436d0..691f65ff4 100644 --- a/src/features/entry/entry.component.ts +++ b/src/features/entry/entry.component.ts @@ -5,6 +5,7 @@ import { SAPI } from 'src/atlasComponents/sapi'; import { Feature } from 'src/atlasComponents/sapi/sxplrTypes'; import { FeatureBase } from '../base'; import * as userInteraction from "src/state/userInteraction" +import { atlasSelection } from 'src/state'; const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => { const returnVal: Record<string, T[]> = {} @@ -31,6 +32,8 @@ export class EntryComponent extends FeatureBase { super() } + public atlas = this.store.select(atlasSelection.selectors.selectedAtlas) + private featureTypes$ = this.sapi.v3Get("/feature/_types", {}).pipe( switchMap(resp => this.sapi.iteratePages( diff --git a/src/features/module.ts b/src/features/module.ts index 16214d8b1..7de3d6ea3 100644 --- a/src/features/module.ts +++ b/src/features/module.ts @@ -12,6 +12,7 @@ import { FeatureNamePipe } from "./featureName.pipe"; import { FetchDirective } from "./fetch.directive"; import { ListComponent } from './list/list.component'; import { CategoryAccDirective } from './category-acc.directive'; +import { SapiViewsFeatureConnectivityModule } from "./connectivity"; @NgModule({ imports: [ @@ -23,6 +24,7 @@ import { CategoryAccDirective } from './category-acc.directive'; MatTooltipModule, UtilModule, MatRippleModule, + SapiViewsFeatureConnectivityModule ], declarations: [ EntryComponent, diff --git a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.template.html b/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.template.html deleted file mode 100644 index df2a233d6..000000000 --- a/tmpFeatures/connectivity/connectivityBrowser/connectivityBrowser.template.html +++ /dev/null @@ -1,131 +0,0 @@ -<div class="w-100 h-100 d-block d-flex flex-column sxplr-pb-2"> - <div> - <div *ngIf="types && types.length" - class="flex-grow-0 flex-shrink-0 d-flex flex-row flex-nowrap d-flex flex-column"> - <mat-form-field class="flex-grow-1 flex-shrink-1 w-100"> - <mat-label> - Modality - </mat-label> - - <mat-select - [value]="selectedType" - (selectionChange)="selectType($event.value)"> - <mat-option - *ngFor="let type of types" - [value]="type.name"> - {{ type.name }} - </mat-option> - </mat-select> - </mat-form-field> - - <mat-form-field *ngIf="!fetching && selectedType" class="flex-grow-1 flex-shrink-1 w-100"> - <mat-label> - Cohort - </mat-label> - - <mat-select - [value]="selectedCohort" - (selectionChange)="selectCohort($event.value)"> - <mat-option - *ngFor="let cohort of cohorts" - [value]="cohort"> - {{ cohort }} - </mat-option> - </mat-select> - </mat-form-field> - </div> - - <mat-radio-group *ngIf="selectedCohort" [(ngModel)]="selectedView"> - <mat-radio-button value="average" class="m-2" [disabled]="averageDisabled" color="primary"> - Average - </mat-radio-button> - <mat-radio-button value="subject" class="m-2" [disabled]="subjectsDisabled" color="primary"> - Subject - </mat-radio-button> - </mat-radio-group> - - <div *ngIf="selectedView !== 'average' && selectedCohort && cohortSubjects" - class="flex-grow-0 flex-shrink-0 d-flex flex-column"> - <div class="flex-grow-1 flex-shrink-1 w-100"> - <mat-label> - Subject - </mat-label> - <mat-slider [min]="0" - [max]="cohortSubjects.length - 1" - (change)="subjectSliderChanged($event.value)" - [value]="selectedSubjectIndex" - thumbLabel - step="1" - class="w-100"> - </mat-slider> - </div> - - <div *ngIf="selectedSubjectsDatasets && selectedSubjectsDatasets.length > 1" - class="flex-grow-0 flex-shrink-0 d-flex flex-nowrap align-items-center"> - <div class="flex-grow-1 flex-shrink-1 w-100"> - <mat-label> - Dataset - </mat-label> - <mat-slider [min]="0" - [max]="selectedSubjectsDatasets.length - 1" - (change)="subjectDatasetSliderChanged($event.value)" - [value]="selectedSubjectDatasetIndex" - thumbLabel - step="1" - class="w-100"> - </mat-slider> - </div> - </div> - </div> - - </div> - - <div class="d-flex justify-content-center"> - <mat-spinner *ngIf="fetching"></mat-spinner> - </div> - - <div *ngIf="regionName && !fetching" - [style.visibility]="selectedCohort && (selectedSubjectDatasetIndex >= 0 || !averageDisabled)? 'visible' : 'hidden'" - class="d-flex align-items-center"> - <mat-checkbox class="mr-2" - [checked]="logChecked" - (change)="changeLog($event.checked)" - [disabled]="logDisabled || noConnectivityForRegion">Log 10</mat-checkbox> - <button mat-button [matMenuTriggerFor]="exportMenu" - [disabled]="!connectedAreas.value"> - <i class="fas fa-download mr-2"></i> - <span>Export</span> - </button> - <button *ngIf="selectedDataset" iav-stop="mousedown click" class="icons" mat-icon-button sxplr-dialog [sxplr-dialog-size]="null" - [sxplr-dialog-data]="{ - title: selectedDataset?.name, - descMd: selectedDataset?.description + '' + selectedDataset?.authors.join(), - actions: selectedDataset | connectivityDoiPipe - }"> - <i class="fas fa-info"></i> - </button> - </div> - - <hbp-connectivity-matrix-row - #connectivityComponent - [style.visibility]="selectedCohort && (selectedSubjectDatasetIndex >= 0 || !averageDisabled)? 'visible' : 'hidden'" - *ngIf="regionName && !fetching && !noConnectivityForRegion" - [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')" - [connections]="connectionsString" - show-export="true" hide-export-view="true" - theme="dark"> - </hbp-connectivity-matrix-row> - <div *ngIf="noConnectivityForRegion">No connectivity for the region.</div> - - <full-connectivity-grid #fullConnectivityGrid - [matrix]="matrixString" - [datasetName]="selectedDataset?.name" - [datasetDescription]="selectedDataset?.description" - only-export="true"> - </full-connectivity-grid> - - <mat-menu #exportMenu="matMenu"> - <button mat-menu-item [disabled]="noConnectivityForRegion" (click)="exportConnectivityProfile()">Regional</button> - <button mat-menu-item (click)="exportFullConnectivity()">Dataset</button> - </mat-menu> -</div> diff --git a/tmpFeatures/connectivity/connectivityDoi.pipe.ts b/tmpFeatures/connectivity/connectivityDoi.pipe.ts deleted file mode 100644 index ee527989c..000000000 --- a/tmpFeatures/connectivity/connectivityDoi.pipe.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core" -import { SapiParcellationFeatureModel } from "src/atlasComponents/sapi/type" - -@Pipe({ - name: 'connectivityDoiPipe', - pure: true - }) - - export class ConnectivityDoiPipe implements PipeTransform { - public transform(dataset: SapiParcellationFeatureModel): string[] { - const url = `https://search.kg.ebrains.eu/instances/${dataset['dataset_id']}` - return [url] - } - } - \ No newline at end of file diff --git a/tmpFeatures/connectivity/hasConnectivity.directive.ts b/tmpFeatures/connectivity/hasConnectivity.directive.ts deleted file mode 100644 index 89168b6ad..000000000 --- a/tmpFeatures/connectivity/hasConnectivity.directive.ts +++ /dev/null @@ -1,106 +0,0 @@ -import {Directive, Input, OnDestroy} from "@angular/core"; -import {Subscription} from "rxjs"; -import {map, take} from "rxjs/operators"; -import {SAPI} from "src/atlasComponents/sapi/sapi.service"; -import { - SapiAtlasModel, SapiModalityModel, - SapiParcellationFeatureModel, - SapiParcellationModel, - SapiRegionModel -} from "src/atlasComponents/sapi/type"; - -@Directive({ - selector: '[sxplr-sapiviews-features-connectivity-check]', - exportAs: 'hasConnectivityDirective' -}) - -export class HasConnectivity implements OnDestroy { - - private subscriptions: Subscription[] = [] - - @Input('sxplr-sapiviews-features-connectivity-check-atlas') - atlas: SapiAtlasModel - - @Input('sxplr-sapiviews-features-connectivity-check-parcellation') - parcellation: SapiParcellationModel - - private _region: SapiRegionModel - - @Input() - set region(val: SapiRegionModel) { - this._region = val - if (val) { - if (!this.connectivityModalities.length) { - this.waitForModalities = true - } else { - this.checkConnectivity() - } - } else { - this.connectivityNumber = 0 - } - } - - get region() { - return this._region - } - - public hasConnectivity = false - public connectivityNumber = 0 - - private connectivityModalities: SapiModalityModel[] = [] - private waitForModalities = false - public defaultProfile: DefaultProfile - public availableModalities: SapiModalityModel[] = [] - public numberOfDatasets: number = 0 - - constructor(private sapi: SAPI) { - this.getModalities() - } - - getModalities() { - this.sapi.getModalities() - .pipe(map((mod: SapiModalityModel[]) => mod.filter((m: SapiModalityModel) => m.types && m.types.find(t => t.includes('siibra/features/connectivity'))))) - .subscribe(modalities => { - this.connectivityModalities = modalities - if (this.waitForModalities) { - this.waitForModalities = false - this.checkConnectivity() - } - - }) - } - - private checkConnectivity() { - if (this.region.name) { - this.connectivityModalities.forEach(m => { - const type = m.types[0] - this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]) - .getFeatures({type, page: 1, size: 1}).pipe(take(1)) - .subscribe((res: SapiParcellationFeatureModel[] | any) => { - if (res && res.items) { - this.availableModalities.push(m) - const firstDataset = res.items[0] - - if (firstDataset) { - this.hasConnectivity = true - } else { - this.hasConnectivity = false - this.connectivityNumber = 0 - } - } - }) - }) - } - } - - ngOnDestroy(){ - while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() - } -} - -type DefaultProfile = { - type: string - selectedDataset: SapiParcellationFeatureModel - matrix: SapiParcellationFeatureModel - numberOfDatasets: number -} \ No newline at end of file diff --git a/tmpFeatures/module.ts b/tmpFeatures/module.ts index 3df348c72..54651dc28 100644 --- a/tmpFeatures/module.ts +++ b/tmpFeatures/module.ts @@ -9,7 +9,6 @@ import { FeatureBadgeFlagPipe } from "./featureBadgeFlag.pipe" import { FeatureBadgeNamePipe } from "./featureBadgeName.pipe" import * as ieeg from "./ieeg" import * as receptor from "./receptors" -import {SapiViewsFeatureConnectivityModule} from "src/atlasComponents/sapiViews/features/connectivity"; import * as voi from "./voi" import { OrderFilterFeaturesPipe } from "./orderFilterFeatureList.pipe" @@ -28,7 +27,6 @@ const { SapiViewsFeaturesVoiModule } = voi SxplrSapiViewsFeaturesIeegModule, AngularMaterialModule, SapiViewsFeaturesVoiModule, - SapiViewsFeatureConnectivityModule, ], declarations: [ FeatureEntryCmp, @@ -49,7 +47,6 @@ const { SapiViewsFeaturesVoiModule } = voi FeatureEntryCmp, SapiViewsFeaturesEntryListItem, SapiViewsFeaturesVoiModule, - SapiViewsFeatureConnectivityModule, OrderFilterFeaturesPipe, ] }) -- GitLab