From e1c0808a9119e63e871522e6dbcbf24c61532b64 Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Wed, 12 Apr 2023 16:54:14 +0200
Subject: [PATCH] refactor: connectivity component

---
 .storybook/preview-head.html                  |   2 +-
 deploy/csp/index.js                           |   2 +-
 package.json                                  |   2 +-
 src/atlasComponents/sapi/schemaV3.ts          |  31 +-
 src/atlasComponents/sapi/sxplrTypes.ts        |   4 +-
 .../connectivityBrowser.component.ts          | 847 +++++++++++-------
 .../connectivityBrowser.template.html         | 210 +++--
 src/features/connectivity/module.ts           |  28 +-
 .../feature-view/feature-view.component.ts    |   4 +-
 src/features/filterCategories.pipe.ts         |   2 +-
 src/features/transform-pd-to-ds.pipe.ts       |  20 +-
 src/index.html                                |   2 +-
 src/viewerModule/nehuba/store/store.ts        |   1 -
 src/viewerModule/nehuba/store/type.ts         |   3 -
 14 files changed, 705 insertions(+), 453 deletions(-)

diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
index 1095b2dd6..4fc7d43c7 100644
--- a/.storybook/preview-head.html
+++ b/.storybook/preview-head.html
@@ -15,5 +15,5 @@
   }
 </style>
 <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
-<script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.5/dist/connectivity-component/connectivity-component.js" defer></script>
+<script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.6/dist/connectivity-component/connectivity-component.js" defer></script>
 <link rel="stylesheet" href="icons/iav-icons.css">
