From 1963c49f256ab0fa70ccf13545120c5d138ae969 Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Fri, 30 Jul 2021 16:21:25 +0200
Subject: [PATCH] hotfix: pli volume

---
 deploy/bkwdCompat/urlState.js                 |  4 +-
 src/glue.ts                                   | 36 +++++++++--
 src/main.module.ts                            |  8 ++-
 .../layerbrowser.component.ts                 | 12 +++-
 src/viewerModule/module.ts                    |  2 +
 .../layerCtrl.service/layerCtrl.service.ts    | 40 ++++++++++--
 .../viewerCmp/viewerCmp.component.ts          | 28 ++++++++-
 .../viewerCmp/viewerCmp.template.html         | 62 ++++++++++++++++++-
 8 files changed, 174 insertions(+), 18 deletions(-)

diff --git a/deploy/bkwdCompat/urlState.js b/deploy/bkwdCompat/urlState.js
index f902a8a9f..3c41eb3c2 100644
--- a/deploy/bkwdCompat/urlState.js
+++ b/deploy/bkwdCompat/urlState.js
@@ -202,8 +202,8 @@ module.exports = (query, _warningCb) => {
       // ignore region selected and move on
     }
   }
-
-  let redirectUrl = '/#'
+  const HOST_PATHNAME = process.env.HOST_PATHNAME || ''
+  let redirectUrl = `${HOST_PATHNAME}/#`
   if (standaloneVolumes) {
     searchParam.set('standaloneVolumes', standaloneVolumes)
     if (nav) redirectUrl += nav
diff --git a/src/glue.ts b/src/glue.ts
index ea07500c1..9a8373c4d 100644
--- a/src/glue.ts
+++ b/src/glue.ts
@@ -11,7 +11,6 @@ import { HttpClient } from "@angular/common/http"
 import { DS_PREVIEW_URL } from 'src/util/constants'
 import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "./services/state/ngViewerState.store.helper"
 import { ARIA_LABELS } from 'common/constants'
-import { NgLayersService } from "src/ui/layerbrowser/ngLayerService.service"
 import { Effect } from "@ngrx/effects"
 import { viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedParcellationSelector } from "./services/state/viewerState/selectors"
 import { ngViewerActionClearView } from './services/state/ngViewerState/actions'
@@ -245,6 +244,13 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
     shareReplay(1),
   )
 
+  public _volumePreview$ = this.previewingDatasetFiles$.pipe(
+    switchMap(arr => arr.length > 0
+      ? forkJoin(arr.map(v => this.getDatasetPreviewFromId(v)))
+      : of([])),
+    map(arr => arr.filter(v => determinePreviewFileType(v) === EnumPreviewFileTypes.VOLUMES))
+  )
+
   private diffPreviewingDatasetFiles$= this.previewingDatasetFiles$.pipe(
     debounceTime(100),
     startWith([] as IDatasetPreviewData[]),
@@ -319,7 +325,6 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
   constructor(
     private store$: Store<any>,
     private http: HttpClient,
-    private layersService: NgLayersService,
     @Optional() @Inject(ACTION_TO_WIDGET_TOKEN) private actionOnWidget: TypeActionToWidget<any>
   ){
     if (!this.actionOnWidget) console.warn(`actionOnWidget not provided in DatasetPreviewGlue. Did you forget to provide it?`)
@@ -351,7 +356,6 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
           distinctUntilChanged(),
         ))
       ).subscribe(([ { prvToShow, prvToDismiss }, templateSelected ]) => {
-
         const filterdPrvs = prvToShow.filter(prv => DatasetPreviewGlue.PreviewFileIsInCorrectSpace(prv, templateSelected))
         for (const prv of filterdPrvs) {
           const { volumes } = prv['data']['iav-registered-volumes']
@@ -389,7 +393,6 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
 
   private openDatasetPreviewWidget(data: IDatasetPreviewData) {
     const { datasetId: kgId, filename } = data
-
     if (!!this.actionOnWidget) {
       const previewId = DatasetPreviewGlue.GetDatasetPreviewId(data)
 
@@ -543,3 +546,28 @@ export class ClickInterceptorService extends RegDeregController<any, boolean>{
     // called when the call has not been intercepted
   }
 }
+
+export type _TPLIVal = {
+  name: string
+  filename: string
+  datasetSchema: string
+  datasetId: string
+  data: {
+    'iav-registered-volumes': {
+      volumes: {
+        name: string
+        source: string
+        shader: string
+        transform: any
+        opacity: string
+      }[]
+    }
+  }
+  referenceSpaces: {
+    name: string
+    fullId: string
+  }[]
+  mimetype: 'application/json'
+}
+
+export const _PLI_VOLUME_INJ_TOKEN = new InjectionToken<Observable<_TPLIVal[]>>('_PLI_VOLUME_INJ_TOKEN')
diff --git a/src/main.module.ts b/src/main.module.ts
index cc25b0115..9c22d1ba6 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -49,7 +49,7 @@ import 'hammerjs'
 import 'src/res/css/extra_styles.css'
 import 'src/res/css/version.css'
 import 'src/theme.scss'
-import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, GlueEffects, ClickInterceptorService } from './glue';
+import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, GlueEffects, ClickInterceptorService, _PLI_VOLUME_INJ_TOKEN } from './glue';
 import { viewerStateHelperReducer, viewerStateMetaReducers, ViewerStateHelperEffect } from './services/state/viewerState.store.helper';
 import { TOS_OBS_INJECTION_TOKEN } from './ui/kgtos';
 import { UiEffects } from './services/state/uiState/ui.effects';
@@ -198,7 +198,11 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
       },
       deps: [ UIService ]
     },
