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