diff --git a/docs/releases/v2.6.6.md b/docs/releases/v2.6.6.md
new file mode 100644
index 0000000000000000000000000000000000000000..ccdd90b285f00430cb4cba39aaa2467c63631cf5
--- /dev/null
+++ b/docs/releases/v2.6.6.md
@@ -0,0 +1,5 @@
+# v2.6.6
+
+## Feature
+
+- (experimental) link to 1um VOI
diff --git a/mkdocs.yml b/mkdocs.yml
index d56285fcb2696c7884fb90844282f9ba87e94d2b..fb8f1aded1ce21f342f7149a36483a6025183377 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -33,6 +33,7 @@ pages:
- Fetching datasets: 'advanced/datasets.md'
- Display non-atlas volumes: 'advanced/otherVolumes.md'
- Release notes:
+ - v2.6.6: 'releases/v2.6.6.md'
- v2.6.5: 'releases/v2.6.5.md'
- v2.6.4: 'releases/v2.6.4.md'
- v2.6.3: 'releases/v2.6.3.md'
diff --git a/package.json b/package.json
index 75826c222e0691790b3faac539f396e0a590bccf..d24a488d857bce622022afa1440248afc640dc21 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "interactive-viewer",
- "version": "2.6.5",
+ "version": "2.6.6",
"description": "HBP interactive atlas viewer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular",
"scripts": {
"build-aot": "ng build && node ./third_party/matomo/processMatomo.js",
diff --git a/src/main.module.ts b/src/main.module.ts
index ea82fa7eb23dbd27e2037f7f52ad1f3e7bdd43e1..dccad299c9f882744a6ae9d7fc40b88844fc782b 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -1,8 +1,8 @@
import { DragDropModule } from '@angular/cdk/drag-drop'
import { CommonModule } from "@angular/common";
-import { NgModule } from "@angular/core";
+import { APP_INITIALIZER, NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
-import { StoreModule, ActionReducer } from "@ngrx/store";
+import { StoreModule, ActionReducer, Store } from "@ngrx/store";
import { AngularMaterialModule } from 'src/sharedModules'
import { AtlasViewer } from "./atlasViewer/atlasViewer.component";
import { ComponentsModule } from "./components/components.module";
@@ -54,10 +54,13 @@ import { MessagingGlue } from './messagingGlue';
import { BS_ENDPOINT } from './util/constants';
import { QuickTourModule } from './ui/quickTour';
import { of } from 'rxjs';
+import { debounceTime, filter, map } from 'rxjs/operators';
import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, kgTos, IAV_DATASET_PREVIEW_ACTIVE } from './databrowser.fallback'
import { CANCELLABLE_DIALOG } from './util/interfaces';
import { environment } from 'src/environments/environment'
import { NotSupportedCmp } from './notSupportedCmp/notSupported.component';
+import { RouterService } from './routerModule/router.service';
+import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from './services/state/ngViewerState.store.helper';
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function(state, action) {
@@ -242,6 +245,43 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
provide: BS_ENDPOINT,
useValue: (environment.BS_REST_URL || `https://siibra-api-stable.apps.hbp.eu/v1_0`).replace(/\/$/, '')
},
+ {
+ /**
+ * monkey patch 1um data as x-voi:d71d369a-c401-4d7e-b97a-3fb78eed06c5
+ */
+ provide: APP_INITIALIZER,
+ useFactory: (rSvc: RouterService, store: Store) => {
+ rSvc.customRoute$.pipe(
+ map(val => val[`x-voi`] === `d71d369a-c401-4d7e-b97a-3fb78eed06c5`),
+ debounceTime(160)
+ ).subscribe(flag => {
+ if (flag) {
+ store.dispatch(
+ ngViewerActionAddNgLayer({
+ layer: {
+ name: `1um`,
+ source: `precomputed://https://1um.brainatlas.eu/cyto_reconstructions/ebrains_release/BB_1um/VOI_1/precomputed`,
+ shader: `void main(){ emitGrayscale(toNormalized(getDataValue()));}`,
+ transform: [[1.002276062965393,0.1810370832681656,0.15283183753490448,-10704839],[-0.14879435300827026,-0.0360119566321373,1.018455982208252,-60994436],[0.18436983227729797,-1.0132216215133667,-0.008890812285244465,-2825862.75],[0,0,0,1]],
+ opacity: 1,
+ }
+ })
+ )
+ } else {
+ store.dispatch(
+ ngViewerActionRemoveNgLayer({
+ layer: {
+ name: `1um`
+ }
+ })
+ )
+ }
+ })
+ return () => Promise.resolve()
+ },
+ multi: true,
+ deps: [ RouterService, Store ]
+ }
],
bootstrap : [
AtlasViewer,
diff --git a/src/services/state/ngViewerState/constants.ts b/src/services/state/ngViewerState/constants.ts
index 3406494f9d6a4a578b54325b1b2869df7f3f3da9..3e93ae4d0ec03ceec73175d1cba8254d6b4b894b 100644
--- a/src/services/state/ngViewerState/constants.ts
+++ b/src/services/state/ngViewerState/constants.ts
@@ -1,12 +1,13 @@
export interface INgLayerInterface {
name: string // displayName
source: string
- mixability: string // base | mixable | nonmixable
+ mixability?: string // base | mixable | nonmixable
annotation?: string //
id?: string // unique identifier
visible?: boolean
shader?: string
transform?: any
+ opacity?: number
}
export enum PANELS {
diff --git a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
index 043e0005a7d84e5a99f7e3acf52a48410df73378..bc866e2556cd784e9668a3f418362553667265ca 100644
--- a/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
+++ b/src/ui/layerbrowser/layerBrowserComponent/layerbrowser.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, Pipe, PipeTransform } from "@angular/core";
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output, Pipe, PipeTransform } from "@angular/core";
import { select, Store } from "@ngrx/store";
import { combineLatest, Observable, Subscription } from "rxjs";
import { debounceTime, distinctUntilChanged, map, shareReplay, startWith } from "rxjs/operators";
@@ -20,7 +20,8 @@ const SHOW_LAYER_NAMES = [
'Blockface Image',
'PLI Transmittance',
'T2w MRI',
- 'MRI Labels'
+ 'MRI Labels',
+ '1um'
]
@Component({
@@ -29,6 +30,7 @@ const SHOW_LAYER_NAMES = [
styleUrls : [
'./layerbrowser.style.css',
],
+ changeDetection: ChangeDetectionStrategy.OnPush
})
export class LayerBrowser implements OnInit, OnDestroy {
@@ -137,7 +139,6 @@ export class LayerBrowser implements OnInit, OnDestroy {
this.forceShowSegment$.subscribe(state => this.forceShowSegmentCurrentState = state),
)
- this.viewer = getViewer()
}
public ngOnDestroy() {
@@ -159,7 +160,9 @@ export class LayerBrowser implements OnInit, OnDestroy {
}
}
- public viewer: any
+ get viewer() {
+ return getViewer()
+ }
public toggleVisibility(layer: any) {
const layerName = layer.name
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
index d3232e3e098d260c3771b95858fb428ea7f1caf0..35732fd5214f68e020ad1da22879d0c96c6ad8b6 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, NEVER, Observable, of, Subject, Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, shareReplay, startWith, switchMap, withLatestFrom } from "rxjs/operators";
+import { debounceTime, distinctUntilChanged, filter, map, mapTo, 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";
@@ -13,6 +13,7 @@ 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";
+import { RouterService } from "src/routerModule/router.service";
export const BACKUP_COLOR = {
red: 255,
@@ -162,6 +163,7 @@ export class NehubaLayerControlService implements OnDestroy{
: NEVER
constructor(
private store$: Store<any>,
+ private routerSvc: RouterService,
@Optional() @Inject(_PLI_VOLUME_INJ_TOKEN) private _pliVol$: Observable<_TPLIVal[]>,
@Optional() @Inject(REGION_OF_INTEREST) roi$: Observable<TRegionDetail>
){
@@ -328,9 +330,15 @@ export class NehubaLayerControlService implements OnDestroy{
this.pliVol$.pipe(
startWith([])
),
+ this.routerSvc.customRoute$.pipe(
+ startWith({}),
+ map(val => val['x-voi'] === "d71d369a-c401-4d7e-b97a-3fb78eed06c5"
+ ? ["1um"]
+ : []),
+ )
]).pipe(
- map(([ expectedLayerNames, layerNames ]) => {
- const ngIdSet = new Set<string>([...layerNames, ...expectedLayerNames])
+ map(([ expectedLayerNames, layerNames, voiLayers ]) => {
+ const ngIdSet = new Set<string>([...layerNames, ...expectedLayerNames, ...voiLayers])
return Array.from(ngIdSet)
})
)
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index 68b8a93746f8cc72d2c29750e8b73aee581f12eb..b1b8c323547335170228b71aac27b0ca937418a8 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactory, ComponentFactoryResolver, Inject, Injector, Input, OnDestroy, Optional, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
import { select, Store } from "@ngrx/store";
import { combineLatest, merge, NEVER, Observable, of, Subscription } from "rxjs";
-import {catchError, debounceTime, distinctUntilChanged, map, shareReplay, startWith, switchMap } from "rxjs/operators";
+import {catchError, debounceTime, distinctUntilChanged, map, shareReplay, startWith, switchMap, mapTo } from "rxjs/operators";
import { viewerStateSetSelectedRegions } from "src/services/state/viewerState/actions";
import {
viewerStateContextedSelectedRegionsSelector,
@@ -26,6 +26,7 @@ import { _PLI_VOLUME_INJ_TOKEN, _TPLIVal } from "src/glue";
import { uiActionSetPreviewingDatasetFiles } from "src/services/state/uiState.store.helper";
import { viewerStateSetViewerMode } from "src/services/state/viewerState.store.helper";
import { DialogService } from "src/services/dialogService.service";
+import { RouterService } from "src/routerModule/router.service";
type TCStoreViewerCmp = {
overlaySideNav: any
@@ -124,6 +125,10 @@ export class ViewerCmp implements OnDestroy {
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 _1umTitle = `Cellular level 3D reconstructed volumes at 1µm resolution within the human occipital cortex (v1.0)`
+ public _1umDesc = ``
+ public _1umLink = `https://search.kg.ebrains.eu/instances/d71d369a-c401-4d7e-b97a-3fb78eed06c5`
+
public CONST = CONST
public ARIA_LABELS = ARIA_LABELS
@@ -185,8 +190,33 @@ export class ViewerCmp implements OnDestroy {
return 'notsupported'
})
)
-
- public pliVol$ = this._pliVol$ || NEVER
+
+ public viewerCtx$ = this.viewerModuleSvc.context$
+
+ private _1umVoi$ = this.routerSvc.customRoute$.pipe(
+ map(obj => obj[`x-voi`] === "d71d369a-c401-4d7e-b97a-3fb78eed06c5"),
+ distinctUntilChanged()
+ )
+
+ public pliVol$ = merge(
+ this._pliVol$?.pipe(
+ mapTo({
+ title: this._pliTitle,
+ description: this._pliDesc,
+ url: [{ doi: this._pliLink }]
+ })
+ ) || NEVER,
+ this._1umVoi$.pipe(
+ map(flag => flag
+ ? ({
+ title: this._1umTitle,
+ description: this._1umDesc,
+ url: [{ doi: this._1umLink }]
+ })
+ : null
+ )
+ )
+ )
/**
* if no regions are selected, nor any additional layers (being deprecated)
@@ -197,13 +227,13 @@ export class ViewerCmp implements OnDestroy {
public onlyShowMiniTray$: Observable<boolean> = combineLatest([
this.selectedRegions$,
this.pliVol$.pipe(
- startWith([])
+ startWith(null as { title: string, description: string, url: { doi: string }[] })
),
this.viewerMode$.pipe(
startWith(null as string)
),
]).pipe(
- map(([ regions, layers, viewerMode ]) => regions.length === 0 && layers.length === 0 && !viewerMode)
+ map(([ regions, layers, viewerMode ]) => regions.length === 0 && !layers && !viewerMode)
)
@ViewChild('viewerStatusCtxMenu', { read: TemplateRef })
@@ -224,6 +254,7 @@ export class ViewerCmp implements OnDestroy {
previewingDatasetFiles: []
})
)
+ this.routerSvc.setCustomRoute('x-voi', null)
}
constructor(
private store$: Store<any>,
@@ -232,6 +263,7 @@ export class ViewerCmp implements OnDestroy {
cfr: ComponentFactoryResolver,
private dialogSvc: DialogService,
private cdr: ChangeDetectorRef,
+ private routerSvc: RouterService,
@Optional() @Inject(_PLI_VOLUME_INJ_TOKEN) private _pliVol$: Observable<_TPLIVal[]>,
@Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any>
){
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index abb3c1c34715215fae7f6d98ed850d1804436bbd..fae280bfbd67128f67fc6567e1fe7b87883aeca2 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -164,7 +164,7 @@
<!-- if pli voi is visible, show pli template
otherwise show region tmpl -->
<ng-template
- [ngTemplateOutlet]="(pliVol$ | async)?.[0]
+ [ngTemplateOutlet]="(pliVol$ | async)
? voiTmpl
: sidenavRegionTmpl"
[ngTemplateOutletContext]="{
@@ -553,7 +553,7 @@
<mat-card class="_pli-container">
<mat-card-title>
- {{ _pliTitle }}
+ {{ pliVol$ | async | getProperty : 'title' }}
</mat-card-title>
<mat-card-subtitle class="d-inline-flex align-items-center flex-wrap">
@@ -564,7 +564,7 @@
<mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider>
- <a [href]="_pliLink"
+ <a *ngFor="let url of pliVol$ | async | getProperty : 'url'" [href]="url.doi"
mat-icon-button
matTooltip="Explore in EBRAINS Knowledge Graph"
target="_blank">
@@ -574,7 +574,7 @@
</mat-card-subtitle>
<small class="d-block text-muted iv-custom-comp darker-bg">
- {{ _pliDesc }}
+ {{ pliVol$ | async | getProperty : 'description' }}
</small>
<mat-expansion-panel class="sidenav-cover-header-container">