From 3bcd85f264911d94d69b36faa162a90a7530713b Mon Sep 17 00:00:00 2001 From: Daviti Gogshelidze <daviti1@mail.com> Date: Tue, 27 Sep 2022 03:02:41 +0200 Subject: [PATCH] Add connectivity filter --- .../connectivityBrowser.component.ts | 108 ++++++++++++------ .../connectivityBrowser.template.html | 67 +++++++++-- .../sapiViews/features/connectivity/module.ts | 2 + src/sharedModules/angularMaterial.module.ts | 3 + 4 files changed, 135 insertions(+), 45 deletions(-) diff --git a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts index 2399e2b66..f4d517a1a 100644 --- a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts +++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts @@ -1,13 +1,20 @@ import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild, Input, ChangeDetectorRef} from "@angular/core"; import {select, Store} from "@ngrx/store"; import {fromEvent, Subscription, BehaviorSubject} from "rxjs"; -import {catchError, take} from "rxjs/operators"; -import {SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel} from "src/atlasComponents/sapi"; +import {catchError} 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} from "src/atlasComponents/sapi/type"; import { of } from "rxjs"; import {CustomLayer} from "src/state/atlasAppearance"; +import {HttpClient} from "@angular/common/http"; +import {environment} from "src/environments/environment"; @Component({ selector: 'sxplr-sapiviews-features-connectivity-browser', @@ -58,11 +65,6 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { private _defaultProfile @Input() set defaultProfile(val: any) { this._defaultProfile = val - this.selectedType = this.types.find(t => t.types.includes(val.type))?.name - this.pageNumber = 1 - this.selectedDataset = this.fixDatasetFormat(val.selectedDataset) - if (val.matrix) this.setMatrixData(val.matrix) - this.numberOfDatasets = val.numberOfDatasets } get defaultProfile() { @@ -70,6 +72,14 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { } public selectedType: any + public selectedTypeId: string + public selectedCohort: Cohort + public selectedSubjectIndex: number + public selectedSubjectDatasetIndex: number + public cohorts: Cohort[] + public selectedView: 'subject' | 'average' | null + public averageDisabled: boolean = true + public subjectsDisabled: boolean = true @Input() set region(val) { @@ -97,10 +107,8 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { public defaultColorMap: Map<string, Map<number, { red: number, green: number, blue: number }>> public matrixString: string public fetching: boolean - public numberOfDatasets: number public connectivityLayerId = 'connectivity-colormap-id' private setCustomLayerOnLoad = false - public pageNumber: number private customLayerEnabled: boolean public logDisabled: boolean = true @@ -113,6 +121,7 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { private store$: Store, private sapi: SAPI, private changeDetectionRef: ChangeDetectorRef, + private httpClient: HttpClient ) {} public ngAfterViewInit(): void { @@ -182,35 +191,56 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { selectType(typeName) { this.selectedType = typeName - this.pageNumber = 1 - this.loadDataset() + this.selectedTypeId = this.types.find(t => t.name === typeName).types[0] + this.selectedCohort = null + this.cohorts = null + this.fetchCohorts() } - datasetSliderChanged(pageNumber) { - this.pageNumber = pageNumber - this.loadDataset() + getCohortsReq() { + const { SIIBRA_API_ENDPOINTS } = environment + const endp = SIIBRA_API_ENDPOINTS.split(',')[0] + return this.sapi.http.get( + `${endp}/atlases/${encodeURIComponent(this.atlas['@id'])}/parcellations/${encodeURIComponent(this.parcellation['@id'])}/features?type=${this.selectedTypeId}&group=cohort_subject`, + ) + } + + fetchCohorts() { + this.subscriptions.push( + this.getCohortsReq().subscribe((c: Cohort[]) => { + this.cohorts = c + }) + ) + } + + selectCohort(cohort: Cohort) { + this.selectedCohort = cohort + + this.averageDisabled = !this.selectedCohort.subjects.find(s => s.subject === 'average') + this.subjectsDisabled = !this.selectedCohort.subjects.find(s => s.subject !== 'average') + + this.selectedView = !this.averageDisabled? 'average' : 'subject' + + this.selectedSubjectIndex = 0 + this.selectedSubjectDatasetIndex = 0 + + this.loadSubjectConnectivity() + } + + subjectSliderChanged(index) { + this.selectedSubjectIndex = this.selectedCohort.subjects.findIndex((s, i) => i === index) + this.selectedSubjectDatasetIndex = 0 + this.loadSubjectConnectivity() + } + + subjectDatasetSliderChanged(index) { + this.selectedSubjectDatasetIndex = index + this.loadSubjectConnectivity() } - loadDataset() { + loadSubjectConnectivity() { this.fetching = true - const type = this.types.find(t => t.name === this.selectedType).types[0] - return this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]) - .getFeatures({type, page: this.pageNumber, size: 1},) - .pipe( - take(1), - catchError(() => { - this.fetching = false - return of(null) - }) - ).subscribe((res: any) => { - if (res && res.items) { - if (res.total !== this.numberOfDatasets) { - this.numberOfDatasets = res.total - } - this.selectedDataset = this.fixDatasetFormat(res.items[0]) - this.fetchConnectivity() - } - }) + this.fetchConnectivity(this.selectedCohort.subjects[this.selectedSubjectIndex].datasets[this.selectedSubjectDatasetIndex]) } // ToDo this temporary fix is for the bug existing on siibra api https://github.com/FZJ-INM1-BDA/siibra-api/issues/100 @@ -219,13 +249,14 @@ export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy { ...JSON.parse(ds.name.substring(ds.name.indexOf('{')).replace(/'/g, '"')) }) : ds - fetchConnectivity() { - this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatureInstance(this.selectedDataset['@id']) + fetchConnectivity(datasetId=null) { + this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).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 }) @@ -336,3 +367,10 @@ export type ConnectedArea = { numberOfConnections: number } +export type Cohort = { + cohort: string + subjects: { + subject: string + datasets: string[] + }[] +} diff --git a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html index fae388859..872958320 100644 --- a/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html +++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html @@ -1,7 +1,8 @@ <div class="w-100 h-100 d-block d-flex flex-column sxplr-pb-2"> <div> - <div *ngIf="types && types.length && selectedType" class="flex-grow-0 flex-shrink-0 d-flex flex-row flex-nowrap"> - <mat-form-field class="flex-grow-1 flex-shrink-1 w-0"> + <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> @@ -17,22 +18,65 @@ </mat-option> </mat-select> </mat-form-field> + + <mat-form-field *ngIf="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.cohort }} + </mat-option> + </mat-select> + </mat-form-field> </div> - <div *ngIf="selectedDataset" class="flex-grow-0 flex-shrink-0 d-flex flex-nowrap align-items-center"> - <div class="flex-grow-1 flex-shrink-1 w-0"> + <mat-radio-group *ngIf="selectedCohort && selectedCohort.subjects" [(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 && selectedCohort.subjects" + class="flex-grow-0 flex-shrink-0 d-flex flex-column"> + <div class="flex-grow-1 flex-shrink-1 w-100"> <mat-label> - Dataset + Subject </mat-label> - <mat-slider [min]="1" - [max]="numberOfDatasets" - (change)="datasetSliderChanged($event.value)" - [value]="pageNumber" + <mat-slider [min]="0" + [max]="selectedCohort.subjects.length-1" + (change)="subjectSliderChanged($event.value)" + [value]="selectedSubjectIndex" thumbLabel step="1" class="w-100"> </mat-slider> </div> + + <div *ngIf="selectedSubjectIndex >= 0 && selectedCohort.subjects[selectedSubjectIndex].datasets.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> + Datasets + </mat-label> + <mat-slider [min]="0" + [max]="selectedCohort.subjects[selectedSubjectIndex].datasets.length-1" + (change)="subjectDatasetSliderChanged($event.value)" + [value]="selectedSubjectDatasetIndex" + thumbLabel + step="1" + class="w-100"> + </mat-slider> + </div> + </div> </div> </div> @@ -41,7 +85,9 @@ <mat-spinner *ngIf="fetching"></mat-spinner> </div> - <div class="d-flex align-items-center" *ngIf="regionName && !fetching"> + <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)" @@ -55,6 +101,7 @@ <hbp-connectivity-matrix-row #connectivityComponent + [style.visibility]="selectedCohort && (selectedSubjectDatasetIndex >= 0 || !averageDisabled)? 'visible' : 'hidden'" *ngIf="regionName && !fetching && !noConnectivityForRegion" [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')" [connections]="connectionsString" diff --git a/src/atlasComponents/sapiViews/features/connectivity/module.ts b/src/atlasComponents/sapiViews/features/connectivity/module.ts index 5402b4039..82134d44f 100644 --- a/src/atlasComponents/sapiViews/features/connectivity/module.ts +++ b/src/atlasComponents/sapiViews/features/connectivity/module.ts @@ -4,10 +4,12 @@ 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"; @NgModule({ imports: [ CommonModule, + FormsModule, AngularMaterialModule ], declarations: [ diff --git a/src/sharedModules/angularMaterial.module.ts b/src/sharedModules/angularMaterial.module.ts index 6f29b8913..914f3f852 100644 --- a/src/sharedModules/angularMaterial.module.ts +++ b/src/sharedModules/angularMaterial.module.ts @@ -31,6 +31,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard' import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import {MatProgressBarModule} from "@angular/material/progress-bar"; import {MatProgressSpinnerModule} from "@angular/material/progress-spinner"; +import {MatRadioModule} from "@angular/material/radio"; const defaultDialogOption: MatDialogConfig = new MatDialogConfig() @@ -67,6 +68,7 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig() ClipboardModule, MatProgressBarModule, MatProgressSpinnerModule, + MatRadioModule ], exports: [ MatButtonModule, @@ -98,6 +100,7 @@ const defaultDialogOption: MatDialogConfig = new MatDialogConfig() ClipboardModule, MatProgressBarModule, MatProgressSpinnerModule, + MatRadioModule ], providers: [{ provide: MAT_DIALOG_DEFAULT_OPTIONS, -- GitLab