diff --git a/deploy/bkwdCompat/urlState.js b/deploy/bkwdCompat/urlState.js index dd5331e3e9967e85eb07778d4f56edfe5a4dde0c..66f92c3dc4f6b375fcfe0c70f16a685b43c30e0d 100644 --- a/deploy/bkwdCompat/urlState.js +++ b/deploy/bkwdCompat/urlState.js @@ -42,7 +42,10 @@ const templateMap = { id: 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', parc: { 'Cytoarchitectonic Maps - v2.4': { - id: 'juelich/iav/atlas/v1.0.0/7' + id: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290', + }, + 'Cytoarchitectonic Maps': { + id: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290', }, 'Cortical Layers Segmentation': { id: 'juelich/iav/atlas/v1.0.0/3' @@ -64,7 +67,7 @@ const templateMap = { id: 'juelich/iav/atlas/v1.0.0/79cbeaa4ee96d5d3dfe2876e9f74b3dc3d3ffb84304fb9b965b1776563a1069c' }, 'Cytoarchitectonic maps - v1.18': { - id: 'juelich/iav/atlas/v1.0.0/8' + id: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579' }, 'Long Bundle': { id: 'juelich/iav/atlas/v1.0.0/5' @@ -94,7 +97,7 @@ const templateMap = { id: 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992', parc: { 'Cytoarchitectonic Maps - v1.18': { - id: 'juelich/iav/atlas/v1.0.0/8' + id: 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579' } } }, diff --git a/docs/releases/v2.4.1.md b/docs/releases/v2.4.1.md index da64e9b3cbb0d49a953218160fd5231cd60b85f5..4768d30acb00b7b73b5472fbb48c86800739152a 100644 --- a/docs/releases/v2.4.1.md +++ b/docs/releases/v2.4.1.md @@ -2,4 +2,4 @@ ## Bugfixes -- fix JulichBrain v2.5.0 URL redirection \ No newline at end of file +- fix JulichBrain v2.5.0/v1.18 URL redirection \ No newline at end of file diff --git a/docs/releases/v2.4.3.md b/docs/releases/v2.4.3.md new file mode 100644 index 0000000000000000000000000000000000000000..ede740c0394d101dc0fdf1074816c325bffbcbeb --- /dev/null +++ b/docs/releases/v2.4.3.md @@ -0,0 +1,5 @@ +# v2.4.3 + +## Bugfixes + +- fix some of the big brain maps \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index fdcc03b3df60b34d1ccefae2152ad01529a712ac..4b7828389a6ab6f4dd0d9e155493cd9dac4bd128 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,7 @@ pages: - Fetching datasets: 'advanced/datasets.md' - Display non-atlas volumes: 'advanced/otherVolumes.md' - Release notes: + - v2.4.3: 'releases/v2.4.3.md' - v2.4.2: 'releases/v2.4.2.md' - v2.4.1: 'releases/v2.4.1.md' - v2.4.0: 'releases/v2.4.0.md' diff --git a/package.json b/package.json index 092b4973a467dfdd8e4332df9da4c4da5895a9d7..71b66c9ed2319db8b479ff6408733b10eaabdc61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "interactive-viewer", - "version": "2.4.2", + "version": "2.4.3", "description": "HBP interactive atlas viewer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular", "scripts": { "build-aot": "PRODUCTION=true GIT_HASH=`node -e 'console.log(require(\"./package.json\").version)'` webpack --config ./webpack/webpack.aot.js && node ./third_party/matomo/processMatomo.js", diff --git a/src/atlasComponents/parcellationRegion/region.base.ts b/src/atlasComponents/parcellationRegion/region.base.ts index c69a2f94658760f599f896f16affca4a70ab277c..29f4073df696e30dda71e2216f20947f9f12dd1b 100644 --- a/src/atlasComponents/parcellationRegion/region.base.ts +++ b/src/atlasComponents/parcellationRegion/region.base.ts @@ -284,7 +284,7 @@ export const regionInOtherTemplateSelector = createSelector( const otherTemplates = fetchedTemplates .filter(({ ['@id']: id }) => id !== regionOfInterest.context.template['@id'] && atlasTemplateSpacesIds.includes(id) - && regionOfInterest.availableIn.map(ai => ai.id).includes(id)) + && (regionOfInterest.availableIn || []).map(ai => ai.id).includes(id)) for (const template of otherTemplates) { const parcellation = template.parcellations.find(p => p['@id'] === regionOfInterest.context.parcellation['@id']) diff --git a/src/util/fn.ts b/src/util/fn.ts index db3ad47ce6e43a8065bef6acc0459be9a72d5d99..23ec5adeec3f09a0573bbb4fe4824f10ae73a84c 100644 --- a/src/util/fn.ts +++ b/src/util/fn.ts @@ -287,14 +287,41 @@ export class MultiDimMap{ } } -export function recursiveMutate<T>(arr: T[], getChildren: (obj: T) => T[], mutateFn: (obj: T) => void){ +export function mutateDeepMerge(toObj: any, fromObj: any){ + if (typeof toObj !== 'object') throw new Error(`toObj needs to be object`) + if (typeof fromObj !== 'object') throw new Error(`fromObj needs to be object`) + + for (const key in fromObj) { + if (!toObj[key]) { + toObj[key] = fromObj[key] + continue + } + if (Array.isArray(toObj[key])) { + const objToAppend = Array.isArray(fromObj[key]) + ? fromObj[key] + : [fromObj[key]] + toObj[key].push(...objToAppend) + continue + } + if (typeof toObj[key] === typeof fromObj[key] && typeof toObj[key] === 'object') { + mutateDeepMerge(toObj[key], fromObj[key]) + continue + } + throw new Error(`cannot mutate ${key} typeof ${typeof fromObj[key]}`) + } + + return toObj +} + +export function recursiveMutate<T>(arr: T[], getChildren: (obj: T) => T[], mutateFn: (obj: T) => void, depthFirst = false){ for (const obj of arr) { - mutateFn(obj) + if (!depthFirst) mutateFn(obj) recursiveMutate( getChildren(obj), getChildren, mutateFn ) + if (depthFirst) mutateFn(obj) } } diff --git a/src/util/patchPureConstants.ts b/src/util/patchPureConstants.ts new file mode 100644 index 0000000000000000000000000000000000000000..a97fe7ec66ec36fb149df93b0beee112f69eace2 --- /dev/null +++ b/src/util/patchPureConstants.ts @@ -0,0 +1,193 @@ +/** + * README: the purpose of this file is to monkey patch discrepency between siibra-api + * backend and original backend. + * + * In principle, these should be built into siibra-python, and this file should become obsolete. + */ + +import { IHasId } from "./interfaces"; +import { TRegion } from "./siibraApiConstants/types"; + +type TAppend = { + parent: IHasId | { name: string } + '@type': 'julich/siibra/append-region/v0.0.1' +} + +type TPatch = { + target: IHasId | { name: string } + '@type': 'julich/siibra/patch-region/v0.0.1' +} + +type TPatchRegion = { + '@id': string + targetSpace: IHasId[] | '*' + targetParcellation: IHasId[] | '*' + payload: Partial<TRegion> +} & (TAppend | TPatch) + +const encoder = new TextEncoder() +async function getShaDigest(input: string){ + const digest = await crypto.subtle.digest('SHA-1', encoder.encode(input)) + const array = Array.from(new Uint8Array(digest)) + const hex = array.map(v => v.toString(16)).join('') + return hex +} +async function getInterpolatedPatchObj(targetName: string, labelIndex: number){ + const returnObj: TPatchRegion = { + '@id': '', + "@type": 'julich/siibra/patch-region/v0.0.1', + "target": { + "name": targetName + }, + "targetParcellation": [{ + "@id": 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290' + }], + "targetSpace": [{ + '@id': 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588' + }], + "payload": { + "volumeSrc": { + 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588': { + "collect": [{ + "@type": "fzj/tmp/volume_type/v0.0.1" as "fzj/tmp/volume_type/v0.0.1", + "@id": "fzj/tmp/volume_type/v0.0.1/interpolated", + "name": "Julich Brain v2.5 interpolated map", + "volume_type": "neuroglancer/precomputed" as "neuroglancer/precomputed", + "url": "https://neuroglancer.humanbrainproject.org/precomputed/BigBrainRelease.2015/2019_05_22_interpolated_areas", + "detail": { + "neuroglancer/precomputed": { + "labelIndex": labelIndex, + "transform": [[1,0,0,-70677184],[0,1,0,-51990000],[0,0,1,-58788284],[0,0,0,1]] + } + } + }] + } + } + } + } + const hex = await getShaDigest(JSON.stringify(returnObj)) + return { + ...returnObj, + '@id': hex + } +} + +async function getIndividualMap(parentName: string, regionName: string, url: string, transform: number[][], labelIndex: number){ + const volumeId = await getShaDigest(url) + const returnObj: TPatchRegion = { + '@id': '', + "@type": 'julich/siibra/append-region/v0.0.1', + "parent": { + "name": parentName + }, + "targetParcellation": [{ + "@id": 'minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579-290' + }], + "targetSpace": [{ + '@id': 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588' + }], + "payload": { + "name": regionName, + "volumeSrc": { + 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588': { + "collect": [{ + "@type": "fzj/tmp/volume_type/v0.0.1" as "fzj/tmp/volume_type/v0.0.1", + "@id": `fzj/tmp/volume_type/v0.0.1/${volumeId}`, + "name": "Julich Brain v2.5 detailed map", + "volume_type": "neuroglancer/precomputed" as "neuroglancer/precomputed", + "url": url, + "detail": { + "neuroglancer/precomputed": { + "labelIndex": labelIndex, + "transform": transform + } + } + }] + } + } + } + } + const hex = await getShaDigest(JSON.stringify(returnObj)) + return { + ...returnObj, + '@id': hex + } +} + +const bigBrainRegions: Promise<TPatchRegion>[] = [ + getInterpolatedPatchObj('Area IFJ1 (IFS,PreCS)', 9), + getInterpolatedPatchObj('Area IFJ2 (IFS,PreCS)', 10), + getInterpolatedPatchObj('Area IFS1 (IFS)', 11), + getInterpolatedPatchObj('Area IFS2 (IFS)', 12), + getInterpolatedPatchObj('Area IFS3 (IFS)', 13), + getInterpolatedPatchObj('Area IFS4 (IFS)', 14), + + getIndividualMap( + 'lateral geniculate body', + 'LGB-lam1 (CGL, Metathalamus)', + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam1/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'lateral geniculate body', + 'LGB-lam2 (CGL, Metathalamus)', + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam2/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'lateral geniculate body', + 'LGB-lam3 (CGL, Metathalamus)', + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam3/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'lateral geniculate body', + 'LGB-lam4 (CGL, Metathalamus)', + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam4/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'lateral geniculate body', + 'LGB-lam5 (CGL, Metathalamus)', + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam5/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'lateral geniculate body', + 'LGB-lam6 (CGL, Metathalamus)', + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2020_11_11_LGB-lam/LGB-lam6/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + + getIndividualMap( + 'medial geniculate body', + "MGB-MGBd (CGM, Metathalamus)", + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2021_04_27_mgb/2021_04_27_MGBd/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'medial geniculate body', + "MGB-MGBm (CGM, Metathalamus)", + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2021_04_27_mgb/2021_04_27_MGBm/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), + getIndividualMap( + 'medial geniculate body', + "MGB-MGBv (CGM, Metathalamus)", + "https://neuroglancer.humanbrainproject.eu/precomputed/BigBrainRelease.2015/2021_04_27_mgb/2021_04_27_MGBv/", + [[1,0,0,-70677184.0],[0,1,0,-7290000.0],[0,0,1,-58788284.0],[0,0,0,1]], + 1 + ), +] + +export const patchRegions = [ + ...bigBrainRegions +] diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts index 0e6a9bc9ebaf706e78bb28fb4e04274449cd09db..6dce026c96ade1309808cad26c096774f04cce72 100644 --- a/src/util/pureConstant.service.ts +++ b/src/util/pureConstant.service.ts @@ -1,6 +1,6 @@ import { Inject, Injectable, OnDestroy } from "@angular/core"; import { Store, select } from "@ngrx/store"; -import { Observable, Subscription, of, forkJoin, fromEvent, combineLatest } from "rxjs"; +import { Observable, Subscription, of, forkJoin, fromEvent, combineLatest, from } from "rxjs"; import { viewerConfigSelectorUseMobileUi } from "src/services/state/viewerConfig.store.helper"; import { shareReplay, tap, scan, catchError, filter, switchMap, map, take, distinctUntilChanged, mapTo } from "rxjs/operators"; import { HttpClient } from "@angular/common/http"; @@ -11,7 +11,8 @@ import { viewerStateFetchedAtlasesSelector, viewerStateSelectedTemplateSelector import { BS_ENDPOINT } from "src/util/constants"; import { flattenReducer } from 'common/util' import { TAtlas, TId, TParc, TRegion, TRegionDetail, TSpaceFull, TSpaceSummary } from "./siibraApiConstants/types"; -import { MultiDimMap, recursiveMutate } from "./fn"; +import { MultiDimMap, mutateDeepMerge, recursiveMutate } from "./fn"; +import { patchRegions } from './patchPureConstants' function getNgId(atlasId: string, tmplId: string, parcId: string, regionKey: string){ const proxyId = MultiDimMap.GetProxyKeyMatch(atlasId, tmplId, parcId, regionKey) @@ -206,6 +207,12 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" ) } + private patchRegions$ = forkJoin( + patchRegions.map(patch => from(patch)) + ).pipe( + shareReplay(1) + ) + private getRegions(atlasId: string, parcId: string, spaceId: string){ return this.http.get<TRegion[]>( `${this.bsEndpoint}/atlases/${encodeURIComponent(atlasId)}/parcellations/${encodeURIComponent(parcId)}/regions`, @@ -215,6 +222,53 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" }, responseType: 'json' } + ).pipe( + switchMap(regions => this.patchRegions$.pipe( + map(patchRegions => { + for (const p of patchRegions) { + if ( + p.targetParcellation !== '*' + && Array.isArray(p.targetParcellation) + && p.targetParcellation.every(p => p["@id"] !== parcId) + ) { + continue + } + if ( + p.targetSpace !== '*' + && Array.isArray(p.targetSpace) + && p.targetSpace.every(sp => sp['@id'] !== spaceId) + ) { + continue + } + + recursiveMutate( + regions, + r => r.children || [], + region => { + + if (p["@type"] === 'julich/siibra/append-region/v0.0.1') { + if (p.parent['name'] === region.name) { + if (!region.children) region.children = [] + region.children.push( + p.payload as TRegion + ) + } + } + if (p['@type'] === 'julich/siibra/patch-region/v0.0.1') { + if (p.target['name'] === region.name) { + mutateDeepMerge( + region, + p.payload + ) + } + } + }, + true + ) + } + return regions + }) + )) ) } diff --git a/src/util/siibraApiConstants/types.ts b/src/util/siibraApiConstants/types.ts index da56c848a3e80b39b6f709373096be1e27e297be..db9dab33fa992ee91c8870420172e081f60b53ed 100644 --- a/src/util/siibraApiConstants/types.ts +++ b/src/util/siibraApiConstants/types.ts @@ -18,6 +18,7 @@ interface IVolumeTypeDetail { 'nii': null 'neuroglancer/precomputed': { 'neuroglancer/precomputed': { + 'labelIndex': number 'transform': TNgTransform } }