From 59ff0b4bc3bcf09901b3b4ad69c43ea7a74312bf Mon Sep 17 00:00:00 2001 From: fsdavid <daviti1@mail.com> Date: Sun, 13 Mar 2022 21:10:01 +0100 Subject: [PATCH] Integrate connectivity to sapi --- common/constants.js | 1 + package.json | 2 +- .../connectivity/hasConnectivity.directive.ts | 62 ---- src/atlasComponents/connectivity/index.ts | 2 - src/atlasComponents/connectivity/module.ts | 25 -- .../parcellationRegion/module.ts | 4 +- .../sapiViews/core/region/module.ts | 6 +- .../region/rich/region.rich.component.ts | 16 +- .../region/rich/region.rich.template.html | 41 ++- .../connectivityBrowser.component.spec.ts | 3 +- .../connectivityBrowser.component.ts | 333 +++++++++++------- .../connectivityBrowser.template.html | 46 ++- .../connectivity/hasConnectivity.directive.ts | 85 +++++ .../sapiViews/features/connectivity/index.ts | 2 + .../sapiViews/features/connectivity/module.ts | 17 +- src/main.module.ts | 2 + src/state/atlasAppearance/index.ts | 3 +- src/state/atlasAppearance/selector.ts | 2 +- src/state/atlasAppearance/store.ts | 2 +- .../layerCtrl.service/layerCtrl.service.ts | 3 +- 20 files changed, 401 insertions(+), 256 deletions(-) delete mode 100644 src/atlasComponents/connectivity/hasConnectivity.directive.ts delete mode 100644 src/atlasComponents/connectivity/index.ts delete mode 100644 src/atlasComponents/connectivity/module.ts rename src/atlasComponents/{ => sapiViews/features}/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts (95%) rename src/atlasComponents/{ => sapiViews/features}/connectivity/connectivityBrowser/connectivityBrowser.component.ts (57%) rename src/atlasComponents/{ => sapiViews/features}/connectivity/connectivityBrowser/connectivityBrowser.template.html (68%) create mode 100644 src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts diff --git a/common/constants.js b/common/constants.js index 1527bc74e..22f52924d 100644 --- a/common/constants.js +++ b/common/constants.js @@ -90,6 +90,7 @@ DOES_NOT_SUPPORT_MULTI_REGION_SELECTION: `Please only select a single region.`, MULTI_REGION_SELECTION: `Multi region selection`, REGIONAL_FEATURES: 'Regional features', + CONNECTIVITY: 'Connectivity', NO_ADDIONTAL_INFO_AVAIL: `Currently, no additional information is linked to this region.`, ATLAS_NOT_FOUND: `Atlas not found. Maybe it is still loading. Try again in a few seconds?`, diff --git a/package.json b/package.json index d4b47cfb3..f0349a5c8 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "acorn": "^8.4.1", "export-nehuba": "0.0.12", "file-loader": "^6.2.0", - "hbp-connectivity-component": "^0.5.2", + "hbp-connectivity-component": "^0.6.0", "jszip": "^3.6.0", "postcss": "^8.3.6", "raw-loader": "^4.0.2", diff --git a/src/atlasComponents/connectivity/hasConnectivity.directive.ts b/src/atlasComponents/connectivity/hasConnectivity.directive.ts deleted file mode 100644 index 8879de8c0..000000000 --- a/src/atlasComponents/connectivity/hasConnectivity.directive.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {Directive, Inject, Input, OnDestroy, OnInit} from "@angular/core"; -import {of, Subscription} from "rxjs"; -import {switchMap} from "rxjs/operators"; -import {BS_ENDPOINT} from "src/util/constants"; -import {HttpClient} from "@angular/common/http"; - -@Directive({ - selector: '[has-connectivity]', - exportAs: 'hasConnectivityDirective' -}) - -export class HasConnectivity implements OnInit, OnDestroy { - - private subscriptions: Subscription[] = [] - - @Input() region: any - - public hasConnectivity = false - public connectivityNumber = 0 - - constructor(@Inject(BS_ENDPOINT) private siibraApiUrl: string, - private httpClient: HttpClient) {} - - ngOnInit() { - this.checkConnectivity(this.region[0]) - } - - checkConnectivity(region) { - if (!region.context) { - this.hasConnectivity = false - return - } - const {atlas, parcellation, template} = region.context - if (region.name) { - const connectivityUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(atlas['@id'])}/parcellations/${encodeURIComponent(parcellation['@id'])}/regions/${encodeURIComponent(region.name)}/features/ConnectivityProfile` - - this.subscriptions.push( - this.httpClient.get<[]>(connectivityUrl).pipe(switchMap((res: any[]) => { - if (res && res.length) { - this.hasConnectivity = true - const url = `${connectivityUrl}/${encodeURIComponent(res[0]['@id'])}` - return this.httpClient.get(url) - } else { - this.hasConnectivity = false - this.connectivityNumber = 0 - } - return of(null) - })).subscribe(res => { - - if (res && res['__profile']) { - this.connectivityNumber = res['__profile'].filter(p => p > 0).length - } - }) - ) - } - } - - ngOnDestroy(){ - while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() - } - -} diff --git a/src/atlasComponents/connectivity/index.ts b/src/atlasComponents/connectivity/index.ts deleted file mode 100644 index c9a2e808f..000000000 --- a/src/atlasComponents/connectivity/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component"; -export { AtlasCmptConnModule } from "./module"; \ No newline at end of file diff --git a/src/atlasComponents/connectivity/module.ts b/src/atlasComponents/connectivity/module.ts deleted file mode 100644 index c9231d460..000000000 --- a/src/atlasComponents/connectivity/module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core"; -import { AngularMaterialModule } from "src/sharedModules"; -import { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component"; -import {HasConnectivity} from "src/atlasComponents/connectivity/hasConnectivity.directive"; - -@NgModule({ - imports: [ - CommonModule, - AngularMaterialModule - ], - declarations: [ - ConnectivityBrowserComponent, - HasConnectivity - ], - exports: [ - ConnectivityBrowserComponent, - HasConnectivity - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA, - ], -}) - -export class AtlasCmptConnModule{} diff --git a/src/atlasComponents/parcellationRegion/module.ts b/src/atlasComponents/parcellationRegion/module.ts index 20f1f4fb3..f2aa660ac 100644 --- a/src/atlasComponents/parcellationRegion/module.ts +++ b/src/atlasComponents/parcellationRegion/module.ts @@ -7,7 +7,6 @@ import { RenderViewOriginDatasetLabelPipe } from "./region.base"; import { RegionDirective } from "./region.directive"; import { SimpleRegionComponent } from "./regionSimple/regionSimple.component"; import { RegionAccordionTooltipTextPipe } from "./regionAccordionTooltipText.pipe"; -import { AtlasCmptConnModule } from "../connectivity"; import { HttpClientModule } from "@angular/common/http"; import { RegionInOtherTmplPipe } from "./regionInOtherTmpl.pipe"; import { SiibraExplorerTemplateModule } from "../template"; @@ -18,7 +17,6 @@ import { SiibraExplorerTemplateModule } from "../template"; UtilModule, AngularMaterialModule, ComponentsModule, - AtlasCmptConnModule, HttpClientModule, SiibraExplorerTemplateModule, @@ -39,4 +37,4 @@ import { SiibraExplorerTemplateModule } from "../template"; ] }) -export class ParcellationRegionModule{} \ No newline at end of file +export class ParcellationRegionModule{} diff --git a/src/atlasComponents/sapiViews/core/region/module.ts b/src/atlasComponents/sapiViews/core/region/module.ts index 511a1fcb8..1d536667c 100644 --- a/src/atlasComponents/sapiViews/core/region/module.ts +++ b/src/atlasComponents/sapiViews/core/region/module.ts @@ -7,13 +7,15 @@ import { SapiViewsCoreRegionRegionListItem } from "./region/listItem/region.list import { SapiViewsCoreRegionRegionBase } from "./region/region.base.directive"; import { SapiViewsCoreRegionRegionalFeatureDirective } from "./region/region.features.directive"; import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.component"; +import {SapiViewsFeatureConnectivityModule} from "src/atlasComponents/sapiViews/features/connectivity"; @NgModule({ imports: [ CommonModule, AngularMaterialModule, SapiViewsUtilModule, - SapiViewsFeaturesModule + SapiViewsFeaturesModule, + SapiViewsFeatureConnectivityModule ], declarations: [ SapiViewsCoreRegionRegionListItem, @@ -29,4 +31,4 @@ import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.compone ] }) -export class SapiViewsCoreRegionModule{} \ No newline at end of file +export class SapiViewsCoreRegionModule{} diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts index 60eb0e497..6cf77ef51 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts @@ -1,9 +1,19 @@ -import { Component, EventEmitter, Inject, Output } from "@angular/core"; +import { + AfterViewInit, + Component, + EventEmitter, + Inject, + Output, + QueryList, + ViewChild, + ViewChildren +} from "@angular/core"; import { Observable, Subject } from "rxjs"; import { DARKTHEME } from "src/util/injectionTokens"; import { SapiViewsCoreRegionRegionBase } from "../region.base.directive"; import { ARIA_LABELS, CONST } from 'common/constants' import { SapiRegionalFeatureModel } from "src/atlasComponents/sapi"; +import {MatExpansionPanel} from "@angular/material/expansion"; @Component({ selector: 'sxplr-sapiviews-core-region-region-rich', @@ -26,6 +36,8 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase @Output('sxplr-sapiviews-core-region-region-rich-feature-clicked') featureClicked = new EventEmitter<SapiRegionalFeatureModel>() + public expandedPanel: string + constructor( @Inject(DARKTHEME) public darktheme$: Observable<boolean> ){ @@ -37,10 +49,12 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase } handleExpansionPanelClosedEv(title: string){ + this.expandedPanel = '' console.log("title", title) } handleExpansionPanelAfterExpandEv(title: string) { + this.expandedPanel = title console.log("title", title) } diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html index 453707e72..64a7dad67 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html @@ -70,6 +70,22 @@ </ng-template> +<!-- connectivity --> +<ng-template #regionConnectivityTmp> + <connectivity-browser class="pe-all flex-shrink-1" + [region]="region" + [sxplr-sapiviews-features-connectivity-matrix-atlas]="atlas" + [sxplr-sapiviews-features-connectivity-matrix-parcellation]="parcellation" + [accordionExpanded]="expandedPanel === CONST.CONNECTIVITY"> + </connectivity-browser> +<!-- <connectivity-browser class="pe-all flex-shrink-1"--> +<!-- [region]="region"--> +<!-- (setOpenState)="expansionPanel.expanded = $event"--> +<!-- [accordionExpanded]="expansionPanel.expanded"--> +<!-- (connectivityNumberReceived)="hasConnectivityDirective.connectivityNumber = $event">--> +<!-- </connectivity-browser>--> +</ng-template> + <mat-accordion class="d-block mt-2"> @@ -85,6 +101,29 @@ </mat-accordion> +<mat-accordion class="d-block mt-2"> + + <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { + title: CONST.CONNECTIVITY, + iconClass: 'fas fa-braille', + content: regionConnectivityTmp, + desc: hasConnectivityDirective.connectivityNumber, + iconTooltip: hasConnectivityDirective.connectivityNumber + 'Connections', + iavNgIf: hasConnectivityDirective.hasConnectivity + }"> + </ng-container> + + <div has-connectivity + [sxplr-sapiviews-features-connectivity-matrix-atlas]="atlas" + [sxplr-sapiviews-features-connectivity-matrix-parcellation]="parcellation" + [region]="region" + #hasConnectivityDirective="hasConnectivityDirective"> + </div> + +</mat-accordion> + + + </ng-template> @@ -139,4 +178,4 @@ <ng-template [ngIf]="!region"> Region must be provided! -</ng-template> \ No newline at end of file +</ng-template> diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts similarity index 95% rename from src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts rename to src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts index d9ebdd9a9..736a55fd5 100644 --- a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts +++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.spec.ts @@ -6,7 +6,6 @@ import {CUSTOM_ELEMENTS_SCHEMA, Directive, Input} from "@angular/core"; import {provideMockActions} from "@ngrx/effects/testing"; import {MockStore, provideMockStore} from "@ngrx/store/testing"; import {Observable, of} from "rxjs"; -import { viewerStateOverwrittenColorMapSelector } from "src/services/state/viewerState/selectors"; import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState.store.helper"; import {BS_ENDPOINT} from "src/util/constants"; @@ -81,7 +80,7 @@ describe('ConnectivityComponent', () => { beforeEach(() => { const mockStore = TestBed.inject(MockStore) - mockStore.overrideSelector(viewerStateOverwrittenColorMapSelector, null) + // mockStore.overrideSelector(viewerStateOverwrittenColorMapSelector, null) mockStore.overrideSelector(ngViewerSelectorClearViewEntries, []) }) diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts similarity index 57% rename from src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts rename to src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts index 0458757bb..cd3ec4695 100644 --- a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.component.ts +++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts @@ -2,24 +2,23 @@ import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, - EventEmitter, OnDestroy, - Output, ViewChild, Input, OnInit, Inject, } from "@angular/core"; import {select, Store} from "@ngrx/store"; -import {fromEvent, Observable, Subscription, Subject, combineLatest, of} from "rxjs"; -import {distinctUntilChanged, filter, map, switchMap, switchMapTo} from "rxjs/operators"; +import {fromEvent, Observable, Subscription, Subject, combineLatest, BehaviorSubject} from "rxjs"; +import {distinctUntilChanged, filter, map, switchMap} from "rxjs/operators"; import { ngViewerSelectorClearViewEntries, ngViewerActionClearView } from "src/services/state/ngViewerState.store.helper"; import {HttpClient} from "@angular/common/http"; import {BS_ENDPOINT} from "src/util/constants"; -import {getIdFromKgIdObj} from "common/util"; import {OVERWRITE_SHOW_DATASET_DIALOG_TOKEN} from "src/util/interfaces"; -import { SAPI, SapiRegionModel } from "src/atlasComponents/sapi"; +import {SAPI, SapiAtlasModel, SapiParcellationModel, SapiRegionModel} from "src/atlasComponents/sapi"; import { actions } from "src/state/atlasSelection"; import { atlasAppearance, atlasSelection } from "src/state"; +import {PARSE_TYPEDARRAY} from "src/atlasComponents/sapi/sapi.service"; +import {SapiParcellationFeatureMatrixModel} from "src/atlasComponents/sapi/type"; const CONNECTIVITY_NAME_PLATE = 'Connectivity' @@ -36,6 +35,12 @@ const CONNECTIVITY_NAME_PLATE = 'Connectivity' }) export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDestroy { + @Input('sxplr-sapiviews-features-connectivity-matrix-atlas') + atlas: SapiAtlasModel + + @Input('sxplr-sapiviews-features-connectivity-matrix-parcellation') + parcellation: SapiParcellationModel + private setColorMap$: Subject<boolean> = new Subject() /** @@ -44,9 +49,7 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe * setcolormaps$ is set by the presence/absence of clearviewqueue[CONNECTIVITY_NAME_PLATE] */ private _isFirstUpdate = true - - public connectivityUrl: string - + private accordionIsExpanded = false @Input() @@ -78,17 +81,6 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe } } - @Output() - connectivityDataReceived = new EventEmitter<any>() - - @Output() - setOpenState: EventEmitter<boolean> = new EventEmitter() - - @Output() - connectivityLoadUrl: EventEmitter<string> = new EventEmitter() - - @Output() connectivityNumberReceived: EventEmitter<string> = new EventEmitter() - @Input() set region(val) { const newRegionName = val && val.name @@ -112,20 +104,14 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe } this.regionName = newRegionName - this.regionId = val.id? val.id.kg? getIdFromKgIdObj(val.id.kg) : val.id : null - this.atlasId = val.context.atlas['@id'] - this.parcellationId = val.context.parcellation['@id'] if(this.selectedDataset) { - this.setConnectivityUrl() - this.setProfileLoadUrl() + this.fetchConnectivity() } // TODO may not be necessary this.changeDetectionRef.detectChanges() } public atlasId: any - public parcellationId: any - public regionId: string public regionName: string public regionHemisphere: string = null public datasetList: any[] = [] @@ -134,18 +120,20 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe public selectedDatasetDescription: string = '' public selectedDatasetKgId: string = '' public selectedDatasetKgSchema: string = '' - public connectedAreas = [] + public connectionsString: string + public connectedAreas: BehaviorSubject<any[]> = new BehaviorSubject([]) // TODO this may be incompatible private selectedParcellationFlatRegions$ = this.store$.pipe( select(atlasSelection.selectors.selectedATP), - switchMap(({ atlas, template, parcellation }) => this.sapi.getParcRegions(atlas["@id"], parcellation["@id"], template["@id"])) + switchMap(({ atlas, template, parcellation }) => this.sapi.getParcRegions(atlas["@id"], parcellation["@id"], template["@id"])) ) public overwrittenColorMap$: Observable<any> private subscriptions: Subscription[] = [] public expandMenuIndex = -1 public allRegions = [] + private regionIndexInMatrix = -1 public defaultColorMap: Map<string, Map<number, { red: number, green: number, blue: number }>> public noDataReceived = false @@ -160,29 +148,26 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe @Inject(BS_ENDPOINT) private siibraApiUrl: string, private sapi: SAPI ) { - this.overwrittenColorMap$ = this.store$.pipe( select(atlasAppearance.selectors.getOverwrittenColormap), distinctUntilChanged() ) } - public loadUrl: string public fullConnectivityLoadUrl: string ngOnInit(): void { - this.setConnectivityUrl() - - this.httpClient.get<[]>(this.connectivityUrl).subscribe(res => { - this.datasetList = res - this.selectedDataset = this.datasetList[0]?.['@id'] - this.selectedDatasetName = this.datasetList[0]?.['src_name'] - this.selectedDatasetDescription = this.datasetList[0]?.['src_info'] - // this.selectedDatasetKgId = this.datasetList[0]?.kgId || null - // this.selectedDatasetKgSchema = this.datasetList[0]?.kgSchema || null - - this.changeDataset() - }) + this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatures() + .then(res => { + this.datasetList = res + .filter(r => ['siibra/features/connectivity', 'siibra/connectivity'].includes(r.type)) + .map((r: any) => ({ + ...r, + connectionType: r.name.substring(0, r.name.indexOf('{') - 1), + dataset: JSON.parse(r.name.substring(r.name.indexOf('{')).replace(/'/g, '"')) + })) + this.selectDataset(this.datasetList[0]['@id']) + }) } public ngAfterViewInit(): void { @@ -196,32 +181,32 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe }) ) - /** - * Listen to of clear view entries - * can come from within the component (when connectivity is not available for the dataset) - * --> do not collapse - * or outside (user clicks x in chip) - * --> collapse - */ - this.subscriptions.push( - this.store$.pipe( - select(ngViewerSelectorClearViewEntries), - map(arr => arr.filter(v => v === CONNECTIVITY_NAME_PLATE)), - filter(arr => arr.length ===0), - distinctUntilChanged() - ).subscribe(() => { - if (!this.noDataReceived) { - this.setOpenState.emit(false) - } - }) - ) - - - this.subscriptions.push(this.overwrittenColorMap$.subscribe(ocm => { - if (this.accordionIsExpanded && !ocm) { - this.setOpenState.emit(false) - } - })) + // /** + // * Listen to of clear view entries + // * can come from within the component (when connectivity is not available for the dataset) + // * --> do not collapse + // * or outside (user clicks x in chip) + // * --> collapse + // */ + // this.subscriptions.push( + // this.store$.pipe( + // select(ngViewerSelectorClearViewEntries), + // map(arr => arr.filter(v => v === CONNECTIVITY_NAME_PLATE)), + // filter(arr => arr.length ===0), + // distinctUntilChanged() + // ).subscribe(() => { + // if (!this.noDataReceived) { + // this.setOpenState.emit(false) + // } + // }) + // ) + + + // this.subscriptions.push(this.overwrittenColorMap$.subscribe(ocm => { + // if (this.accordionIsExpanded && !ocm) { + // this.setOpenState.emit(false) + // } + // })) this.subscriptions.push( this.selectedParcellationFlatRegions$.subscribe(flattenedRegions => { @@ -234,20 +219,23 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe * setting/restoring colormap */ this.subscriptions.push( - combineLatest( + combineLatest([ this.setColorMap$.pipe( distinctUntilChanged() ), - fromEvent(this.connectivityComponentElement?.nativeElement, 'connectivityDataReceived').pipe( - map((e: CustomEvent) => { - if (e.detail !== 'No data') { - this.connectivityNumberReceived.emit(e.detail.length) - } - return e.detail - }) - ) - ).subscribe(([flag, connectedAreas]) => { - if (connectedAreas === 'No data') { + // fromEvent(this.connectivityComponentElement?.nativeElement, 'connectivityDataReceived').pipe( + // map((e: CustomEvent) => { + // // if (e.detail !== 'No data') { + // // this.connectivityNumberReceived.emit(e.detail.length) + // // } + // return e.detail + // }) + // ) + this.connectedAreas + ]).subscribe(([flag, connectedAreas]) => { + console.log('flag') + console.log(flag) + if (connectedAreas.length === 0) { this.noDataReceived = true return this.clearViewer() } else { @@ -259,8 +247,9 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe }) ) this.noDataReceived = false - this.connectivityNumberReceived.emit(connectedAreas.length) - this.connectedAreas = connectedAreas + + // this.connectivityNumberReceived.emit(connectedAreas.length) + // this.connectedAreas = connectedAreas if (flag) { this.addNewColorMap() @@ -289,34 +278,49 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe }), fromEvent(this.connectivityComponentElement?.nativeElement, 'connectedRegionClicked', {capture: true}) .subscribe((e: CustomEvent) => { - this.navigateToRegion(e.detail.name) + this.navigateToRegion(this.getRegionWithName(e.detail.name)) }), ) } - public ngOnDestroy(): void { - this.connectivityNumberReceived.emit(null) - this.store$.dispatch( - ngViewerActionClearView({ - payload: { - [CONNECTIVITY_NAME_PLATE]: false - } - }) - ) - this.restoreDefaultColormap() - this.subscriptions.forEach(s => s.unsubscribe()) - } + selectDataset(datasetId) { + const dataset = this.datasetList.find(d => d['@id'] === datasetId) + this.selectedDataset = dataset['@id'] + this.selectedDatasetName = dataset.dataset.name + this.selectedDatasetDescription = dataset.dataset.description + this.selectedDatasetKgId = dataset.dataset['dataset_id'] - private setConnectivityUrl() { - this.connectivityUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/regions/${encodeURIComponent(this.regionName)}/features/ConnectivityProfile` + this.fetchConnectivity() } - private setProfileLoadUrl() { - const url = `${this.connectivityUrl}/${encodeURIComponent(this.selectedDataset)}` - this.connectivityLoadUrl.emit(url) - this.loadUrl = url + fetchConnectivity() { + this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatureInstance(this.selectedDataset) + .then(ds=> { + const matrixData = ds as SapiParcellationFeatureMatrixModel + this.regionIndexInMatrix = (matrixData.columns as Array<string>).findIndex(md => md === this.regionName) + this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(matrixData.matrix, PARSE_TYPEDARRAY.RAW_ARRAY) + .then(matrix => { + const areas = {} + matrix.rawArray[this.regionIndexInMatrix].forEach((value, i) => { + areas[matrixData.columns[i]] = value + }) + this.connectionsString = JSON.stringify(areas) + this.connectedAreas.next(this.cleanConnectedAreas(areas)) + }) + + }) } + // private setConnectivityUrl() { + // this.connectivityUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/regions/${encodeURIComponent(this.regionName)}/features/ConnectivityProfile` + // } + + // private setProfileLoadUrl() { + // const url = `${this.connectivityUrl}/${encodeURIComponent(this.selectedDataset)}` + // this.connectivityLoadUrl.emit(url) + // this.loadUrl = url + // } + clearViewer() { this.store$.dispatch( ngViewerActionClearView({ @@ -325,29 +329,12 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe } }) ) - this.connectedAreas = [] - this.connectivityNumberReceived.emit('0') + this.connectedAreas.next([]) return this.restoreDefaultColormap() } - // ToDo Affect on component - changeDataset(event = null) { - if (event) { - this.selectedDataset = event.value - const foundDataset = this.datasetList.find(d => d['@id'] === this.selectedDataset) - this.selectedDatasetName = foundDataset?.['src_name'] - this.selectedDatasetDescription = foundDataset?.['src_info'] - // this.selectedDatasetKgId = foundDataset?.kgId || null - // this.selectedDatasetKgSchema = foundDataset?.kgSchema || null - } - if (this.datasetList.length && this.selectedDataset) { - this.setProfileLoadUrl() - - this.fullConnectivityLoadUrl = `${this.siibraApiUrl}/atlases/${encodeURIComponent(this.atlasId)}/parcellations/${encodeURIComponent(this.parcellationId)}/features/ConnectivityMatrix/${encodeURIComponent(this.selectedDatasetName)}` - } - } - + //ToDo navigateRegion action does not work any more navigateToRegion(region: SapiRegionModel) { this.store$.dispatch( atlasSelection.actions.navigateToRegion({ @@ -365,22 +352,7 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe } getRegionWithName(region) { - return this.allRegions.find(ar => { - if (this.regionHemisphere) { - let regionName = region - let regionStatus = null - if (regionName.includes('left hemisphere')) { - regionStatus = 'left hemisphere' - regionName = regionName.replace(' - left hemisphere', ''); - } else if (regionName.includes('right hemisphere')) { - regionStatus = 'right hemisphere' - regionName = regionName.replace(' - right hemisphere', ''); - } - return ar.name === regionName && ar.status === regionStatus - } - - return ar.name === region - }) + return this.allRegions.find(r => r.name === region) } public restoreDefaultColormap() { @@ -402,7 +374,7 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe } }) - this.connectedAreas.forEach(area => { + this.connectedAreas.value.forEach(area => { const areaAsRegion = this.allRegions .filter(r => { @@ -443,6 +415,93 @@ export class ConnectivityBrowserComponent implements OnInit, AfterViewInit, OnDe this.fullConnectivityGridElement?.nativeElement['downloadCSV']() } + + public ngOnDestroy(): void { + this.store$.dispatch( + ngViewerActionClearView({ + payload: { + [CONNECTIVITY_NAME_PLATE]: false + } + }) + ) + this.restoreDefaultColormap() + this.subscriptions.forEach(s => s.unsubscribe()) + } + + + + + + + + + + private floatConnectionNumbers + + cleanConnectedAreas = (areas) => { + const cleanedObj = Object.keys(areas) + .map(key => ({name: key, numberOfConnections: areas[key]})) + .filter(f => f.numberOfConnections > 0) + .sort((a, b) => +b.numberOfConnections - +a.numberOfConnections) + + this.floatConnectionNumbers = cleanedObj[0].numberOfConnections <= 1 + const logMax = this.floatConnectionNumbers ? cleanedObj[0].numberOfConnections : Math.log(cleanedObj[0].numberOfConnections) + const colorAreas = [] + + cleanedObj.forEach((a, i) => { + if (a.name.includes(' - both hemispheres')) { + + const rightTitle = a.name.replace(' - both hemispheres', ' - right hemisphere') + const rightHemisphereItemToAdd = {...a, name: rightTitle} + cleanedObj.splice(i + 1, 0, rightHemisphereItemToAdd) + + cleanedObj[i] = { + ...cleanedObj[i], + name: cleanedObj[i].name.replace(' - both hemispheres', ' - left hemisphere') + } + } + }) + cleanedObj.forEach((a) => { + colorAreas.push({ + ...a, + color: { + r: this.colormap_red(this.floatConnectionNumbers ? a.numberOfConnections : Math.log(a.numberOfConnections) / logMax), + g: this.colormap_green(this.floatConnectionNumbers ? a.numberOfConnections : Math.log(a.numberOfConnections) / logMax), + b: this.colormap_blue(this.floatConnectionNumbers ? a.numberOfConnections : Math.log(a.numberOfConnections) / logMax) + } + }) + }) + return colorAreas + } + + clamp = val => Math.round(Math.max(0, Math.min(1.0, val)) * 255) + + colormap_red = (x) => { + if (x < 0.7) { + return this.clamp(4.0 * x - 1.5); + } else { + return this.clamp(-4.0 * x + 4.5); + } + } + + colormap_green = (x) => { + if (x < 0.5) { + return this.clamp(4.0 * x - 0.5); + } else { + return this.clamp(-4.0 * x + 3.5); + } + } + + colormap_blue = (x) => { + if (x < 0.3) { + return this.clamp(4.0 * x + 0.5); + } else { + return this.clamp(-4.0 * x + 2.5); + } + } + + + } function getWindow(): any { diff --git a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html similarity index 68% rename from src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html rename to src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html index 646d4b0ab..b4754b80a 100644 --- a/src/atlasComponents/connectivity/connectivityBrowser/connectivityBrowser.template.html +++ b/src/atlasComponents/sapiViews/features/connectivity/connectivityBrowser/connectivityBrowser.template.html @@ -1,11 +1,28 @@ <div class="w-100 h-100 d-block d-flex flex-column sxplr-pb-2"> - <hbp-connectivity-matrix-row - (connectivityDataReceived)="connectivityDataReceived.emit($event)" +<!-- <hbp-connectivity-matrix-row--> +<!-- (connectivityDataReceived)="connectivityDataReceived.emit($event)"--> +<!-- #connectivityComponent--> +<!-- *ngIf="regionName"--> +<!-- [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')"--> +<!-- theme="dark"--> +<!-- [loadurl]="loadUrl"--> +<!-- show-export="true"--> +<!-- show-source="false"--> +<!-- show-title="false"--> +<!-- show-toolbar="false"--> +<!-- show-description="false"--> +<!-- show-dataset-name="false"--> +<!-- custom-dataset-selector="true"--> +<!-- tools_showlog="true"--> +<!-- [tools_custom]='[{"name": "exportslot", "type": "slot"}]'--> +<!-- hide-export-view="true">--> + + <hbp-connectivity-matrix-row #connectivityComponent *ngIf="regionName" [region]="regionName + (regionHemisphere? ' - ' + regionHemisphere : '')" + [connections]="connectionsString" theme="dark" - [loadurl]="loadUrl" show-export="true" show-source="false" show-title="false" @@ -17,6 +34,9 @@ [tools_custom]='[{"name": "exportslot", "type": "slot"}]' hide-export-view="true"> + + + <div slot="dataset"> <div *ngIf="datasetList.length && selectedDataset" 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"> @@ -25,18 +45,18 @@ </mat-label> <mat-select - panelClass="no-max-width" [value]="selectedDataset" - (selectionChange)="changeDataset($event)"> + (selectionChange)="selectDataset($event.value)"> <mat-option + [matTooltip]="dataset.dataset.name" *ngFor="let dataset of datasetList" [value]="dataset['@id']"> - {{ dataset['src_name'] }} + {{ dataset.dataset.name }} </mat-option> </mat-select> </mat-form-field> <ng-container *ngIf="selectedDataset && (selectedDatasetDescription || selectedDatasetKgId)" > - TODO please reimplmenent button to explore KG dataset +<!-- TODO please reimplmenent button to explore KG dataset--> <button class="flex-grow-0 flex-shrink-0" mat-icon-button (click)="exportFullConnectivity()" @@ -79,10 +99,10 @@ </button> </div> </hbp-connectivity-matrix-row> - <full-connectivity-grid #fullConnectivityGrid - [loadurl]="fullConnectivityLoadUrl" - [name]="selectedDataset" - [description]="selectedDatasetDescription" - only-export="true"> - </full-connectivity-grid> +<!-- <full-connectivity-grid #fullConnectivityGrid--> +<!-- [loadurl]="fullConnectivityLoadUrl"--> +<!-- [name]="selectedDataset"--> +<!-- [description]="selectedDatasetDescription"--> +<!-- only-exporselectedDatasett="true">--> +<!-- </full-connectivity-grid>--> </div> diff --git a/src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts b/src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts new file mode 100644 index 000000000..a636e43ee --- /dev/null +++ b/src/atlasComponents/sapiViews/features/connectivity/hasConnectivity.directive.ts @@ -0,0 +1,85 @@ +import {Directive, Inject, Input, OnDestroy} from "@angular/core"; +import {from, of, Subscription} from "rxjs"; +import {switchMap} from "rxjs/operators"; +import {BS_ENDPOINT} from "src/util/constants"; +import {HttpClient} from "@angular/common/http"; +import {PARSE_TYPEDARRAY, SAPI} from "src/atlasComponents/sapi/sapi.service"; +import {SapiAtlasModel, SapiParcellationFeatureMatrixModel, SapiParcellationModel} from "src/atlasComponents/sapi/type"; + +@Directive({ + selector: '[has-connectivity]', + exportAs: 'hasConnectivityDirective' +}) + +export class HasConnectivity implements OnDestroy { + + private subscriptions: Subscription[] = [] + + @Input('sxplr-sapiviews-features-connectivity-matrix-atlas') + atlas: SapiAtlasModel + + @Input('sxplr-sapiviews-features-connectivity-matrix-parcellation') + parcellation: SapiParcellationModel + + private _region: any + + @Input() + set region(val) { + this._region = val + if (val) { + this.checkConnectivity() + } else { + this.connectivityNumber = 0 + } + } + + get region() { + return this._region + } + + private regionIndex: number + public hasConnectivity = false + public connectivityNumber = 0 + + constructor(@Inject(BS_ENDPOINT) private siibraApiUrl: string, + private httpClient: HttpClient, + private sapi: SAPI) {} + + checkConnectivity() { + if (this.region.name) { + this.subscriptions.push( + from(this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatures()) + .pipe( + switchMap((res: any[]) => { + const firstCon = res.find(r => r.type === 'siibra/features/connectivity') + if (firstCon) { + this.hasConnectivity = true + return this.sapi.getParcellation(this.atlas["@id"], this.parcellation["@id"]).getFeatureInstance(firstCon['@id']) + } else { + this.hasConnectivity = false + this.connectivityNumber = 0 + } + return of(null) + }), + switchMap(res => { + if (res) { + const matrixData = res as SapiParcellationFeatureMatrixModel + this.regionIndex = (matrixData.columns as Array<string>).findIndex(md => md === this.region.name) + return from(this.sapi.processNpArrayData<PARSE_TYPEDARRAY.RAW_ARRAY>(matrixData.matrix, PARSE_TYPEDARRAY.RAW_ARRAY)) + } + return of(null) + }) + ).subscribe(res => { + if (res && res.rawArray && res.rawArray[this.regionIndex]) { + this.connectivityNumber = res.rawArray[this.regionIndex].filter(p => p > 0).length + } + }) + ) + } + } + + ngOnDestroy(){ + while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() + } + +} diff --git a/src/atlasComponents/sapiViews/features/connectivity/index.ts b/src/atlasComponents/sapiViews/features/connectivity/index.ts index e69de29bb..b86944877 100644 --- a/src/atlasComponents/sapiViews/features/connectivity/index.ts +++ b/src/atlasComponents/sapiViews/features/connectivity/index.ts @@ -0,0 +1,2 @@ +export { ConnectivityBrowserComponent } from "./connectivityBrowser/connectivityBrowser.component"; +export { SapiViewsFeatureConnectivityModule } from "./module"; diff --git a/src/atlasComponents/sapiViews/features/connectivity/module.ts b/src/atlasComponents/sapiViews/features/connectivity/module.ts index 745df380c..5bde71aaf 100644 --- a/src/atlasComponents/sapiViews/features/connectivity/module.ts +++ b/src/atlasComponents/sapiViews/features/connectivity/module.ts @@ -1,21 +1,32 @@ import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; +import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from "@angular/core"; import { SAPI } from "src/atlasComponents/sapi"; import { ConnectivityMatrixView } from "./connectivityMatrix/connectivityMatrix.component"; +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"; @NgModule({ imports: [ CommonModule, + AngularMaterialModule ], declarations: [ ConnectivityMatrixView, + ConnectivityBrowserComponent, + HasConnectivity ], exports: [ ConnectivityMatrixView, + ConnectivityBrowserComponent, + HasConnectivity ], providers: [ SAPI, - ] + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA, + ], }) -export class SapiViewsFeatureConnectivityModule{} \ No newline at end of file +export class SapiViewsFeatureConnectivityModule{} diff --git a/src/main.module.ts b/src/main.module.ts index 99766310f..73cf87dc9 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -54,6 +54,7 @@ import { NotSupportedCmp } from './notSupportedCmp/notSupported.component'; import { atlasSelection, + atlasAppearance, annotation, userInterface, userInteraction, @@ -109,6 +110,7 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { uiState, userConfigState, [atlasSelection.nameSpace]: atlasSelection.reducer, + [atlasAppearance.nameSpace]: atlasAppearance.reducer, [userInterface.nameSpace]: userInterface.reducer, [userInteraction.nameSpace]: userInteraction.reducer, [annotation.nameSpace]: annotation.reducer, diff --git a/src/state/atlasAppearance/index.ts b/src/state/atlasAppearance/index.ts index cf777e1fe..b74d3811c 100644 --- a/src/state/atlasAppearance/index.ts +++ b/src/state/atlasAppearance/index.ts @@ -1,3 +1,4 @@ export * as actions from "./action" export * as selectors from "./selector" -export { reducer } from "./store" \ No newline at end of file +export { nameSpace } from "./const" +export { reducer, AtlasAppearanceStore, defaultState } from "./store" diff --git a/src/state/atlasAppearance/selector.ts b/src/state/atlasAppearance/selector.ts index c43c61978..749f7d0cd 100644 --- a/src/state/atlasAppearance/selector.ts +++ b/src/state/atlasAppearance/selector.ts @@ -7,4 +7,4 @@ const selectStore = state => state[nameSpace] as AtlasAppearanceStore export const getOverwrittenColormap = createSelector( selectStore, state => state.overwrittenColormap -) \ No newline at end of file +) diff --git a/src/state/atlasAppearance/store.ts b/src/state/atlasAppearance/store.ts index 817603c93..b32e904ad 100644 --- a/src/state/atlasAppearance/store.ts +++ b/src/state/atlasAppearance/store.ts @@ -5,7 +5,7 @@ export type AtlasAppearanceStore = { overwrittenColormap: Record<string, number[]> } -const defaultState: AtlasAppearanceStore = { +export const defaultState: AtlasAppearanceStore = { overwrittenColormap: null } diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts index 156410ee6..71004c2a9 100644 --- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts +++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts @@ -80,7 +80,7 @@ export class NehubaLayerControlService implements OnDestroy{ const returnVal: IColorMap = {} for (const r of regions) { - + if (!r.hasAnnotation) continue if (!r.hasAnnotation.visualizedIn) continue @@ -94,6 +94,7 @@ export class NehubaLayerControlService implements OnDestroy{ } returnVal[ngId][labelIndex] = { red, green, blue } } + this.activeColorMap = returnVal return returnVal }) ), -- GitLab