-
+    {
+      provide: _PLI_VOLUME_INJ_TOKEN,
+      useFactory: (glue: DatasetPreviewGlue) => glue._volumePreview$,
+      deps: [ DatasetPreviewGlue ]
+    },
     {
       provide: IAV_DATASET_PREVIEW_ACTIVE,
       useFactory: (glue: DatasetPreviewGlue) => glue.datasetPreviewDisplayed.bind(glue),
diff --git a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
index 72a0687e3..043e0005a 100644
--- a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
+++ b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
@@ -13,6 +13,16 @@ import { ARIA_LABELS } from 'common/constants'
 
 import { INgLayerInterface } from "../index";
 
+const SHOW_LAYER_NAMES = [
+  'PLI Fiber Orientation Red Channel',
+  'PLI Fiber Orientation Green Channel',
+  'PLI Fiber Orientation Blue Channel',
+  'Blockface Image',
+  'PLI Transmittance',
+  'T2w MRI',
+  'MRI Labels'
+]
+
 @Component({
   selector : 'layer-browser',
   templateUrl : './layerbrowser.template.html',
@@ -98,7 +108,7 @@ export class LayerBrowser implements OnInit, OnDestroy {
     ).pipe(
       map(([baseNgLayerNames, loadedNgLayers]) => {
         const baseNameSet = new Set(baseNgLayerNames)
-        return loadedNgLayers.filter(l => !baseNameSet.has(l.name))
+        return loadedNgLayers.filter(l => SHOW_LAYER_NAMES.includes(l.name))
       }),
       distinctUntilChanged()
     )
diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts
index 7abfbb1be..25ea0facf 100644
--- a/src/viewerModule/module.ts
+++ b/src/viewerModule/module.ts
@@ -25,6 +25,7 @@ import { map } from "rxjs/operators";
 import { TContextArg } from "./viewer.interface";
 import { ViewerStateBreadCrumbModule } from "./viewerStateBreadCrumb/module";
 import { KgRegionalFeatureModule } from "src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature";
+import { LayerBrowserModule } from "src/ui/layerbrowser";
 
 @NgModule({
   imports: [
@@ -47,6 +48,7 @@ import { KgRegionalFeatureModule } from "src/atlasComponents/regionalFeatures/bs
     ContextMenuModule,
     ViewerStateBreadCrumbModule,
     KgRegionalFeatureModule,
+    LayerBrowserModule,
   ],
   declarations: [
     ViewerCmp,
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
index 56e87c065..43b6d922a 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
@@ -1,7 +1,7 @@
 import { Inject, Injectable, OnDestroy, Optional } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { BehaviorSubject, combineLatest, from, merge, Observable, of, Subject, Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, shareReplay, switchMap, withLatestFrom } from "rxjs/operators";
+import { BehaviorSubject, combineLatest, from, merge, NEVER, Observable, of, Subject, Subscription } from "rxjs";
+import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, switchMap, withLatestFrom } from "rxjs/operators";
 import { viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors";
 import { getRgb, IColorMap, INgLayerCtrl, INgLayerInterface, TNgLayerCtrl } from "./layerCtrl.util";
 import { getMultiNgIdsRegionsLabelIndexMap } from "../constants";
@@ -12,6 +12,7 @@ import { EnumColorMapName } from "src/util/colorMaps";
 import { getShader, PMAP_DEFAULT_CONFIG } from "src/util/constants";
 import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, ngViewerSelectorClearView, ngViewerSelectorLayers } from "src/services/state/ngViewerState.store.helper";
 import { serialiseParcellationRegion } from 'common/util'
+import { _PLI_VOLUME_INJ_TOKEN, _TPLIVal } from "src/glue";
 
 export const BACKUP_COLOR = {
   red: 255,
@@ -36,7 +37,9 @@ export function getAuxMeshesAndReturnIColor(auxMeshes: IAuxMesh[]): IColorMap{
   return returnVal
 }
 
-@Injectable()
+@Injectable({
+  providedIn: 'root'
+})
 export class NehubaLayerControlService implements OnDestroy{
 
   static PMAP_LAYER_NAME = 'regional-pmap'
@@ -144,10 +147,25 @@ export class NehubaLayerControlService implements OnDestroy{
     while (this.sub.length > 0) this.sub.pop().unsubscribe()
   }
 
+  private pliVol$: Observable<string[]> = this._pliVol$
+  ? this._pliVol$.pipe(
+      map(arr => {
+        const output = []
+        for (const item of arr) {
+          for (const volume of item.data["iav-registered-volumes"].volumes) {
+            output.push(volume.name)
+          }
+        }
+        return output
+      })
+    )
+  : NEVER
   constructor(
     private store$: Store<any>,
+    @Optional() @Inject(_PLI_VOLUME_INJ_TOKEN) private _pliVol$: Observable<_TPLIVal[]>,
     @Optional() @Inject(REGION_OF_INTEREST) roi$: Observable<TRegionDetail>
   ){
+
     if (roi$) {
 
       this.sub.push(
@@ -285,10 +303,10 @@ export class NehubaLayerControlService implements OnDestroy{
     shareReplay(1)
   )
 
-  public visibleLayer$: Observable<string[]> = combineLatest([
+  public expectedLayerNames$ = combineLatest([
     this.selectedTemplateSelector$,
     this.auxMeshes$,
-    this.selParcNgIdMap$
+    this.selParcNgIdMap$,
   ]).pipe(
     map(([ tmpl, auxMeshes, parcNgIdMap ]) => {
       const ngIdSet = new Set<string>()
@@ -305,6 +323,18 @@ export class NehubaLayerControlService implements OnDestroy{
     })
   )
 
+  public visibleLayer$: Observable<string[]> = combineLatest([
+    this.expectedLayerNames$,
+    this.pliVol$.pipe(
+      startWith([])
+    ),
+  ]).pipe(
+    map(([ expectedLayerNames, layerNames ]) => {
+      const ngIdSet = new Set<string>([...layerNames, ...expectedLayerNames])
+      return Array.from(ngIdSet)
+    })
+  )
+
   /**
    * define when shown segments should be updated
    */
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index f0eb2af8c..1ffe2929e 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -1,6 +1,6 @@
 import { Component, ComponentFactory, ComponentFactoryResolver, ElementRef, Inject, Injector, Input, OnDestroy, Optional, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import {combineLatest, merge, Observable, of, Subject, Subscription} from "rxjs";
+import {combineLatest, merge, NEVER, Observable, of, Subject, Subscription} from "rxjs";
 import {catchError, distinctUntilChanged, filter, map, shareReplay, startWith, switchMap } from "rxjs/operators";
 import { viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions";
 import {
@@ -23,6 +23,8 @@ import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
 import { ComponentStore } from "../componentStore";
 import { MAT_DIALOG_DATA } from "@angular/material/dialog";
 import { GenericInfoCmp } from "src/atlasComponents/regionalFeatures/bsFeatures/genericInfo";
+import { _PLI_VOLUME_INJ_TOKEN, _TPLIVal } from "src/glue";
+import { uiActionSetPreviewingDatasetFiles } from "src/services/state/uiState.store.helper";
 
 type TCStoreViewerCmp = {
   overlaySideNav: any
@@ -115,7 +117,10 @@ export function ROIFactory(store: Store<any>, svc: PureContantService){
 })
 
 export class ViewerCmp implements OnDestroy {
-
+  public _pliTitle = "Fiber structures of a human hippocampus based on joint DMRI, 3D-PLI, and TPFM acquisitions"
+  public _pliDesc = "The collected datasets provide real multimodal, multiscale structural connectivity insights into the human hippocampus. One post mortem hippocampus was scanned with Anatomical and Diffusion MRI (dMRI) [1], 3D Polarized Light Imaging (3D-PLI) [2], and Two-Photon Fluorescence Microscopy (TPFM) [3] using protocols specifically developed during SGA1 and SGA2, rendering joint tissue imaging possible. MRI scanning was performed with a 11.7 T Preclinical MRI system (gradients: 760 mT/m, slew rate: 9500 T/m/s) yielding T1-w and T2-w maps at 200 µm and dMRI-based maps at 300 µm resolution. During tissue sectioning (60 µm thickness) blockface (en-face) images were acquired from the surface of the frozen brain block, serving as reference for data integration/co-alignment. 530 brain sections were scanned with 3D-PLI. HPC-based image analysis provided transmittance, retardation, and fiber orientation maps at 1.3 µm in-plane resolution. TPFM was finally applied to selected brain sections utilizing autofluorescence properties of the fibrous tissue which appears after PBS washing (MAGIC protocol). The TPFM measurements provide a resolution of 0.44 µm x 0.44 µm x 1 µm."
+  public _pliLink = "https://doi.org/10.25493/JQ30-E08"
+  
   public CONST = CONST
   public ARIA_LABELS = ARIA_LABELS
 
@@ -215,17 +220,36 @@ export class ViewerCmp implements OnDestroy {
   private getRegionFromlabelIndexId: Function
 
   private genericInfoCF: ComponentFactory<GenericInfoCmp>
+
+  public pliVol$ = this._pliVol$ || NEVER
+  public clearVoi(){
+    this.store$.dispatch(
+      uiActionSetPreviewingDatasetFiles({
+        previewingDatasetFiles: []
+      })
+    )
+  }
   constructor(
     private store$: Store<any>,
     private viewerModuleSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>,
     private cStore: ComponentStore<TCStoreViewerCmp>,
     cfr: ComponentFactoryResolver,
+    @Optional() @Inject(_PLI_VOLUME_INJ_TOKEN) private _pliVol$: Observable<_TPLIVal[]>,
     @Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any>
   ){
 
     this.genericInfoCF = cfr.resolveComponentFactory(GenericInfoCmp)
 
     this.subscriptions.push(
+      this.pliVol$.subscribe(val => {
+        if (val.length > 0) {
+          this.sidenavTopSwitch && this.sidenavTopSwitch.open()
+          this.sidenavLeftSwitch && this.sidenavLeftSwitch.open()
+        } else {
+          this.sidenavTopSwitch && this.sidenavTopSwitch.close()
+          this.sidenavLeftSwitch && this.sidenavLeftSwitch.close()
+        }
+      }),
       this.selectedRegions$.subscribe(() => {
         this.clearPreviewingDataset()
       }),
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index aa2004d86..4b5a2e90e 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -213,9 +213,14 @@
         'invisible overflow-hidden h-0': overlaySidenav$ | async,
         'h-100': !(overlaySidenav$ | async)
       }" class="position-relative d-flex flex-column">
+        
+        <ng-template let-pliVol [ngIf]="pliVol$ | async" [ngIfElse]="sidenavRegionTmpl">
+          <ng-template [ngIf]="pliVol.length > 0" [ngIfElse]="sidenavRegionTmpl">
+            <ng-template [ngTemplateOutlet]="voiTmpl">
 
-        <ng-container *ngTemplateOutlet="sidenavRegionTmpl">
-        </ng-container>
+            </ng-template>
+          </ng-template>
+        </ng-template>
 
         <!-- TODO dataset preview will become deprecated in the future.
         Regional feature/data feature will replace it -->
@@ -405,6 +410,59 @@
   </button>
 </ng-template>
 
+<!-- VOI sidenav tmpl -->
+<ng-template #voiTmpl>
+
+  <!-- back btn -->
+  <button mat-button
+    (click)="clearVoi()"
+    [attr.aria-label]="ARIA_LABELS.CLOSE"
+    class="position-absolute z-index-10 m-2">
+    <i class="fas fa-chevron-left"></i>
+    <span class="ml-1">
+      Back
+    </span>
+  </button>
+
+  <mat-card class="sidenav-cover-header-container">
+    <div class="sidenav-cover-header-container">
+      <mat-card-title>
+        {{ _pliTitle }}
+      </mat-card-title>
+
+      <mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
+        <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon>
+        <span>
+          Dataset preview
+        </span>
+
+        <mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider>
+
+        <a [href]="_pliLink"
+          mat-icon-button
+          matTooltip="Explore in EBRAINS Knowledge Graph"
+          target="_blank">
+          <i class="fas fa-external-link-alt"></i>
+        </a>
+
+      </mat-card-subtitle>
+    </div>
+
+    <small class="text-muted iv-custom-comp darker-bg">
+      {{ _pliDesc }}
+    </small>
+
+    <mat-expansion-panel class="sidenav-cover-header-container">
+      <mat-expansion-panel-header>
+        <mat-panel-title>
+          Registered Volumes
+        </mat-panel-title>
+      </mat-expansion-panel-header>
+      <layer-browser></layer-browser>
+    </mat-expansion-panel>
+  </mat-card>
+</ng-template>
+
 <!-- region sidenav tmpl -->
 <ng-template #sidenavRegionTmpl>
 
-- 
GitLab