diff --git a/deploy/csp/index.js b/deploy/csp/index.js
index 4905289c3..1d67da419 100644
--- a/deploy/csp/index.js
+++ b/deploy/csp/index.js
@@ -116,7 +116,7 @@ module.exports = {
           'https://unpkg.com/mathjax@3.1.2/', // math jax
           'https://unpkg.com/three-surfer@0.0.13/dist/bundle.js', // for threeSurfer (freesurfer support in browser)
           'https://unpkg.com/ng-layer-tune@0.0.6/dist/ng-layer-tune/', // needed for ng layer control
-          'https://unpkg.com/hbp-connectivity-component@0.6.5/', // needed for connectivity component
+          'https://unpkg.com/hbp-connectivity-component@0.6.6/', // needed for connectivity component
           (req, res) => res.locals.nonce ? `'nonce-${res.locals.nonce}'` : null,
           ...SCRIPT_SRC,
           ...WHITE_LIST_SRC,
diff --git a/package.json b/package.json
index 5766b9546..2ee728b99 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
     "watch": "ng build --watch --configuration development",
     "test": "ng test",
     "test-ci": "ng test --progress false --watch false --browsers=ChromeHeadless",
-    "sapi-schema": "npx openapi-typescript@6.1.0 https://siibra-api-latest.apps-dev.hbp.eu/v3_0/openapi.json --output ./src/atlasComponents/sapi/schemaV3.ts && eslint ./src/atlasComponents/sapi/schemaV3.ts --no-ignore --fix",
+    "sapi-schema": "npx openapi-typescript@6.1.0 https://siibra-api-stable.apps-dev.hbp.eu/v3_0/openapi.json --output ./src/atlasComponents/sapi/schemaV3.ts && eslint ./src/atlasComponents/sapi/schemaV3.ts --no-ignore --fix",
     "api-schema": "node src/plugin/generateTypes.js",
     "docs:json": "compodoc -p ./tsconfig.json -e json -d .",
     "storybook": "npm run docs:json && start-storybook -p 6006",
diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts
index 7c926b526..c6740c619 100644
--- a/src/atlasComponents/sapi/schemaV3.ts
+++ b/src/atlasComponents/sapi/schemaV3.ts
@@ -489,11 +489,11 @@ export interface components {
       /** @Type */
       "@type": string
       /** Index */
-      index: string[]
+      index: unknown[]
       /** Dtype */
       dtype: string
       /** Columns */
-      columns: string[]
+      columns: unknown[]
       /** Ndim */
       ndim: number
       /** Data */
@@ -510,7 +510,7 @@ export interface components {
       /** Urls */
       urls: (components["schemas"]["EbrainsDsUrl"])[]
       /** Description */
-      description: string
+      description?: string
       /** Contributors */
       contributors: (components["schemas"]["EbrainsDsPerson"])[]
       /** Ebrains Page */
@@ -722,6 +722,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[FeatureMetaModel] */
     Page_FeatureMetaModel_: {
@@ -733,6 +735,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[ParcellationEntityVersionModel] */
     Page_ParcellationEntityVersionModel_: {
@@ -744,6 +748,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraAtlasModel] */
     Page_SiibraAtlasModel_: {
@@ -755,6 +761,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraCorticalProfileModel] */
     Page_SiibraCorticalProfileModel_: {
@@ -766,6 +774,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraEbrainsDataFeatureModel] */
     Page_SiibraEbrainsDataFeatureModel_: {
@@ -777,6 +787,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraParcellationModel] */
     Page_SiibraParcellationModel_: {
@@ -788,6 +800,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraRegionalConnectivityModel] */
     Page_SiibraRegionalConnectivityModel_: {
@@ -799,6 +813,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraTabularModel] */
     Page_SiibraTabularModel_: {
@@ -810,6 +826,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[SiibraVoiModel] */
     Page_SiibraVoiModel_: {
@@ -821,6 +839,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** Page[Union[SiibraCorticalProfileModel, SiibraReceptorDensityFp, SiibraTabularModel]] */
     Page_Union_SiibraCorticalProfileModel__SiibraReceptorDensityFp__SiibraTabularModel__: {
@@ -832,6 +852,8 @@ export interface components {
       page: number
       /** Size */
       size: number
+      /** Pages */
+      pages?: number
     }
     /** ParcellationEntityVersionModel */
     ParcellationEntityVersionModel: {
@@ -985,7 +1007,8 @@ export interface components {
     SiibraAnchorModel: {
       /** @Type */
       "@type": string
-      location?: components["schemas"]["LocationModel"]
+      /** Location */
+      location?: components["schemas"]["LocationModel"] | components["schemas"]["CoordinatePointModel"]
       /** Regions */
       regions: (components["schemas"]["SiibraRegionAssignmentQual"])[]
     }
diff --git a/src/atlasComponents/sapi/sxplrTypes.ts b/src/atlasComponents/sapi/sxplrTypes.ts
index 0fcf7abfe..ef556f25e 100644
--- a/src/atlasComponents/sapi/sxplrTypes.ts
+++ b/src/atlasComponents/sapi/sxplrTypes.ts
@@ -125,8 +125,8 @@ export type CorticalFeature<T extends CorticalDataType, IndexType extends string
 type TabularDataType = number | string | number[]
 
 export type TabularFeature<T extends TabularDataType> = {
-  index: string[]
-  columns: string[]
+  index: unknown[]
+  columns: unknown[]
   data?: T[][]
 } & Feature
 
diff --git a/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
index 05ffb3f2c..dbc546a7e 100644
--- a/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
+++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts
@@ -1,387 +1,554 @@
-import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild, Input, ChangeDetectorRef} from "@angular/core";
-import {select, Store} from "@ngrx/store";
-import {fromEvent, Subscription, BehaviorSubject, Observable} from "rxjs";
-import {catchError, take, switchMap} from "rxjs/operators";
+import {  Component, ElementRef, OnDestroy, ViewChild, Input, SimpleChanges, HostListener, OnChanges } from "@angular/core";
+import { Store, select} from "@ngrx/store";
+import { Subscription, BehaviorSubject, combineLatest, merge, concat, NEVER} from "rxjs";
+import { switchMap, map, tap, shareReplay, distinctUntilChanged, withLatestFrom, filter, finalize } from "rxjs/operators";
 
-import { atlasAppearance } from "src/state";
-import {SAPI} from "src/atlasComponents/sapi/sapi.service";
+import { atlasAppearance, atlasSelection } from "src/state";
+import { SAPI } from "src/atlasComponents/sapi/sapi.service";
 import { of } from "rxjs";
-import { HttpClient } from "@angular/common/http";
 import { SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
-import { actions, selectors } from "src/state/atlasSelection";
+import { actions } from "src/state/atlasSelection";
 import { translateV3Entities } from "src/atlasComponents/sapi/translateV3";
+import { DS } from "src/features/filterCategories.pipe";
+import { FormControl, FormGroup } from "@angular/forms";
+import { PathReturn } from "src/atlasComponents/sapi/typeV3";
+import { arrayEqual } from "src/util/array";
+import { switchMapWaitFor } from "src/util/fn";
+
+type PathParam = DS['value'][number]
+type ConnFeat = PathReturn<"/feature/RegionalConnectivity/{feature_id}">
 
 @Component({
   selector: 'sxplr-features-connectivity-browser',
   templateUrl: './connectivityBrowser.template.html',
   styleUrls: ['./connectivityBrowser.style.scss']
 })
-export class ConnectivityBrowserComponent implements AfterViewInit, OnDestroy {
+export class ConnectivityBrowserComponent implements OnChanges, OnDestroy {
 
+  @Input('sxplr-features-connectivity-browser-atlas')
+  atlas: SxplrAtlas
 
-    @Input('sxplr-features-connectivity-browser-atlas')
-    atlas: SxplrAtlas
+  @Input('sxplr-features-connectivity-browser-template')
+  template: SxplrTemplate
 
-    @Input('sxplr-features-connectivity-browser-template')
-    template: SxplrTemplate
+  @Input('sxplr-features-connectivity-browser-parcellation')
+  parcellation: SxplrParcellation
 
-    @Input('sxplr-features-connectivity-browser-parcellation')
-    parcellation: SxplrParcellation
+  parcellation$ = new BehaviorSubject<SxplrParcellation>(null)
 
-    /**
-     * accordion expansion should only toggle the clearviewqueue state
-     * which should be the single source of truth
-     * setcolormaps$ is set by the presence/absence of clearviewqueue[CONNECTIVITY_NAME_PLATE]
-     */
-    private _isFirstUpdate = true
-    @Input()
-    set accordionExpanded(flag: boolean) {
-      /**
-         * ignore first update
-         */
-      if (this._isFirstUpdate) {
-        this._isFirstUpdate = false
-        return
-      }
+  #accordionExpanded$ = new BehaviorSubject<boolean>(null)
+  @Input()
+  set accordionExpanded(flag: boolean) {
+    this.#accordionExpanded$.next(flag)
+  }
+  
+  region$ = new BehaviorSubject<SxplrRegion>(null)
+  @Input()
+  set region(region: SxplrRegion) {
+    this.region$.next(region)
+  }
 
-      if (this.types.length && !this.selectedType) this.selectType(this._types[0])
+  types$ = new BehaviorSubject<PathParam[]>(null)
+  @Input()
+  types: PathParam[]
 
-      if (flag) {
-        if (this.selectedSubjectIndex >= 0 && this.allRegions.length) {
-          this.setCustomLayer()
-        } else {
-          this.setCustomLayerOnLoad = true
-        }
-      } else {
-        this.removeCustomLayer()
-      }
+  connectivityFilterForm = new FormGroup({
+    selectedType: new FormControl<PathParam>(null),
+    selectedView: new FormControl<'average'|'subject'>('subject'),
+    selectedCohort: new FormControl<string>(null),
+    selectedDatasetIndex: new FormControl<number>(0),
+    selectedSubjectIndex: new FormControl<number>(0),
+  })
 
-    }
-    
-    private _region: SxplrRegion
-    @Input()
-    set region(region) {
-      this._region = region
-      this.regionName = region.name
-    }
+  displayForm = new FormGroup({
+    logChecked: new FormControl<boolean>(false)
+  })
 
-    get region() {
-      return this._region
-    }
+  formValue$ = this.connectivityFilterForm.valueChanges.pipe(
+    shareReplay(1),
+  )
 
-    
-    private _types: any[] = []
-    @Input()
-    set types(val) {
-      this._types = val.map(t => ({...t, shortName: t.name.split('.').pop()}))
-    }
-    get types() {
-      return this._types
-    }
+  private subscriptions: Subscription[] = []
 
+  static LayerId = 'connectivity-colormap-id'
 
-    public selectedType: any
-    public selectedCohort: string
-
-    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
-
-    public regionName: string
-
-    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: 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
-    private customLayerEnabled: boolean
-
-    public logDisabled: boolean = true
-    public logChecked: boolean = true
-
-    private endpoint: string
-
-
-    @ViewChild('connectivityComponent') public connectivityComponentElement: ElementRef<any>
-    @ViewChild('fullConnectivityGrid') public fullConnectivityGridElement: ElementRef<any>
-
-    constructor(
-        private store$: Store,
-        private http: HttpClient,
-        private changeDetectionRef: ChangeDetectorRef,
-        protected sapi: SAPI
-    ) {
-      SAPI.BsEndpoint$.pipe(take(1)).subscribe(en => this.endpoint = `${en}/feature/RegionalConnectivity`)
-    }
+  @ViewChild('connectivityComponent') public connectivityComponentElement: ElementRef<any>
+  @ViewChild('fullConnectivityGrid') public fullConnectivityGridElement: ElementRef<any>
 
-    public ngAfterViewInit(): void {
-      this.subscriptions.push(
-
-        this.store$.pipe(
-          select(selectors.selectedParcAllRegions)
-        ).subscribe(flattenedRegions => {
-          this.defaultColorMap = null
-          this.allRegions = flattenedRegions
-          if (this.setCustomLayerOnLoad) {
-            this.setCustomLayer()
-            this.setCustomLayerOnLoad = false
-          }
-        }),
-      )
+  constructor(
+    private store$: Store,
+    protected sapi: SAPI
+  ) {
 
-      this.subscriptions.push(
-        fromEvent(this.connectivityComponentElement.nativeElement, 'connectedRegionClicked', {capture: true})
-          .subscribe((e: CustomEvent) => {
-            this.navigateToRegion(e.detail.name)
-          }),
-      )
-    }
-
-    setCustomLayer() {
-      if (this.customLayerEnabled) {
-        this.removeCustomLayer()
-      }
-      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)
-        if (area) {
-          map.set(region, Object.values(area.color))
-        } else {
-          map.set(region, [255,255,255,0.1])
+    this.subscriptions.push(
+      /**
+       * on accordion expansion, if nothing is selected, select default (0) type
+       */
+      combineLatest([
+        this.#accordionExpanded$,
+        this.types$,
+        concat(
+          of(null as PathParam),
+          this.formValue$.pipe(
+            map(v => v.selectedType),
+            distinctUntilChanged((n, o) => n.name === o.name)
+          )
+        ),
+      ]).pipe(
+      ).subscribe(([flag, types, selectedType]) => {
+        if (flag && !selectedType) {
+          this.connectivityFilterForm.patchValue({
+            selectedType: types[0]
+          })
         }
-      }
-      this.customLayerEnabled = true
-      const customLayer: atlasAppearance.const.CustomLayer = {
-        clType: 'customlayer/colormap',
-        id: this.connectivityLayerId,
-        colormap: map
-      }
-
-      this.store$.dispatch(
-        atlasAppearance.actions.addCustomLayer({customLayer})
-      )
-    }
-
-    removeCustomLayer() {
-      this.store$.dispatch(
-        atlasAppearance.actions.removeCustomLayer({
-          id: this.connectivityLayerId
+      }),
+      /**
+       * on set log
+       */
+      this.displayForm.valueChanges.pipe(
+        map(v => v.logChecked),
+        switchMap(switchMapWaitFor({
+          condition: () => !!this.connectivityComponentElement,
+          leading: true
+        }))
+      ).subscribe(flag => {
+        const el = this.connectivityComponentElement
+        el.nativeElement.setShowLog(flag)
+      }),
+      /**
+       * on type selection, select first cohort
+       */
+      this.formValue$.pipe(
+        map(v => v.selectedType),
+        distinctUntilChanged((n, o) => n.name === o.name),
+        switchMap(() =>
+          this.cohorts$.pipe(
+            /**
+             * it's important to not use distinctUntilChanged
+             * new corhots emit should always trigger this flow
+             */
+          )
+        )
+      ).subscribe(cohorts => {
+        if (cohorts.length > 0) {
+          this.connectivityFilterForm.patchValue({
+            selectedCohort: cohorts[0]
+          })
+        }
+      }),
+      /**
+       * on select cohort
+       */
+      
+      this.selectedCohort$.pipe(
+        switchMap(() => this.cohortDatasets$.pipe(
+          map(dss => dss.length),
+          distinctUntilChanged(),
+          filter(length => length > 0),
+        ))
+      ).subscribe(() => {
+        this.connectivityFilterForm.patchValue({
+          selectedDatasetIndex: 0,
+          selectedSubjectIndex: 0,
         })
-      )
-    }
-
-    clearCohortSelection() {
-      this.fetchedItems = []
-      this.selectedCohort = null
-      this.cohorts = []
-      this.selectedCohort = null
-      this.selectedCohortDatasetIndex = null
-      this.selectedCohortSubjects = null
-
-      this.selectedSubjectIndex = null
-    }
-
-    selectType(type) {
-      this.clearCohortSelection()
-      this.selectedType = type
-      this.removeCustomLayer()
-      this.getModality()
-    }
-
-
-    getModality() {
-      this.fetchingPreData = true
-      this.fetchModality().subscribe((res: any) => {
+      }),
+      /**
+       * on
+       * - accordion update
+       * - colormap change
+       * - fetching matrix
+       * remove custom layer
+       */
+      merge(
+        this.#accordionExpanded$,
+        this.colormap$,
+        this.#fetchingMatrix$,
+      ).subscribe(() => {
+        this.removeCustomLayer()
+      }),
+      /**
+       * on update colormap, add new custom layer
+       */
+      combineLatest([
+        this.#accordionExpanded$,
+        this.colormap$,
+      ]).pipe(
+        withLatestFrom(
+          this.store$.pipe(
+            select(atlasSelection.selectors.selectedParcAllRegions)
+          )
+        )
+      ).subscribe(([[accordionExpanded, conn], allregions]) => {
+        if (!accordionExpanded || !conn) {
+          return
+        }
 
-        this.fetchedItems.push(...res.items)
+        const map = new Map<SxplrRegion, number[]>()
+        for (const region of allregions) {
+          const area = conn.find(a => a.name === region.name)
+          if (area) {
+            map.set(region, Object.values(area.color))
+          } else {
+            map.set(region, [255,255,255,0.1])
+          }
+        }
         
-        this.cohorts = [...new Set(this.fetchedItems.map(item => item.cohort))]
-        this.fetchingPreData = false
-        this.changeDetectionRef.detectChanges()
-        this.selectCohort(this.cohorts[0])
-      
+        this.store$.dispatch(
+          atlasAppearance.actions.addCustomLayer({
+            customLayer: {
+              clType: 'customlayer/colormap',
+              id: ConnectivityBrowserComponent.LayerId,
+              colormap: map
+            }
+          })
+        )
+      }),
+      /**
+       * on pure connection update, update logchecked box
+       */
+      this.#pureConnections$.subscribe(v => {
+        for (const val of Object.values(v)) {
+          if (val > 1) {
+            this.displayForm.get("logChecked").enable()
+            return
+          }
+        }
+        this.displayForm.get("logChecked").patchValue(false)
+        this.displayForm.get("logChecked").disable()
       })
-    }
-
-    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.subjects.length)
-      this.subjectsDisabled = !this.fetchedItems.find(s => s.cohort === this.selectedCohort && s.subjects.length > 0)
-      this.selectedView = !this.averageDisabled? 'average' : 'subject'
-
-      this.cohortDatasets = this.fetchedItems.filter(i => this.selectedCohort === i.cohort)
-      
-      this.selectedCohortDatasetChanged(0)
-    }
-
-    selectedCohortDatasetChanged(index) {
-      this.selectedCohortDatasetIndex = index
-      this.selectedCohortSubjects = this.cohortDatasets[index].subjects
+    )
+  }
 
-      this.selectedDataset = this.cohortDatasets[index].datasets[0]
-
-      
-      const keepSubject = this.selectedSubjectIndex >= 0 && this.cohortDatasets[this.selectedCohortDatasetIndex].subjects
-        .includes(this.selectedCohortSubjects[this.selectedSubjectIndex])
-
-      this.subjectSliderChanged(keepSubject? this.selectedSubjectIndex : 0)
-    }
-
-    subjectSliderChanged(index: number) {
-      this.selectedSubjectIndex = index
-      this.fetchConnectivity()
+  public ngOnChanges(changes: SimpleChanges): void {
+    const { parcellation, types } = changes
+    if (parcellation) {
+      this.parcellation$.next(parcellation.currentValue)
     }
-
-    fetchConnectivity() {
-      const subject = this.selectedCohortSubjects[this.selectedSubjectIndex]
-      const dataset = this.cohortDatasets[this.selectedCohortDatasetIndex]
-
-      this.fetching = true
-      const url = `${this.endpoint}/${dataset.id}?parcellation_id=${this.parcellation.id}&subject=${subject}&type=${this.selectedType.shortName}`
-
-      this.http.get(url).pipe(catchError(() => {
-        this.fetching = false
-        return of(null)
-      })).subscribe(ds => {
-        this.fetching = false
-        this.setMatrixData(ds.matrices[subject])
+    if (types) [
+      this.types$.next(types.currentValue)
+    ]
+  }
+
+  removeCustomLayer() {
+    this.store$.dispatch(
+      atlasAppearance.actions.removeCustomLayer({
+        id: ConnectivityBrowserComponent.LayerId
       })
-    }
-
-    setMatrixData(data) {
-      this.regionIndexInMatrix = data.columns.findIndex(re => this.region.id === re['@id'])
-
-      if (this.regionIndexInMatrix < 0) {
-        this.noConnectivityForRegion = true
-        this.changeDetectionRef.detectChanges()
-        return
-      } else if (this.noConnectivityForRegion) {
-        this.noConnectivityForRegion = false
-      }
-
-      const regionProfile = data.data[this.regionIndexInMatrix]
-
-      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
+    )
+  }
+
+  busy$ = new BehaviorSubject<boolean>(false)
+
+  #selectedType$ = this.formValue$.pipe(
+    map(v => v.selectedType),
+    distinctUntilChanged((o, n) => o?.name === n?.name),
+    shareReplay(1),
+  )
+
+  #connFeatures$ = this.parcellation$.pipe(
+    switchMap(parc => concat(
+      of(null as PathParam),
+      this.#selectedType$,
+    ).pipe(
+      switchMap(selectedType => {
+        if (!selectedType || !parc) {
+          return of([] as ConnFeat[])
         }
-      }, {})
 
-      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
-    }
-
-
-    changeLog(checked: boolean) {
-      this.logChecked = checked
-      this.connectedAreas.next(this.formatConnections(this.pureConnections))
-      this.connectivityComponentElement.nativeElement.toggleShowLog()
-      this.setCustomLayer()
-    }
+        const typedName = getType(selectedType.name)
+        if (!guardType(typedName)) {
+          throw new Error(`type ${typedName} is not in ${validTypes.join(',')}`)
+        }
+        const query = {
+          parcellation_id: parc.id,
+          type: typedName
+        }
+        this.busy$.next(true)
+        return this.sapi.v3Get(
+          "/feature/RegionalConnectivity",
+          { query }
+        ).pipe(
+          switchMap(resp =>
+            this.sapi.iteratePages(
+              resp,
+              page => this.sapi.v3Get(
+                "/feature/RegionalConnectivity",
+                { query: { ...query, page } }
+              )
+            )
+          ),
+          finalize(() => {
+            this.busy$.next(false)
+          })
+        )
+      })
+    )),
+  )
+
+  cohorts$ = this.#connFeatures$.pipe(
+    map(v => {
+      const cohorts: string[] = []
+      for (const item of v) {
+        if (!cohorts.includes(item.cohort)) {
+          cohorts.push(item.cohort)
+        }
+      }
+      return cohorts
+    })
+  )
+
+  selectedCohort$ = this.formValue$.pipe(
+    map(v => v.selectedCohort),
+    distinctUntilChanged()
+  )
+
+  cohortDatasets$ = combineLatest([
+    this.#connFeatures$,
+    this.formValue$.pipe(
+      map(v => v.selectedCohort),
+      distinctUntilChanged()
+    ),
+  ]).pipe(
+    map(([ features, selectedCohort ]) => features.filter(f => f.cohort === selectedCohort)),
+    distinctUntilChanged(
+      arrayEqual((o, n) => o?.id === n?.id)
+    ),
+    shareReplay(1),
+  )
+
+  selectedDataset$ = this.cohortDatasets$.pipe(
+    switchMap(features => this.formValue$.pipe(
+      map(v => v.selectedDatasetIndex),
+      distinctUntilChanged(),
+      map(dsIdx =>  features[dsIdx]),
+      shareReplay(1),
+    )),
+  )
+  
+  displaySubject$ = this.selectedDataset$.pipe(
+    distinctUntilChanged((o, n) => o?.id === n?.id),
+    map(ds => {
+      return (idx: number) => ds.subjects[idx]
+    })  
+  )
+
+  selectedDatasetAdditionalInfos$ = this.selectedDataset$.pipe(
+    map(ds => ds ? ds.datasets : [])
+  )
+
+  #fetchingMatrix$ = new BehaviorSubject<boolean>(true)
+
+  #matrixInput$ = combineLatest([
+    this.parcellation$,
+    this.formValue$,
+    this.cohortDatasets$,
+  ]).pipe(
+    map(([ parcellation, form, dss ]) => {
+      const {
+        selectedDatasetIndex: dsIdx,
+        selectedSubjectIndex: subIdx
+      } = form
+      const ds = dss[dsIdx]
+      if (!ds) {
+        return null
+      }
 
-    navigateToRegion(regionName: string) {
-        this.sapi.v3Get("/regions/{region_id}", {
-          path: {region_id: regionName},
+      const subject = ds.subjects[subIdx]
+      if (!subject) {
+        return null
+      }
+      return {
+        parcellation,
+        feature_id: ds.id,
+        subject
+      }
+    }),
+    shareReplay(1),
+  )
+
+  #selectedMatrix$ = this.#matrixInput$.pipe(
+    switchMap(input => {
+      if (!input) {
+        return NEVER
+      }
+      const { parcellation, feature_id, subject } = input
+      
+      this.#fetchingMatrix$.next(true)
+      return this.sapi.v3Get(
+        "/feature/RegionalConnectivity/{feature_id}",
+        {
           query: {
-            parcellation_id: this.parcellation.id,
-            space_id: this.template.id
+            parcellation_id: parcellation.id,
+            subject,
+          },
+          path: {
+            feature_id
           }
-        }).pipe(
-          switchMap(r => translateV3Entities.translateRegion(r))
-        ).subscribe(region => {
-          const centroid = region.centroid?.loc
-          if (centroid) {
-            this.store$.dispatch(
-              actions.navigateTo({
-                navigation: {
-                  position: centroid.map(v => v*1e6),
-                },
-                animation: true
-              })
-            )
+        }
+      )
+    }),
+    tap(() => this.#fetchingMatrix$.next(false)),
+    shareReplay(1),
+  )
+
+  #pureConnections$ = this.#matrixInput$.pipe(
+    filter(v => !!v),
+    switchMap(({ subject }) =>
+      this.#selectedMatrix$.pipe(
+        filter(v => !!v.matrices[subject]),
+        withLatestFrom(this.region$),
+        map(([ v, region ]) => {
+          const b = v.matrices[subject]
+          const foundIdx = b.columns.findIndex(v => v['name'] === region.name)
+          if (typeof foundIdx !== 'number') {
+            return null
           }
+          const profile = b.data[foundIdx]
+          if (!profile) {
+            return null
+          }
+          const rObj: Record<string, number> = {}
+          b.columns.reduce((acc, curr, idx) => {
+            const rName = curr['name'] as string
+            acc[rName] = profile[idx] as number
+            return acc
+          }, rObj)
+          return rObj
         })
-    }
-
-    getRegionWithName(region: string) {
-      return this.allRegions.find(r => r.name === region)
-    }
+      ),
+    ),
+  )
+
+  colormap$ = this.#matrixInput$.pipe(
+    switchMap(() => concat(
+      of(null as ConnectedArea[]),
+      combineLatest([
+        this.#pureConnections$,
+        this.displayForm.valueChanges.pipe(
+          map(v => v.logChecked),
+          distinctUntilChanged()
+        )
+      ]).pipe(
+        map(([ conn, flag ]) => processProfile(conn, flag))
+      )
+    ))
+  )
+  
+  view$ = combineLatest([
+    this.selectedDataset$,
+    this.formValue$,
+    this.#fetchingMatrix$,
+    concat(
+      of(null as Record<string, number>),
+      this.#pureConnections$,
+    ),
+    this.region$,
+  ]).pipe(
+    map(([sDs, form, fetchingMatrix, pureConnections, region]) => {
+      return {
+        showSubject: sDs && form.selectedView === "subject",
+        numSubjects: sDs?.subjects.length,
+        fetchingMatrix,
+        connections: pureConnections,
+        region,
+      }
+    }),
+    shareReplay(1),
+  )
+
+  @HostListener('connectedRegionClicked', ['$event'])
+  onRegionClicked(event: CustomEvent) {
+    const regionName = event.detail.name as string
+    this.sapi.v3Get("/regions/{region_id}", {
+      path: {region_id: regionName},
+      query: {
+        parcellation_id: this.parcellation.id,
+        space_id: this.template.id
+      }
+    }).pipe(
+      switchMap(r => translateV3Entities.translateRegion(r))
+    ).subscribe(region => {
+      const centroid = region.centroid?.loc
+      if (centroid) {
+        this.store$.dispatch(
+          actions.navigateTo({
+            navigation: {
+              position: centroid.map(v => v*1e6),
+            },
+            animation: true
+          })
+        )
+      }
+    })
+  }
+
+  exportConnectivityProfile() {
+    const a = document.querySelector('hbp-connectivity-matrix-row');
+    (a as any).downloadCSV()
+  }
+
+  public exportFullConnectivity() {
+    this.fullConnectivityGridElement?.nativeElement['downloadCSV']()
+  }
+
+  public ngOnDestroy(): void {
+    this.removeCustomLayer()
+    this.subscriptions.forEach(s => s.unsubscribe())
+  }
+}
 
-    exportConnectivityProfile() {
-      const a = document.querySelector('hbp-connectivity-matrix-row');
-      (a as any).downloadCSV()
-    }
+function clamp(min: number, max: number) {
+  return function(val: number) {
+    return Math.max(min, Math.min(max, val))
+  }
+}
+const clamp01 = clamp(0, 1)
+function interpolate255(val: number) {
+  return Math.round(clamp01(val) * 255)
+}
+function jet(val: number) {
+  return {
+    r: val < 0.7 
+      ? interpolate255(4 * val - 1.5)
+      : interpolate255(-4.0 * val + 4.5),
+    g: val < 0.5
+      ? interpolate255(4.0 * val - 0.5)
+      : interpolate255(-4.0 * val + 3.5),
+    b: val < 0.3
+      ? interpolate255(4.0 * val + 0.5)
+      : interpolate255(-4.0 * val + 2.5)
+  }
+}
 
-    public exportFullConnectivity() {
-      this.fullConnectivityGridElement?.nativeElement['downloadCSV']()
-    }
+function processProfile(areas: Record<string, number>, logFlag=false): ConnectedArea[] {
+  const returnValue: Omit<ConnectedArea, "color">[] = []
+  for (const areaname in areas) {
+    returnValue.push({
+      name: areaname,
+      numberOfConnections: areas[areaname],
+    })
+  }
+  returnValue.sort((a, b) => b.numberOfConnections - a.numberOfConnections)
+  if (returnValue.length === 0) {
+    return []
+  }
+  const preprocess = (v: number) => logFlag ? Math.log10(v) : v
+  return returnValue.map(v => ({
+    ...v,
+    color: jet(
+      preprocess(v.numberOfConnections) / preprocess(returnValue[0].numberOfConnections)
+    )
+  }))
+}
 
-    public ngOnDestroy(): void {
-      this.removeCustomLayer()
-      this.subscriptions.forEach(s => s.unsubscribe())
-    }
+function getType(name: string) {
+  return name.split(".").slice(-1)[0]
+}
 
-    private formatConnections(areas: { [key: string]: number }) {
-      const cleanedObj = Object.keys(areas)
-        .map(key => ({name: key, numberOfConnections: areas[key]}))
-        .filter(f => f.numberOfConnections > 0)
-        .sort((a, b) => +b.numberOfConnections - +a.numberOfConnections)
-
-      const logMax = this.logChecked ? Math.log(cleanedObj[0].numberOfConnections) : cleanedObj[0].numberOfConnections
-      const colorAreas = []
-
-      cleanedObj.forEach((a) => {
-        colorAreas.push({
-          ...a,
-          color: {
-            r: this.colormap_red((this.logChecked ? Math.log(a.numberOfConnections) : a.numberOfConnections) / logMax ),
-            g: this.colormap_green((this.logChecked ? Math.log(a.numberOfConnections) : a.numberOfConnections) / logMax ),
-            b: this.colormap_blue((this.logChecked ? Math.log(a.numberOfConnections) : a.numberOfConnections) / logMax )
-          },
-        })
-      })
-      return colorAreas
-    }
-    private clamp = val => Math.round(Math.max(0, Math.min(1.0, val)) * 255)
-    private colormap_red = x => x < 0.7? this.clamp(4.0 * x - 1.5) : this.clamp(-4.0 * x + 4.5)
-    private colormap_green = x => x < 0.5? this.clamp(4.0 * x - 0.5) : this.clamp(-4.0 * x + 3.5)
-    private colormap_blue = x => x < 0.3? this.clamp(4.0 * x + 0.5) : this.clamp(-4.0 * x + 2.5)
+const validTypes = ["FunctionalConnectivity", "StreamlineCounts", "StreamlineLengths"]
 
+function guardType(name: unknown): name is "FunctionalConnectivity" | "StreamlineCounts" | "StreamlineLengths" {
+  return typeof name === "string" && validTypes.includes(name)
 }
 
 type ConnectedArea = {
diff --git a/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
index 87e51584a..a7061fe7a 100644
--- a/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
+++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.template.html
@@ -1,104 +1,142 @@
 <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>
+    <form [formGroup]="connectivityFilterForm">
+        
+        <ng-template [ngIf]="types$ | async" let-types>
 
-            <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>
+            <div
+                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 formControlName="selectedType">
+                        <mat-option *ngFor="let type of types" [value]="type">
+                            {{ type.display_name }}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
 
-        <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>
+                <mat-form-field *ngIf="!(busy$ | async) && (formValue$ | async | getProperty : 'selectedType')" class="flex-grow-1 flex-shrink-1 w-100">
+                    <mat-label>
+                        Cohort
+                    </mat-label>
+                    <mat-select formControlName="selectedCohort">
+                        <mat-option *ngFor="let cohort of cohorts$ | async"
+                            [value]="cohort">
+                            {{ cohort }}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
             </div>
-        </div>
+        </ng-template>
+
+        <ng-template [ngIf]="formValue$ | async | getProperty : 'selectedCohort'">
+            <mat-radio-group formControlName="selectedView">
+                <mat-radio-button value="average" class="m-2" color="primary">
+                    Average
+                </mat-radio-button>
+                <mat-radio-button value="subject" class="m-2" color="primary">
+                    Subject
+                </mat-radio-button>
+            </mat-radio-group>
+        </ng-template>
+
+        <ng-template [ngIf]="cohortDatasets$ | async" let-cohortDatasets>
+            <ng-template [ngIf]="cohortDatasets.length > 1">
+                <div 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"
+                            step="1"
+                            formControlName="selectedDatasetIndex"
+                            thumbLabel
+                            class="w-100">
+                        </mat-slider>
+                    </div>
+                </div>
+            </ng-template>
+        </ng-template>
 
-        <div *ngIf="selectedCohortDatasetIndex >= 0 && selectedCohortSubjects"
+        <div *ngIf="view$ | async | getProperty : 'showSubject'"
             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 [min]="0"
+                    [max]="(view$ | async | getProperty : 'numSubjects') - 1"
+                    thumbLabel
+                    [displayWith]="displaySubject$ | async"
+                    step="1"
+                    formControlName="selectedSubjectIndex"
+                    class="w-100">
                 </mat-slider>
             </div>
         </div>
-    </div>
 
-    <div class="d-flex justify-content-center">
-        <mat-spinner *ngIf="fetching"></mat-spinner>
-    </div>
+    </form>
 
-    <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>
+    <ng-template [ngIf]="view$ | async | getProperty : 'region'" let-region>
+
+        <!-- loading spinner -->
+        <ng-template [ngIf]="view$ | async | getProperty : 'fetchingMatrix'"
+            [ngIfElse]="profileTmpl">
+            <div class="d-flex justify-content-center" id = 'blabla'>
+                <mat-spinner></mat-spinner>
+            </div>
+        </ng-template>
 
-    <hbp-connectivity-matrix-row #connectivityComponent
-        [style.visibility]="regionName && !fetching && !noConnectivityForRegion && selectedCohort
-                             && (selectedSubjectIndex >= 0 || !averageDisabled)? 'visible' : 'hidden'"
-        [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>
+        <!-- profile -->
+        <!-- <pre>{{ view$ | async | json }}</pre> -->
+        <ng-template #profileTmpl>
+            <ng-template [ngIf]="view$ | async | getProperty : 'connections'" let-conn>
+                <div class="d-flex align-items-center">
+                    <form [formGroup]="displayForm">
+                        <mat-checkbox
+                            class="mr-2"
+                            formControlName="logChecked">
+                            Log 10
+                        </mat-checkbox>
+                    </form>
+                    <button mat-button [matMenuTriggerFor]="exportMenu">
+                        <i class="fas fa-download mr-2"></i>
+                        <span>Export</span>
+                    </button>
+                    <ng-template ngFor [ngForOf]="selectedDatasetAdditionalInfos$ | async" let-info>
+                        <button class="icons"
+                            mat-icon-button
+                            sxplr-dialog
+                            [sxplr-dialog-size]="null"
+                            [sxplr-dialog-data]="{
+                                title: info?.name,
+                                descMd: info?.description,
+                                actions: [info.ebrains_page]
+                            }">
+                            <i class="fas fa-info"></i>
+                        </button>
+                    </ng-template>
+                </div>
+            
+                <hbp-connectivity-matrix-row #connectivityComponent
+                    [region]="region.name"
+                    [connections]="conn | json"
+                    show-export="true"
+                    hide-export-view="true"
+                    theme="dark">
+                </hbp-connectivity-matrix-row>
+            </ng-template>
+        </ng-template>
+    </ng-template>
+</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
+<mat-menu #exportMenu="matMenu">
+    <button mat-menu-item
+        (click)="exportConnectivityProfile()">
+        Regional
+    </button>
+    <button mat-menu-item (click)="exportFullConnectivity()">Dataset</button>
+</mat-menu>
diff --git a/src/features/connectivity/module.ts b/src/features/connectivity/module.ts
index 97a7793ff..221ad467e 100644
--- a/src/features/connectivity/module.ts
+++ b/src/features/connectivity/module.ts
@@ -1,19 +1,31 @@
 import { CommonModule } from "@angular/common";
 import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from "@angular/core";
 import { SAPI } from "src/atlasComponents/sapi";
-
-import {AngularMaterialModule} from "src/sharedModules";
-import {FormsModule} from "@angular/forms";
-import { DialogModule } from "src/ui/dialogInfo";
-
 import { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component";
+import { ReactiveFormsModule } from "@angular/forms";
+import { DialogModule } from "src/ui/dialogInfo";
+import { MatSelectModule } from "@angular/material/select";
+import { MatRadioModule } from "@angular/material/radio";
+import { MatSliderModule } from "@angular/material/slider";
+import { MatMenuModule } from "@angular/material/menu";
+import { UtilModule } from "src/util";
+import { MatCheckboxModule } from "@angular/material/checkbox";
+import { MatButtonModule } from "@angular/material/button";
+import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
 
 @NgModule({
   imports: [
     CommonModule,
-    FormsModule,
-    AngularMaterialModule,
-    DialogModule
+    ReactiveFormsModule,
+    MatSelectModule,
+    MatRadioModule,
+    MatSliderModule,
+    MatMenuModule,
+    DialogModule,
+    UtilModule,
+    MatCheckboxModule,
+    MatButtonModule,
+    MatProgressSpinnerModule,
   ],
   declarations: [
     ConnectivityBrowserComponent,
diff --git a/src/features/feature-view/feature-view.component.ts b/src/features/feature-view/feature-view.component.ts
index 400644f14..3d9f1cdc9 100644
--- a/src/features/feature-view/feature-view.component.ts
+++ b/src/features/feature-view/feature-view.component.ts
@@ -43,7 +43,7 @@ export class FeatureViewComponent implements OnChanges {
   voi$ = new BehaviorSubject<VoiFeature>(null)
   columns$: Observable<string[]> = this.tabular$.pipe(
     map(data => data
-      ? ['index', ...data.columns]
+      ? ['index', ...data.columns] as string[]
       : []),
   )
 
@@ -52,7 +52,7 @@ export class FeatureViewComponent implements OnChanges {
     map(v => {
       return v.index.map((receptor, idx) => ({
         receptor: {
-          label: receptor
+          label: receptor as string
         },
         density: {
           mean: v.data[idx][0] as number,
diff --git a/src/features/filterCategories.pipe.ts b/src/features/filterCategories.pipe.ts
index 8f1033287..8521c7454 100644
--- a/src/features/filterCategories.pipe.ts
+++ b/src/features/filterCategories.pipe.ts
@@ -2,7 +2,7 @@ import { KeyValue } from "@angular/common"
 import { Pipe, PipeTransform } from "@angular/core"
 import { PathReturn } from "src/atlasComponents/sapi/typeV3"
 
-type DS = KeyValue<string, PathReturn<"/feature/_types">["items"]>
+export type DS = KeyValue<string, PathReturn<"/feature/_types">["items"]>
 
 @Pipe({
   name: 'filterCategory',
diff --git a/src/features/transform-pd-to-ds.pipe.ts b/src/features/transform-pd-to-ds.pipe.ts
index 09f598d9d..c0cc94e29 100644
--- a/src/features/transform-pd-to-ds.pipe.ts
+++ b/src/features/transform-pd-to-ds.pipe.ts
@@ -2,6 +2,14 @@ import { CdkTableDataSourceInput } from '@angular/cdk/table';
 import { Pipe, PipeTransform } from '@angular/core';
 import { TabularFeature } from 'src/atlasComponents/sapi/sxplrTypes';
 
+function typeGuard(input: unknown): input is string | number | number[]{
+  return typeof input === "string" || typeof input === "number" || (Array.isArray(input) && input.every(v => typeof v === "number"))
+}
+
+function isString(input: unknown): input is string {
+  return typeof input === "string"
+}
+
 @Pipe({
   name: 'transformPdToDs',
   pure: true
@@ -10,11 +18,19 @@ export class TransformPdToDsPipe implements PipeTransform {
 
   transform(pd: TabularFeature<string|number|number[]>): CdkTableDataSourceInput<unknown> {
     return pd.data.map((arr, idx) => {
+      const val = pd.index[idx]
+      if (!typeGuard(val)) {
+        throw new Error(`Expected val to be of type string, number or number[], but was none.`)
+      }
       const returnVal: Record<string, string|number|number[]> = {
-        index: pd.index[idx],
+        index: val,
       }
       arr.forEach((val, colIdx) => {
-        returnVal[pd.columns[colIdx]] = val
+        const key = pd.columns[colIdx]
+        if (!isString(key)) {
+          throw new Error(`Expected key to be of type string,  number or number[], but was not`)
+        }
+        returnVal[key] = val
       })
       return returnVal
     })
diff --git a/src/index.html b/src/index.html
index 8b04e68c0..0f009f4f2 100644
--- a/src/index.html
+++ b/src/index.html
@@ -15,7 +15,7 @@
   <script src="https://unpkg.com/kg-dataset-previewer@1.2.0/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer></script>
   <script src="https://unpkg.com/three-surfer@0.0.13/dist/bundle.js" defer></script>
   <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.6/dist/ng-layer-tune/ng-layer-tune.esm.js"></script>
-  <script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.5/dist/connectivity-component/connectivity-component.js" ></script>
+  <script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.6/dist/connectivity-component/connectivity-component.js" ></script>
   <script defer src="https://unpkg.com/mathjax@3.1.2/es5/tex-svg.js"></script>
   <script defer src="https://unpkg.com/d3@6.2.0/dist/d3.min.js"></script>
   <title>Siibra Explorer</title>
diff --git a/src/viewerModule/nehuba/store/store.ts b/src/viewerModule/nehuba/store/store.ts
index 0dd7043f3..33f4275eb 100644
--- a/src/viewerModule/nehuba/store/store.ts
+++ b/src/viewerModule/nehuba/store/store.ts
@@ -20,7 +20,6 @@ const defaultState: INehubaFeature = {
   panelMode: EnumPanelMode.FOUR_PANEL,
   panelOrder: '0123',
   octantRemoval: true,
-  clearViewQueue: {},
   auxMeshes: []
 }
 
diff --git a/src/viewerModule/nehuba/store/type.ts b/src/viewerModule/nehuba/store/type.ts
index a55be8ced..7908af13a 100644
--- a/src/viewerModule/nehuba/store/type.ts
+++ b/src/viewerModule/nehuba/store/type.ts
@@ -16,8 +16,5 @@ export interface INehubaFeature {
   panelMode: string
   panelOrder: string
   octantRemoval: boolean
-  clearViewQueue: {
-    [key: string]: boolean
-  }
   auxMeshes: IAuxMesh[]
 }
-- 
GitLab