diff --git a/docs/releases/v2.0.0.md b/docs/releases/v2.0.0.md index 16235152437e2ab4cdd4f8bd6bf94baff3368a93..77b3c544ec74cb18b0691afe6d3328429823aedc 100644 --- a/docs/releases/v2.0.0.md +++ b/docs/releases/v2.0.0.md @@ -5,7 +5,7 @@ [release](https://github.com/HumanBrainProject/interactive-viewer/releases/tag/v2.0.0) - Added support for new atlases: - - cytoarchitechtonic maps (mapped and interpolated) in Big Brain template space + - cytoarchitectonic maps (mapped and interpolated) in Big Brain template space - JuBrain v18 in Colin 27 and MNI 152 2009c Nonlinear Asymmetric diff --git a/docs/releases/v2.3.0.md b/docs/releases/v2.3.0.md index a4bf02816a303bd2a99582a30eb82172fb5fa1bc..732dcb1511938b623ca35407e851e5e5ea610c53 100644 --- a/docs/releases/v2.3.0.md +++ b/docs/releases/v2.3.0.md @@ -10,8 +10,11 @@ - introduced zoom buttons - major UI overhaul - tweaked mesh loading strategies. Now it will wait for all image chunks to be loaded before loading any meshes +- showing contributors to a regional feature/dataset if publications are not available +- added the ability to customize preview origin dataset to labels other to `View probability map` # Bugfixes: - dataset list view explicitly show loading status - +- fixed a few typos +- fixed the reference space name of `ICBM 152 2009c Nonlinear Asymmetric` diff --git a/src/res/ext/MNI152.json b/src/res/ext/MNI152.json index 6fb0c666b11ad3c03638fbccda8f945d838ff3e3..d4a130ba7a3f0d2f401b144a6c6f33b96cc51690 100644 --- a/src/res/ext/MNI152.json +++ b/src/res/ext/MNI152.json @@ -1,5 +1,6 @@ { "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "displayName": "ICBM 152 2009c Nonlinear Asymmetric", "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "fullId": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "type": "template", @@ -11,7 +12,7 @@ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", - "name": "Cytoarchitechtonic maps", + "name": "Cytoarchitectonic maps", "ngId": "jubrain mni152 v18 left", "auxillaryMeshIndices": [ 65535 diff --git a/src/res/ext/atlas/atlas_multiLevelHuman.json b/src/res/ext/atlas/atlas_multiLevelHuman.json index 0e5c934e0913c3c6e0abdbdfc8cd4251ce3b098e..1185185a6966a942b7dbd19132358f2d8b4c6863 100644 --- a/src/res/ext/atlas/atlas_multiLevelHuman.json +++ b/src/res/ext/atlas/atlas_multiLevelHuman.json @@ -9,7 +9,7 @@ "availableIn": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", - "name": "Cytoarchitechtonic Maps" + "name": "Cytoarchitectonic maps" }, { "name": "Cortical Layers Segmentation", @@ -24,10 +24,11 @@ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "displayName": "ICBM 152 2009c Nonlinear Asymmetric", "availableIn": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", - "name": "Cytoarchitechtonic maps" + "name": "Cytoarchitectonic maps" }, { "name": "Long Bundle", @@ -66,7 +67,7 @@ "availableIn": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", - "name": "Cytoarchitechtonic maps" + "name": "Cytoarchitectonic maps" } ] } @@ -74,7 +75,7 @@ "parcellations": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", - "name": "Cytoarchitechtonic maps", + "name": "Cytoarchitectonic maps", "baseLayer": true, "availableIn": [ { @@ -83,11 +84,17 @@ }, { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "probability map" + }] }, { "@id": "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992", - "name": "MNI Colin 27" + "name": "MNI Colin 27", + "originalDatasetFormats": [{ + "name": "probability map" + }] } ] }, @@ -118,7 +125,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "probability map" + }] } ] }, @@ -130,7 +140,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "probability map" + }] } ] }, @@ -141,7 +154,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "continuous map" + }] } ] }, @@ -152,7 +168,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "continuous map" + }] } ] }, @@ -163,7 +182,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "continuous map" + }] } ] }, @@ -174,7 +196,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "continuous map" + }] } ] }, @@ -185,7 +210,10 @@ "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", - "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" + "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "originalDatasetFormats": [{ + "name": "continuous map" + }] } ] } diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index 34283ab014a770fa3d9585d919b4b5df1fe6050a..a4a1c2faf92d6f75a47192be1f73b65ba8373095 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -135,7 +135,9 @@ export const viewerStateGetOverlayingAdditionalParcellations = createSelector( export const viewerStateGetSelectedAtlas = createSelector( state => state[viewerStateHelperStoreName], - ({ selectedAtlasId, fetchedAtlases }) => { + helperState => { + if (!helperState) return null + const { selectedAtlasId, fetchedAtlases } = helperState return selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) } ) diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.template.html b/src/ui/atlasLayerSelector/atlasLayerSelector.template.html index 0e1696e66c6d509f7b291e226e354c69953f093f..f7e4daf09b8e5658f64d6d154426da0deb09e6ee 100644 --- a/src/ui/atlasLayerSelector/atlasLayerSelector.template.html +++ b/src/ui/atlasLayerSelector/atlasLayerSelector.template.html @@ -124,7 +124,7 @@ <!-- text container --> <div class="d-flex justify-content-center"> - <small class="iv-custom-comp text ml-1 mr-1 mt-2 text-break">{{ tileSrc.name }}</small> + <small class="iv-custom-comp text ml-1 mr-1 mt-2 text-break">{{ tileSrc.displayName || tileSrc.name }}</small> </div> </div> </ng-template> diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html index 7d811342741964d98d03f6851e28e0da2034eb80..c0e0b370f8aed983c477ee0f191b741206cc5bf9 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html @@ -41,6 +41,13 @@ </ng-template> </small> </ng-container> + + <!-- contributors, if publications not available --> + <ng-container *ngIf="publications && publications.length == 0 && contributors && contributors.length > 0"> + <i class="small" *ngFor="let contributor of contributors; let lastFlag = last;"> + {{ contributor }}{{ lastFlag ? '' : ',' }} + </i> + </ng-container> </mat-card-content> diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts index 4b9e9254c5304782e21ec2dd0d9bb6a22907df2a..12d3bfd05b27f8a465a17e861273c05050ee38da 100644 --- a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts +++ b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts @@ -33,6 +33,8 @@ export class SingleDatasetBase implements OnChanges, OnDestroy { @Input() public description?: string @Input() public publications?: IPublication[] + @Input() public contributors: any[] = [] + public fetchFlag = false private _fullId: string @@ -127,10 +129,11 @@ export class SingleDatasetBase implements OnChanges, OnDestroy { if (!dataset) return const { kgSchema, kgId } = this - const { name, description, publications, fullId, kgReference, files } = dataset + const { name, description, publications, fullId, kgReference, files, contributors, ...rest } = dataset this.name = name this.description = description this.publications = publications + this.contributors = contributors this.files = files this.fullId = fullId diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts index 9876a38425390e2e40f40fe6cbf061a9f26d73c9..1711cc5b984f271e292f0daea58d1c860aeda346 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts @@ -33,6 +33,7 @@ import { NehubaModule } from './nehuba.module' import { CommonModule } from '@angular/common' import { IMPORT_NEHUBA_INJECT_TOKEN } from './nehubaViewer/nehubaViewer.component' import { viewerStateHelperStoreName } from 'src/services/state/viewerState.store.helper' +import { RenderViewOriginDatasetLabelPipe } from '../parcellationRegion/region.base' const _bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json') const _bigbrainNehubaConfigJson = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json') @@ -84,7 +85,8 @@ describe('> nehubaContainer.component.ts', () => { // pipes MobileControlNubStylePipe, ReorderPanelIndexPipe, - SafeStylePipe + SafeStylePipe, + RenderViewOriginDatasetLabelPipe ], providers: [ provideMockStore({ initialState: defaultRootState }), diff --git a/src/ui/parcellationRegion/region.base.ts b/src/ui/parcellationRegion/region.base.ts index e2fa37ab8f1fe2d30bef7ebd67cbaf0c96b0cb98..31f3ef3734ac48c0adabb78b283ce55eaaad47f5 100644 --- a/src/ui/parcellationRegion/region.base.ts +++ b/src/ui/parcellationRegion/region.base.ts @@ -1,11 +1,11 @@ -import { EventEmitter, Input, Output } from "@angular/core"; +import { EventEmitter, Input, Output, Pipe, PipeTransform } from "@angular/core"; import { select, Store, createSelector } from "@ngrx/store"; import { uiStateOpenSidePanel, uiStateExpandSidePanel, uiActionShowSidePanelConnectivity } from 'src/services/state/uiState.store.helper' -import { distinctUntilChanged, switchMap, filter } from "rxjs/operators"; -import { Observable, BehaviorSubject } from "rxjs"; +import { distinctUntilChanged, switchMap, filter, map, tap } from "rxjs/operators"; +import { Observable, BehaviorSubject, combineLatest } from "rxjs"; import { ARIA_LABELS } from 'common/constants' import { flattenRegions, getIdFromFullId, rgbToHsl } from 'common/util' -import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect } from "src/services/state/viewerState.store.helper"; +import { viewerStateSetConnectivityRegion, viewerStateNavigateToRegion, viewerStateToggleRegionSelect, viewerStateGetSelectedAtlas } from "src/services/state/viewerState.store.helper"; export class RegionBase { @@ -40,6 +40,7 @@ export class RegionBase { public sameRegionTemplate: any[] = [] public regionInOtherTemplates$: Observable<any[]> + public regionOriginDatasetLabels$: Observable<{ name: string }[]> constructor( private store$: Store<any>, @@ -55,6 +56,14 @@ export class RegionBase { ) )) ) + + this.regionOriginDatasetLabels$ = combineLatest( + this.store$, + this.region$ + ).pipe( + map(([state, region]) => getRegionParentParcRefSpace(state, { region })), + map(({ template }) => (template && template.originalDatasetFormats) || []) + ) } @@ -115,6 +124,71 @@ export class RegionBase { public AVAILABILITY_IN_OTHER_REF_SPACE = ARIA_LABELS.AVAILABILITY_IN_OTHER_REF_SPACE } +export const getRegionParentParcRefSpace = createSelector( + (state: any) => state.viewerState, + viewerStateGetSelectedAtlas, + (viewerState, selectedAtlas, prop) => { + const { region: regionOfInterest } = prop + /** + * if region is undefined, return null + */ + if (!regionOfInterest || !viewerState.parcellationSelected || !viewerState.templateSelected) { + return { + template: null, + parcellation: null + } + } + /** + * first check if the region can be found in the currently selected parcellation + */ + const checkRegions = regions => { + for (const region of regions) { + + /** + * check ROI to iterating regions + */ + if (region.name === regionOfInterest.name) return true + + if (region && region.children && Array.isArray(region.children)) { + const flag = checkRegions(region.children) + if (flag) return true + } + } + return false + } + const regionInParcSelected = checkRegions(viewerState.parcellationSelected.regions) + + if (regionInParcSelected) { + const p = selectedAtlas.parcellations.find(p => p['@id'] === viewerState.parcellationSelected['@id']) + if (p) { + const refSpace = p.availableIn.find(refSpace => refSpace['@id'] === viewerState.templateSelected['@id']) + return { + template: refSpace, + parcellation: p + } + } + } + + return { + template: null, + parcellation: null + } + } +) + +@Pipe({ + name: 'renderViewOriginDatasetlabel' +}) + +export class RenderViewOriginDatasetLabelPipe implements PipeTransform{ + public transform(originDatasetlabels: { name: string }[], index: string|number){ + if (!!originDatasetlabels && !!originDatasetlabels[index] && !!originDatasetlabels[index].name) { + return `View ${originDatasetlabels[index]['name']}` + } + return `View origin dataset` + } +} + export const regionInOtherTemplateSelector = createSelector( (state: any) => state.viewerState, (viewerState, prop) => { diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts b/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts index c98dc9754190acbeebff6af212f7fedce139cfeb..eda0f43eb94b303370a15b4f0bf4b87b35733544 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.component.spec.ts @@ -5,7 +5,7 @@ import { UtilModule } from "src/util/util.module" import { CommonModule } from "@angular/common" import { PreviewDatasetFile } from "src/ui/databrowserModule/singleDataset/datasetPreviews/previewDatasetFile.directive" import { provideMockStore, MockStore } from "@ngrx/store/testing" -import { regionInOtherTemplateSelector } from '../region.base' +import { regionInOtherTemplateSelector, RenderViewOriginDatasetLabelPipe } from '../region.base' import { ARIA_LABELS } from 'common/constants' import { By } from "@angular/platform-browser" @@ -67,7 +67,7 @@ describe('> regionMenu.component.ts', () => { ], declarations: [ RegionMenuComponent, - + RenderViewOriginDatasetLabelPipe, /** * Used by regionMenu.template.html to show region preview */ diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html index 4d92d2d9d48fcba0def611154a6e64d3d3c6f9ba..b3f5408d37394f6a7d9dd6f704e4b0d2217a01f6 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html @@ -43,7 +43,7 @@ </mat-list-item> <!-- originData --> - <mat-list-item *ngFor="let originDataset of (region.originDatasets || [])" + <mat-list-item *ngFor="let originDataset of (region.originDatasets || []); let index = index" iav-dataset-preview-dataset-file [iav-dataset-preview-dataset-file-kgid]="originDataset.kgId" [iav-dataset-preview-dataset-file-filename]="originDataset.filename" @@ -56,7 +56,7 @@ mat-ripple> <mat-icon fontSet="fas" fontIcon="fa-eye" mat-list-icon></mat-icon> <div mat-line> - View probability map + {{ regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }} </div> </mat-list-item> diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index d185e1440e52470da9825000e092eed1d198125d..b1b1f6c257fa92ce444889daa422bd9c4e71100a 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -91,6 +91,7 @@ import { AtlasDropdownSelector } from './atlasDropdown/atlasDropdown.component' import {AtlasLayerSelector} from "src/ui/atlasLayerSelector/atlasLayerSelector.component"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { RegionDirective } from "./parcellationRegion/region.directive"; +import { RenderViewOriginDatasetLabelPipe } from "./parcellationRegion/region.base"; @NgModule({ imports : [ @@ -180,6 +181,7 @@ import { RegionDirective } from "./parcellationRegion/region.directive"; HumanReadableFileSizePipe, ReorderPanelIndexPipe, GetInitialLayerOpacityPipe, + RenderViewOriginDatasetLabelPipe, /* directive */ DownloadDirective,